// SetNewReplicaSetAnnotations sets new replica set's annotations appropriately by updating its revision and // copying required deployment annotations to it; it returns true if replica set's annotation is changed. func SetNewReplicaSetAnnotations(deployment *extensions.Deployment, newRS *extensions.ReplicaSet, newRevision string, exists bool) bool { // First, copy deployment's annotations (except for apply and revision annotations) annotationChanged := copyDeploymentAnnotationsToReplicaSet(deployment, newRS) // Then, update replica set's revision annotation if newRS.Annotations == nil { newRS.Annotations = make(map[string]string) } oldRevision, ok := newRS.Annotations[RevisionAnnotation] // The newRS's revision should be the greatest among all RSes. Usually, its revision number is newRevision (the max revision number // of all old RSes + 1). However, it's possible that some of the old RSes are deleted after the newRS revision being updated, and // newRevision becomes smaller than newRS's revision. We should only update newRS revision when it's smaller than newRevision. if oldRevision < newRevision { newRS.Annotations[RevisionAnnotation] = newRevision annotationChanged = true glog.V(4).Infof("Updating replica set %q revision to %s", newRS.Name, newRevision) } // If a revision annotation already existed and this replica set was updated with a new revision // then that means we are rolling back to this replica set. We need to preserve the old revisions // for historical information. if ok && annotationChanged { revisionHistoryAnnotation := newRS.Annotations[RevisionHistoryAnnotation] oldRevisions := strings.Split(revisionHistoryAnnotation, ",") if len(oldRevisions[0]) == 0 { newRS.Annotations[RevisionHistoryAnnotation] = oldRevision } else { oldRevisions = append(oldRevisions, oldRevision) newRS.Annotations[RevisionHistoryAnnotation] = strings.Join(oldRevisions, ",") } } // If the new replica set is about to be created, we need to add replica annotations to it. if !exists && SetReplicasAnnotations(newRS, deployment.Spec.Replicas, deployment.Spec.Replicas+MaxSurge(*deployment)) { annotationChanged = true } return annotationChanged }
// updateReplicaCount attempts to update the Status.Replicas of the given ReplicaSet, with a single GET/PUT retry. func updateReplicaCount(rsClient client.ReplicaSetInterface, rs extensions.ReplicaSet, numReplicas, numFullyLabeledReplicas int) (updateErr error) { // This is the steady state. It happens when the ReplicaSet doesn't have any expectations, since // we do a periodic relist every 30s. If the generations differ but the replicas are // the same, a caller might've resized to the same replica count. if int(rs.Status.Replicas) == numReplicas && int(rs.Status.FullyLabeledReplicas) == numFullyLabeledReplicas && rs.Generation == rs.Status.ObservedGeneration { return nil } // Save the generation number we acted on, otherwise we might wrongfully indicate // that we've seen a spec update when we retry. // TODO: This can clobber an update if we allow multiple agents to write to the // same status. generation := rs.Generation var getErr error for i, rs := 0, &rs; ; i++ { glog.V(4).Infof(fmt.Sprintf("Updating replica count for ReplicaSet: %s/%s, ", rs.Namespace, rs.Name) + fmt.Sprintf("replicas %d->%d (need %d), ", rs.Status.Replicas, numReplicas, rs.Spec.Replicas) + fmt.Sprintf("fullyLabeledReplicas %d->%d, ", rs.Status.FullyLabeledReplicas, numFullyLabeledReplicas) + fmt.Sprintf("sequence No: %v->%v", rs.Status.ObservedGeneration, generation)) rs.Status = extensions.ReplicaSetStatus{Replicas: int32(numReplicas), FullyLabeledReplicas: int32(numFullyLabeledReplicas), ObservedGeneration: generation} _, updateErr = rsClient.UpdateStatus(rs) if updateErr == nil || i >= statusUpdateRetries { return updateErr } // Update the ReplicaSet with the latest resource version for the next poll if rs, getErr = rsClient.Get(rs.Name); getErr != nil { // If the GET fails we can't trust status.Replicas anymore. This error // is bound to be more interesting than the update failure. return getErr } } }
func Convert_v1_ReplicationController_to_extensions_ReplicaSet(in *ReplicationController, out *extensions.ReplicaSet, s conversion.Scope) error { out.ObjectMeta = in.ObjectMeta if err := Convert_v1_ReplicationControllerSpec_to_extensions_ReplicaSetSpec(&in.Spec, &out.Spec, s); err != nil { return err } if err := Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus(&in.Status, &out.Status, s); err != nil { return err } return nil }
func TestWatchControllers(t *testing.T) { fakeWatch := watch.NewFake() client := &fake.Clientset{} client.AddWatchReactor("*", core.DefaultWatchReactor(fakeWatch, nil)) manager := NewReplicaSetControllerFromClient(client, controller.NoResyncPeriodFunc, BurstReplicas, 0) manager.podStoreSynced = alwaysReady var testRSSpec extensions.ReplicaSet received := make(chan string) // The update sent through the fakeWatcher should make its way into the workqueue, // and eventually into the syncHandler. The handler validates the received controller // and closes the received channel to indicate that the test can finish. manager.syncHandler = func(key string) error { obj, exists, err := manager.rsStore.Store.GetByKey(key) if !exists || err != nil { t.Errorf("Expected to find replica set under key %v", key) } rsSpec := *obj.(*extensions.ReplicaSet) if !api.Semantic.DeepDerivative(rsSpec, testRSSpec) { t.Errorf("Expected %#v, but got %#v", testRSSpec, rsSpec) } close(received) return nil } // Start only the ReplicaSet watcher and the workqueue, send a watch event, // and make sure it hits the sync method. stopCh := make(chan struct{}) defer close(stopCh) go manager.rsController.Run(stopCh) go wait.Until(manager.worker, 10*time.Millisecond, stopCh) testRSSpec.Name = "foo" fakeWatch.Add(&testRSSpec) select { case <-received: case <-time.After(wait.ForeverTestTimeout): t.Errorf("unexpected timeout from result channel") } }
// setNewReplicaSetAnnotations sets new replica set's annotations appropriately by updating its revision and // copying required deployment annotations to it; it returns true if replica set's annotation is changed. func setNewReplicaSetAnnotations(deployment *extensions.Deployment, rs *extensions.ReplicaSet, newRevision string) bool { // First, copy deployment's annotations annotationChanged := copyDeploymentAnnotationsToReplicaSet(deployment, rs) // Then, update replica set's revision annotation if rs.Annotations == nil { rs.Annotations = make(map[string]string) } if rs.Annotations[deploymentutil.RevisionAnnotation] != newRevision { rs.Annotations[deploymentutil.RevisionAnnotation] = newRevision annotationChanged = true glog.V(4).Infof("updating replica set %q's revision to %s - %+v\n", rs.Name, newRevision) } return annotationChanged }
// updateReplicaSetStatus attempts to update the Status.Replicas of the given ReplicaSet, with a single GET/PUT retry. func updateReplicaSetStatus(c unversionedextensions.ReplicaSetInterface, rs extensions.ReplicaSet, newStatus extensions.ReplicaSetStatus) (updateErr error) { // This is the steady state. It happens when the ReplicaSet doesn't have any expectations, since // we do a periodic relist every 30s. If the generations differ but the replicas are // the same, a caller might've resized to the same replica count. if rs.Status.Replicas == newStatus.Replicas && rs.Status.FullyLabeledReplicas == newStatus.FullyLabeledReplicas && rs.Status.ReadyReplicas == newStatus.ReadyReplicas && rs.Status.AvailableReplicas == newStatus.AvailableReplicas && rs.Generation == rs.Status.ObservedGeneration && reflect.DeepEqual(rs.Status.Conditions, newStatus.Conditions) { return nil } // deep copy to avoid mutation now. // TODO this method need some work. Retry on conflict probably, though I suspect this is stomping status to something it probably shouldn't copyObj, err := api.Scheme.DeepCopy(rs) if err != nil { return err } rs = copyObj.(extensions.ReplicaSet) // Save the generation number we acted on, otherwise we might wrongfully indicate // that we've seen a spec update when we retry. // TODO: This can clobber an update if we allow multiple agents to write to the // same status. newStatus.ObservedGeneration = rs.Generation var getErr error for i, rs := 0, &rs; ; i++ { glog.V(4).Infof(fmt.Sprintf("Updating replica count for ReplicaSet: %s/%s, ", rs.Namespace, rs.Name) + fmt.Sprintf("replicas %d->%d (need %d), ", rs.Status.Replicas, newStatus.Replicas, rs.Spec.Replicas) + fmt.Sprintf("fullyLabeledReplicas %d->%d, ", rs.Status.FullyLabeledReplicas, newStatus.FullyLabeledReplicas) + fmt.Sprintf("readyReplicas %d->%d, ", rs.Status.ReadyReplicas, newStatus.ReadyReplicas) + fmt.Sprintf("availableReplicas %d->%d, ", rs.Status.AvailableReplicas, newStatus.AvailableReplicas) + fmt.Sprintf("sequence No: %v->%v", rs.Status.ObservedGeneration, newStatus.ObservedGeneration)) rs.Status = newStatus _, updateErr = c.UpdateStatus(rs) if updateErr == nil || i >= statusUpdateRetries { return updateErr } // Update the ReplicaSet with the latest resource version for the next poll if rs, getErr = c.Get(rs.Name); getErr != nil { // If the GET fails we can't trust status.Replicas anymore. This error // is bound to be more interesting than the update failure. return getErr } } }
// SetReplicasAnnotations sets the desiredReplicas and maxReplicas into the annotations func SetReplicasAnnotations(rs *extensions.ReplicaSet, desiredReplicas, maxReplicas int32) bool { updated := false if rs.Annotations == nil { rs.Annotations = make(map[string]string) } desiredString := fmt.Sprintf("%d", desiredReplicas) if hasString := rs.Annotations[DesiredReplicasAnnotation]; hasString != desiredString { rs.Annotations[DesiredReplicasAnnotation] = desiredString updated = true } maxString := fmt.Sprintf("%d", maxReplicas) if hasString := rs.Annotations[MaxReplicasAnnotation]; hasString != maxString { rs.Annotations[MaxReplicasAnnotation] = maxString updated = true } return updated }
// copyDeploymentAnnotationsToReplicaSet copies deployment's annotations to replica set's annotations, // and returns true if replica set's annotation is changed. // Note that apply and revision annotations are not copied. func copyDeploymentAnnotationsToReplicaSet(deployment *extensions.Deployment, rs *extensions.ReplicaSet) bool { rsAnnotationsChanged := false if rs.Annotations == nil { rs.Annotations = make(map[string]string) } for k, v := range deployment.Annotations { // newRS revision is updated automatically in getNewReplicaSet, and the deployment's revision number is then updated // by copying its newRS revision number. We should not copy deployment's revision to its newRS, since the update of // deployment revision number may fail (revision becomes stale) and the revision number in newRS is more reliable. if skipCopyAnnotation(k) || rs.Annotations[k] == v { continue } rs.Annotations[k] = v rsAnnotationsChanged = true } return rsAnnotationsChanged }
// copyDeploymentAnnotationsToReplicaSet copies deployment's annotations to replica set's annotations, // and returns true if replica set's annotation is changed func copyDeploymentAnnotationsToReplicaSet(deployment *extensions.Deployment, rs *extensions.ReplicaSet) bool { rsAnnotationsChanged := false if rs.Annotations == nil { rs.Annotations = make(map[string]string) } for k, v := range deployment.Annotations { // Skip apply annotations // TODO: How to decide which annotations should / should not be copied? // See https://github.com/kubernetes/kubernetes/pull/20035#issuecomment-179558615 if k == kubectl.LastAppliedConfigAnnotation || rs.Annotations[k] == v { continue } rs.Annotations[k] = v rsAnnotationsChanged = true } return rsAnnotationsChanged }
// setNewReplicaSetAnnotations sets new replica set's annotations appropriately by updating its revision and // copying required deployment annotations to it; it returns true if replica set's annotation is changed. func setNewReplicaSetAnnotations(deployment *extensions.Deployment, newRS *extensions.ReplicaSet, newRevision string) bool { // First, copy deployment's annotations (except for apply and revision annotations) annotationChanged := copyDeploymentAnnotationsToReplicaSet(deployment, newRS) // Then, update replica set's revision annotation if newRS.Annotations == nil { newRS.Annotations = make(map[string]string) } // The newRS's revision should be the greatest among all RSes. Usually, its revision number is newRevision (the max revision number // of all old RSes + 1). However, it's possible that some of the old RSes are deleted after the newRS revision being updated, and // newRevision becomes smaller than newRS's revision. We should only update newRS revision when it's smaller than newRevision. if newRS.Annotations[deploymentutil.RevisionAnnotation] < newRevision { newRS.Annotations[deploymentutil.RevisionAnnotation] = newRevision annotationChanged = true glog.V(4).Infof("updating replica set %q's revision to %s - %+v\n", newRS.Name, newRevision, newRS) } return annotationChanged }
// copyDeploymentAnnotationsToReplicaSet copies deployment's annotations to replica set's annotations, // and returns true if replica set's annotation is changed. // Note that apply and revision annotations are not copied. func copyDeploymentAnnotationsToReplicaSet(deployment *extensions.Deployment, rs *extensions.ReplicaSet) bool { rsAnnotationsChanged := false if rs.Annotations == nil { rs.Annotations = make(map[string]string) } for k, v := range deployment.Annotations { // TODO: How to decide which annotations should / should not be copied? // See https://github.com/kubernetes/kubernetes/pull/20035#issuecomment-179558615 // Skip apply annotations and revision annotations. // newRS revision is updated automatically in getNewReplicaSet, and the deployment's revision number is then updated // by copying its newRS revision number. We should not copy deployment's revision to its newRS, since the update of // deployment revision number may fail (revision becomes stale) and the revision number in newRS is more reliable. if k == kubectl.LastAppliedConfigAnnotation || k == deploymentutil.RevisionAnnotation || rs.Annotations[k] == v { continue } rs.Annotations[k] = v rsAnnotationsChanged = true } return rsAnnotationsChanged }