// GetNewReplicaSetTemplate returns the desired PodTemplateSpec for the new ReplicaSet corresponding to the given ReplicaSet. func GetNewReplicaSetTemplate(deployment *extensions.Deployment) api.PodTemplateSpec { // newRS will have the same template as in deployment spec, plus a unique label in some cases. newRSTemplate := api.PodTemplateSpec{ ObjectMeta: deployment.Spec.Template.ObjectMeta, Spec: deployment.Spec.Template.Spec, } newRSTemplate.ObjectMeta.Labels = labelsutil.CloneAndAddLabel( deployment.Spec.Template.ObjectMeta.Labels, extensions.DefaultDeploymentUniqueLabelKey, podutil.GetPodTemplateSpecHash(newRSTemplate)) return newRSTemplate }
// Returns an RC that matches the intent of the given deployment. // It creates a new RC if required. func (dc *DeploymentController) getNewRC(deployment extensions.Deployment) (*api.ReplicationController, error) { existingNewRC, err := deploymentutil.GetNewRCFromList(deployment, dc.client, func(namespace string, options api.ListOptions) ([]api.ReplicationController, error) { return dc.rcStore.List() }) if err != nil || existingNewRC != nil { return existingNewRC, err } // Check the rc expectations of deployment before creating a new rc dKey, err := controller.KeyFunc(&deployment) if err != nil { return nil, fmt.Errorf("couldn't get key for deployment %#v: %v", deployment, err) } if !dc.rcExpectations.SatisfiedExpectations(dKey) { dc.enqueueDeployment(&deployment) return nil, fmt.Errorf("RC expectations not met yet before getting new RC\n") } // new RC does not exist, create one. namespace := deployment.ObjectMeta.Namespace podTemplateSpecHash := podutil.GetPodTemplateSpecHash(deployment.Spec.Template) newRCTemplate := deploymentutil.GetNewRCTemplate(deployment) // Add podTemplateHash label to selector. newRCSelector := labelsutil.CloneAndAddLabel(deployment.Spec.Selector, deployment.Spec.UniqueLabelKey, podTemplateSpecHash) // Set RC expectations (1 rc should be created) dKey, err = controller.KeyFunc(&deployment) if err != nil { return nil, fmt.Errorf("couldn't get key for deployment controller %#v: %v", deployment, err) } dc.rcExpectations.ExpectCreations(dKey, 1) // Create new RC newRC := api.ReplicationController{ ObjectMeta: api.ObjectMeta{ GenerateName: deployment.Name + "-", Namespace: namespace, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: newRCSelector, Template: &newRCTemplate, }, } createdRC, err := dc.client.ReplicationControllers(namespace).Create(&newRC) if err != nil { dc.rcExpectations.DeleteExpectations(dKey) return nil, fmt.Errorf("error creating replication controller: %v", err) } return createdRC, nil }
// Returns a replica set that matches the intent of the given deployment. Returns nil if the new replica set doesn't exist yet. // 1. Get existing new RS (the RS that the given deployment targets, whose pod template is the same as deployment's). // 2. If there's existing new RS, update its revision number if it's smaller than (maxOldRevision + 1), where maxOldRevision is the max revision number among all old RSes. // 3. If there's no existing new RS and createIfNotExisted is true, create one with appropriate revision number (maxOldRevision + 1) and replicas. // Note that the pod-template-hash will be added to adopted RSes and pods. func (dc *DeploymentController) getNewReplicaSet(deployment *extensions.Deployment, rsList, oldRSs []*extensions.ReplicaSet, createIfNotExisted bool) (*extensions.ReplicaSet, error) { existingNewRS, err := deploymentutil.FindNewReplicaSet(deployment, rsList) if err != nil { return nil, err } // Calculate the max revision number among all old RSes maxOldRevision := deploymentutil.MaxRevision(oldRSs) // Calculate revision number for this new replica set newRevision := strconv.FormatInt(maxOldRevision+1, 10) // Latest replica set exists. We need to sync its annotations (includes copying all but // annotationsToSkip from the parent deployment, and update revision, desiredReplicas, // and maxReplicas) and also update the revision annotation in the deployment with the // latest revision. if existingNewRS != nil { objCopy, err := api.Scheme.Copy(existingNewRS) if err != nil { return nil, err } rsCopy := objCopy.(*extensions.ReplicaSet) // Set existing new replica set's annotation annotationsUpdated := deploymentutil.SetNewReplicaSetAnnotations(deployment, rsCopy, newRevision, true) minReadySecondsNeedsUpdate := rsCopy.Spec.MinReadySeconds != deployment.Spec.MinReadySeconds if annotationsUpdated || minReadySecondsNeedsUpdate { rsCopy.Spec.MinReadySeconds = deployment.Spec.MinReadySeconds return dc.client.Extensions().ReplicaSets(rsCopy.ObjectMeta.Namespace).Update(rsCopy) } updateConditions := deploymentutil.SetDeploymentRevision(deployment, newRevision) // If no other Progressing condition has been recorded and we need to estimate the progress // of this deployment then it is likely that old users started caring about progress. In that // case we need to take into account the first time we noticed their new replica set. cond := deploymentutil.GetDeploymentCondition(deployment.Status, extensions.DeploymentProgressing) if deployment.Spec.ProgressDeadlineSeconds != nil && cond == nil { msg := fmt.Sprintf("Found new replica set %q", rsCopy.Name) condition := deploymentutil.NewDeploymentCondition(extensions.DeploymentProgressing, v1.ConditionTrue, deploymentutil.FoundNewRSReason, msg) deploymentutil.SetDeploymentCondition(&deployment.Status, *condition) updateConditions = true } if updateConditions { if deployment, err = dc.client.Extensions().Deployments(deployment.Namespace).UpdateStatus(deployment); err != nil { return nil, err } } return rsCopy, nil } if !createIfNotExisted { return nil, nil } // new ReplicaSet does not exist, create one. namespace := deployment.Namespace podTemplateSpecHash := fmt.Sprintf("%d", deploymentutil.GetPodTemplateSpecHash(deployment.Spec.Template)) newRSTemplate := deploymentutil.GetNewReplicaSetTemplate(deployment) newRSTemplate.Labels = labelsutil.CloneAndAddLabel(deployment.Spec.Template.Labels, extensions.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash) // Add podTemplateHash label to selector. newRSSelector := labelsutil.CloneSelectorAndAddLabel(deployment.Spec.Selector, extensions.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash) // Create new ReplicaSet newRS := extensions.ReplicaSet{ ObjectMeta: metav1.ObjectMeta{ // Make the name deterministic, to ensure idempotence Name: deployment.Name + "-" + podTemplateSpecHash, Namespace: namespace, }, Spec: extensions.ReplicaSetSpec{ Replicas: func(i int32) *int32 { return &i }(0), MinReadySeconds: deployment.Spec.MinReadySeconds, Selector: newRSSelector, Template: newRSTemplate, }, } var trueVar = true controllerRef := &metav1.OwnerReference{ APIVersion: getDeploymentKind().GroupVersion().String(), Kind: getDeploymentKind().Kind, Name: deployment.Name, UID: deployment.UID, Controller: &trueVar, } newRS.OwnerReferences = append(newRS.OwnerReferences, *controllerRef) allRSs := append(oldRSs, &newRS) newReplicasCount, err := deploymentutil.NewRSNewReplicas(deployment, allRSs, &newRS) if err != nil { return nil, err } *(newRS.Spec.Replicas) = newReplicasCount // Set new replica set's annotation deploymentutil.SetNewReplicaSetAnnotations(deployment, &newRS, newRevision, false) createdRS, err := dc.client.Extensions().ReplicaSets(namespace).Create(&newRS) switch { // We may end up hitting this due to a slow cache or a fast resync of the deployment. // TODO: Restore once https://github.com/kubernetes/kubernetes/issues/29735 is fixed // ie. we start using a new hashing algorithm. case errors.IsAlreadyExists(err): return nil, err // return dc.rsLister.ReplicaSets(namespace).Get(newRS.Name) case err != nil: msg := fmt.Sprintf("Failed to create new replica set %q: %v", newRS.Name, err) if deployment.Spec.ProgressDeadlineSeconds != nil { cond := deploymentutil.NewDeploymentCondition(extensions.DeploymentProgressing, v1.ConditionFalse, deploymentutil.FailedRSCreateReason, msg) deploymentutil.SetDeploymentCondition(&deployment.Status, *cond) // We don't really care about this error at this point, since we have a bigger issue to report. // TODO: Update the rest of the Deployment status, too. We may need to do this every time we // error out in all other places in the controller so that we let users know that their deployments // have been noticed by the controller, albeit with errors. // TODO: Identify which errors are permanent and switch DeploymentIsFailed to take into account // these reasons as well. Related issue: https://github.com/kubernetes/kubernetes/issues/18568 _, _ = dc.client.Extensions().Deployments(deployment.ObjectMeta.Namespace).UpdateStatus(deployment) } dc.eventRecorder.Eventf(deployment, v1.EventTypeWarning, deploymentutil.FailedRSCreateReason, msg) return nil, err } if newReplicasCount > 0 { dc.eventRecorder.Eventf(deployment, v1.EventTypeNormal, "ScalingReplicaSet", "Scaled up replica set %s to %d", createdRS.Name, newReplicasCount) } deploymentutil.SetDeploymentRevision(deployment, newRevision) if deployment.Spec.ProgressDeadlineSeconds != nil { msg := fmt.Sprintf("Created new replica set %q", createdRS.Name) condition := deploymentutil.NewDeploymentCondition(extensions.DeploymentProgressing, v1.ConditionTrue, deploymentutil.NewReplicaSetReason, msg) deploymentutil.SetDeploymentCondition(&deployment.Status, *condition) } _, err = dc.client.Extensions().Deployments(deployment.Namespace).UpdateStatus(deployment) return createdRS, err }
// Returns an RC that matches the intent of the given deployment. // It creates a new RC if required. // The revision of the new RC will be updated to maxOldRevision + 1 func (dc *DeploymentController) getNewRC(deployment extensions.Deployment, maxOldRevision int) (*api.ReplicationController, error) { // Calculate revision number for this new RC newRevision := strconv.Itoa(maxOldRevision + 1) existingNewRC, err := deploymentutil.GetNewRCFromList(deployment, dc.client, func(namespace string, options api.ListOptions) ([]api.ReplicationController, error) { return dc.rcStore.ReplicationControllers(namespace).List(options.LabelSelector) }) if err != nil { return nil, err } else if existingNewRC != nil { if existingNewRC.Annotations == nil { existingNewRC.Annotations = make(map[string]string) } if existingNewRC.Annotations[deploymentutil.RevisionAnnotation] != newRevision { existingNewRC.Annotations[deploymentutil.RevisionAnnotation] = newRevision glog.V(4).Infof("update existingNewRC's revision to %s - %+v\n", newRevision, existingNewRC) return dc.client.ReplicationControllers(deployment.ObjectMeta.Namespace).Update(existingNewRC) } return existingNewRC, nil } // Check the rc expectations of deployment before creating a new rc dKey, err := controller.KeyFunc(&deployment) if err != nil { return nil, fmt.Errorf("couldn't get key for deployment %#v: %v", deployment, err) } if !dc.rcExpectations.SatisfiedExpectations(dKey) { dc.enqueueDeployment(&deployment) return nil, fmt.Errorf("RC expectations not met yet before getting new RC\n") } // new RC does not exist, create one. namespace := deployment.ObjectMeta.Namespace podTemplateSpecHash := podutil.GetPodTemplateSpecHash(deployment.Spec.Template) newRCTemplate := deploymentutil.GetNewRCTemplate(deployment) // Add podTemplateHash label to selector. newRCSelector := labelsutil.CloneAndAddLabel(deployment.Spec.Selector, deployment.Spec.UniqueLabelKey, podTemplateSpecHash) // Set RC expectations (1 rc should be created) dKey, err = controller.KeyFunc(&deployment) if err != nil { return nil, fmt.Errorf("couldn't get key for deployment controller %#v: %v", deployment, err) } dc.rcExpectations.ExpectCreations(dKey, 1) // Create new RC newRC := api.ReplicationController{ ObjectMeta: api.ObjectMeta{ GenerateName: deployment.Name + "-", Namespace: namespace, Annotations: map[string]string{deploymentutil.RevisionAnnotation: newRevision}, }, Spec: api.ReplicationControllerSpec{ Replicas: 0, Selector: newRCSelector, Template: &newRCTemplate, }, } createdRC, err := dc.client.ReplicationControllers(namespace).Create(&newRC) if err != nil { dc.rcExpectations.DeleteExpectations(dKey) return nil, fmt.Errorf("error creating replication controller: %v", err) } if err = dc.updateDeploymentRevision(deployment, newRevision); err != nil { return createdRC, err } return createdRC, nil }