diff --git a/examples/extensions/deployment.yaml b/examples/extensions/deployment.yaml index 340103836ac..98e614ceb2e 100644 --- a/examples/extensions/deployment.yaml +++ b/examples/extensions/deployment.yaml @@ -7,7 +7,8 @@ metadata: spec: replicas: 3 selector: - name: nginx + matchLabels: + name: nginx template: metadata: labels: diff --git a/pkg/apis/extensions/helpers.go b/pkg/apis/extensions/helpers.go index 434befd7403..19068de660e 100644 --- a/pkg/apis/extensions/helpers.go +++ b/pkg/apis/extensions/helpers.go @@ -18,10 +18,15 @@ package extensions import ( "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" ) // ScaleFromDeployment returns a scale subresource for a deployment. -func ScaleFromDeployment(deployment *Deployment) *Scale { +func ScaleFromDeployment(deployment *Deployment) (*Scale, error) { + selector, err := unversioned.LabelSelectorAsSelector(deployment.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("failed to convert label selector to selector: %v", err) + } return &Scale{ ObjectMeta: api.ObjectMeta{ Name: deployment.Name, @@ -33,7 +38,7 @@ func ScaleFromDeployment(deployment *Deployment) *Scale { }, Status: ScaleStatus{ Replicas: deployment.Status.Replicas, - Selector: deployment.Spec.Selector, + Selector: selector.String(), }, - } + }, nil } diff --git a/pkg/apis/extensions/types.go b/pkg/apis/extensions/types.go index 1289b9341eb..9f000d943bf 100644 --- a/pkg/apis/extensions/types.go +++ b/pkg/apis/extensions/types.go @@ -233,9 +233,9 @@ type DeploymentSpec struct { // zero and not specified. Defaults to 1. Replicas int `json:"replicas,omitempty"` - // Label selector for pods. Existing ReplicationControllers whose pods are + // Label selector for pods. Existing ReplicaSets whose pods are // selected by this will be the ones affected by this deployment. - Selector map[string]string `json:"selector,omitempty"` + Selector *LabelSelector `json:"selector,omitempty"` // Template describes the pods that will be created. Template api.PodTemplateSpec `json:"template"` diff --git a/pkg/apis/extensions/v1beta1/conversion.go b/pkg/apis/extensions/v1beta1/conversion.go index f578c17ccd2..def484fb2b8 100644 --- a/pkg/apis/extensions/v1beta1/conversion.go +++ b/pkg/apis/extensions/v1beta1/conversion.go @@ -104,9 +104,9 @@ func Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec(in *extensions. out.Replicas = new(int32) *out.Replicas = int32(in.Replicas) if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val + out.Selector = new(LabelSelector) + if err := Convert_extensions_LabelSelector_To_v1beta1_LabelSelector(in.Selector, out.Selector, s); err != nil { + return err } } else { out.Selector = nil @@ -139,10 +139,11 @@ func Convert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec(in *DeploymentS if in.Replicas != nil { out.Replicas = int(*in.Replicas) } + if in.Selector != nil { - out.Selector = make(map[string]string) - for key, val := range in.Selector { - out.Selector[key] = val + out.Selector = new(extensions.LabelSelector) + if err := Convert_v1beta1_LabelSelector_To_extensions_LabelSelector(in.Selector, out.Selector, s); err != nil { + return err } } else { out.Selector = nil diff --git a/pkg/apis/extensions/v1beta1/defaults.go b/pkg/apis/extensions/v1beta1/defaults.go index bf84a78f918..4d3b0e2ee9d 100644 --- a/pkg/apis/extensions/v1beta1/defaults.go +++ b/pkg/apis/extensions/v1beta1/defaults.go @@ -67,8 +67,8 @@ func addDefaultingFuncs(scheme *runtime.Scheme) { labels := obj.Spec.Template.Labels if labels != nil { - if len(obj.Spec.Selector) == 0 { - obj.Spec.Selector = labels + if obj.Spec.Selector == nil { + obj.Spec.Selector = &LabelSelector{MatchLabels: labels} } if len(obj.Labels) == 0 { obj.Labels = labels diff --git a/pkg/apis/extensions/v1beta1/types.go b/pkg/apis/extensions/v1beta1/types.go index 52ac7c6e328..52bed45cb9c 100644 --- a/pkg/apis/extensions/v1beta1/types.go +++ b/pkg/apis/extensions/v1beta1/types.go @@ -220,7 +220,7 @@ type DeploymentSpec struct { // Label selector for pods. Existing ReplicationControllers whose pods are // selected by this will be the ones affected by this deployment. - Selector map[string]string `json:"selector,omitempty"` + Selector *LabelSelector `json:"selector,omitempty"` // Template describes the pods that will be created. Template v1.PodTemplateSpec `json:"template"` diff --git a/pkg/apis/extensions/validation/validation.go b/pkg/apis/extensions/validation/validation.go index 9ddc3613fcf..bc67ecbbb1a 100644 --- a/pkg/apis/extensions/validation/validation.go +++ b/pkg/apis/extensions/validation/validation.go @@ -346,9 +346,24 @@ func ValidateRollback(rollback *extensions.RollbackConfig, fldPath *field.Path) // Validates given deployment spec. func ValidateDeploymentSpec(spec *extensions.DeploymentSpec, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - allErrs = append(allErrs, apivalidation.ValidateNonEmptySelector(spec.Selector, fldPath.Child("selector"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.Replicas), fldPath.Child("replicas"))...) - allErrs = append(allErrs, apivalidation.ValidatePodTemplateSpecForRC(&spec.Template, spec.Selector, spec.Replicas, fldPath.Child("template"))...) + + if spec.Selector == nil { + allErrs = append(allErrs, field.Required(fldPath.Child("selector"), "")) + } else { + allErrs = append(allErrs, ValidateLabelSelector(spec.Selector, fldPath.Child("selector"))...) + if len(spec.Selector.MatchLabels)+len(spec.Selector.MatchExpressions) == 0 { + allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "empty selector is not valid for deployment.")) + } + } + + selector, err := extensions.LabelSelectorAsSelector(spec.Selector) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("selector"), spec.Selector, "failed to convert LabelSelector to Selector.")) + } else { + allErrs = append(allErrs, ValidatePodTemplateSpecForReplicaSet(&spec.Template, selector, spec.Replicas, fldPath.Child("template"))...) + } + allErrs = append(allErrs, ValidateDeploymentStrategy(&spec.Strategy, fldPath.Child("strategy"))...) allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(spec.MinReadySeconds), fldPath.Child("minReadySeconds"))...) if spec.RevisionHistoryLimit != nil { diff --git a/pkg/apis/extensions/validation/validation_test.go b/pkg/apis/extensions/validation/validation_test.go index 02e9c983426..0b687513693 100644 --- a/pkg/apis/extensions/validation/validation_test.go +++ b/pkg/apis/extensions/validation/validation_test.go @@ -952,8 +952,10 @@ func validDeployment() *extensions.Deployment { Namespace: api.NamespaceDefault, }, Spec: extensions.DeploymentSpec{ - Selector: map[string]string{ - "name": "abc", + Selector: &extensions.LabelSelector{ + MatchLabels: map[string]string{ + "name": "abc", + }, }, Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ @@ -1000,8 +1002,10 @@ func TestValidateDeployment(t *testing.T) { } // selector should match the labels in pod template. invalidSelectorDeployment := validDeployment() - invalidSelectorDeployment.Spec.Selector = map[string]string{ - "name": "def", + invalidSelectorDeployment.Spec.Selector = &extensions.LabelSelector{ + MatchLabels: map[string]string{ + "name": "def", + }, } errorCases["`selector` does not match template `labels`"] = invalidSelectorDeployment diff --git a/pkg/client/cache/listers.go b/pkg/client/cache/listers.go index e9ce804353d..42e0ecf4235 100644 --- a/pkg/client/cache/listers.go +++ b/pkg/client/cache/listers.go @@ -236,33 +236,34 @@ func (s *StoreToDeploymentLister) List() (deployments []extensions.Deployment, e return deployments, nil } -// GetDeploymentsForRC returns a list of deployments managing a replication controller. Returns an error only if no matching deployments are found. -func (s *StoreToDeploymentLister) GetDeploymentsForRC(rc *api.ReplicationController) (deployments []extensions.Deployment, err error) { - var selector labels.Selector +// GetDeploymentsForReplicaSet returns a list of deployments managing a replica set. Returns an error only if no matching deployments are found. +func (s *StoreToDeploymentLister) GetDeploymentsForReplicaSet(rs *extensions.ReplicaSet) (deployments []extensions.Deployment, err error) { var d extensions.Deployment - if len(rc.Labels) == 0 { - err = fmt.Errorf("no deployments found for replication controller %v because it has no labels", rc.Name) + if len(rs.Labels) == 0 { + err = fmt.Errorf("no deployments found for ReplicaSet %v because it has no labels", rs.Name) return } // TODO: MODIFY THIS METHOD so that it checks for the podTemplateSpecHash label for _, m := range s.Store.List() { d = *m.(*extensions.Deployment) - if d.Namespace != rc.Namespace { + if d.Namespace != rs.Namespace { continue } - labelSet := labels.Set(d.Spec.Selector) - selector = labels.Set(d.Spec.Selector).AsSelector() + selector, err := extensions.LabelSelectorAsSelector(rs.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("failed to convert LabelSelector to Selector: %v", err) + } // If a deployment with a nil or empty selector creeps in, it should match nothing, not everything. - if labelSet.AsSelector().Empty() || !selector.Matches(labels.Set(rc.Labels)) { + if selector.Empty() || !selector.Matches(labels.Set(rs.Labels)) { continue } deployments = append(deployments, d) } if len(deployments) == 0 { - err = fmt.Errorf("could not find deployments set for replication controller %s in namespace %s with labels: %v", rc.Name, rc.Namespace, rc.Labels) + err = fmt.Errorf("could not find deployments set for ReplicaSet %s in namespace %s with labels: %v", rs.Name, rs.Namespace, rs.Labels) } return } diff --git a/pkg/controller/deployment/deployment_controller.go b/pkg/controller/deployment/deployment_controller.go index a44e70acc61..a36bff98e39 100644 --- a/pkg/controller/deployment/deployment_controller.go +++ b/pkg/controller/deployment/deployment_controller.go @@ -52,13 +52,13 @@ const ( // of all deployments that have fulfilled their expectations at least this often. // This recomputation happens based on contents in the local caches. FullDeploymentResyncPeriod = 30 * time.Second - // We must avoid creating new rc until the rc store has synced. If it hasn't synced, to + // We must avoid creating new replica set until the replica set store has synced. If it hasn't synced, to // avoid a hot loop, we'll wait this long between checks. - RcStoreSyncedPollPeriod = 100 * time.Millisecond + RSStoreSyncedPollPeriod = 100 * time.Millisecond ) // DeploymentController is responsible for synchronizing Deployment objects stored -// in the system with actual running rcs and pods. +// in the system with actual running replica sets and pods. type DeploymentController struct { client clientset.Interface eventRecorder record.EventRecorder @@ -70,13 +70,13 @@ type DeploymentController struct { dStore cache.StoreToDeploymentLister // Watches changes to all deployments dController *framework.Controller - // A store of replication controllers, populated by the rcController - rcStore cache.StoreToReplicationControllerLister - // Watches changes to all replication controllers - rcController *framework.Controller - // rcStoreSynced returns true if the RC store has been synced at least once. + // A store of ReplicaSets, populated by the rsController + rsStore cache.StoreToReplicaSetLister + // Watches changes to all ReplicaSets + rsController *framework.Controller + // rsStoreSynced returns true if the ReplicaSet store has been synced at least once. // Added as a member to the struct to allow injection for testing. - rcStoreSynced func() bool + rsStoreSynced func() bool // A store of pods, populated by the podController podStore cache.StoreToPodLister // Watches changes to all pods @@ -88,9 +88,9 @@ type DeploymentController struct { // A TTLCache of pod creates/deletes each deployment expects to see podExpectations controller.ControllerExpectationsInterface - // A TTLCache of rc creates/deletes each deployment expects to see - // TODO: make expectation model understand (rc) updates (besides adds and deletes) - rcExpectations controller.ControllerExpectationsInterface + // A TTLCache of ReplicaSet creates/deletes each deployment it expects to see + // TODO: make expectation model understand (ReplicaSet) updates (besides adds and deletes) + rsExpectations controller.ControllerExpectationsInterface // Deployments that need to be synced queue *workqueue.Type @@ -108,7 +108,7 @@ func NewDeploymentController(client clientset.Interface, resyncPeriod controller eventRecorder: eventBroadcaster.NewRecorder(api.EventSource{Component: "deployment-controller"}), queue: workqueue.New(), podExpectations: controller.NewControllerExpectations(), - rcExpectations: controller.NewControllerExpectations(), + rsExpectations: controller.NewControllerExpectations(), } dc.dStore.Store, dc.dController = framework.NewInformer( @@ -143,21 +143,21 @@ func NewDeploymentController(client clientset.Interface, resyncPeriod controller }, ) - dc.rcStore.Store, dc.rcController = framework.NewInformer( + dc.rsStore.Store, dc.rsController = framework.NewInformer( &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { - return dc.client.Core().ReplicationControllers(api.NamespaceAll).List(options) + return dc.client.Extensions().ReplicaSets(api.NamespaceAll).List(options) }, WatchFunc: func(options api.ListOptions) (watch.Interface, error) { - return dc.client.Core().ReplicationControllers(api.NamespaceAll).Watch(options) + return dc.client.Extensions().ReplicaSets(api.NamespaceAll).Watch(options) }, }, - &api.ReplicationController{}, + &extensions.ReplicaSet{}, resyncPeriod(), framework.ResourceEventHandlerFuncs{ - AddFunc: dc.addRC, - UpdateFunc: dc.updateRC, - DeleteFunc: dc.deleteRC, + AddFunc: dc.addReplicaSet, + UpdateFunc: dc.updateReplicaSet, + DeleteFunc: dc.deleteReplicaSet, }, ) @@ -181,7 +181,7 @@ func NewDeploymentController(client clientset.Interface, resyncPeriod controller ) dc.syncHandler = dc.syncDeployment - dc.rcStoreSynced = dc.rcController.HasSynced + dc.rsStoreSynced = dc.rsController.HasSynced dc.podStoreSynced = dc.podController.HasSynced return dc } @@ -190,7 +190,7 @@ func NewDeploymentController(client clientset.Interface, resyncPeriod controller func (dc *DeploymentController) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() go dc.dController.Run(stopCh) - go dc.rcController.Run(stopCh) + go dc.rsController.Run(stopCh) go dc.podController.Run(stopCh) for i := 0; i < workers; i++ { go wait.Until(dc.worker, time.Second, stopCh) @@ -200,30 +200,30 @@ func (dc *DeploymentController) Run(workers int, stopCh <-chan struct{}) { dc.queue.ShutDown() } -// addRC enqueues the deployment that manages an RC when the RC is created. -func (dc *DeploymentController) addRC(obj interface{}) { - rc := obj.(*api.ReplicationController) - glog.V(4).Infof("Replication controller %s added.", rc.Name) - if d := dc.getDeploymentForRC(rc); d != nil { +// addReplicaSet enqueues the deployment that manages a ReplicaSet when the ReplicaSet is created. +func (dc *DeploymentController) addReplicaSet(obj interface{}) { + rs := obj.(*extensions.ReplicaSet) + glog.V(4).Infof("ReplicaSet %s added.", rs.Name) + if d := dc.getDeploymentForReplicaSet(rs); d != nil { dKey, err := controller.KeyFunc(d) if err != nil { glog.Errorf("Couldn't get key for deployment controller %#v: %v", d, err) return } - dc.rcExpectations.CreationObserved(dKey) + dc.rsExpectations.CreationObserved(dKey) dc.enqueueDeployment(d) } } -// getDeploymentForRC returns the deployment managing the given RC. -// TODO: Surface that we are ignoring multiple deployments for a given controller. -func (dc *DeploymentController) getDeploymentForRC(rc *api.ReplicationController) *extensions.Deployment { - deployments, err := dc.dStore.GetDeploymentsForRC(rc) +// getDeploymentForReplicaSet returns the deployment managing the given ReplicaSet. +// TODO: Surface that we are ignoring multiple deployments for a given ReplicaSet. +func (dc *DeploymentController) getDeploymentForReplicaSet(rs *extensions.ReplicaSet) *extensions.Deployment { + deployments, err := dc.dStore.GetDeploymentsForReplicaSet(rs) if err != nil || len(deployments) == 0 { - glog.V(4).Infof("Error: %v. No deployment found for replication controller %v, deployment controller will avoid syncing.", err, rc.Name) + glog.V(4).Infof("Error: %v. No deployment found for ReplicaSet %v, deployment controller will avoid syncing.", err, rs.Name) return nil } - // Because all RC's belonging to a deployment should have a unique label key, + // Because all ReplicaSet's belonging to a deployment should have a unique label key, // there should never be more than one deployment returned by the above method. // If that happens we should probably dynamically repair the situation by ultimately // trying to clean up one of the controllers, for now we just return one of the two, @@ -231,68 +231,69 @@ func (dc *DeploymentController) getDeploymentForRC(rc *api.ReplicationController return &deployments[0] } -// updateRC figures out what deployment(s) manage an RC when the RC is updated and -// wake them up. If the anything of the RCs have changed, we need to awaken both -// the old and new deployments. old and cur must be *api.ReplicationController types. -func (dc *DeploymentController) updateRC(old, cur interface{}) { +// updateReplicaSet figures out what deployment(s) manage a ReplicaSet when the ReplicaSet +// is updated and wake them up. If the anything of the ReplicaSets have changed, we need to +// awaken both the old and new deployments. old and cur must be *extensions.ReplicaSet +// types. +func (dc *DeploymentController) updateReplicaSet(old, cur interface{}) { if api.Semantic.DeepEqual(old, cur) { // A periodic relist will send update events for all known controllers. return } // TODO: Write a unittest for this case - curRC := cur.(*api.ReplicationController) - glog.V(4).Infof("Replication controller %s updated.", curRC.Name) - if d := dc.getDeploymentForRC(curRC); d != nil { + curRS := cur.(*extensions.ReplicaSet) + glog.V(4).Infof("ReplicaSet %s updated.", curRS.Name) + if d := dc.getDeploymentForReplicaSet(curRS); d != nil { dc.enqueueDeployment(d) } // A number of things could affect the old deployment: labels changing, // pod template changing, etc. - oldRC := old.(*api.ReplicationController) - if !api.Semantic.DeepEqual(oldRC, curRC) { - if oldD := dc.getDeploymentForRC(oldRC); oldD != nil { + oldRS := old.(*extensions.ReplicaSet) + if !api.Semantic.DeepEqual(oldRS, curRS) { + if oldD := dc.getDeploymentForReplicaSet(oldRS); oldD != nil { dc.enqueueDeployment(oldD) } } } -// deleteRC enqueues the deployment that manages an RC when the RC is deleted. -// obj could be an *api.ReplicationController, or a DeletionFinalStateUnknown -// marker item. -func (dc *DeploymentController) deleteRC(obj interface{}) { - rc, ok := obj.(*api.ReplicationController) +// deleteReplicaSet enqueues the deployment that manages a ReplicaSet when +// the ReplicaSet is deleted. obj could be an *extensions.ReplicaSet, or +// a DeletionFinalStateUnknown marker item. +func (dc *DeploymentController) deleteReplicaSet(obj interface{}) { + rs, ok := obj.(*extensions.ReplicaSet) // When a delete is dropped, the relist will notice a pod in the store not // in the list, leading to the insertion of a tombstone object which contains - // the deleted key/value. Note that this value might be stale. If the RC + // the deleted key/value. Note that this value might be stale. If the ReplicaSet // changed labels the new deployment will not be woken up till the periodic resync. if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { - glog.Errorf("Couldn't get object from tombstone %+v, could take up to %v before a deployment recreates/updates controllers", obj, FullDeploymentResyncPeriod) + glog.Errorf("Couldn't get object from tombstone %+v, could take up to %v before a deployment recreates/updates replicasets", obj, FullDeploymentResyncPeriod) return } - rc, ok = tombstone.Obj.(*api.ReplicationController) + rs, ok = tombstone.Obj.(*extensions.ReplicaSet) if !ok { - glog.Errorf("Tombstone contained object that is not an rc %+v, could take up to %v before a deployment recreates/updates controllers", obj, FullDeploymentResyncPeriod) + glog.Errorf("Tombstone contained object that is not a ReplicaSet %+v, could take up to %v before a deployment recreates/updates replicasets", obj, FullDeploymentResyncPeriod) return } } - glog.V(4).Infof("Replication controller %s deleted.", rc.Name) - if d := dc.getDeploymentForRC(rc); d != nil { + glog.V(4).Infof("ReplicaSet %s deleted.", rs.Name) + if d := dc.getDeploymentForReplicaSet(rs); d != nil { dc.enqueueDeployment(d) } } -// getDeploymentForPod returns the deployment managing the RC that manages the given Pod. +// getDeploymentForPod returns the deployment managing the ReplicaSet that manages the given Pod. // TODO: Surface that we are ignoring multiple deployments for a given Pod. func (dc *DeploymentController) getDeploymentForPod(pod *api.Pod) *extensions.Deployment { - rcs, err := dc.rcStore.GetPodControllers(pod) + rss, err := dc.rsStore.GetPodReplicaSets(pod) if err != nil { - glog.V(4).Infof("Error: %v. No replication controllers found for pod %v, deployment controller will avoid syncing.", err, pod.Name) + glog.V(4).Infof("Error: %v. No ReplicaSets found for pod %v, deployment controller will avoid syncing.", err, pod.Name) return nil } - for _, rc := range rcs { - deployments, err := dc.dStore.GetDeploymentsForRC(&rc) + for _, rs := range rss { + deployments, err := dc.dStore.GetDeploymentsForReplicaSet(&rs) if err == nil && len(deployments) > 0 { return &deployments[0] } @@ -301,7 +302,7 @@ func (dc *DeploymentController) getDeploymentForPod(pod *api.Pod) *extensions.De return nil } -// updatePod figures out what deployment(s) manage the RC that manages the Pod when the Pod +// updatePod figures out what deployment(s) manage the ReplicaSet that manages the Pod when the Pod // is updated and wake them up. If anything of the Pods have changed, we need to awaken both // the old and new deployments. old and cur must be *api.Pod types. func (dc *DeploymentController) updatePod(old, cur interface{}) { @@ -328,16 +329,17 @@ func (dc *DeploymentController) deletePod(obj interface{}) { // When a delete is dropped, the relist will notice a pod in the store not // in the list, leading to the insertion of a tombstone object which contains // the deleted key/value. Note that this value might be stale. If the pod - // changed labels the new rc will not be woken up till the periodic resync. + // changed labels the new ReplicaSet will not be woken up till the periodic + // resync. if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { - glog.Errorf("Couldn't get object from tombstone %+v, could take up to %v before a controller recreates a replica", obj, controller.ExpectationsTimeout) + glog.Errorf("Couldn't get object from tombstone %+v, could take up to %v before a ReplicaSet recreates a replica", obj, controller.ExpectationsTimeout) return } pod, ok = tombstone.Obj.(*api.Pod) if !ok { - glog.Errorf("Tombstone contained object that is not a pod %+v, could take up to %v before controller recreates a replica", obj, controller.ExpectationsTimeout) + glog.Errorf("Tombstone contained object that is not a pod %+v, could take up to %v before ReplicaSet recreates a replica", obj, controller.ExpectationsTimeout) return } } @@ -361,11 +363,11 @@ func (dc *DeploymentController) enqueueDeployment(obj interface{}) { } // TODO: Handle overlapping deployments better. Either disallow them at admission time or - // deterministically avoid syncing deployments that fight over RC's. Currently, we only - // ensure that the same deployment is synced for a given RC. When we periodically relist - // all deployments there will still be some RC instability. One way to handle this is - // by querying the store for all deployments that this deployment overlaps, as well as all - // deployments that overlap this deployments, and sorting them. + // deterministically avoid syncing deployments that fight over ReplicaSet's. Currently, we + // only ensure that the same deployment is synced for a given ReplicaSet. When we + // periodically relist all deployments there will still be some ReplicaSet instability. One + // way to handle this is by querying the store for all deployments that this deployment + // overlaps, as well as all deployments that overlap this deployments, and sorting them. dc.queue.Add(key) } @@ -404,14 +406,14 @@ func (dc *DeploymentController) syncDeployment(key string) error { if !exists { glog.Infof("Deployment has been deleted %v", key) dc.podExpectations.DeleteExpectations(key) - dc.rcExpectations.DeleteExpectations(key) + dc.rsExpectations.DeleteExpectations(key) return nil } d := *obj.(*extensions.Deployment) - if !dc.rcStoreSynced() { - // Sleep so we give the rc reflector goroutine a chance to run. - time.Sleep(RcStoreSyncedPollPeriod) - glog.Infof("Waiting for rc controller to sync, requeuing deployment %s", d.Name) + if !dc.rsStoreSynced() { + // Sleep so we give the replica set reflector goroutine a chance to run. + time.Sleep(RSStoreSyncedPollPeriod) + glog.Infof("Waiting for replica set controller to sync, requeuing deployment %s", d.Name) dc.enqueueDeployment(&d) return nil } @@ -439,31 +441,31 @@ func (dc *DeploymentController) syncDeployment(key string) error { // Rolling back to a revision; no-op if the toRevision is deployment's current revision func (dc *DeploymentController) rollback(deployment *extensions.Deployment, toRevision *int64) (*extensions.Deployment, error) { - newRC, allOldRCs, err := dc.getNewRCAndAllOldRCs(*deployment) + newRS, allOldRSs, err := dc.getNewAndAllOldReplicaSets(*deployment) if err != nil { return nil, err } - allRCs := append(allOldRCs, newRC) + allRSs := append(allOldRSs, newRS) // If rollback revision is 0, rollback to the last revision if *toRevision == 0 { - if *toRevision = lastRevision(allRCs); *toRevision == 0 { + if *toRevision = lastRevision(allRSs); *toRevision == 0 { // If we still can't find the last revision, gives up rollback dc.emitRollbackWarningEvent(deployment, deploymentutil.RollbackRevisionNotFound, "Unable to find last revision.") // Gives up rollback return dc.updateDeploymentAndClearRollbackTo(deployment) } } - for _, rc := range allRCs { - v, err := deploymentutil.Revision(rc) + for _, rs := range allRSs { + v, err := deploymentutil.Revision(rs) if err != nil { - glog.V(4).Infof("Unable to extract revision from deployment's rc %q: %v", rc.Name, err) + glog.V(4).Infof("Unable to extract revision from deployment's replica set %q: %v", rs.Name, err) continue } if v == *toRevision { - glog.V(4).Infof("Found rc %q with desired revision %d", rc.Name, v) - // rollback by copying podTemplate.Spec from the rc, and increment revision number by 1 + glog.V(4).Infof("Found replica set %q with desired revision %d", rs.Name, v) + // rollback by copying podTemplate.Spec from the replica set, and increment revision number by 1 // no-op if the the spec matches current deployment's podTemplate.Spec - deployment, performedRollback, err := dc.rollbackToTemplate(deployment, rc) + deployment, performedRollback, err := dc.rollbackToTemplate(deployment, rs) if performedRollback && err == nil { dc.emitRollbackNormalEvent(deployment, fmt.Sprintf("Rolled back deployment %q to revision %d", deployment.Name, *toRevision)) } @@ -491,141 +493,141 @@ func (dc *DeploymentController) updateDeploymentAndClearRollbackTo(deployment *e } func (dc *DeploymentController) syncRecreateDeployment(deployment extensions.Deployment) error { - newRC, oldRCs, err := dc.getNewRCAndOldRCs(deployment) + newRS, oldRSs, err := dc.getNewAndOldReplicaSets(deployment) if err != nil { return err } - allRCs := append(oldRCs, newRC) + allRSs := append(oldRSs, newRS) - // scale down old rcs - scaledDown, err := dc.scaleDownOldRCsForRecreate(oldRCs, deployment) + // scale down old replica sets + scaledDown, err := dc.scaleDownOldReplicaSetsForRecreate(oldRSs, deployment) if err != nil { return err } if scaledDown { // Update DeploymentStatus - return dc.updateDeploymentStatus(allRCs, newRC, deployment) + return dc.updateDeploymentStatus(allRSs, newRS, deployment) } - // scale up new rc - scaledUp, err := dc.scaleUpNewRCForRecreate(newRC, deployment) + // scale up new replica set + scaledUp, err := dc.scaleUpNewReplicaSetForRecreate(newRS, deployment) if err != nil { return err } if scaledUp { // Update DeploymentStatus - return dc.updateDeploymentStatus(allRCs, newRC, deployment) + return dc.updateDeploymentStatus(allRSs, newRS, deployment) } if deployment.Spec.RevisionHistoryLimit != nil { - // Cleanup old RCs - dc.cleanupOldRcs(oldRCs, deployment) + // Cleanup old replica sets + dc.cleanupOldReplicaSets(oldRSs, deployment) } // Sync deployment status - return dc.syncDeploymentStatus(allRCs, newRC, deployment) + return dc.syncDeploymentStatus(allRSs, newRS, deployment) // TODO: raise an event, neither scaled up nor down. } func (dc *DeploymentController) syncRollingUpdateDeployment(deployment extensions.Deployment) error { - newRC, oldRCs, err := dc.getNewRCAndOldRCs(deployment) + newRS, oldRSs, err := dc.getNewAndOldReplicaSets(deployment) if err != nil { return err } - allRCs := append(oldRCs, newRC) + allRSs := append(oldRSs, newRS) // Scale up, if we can. - scaledUp, err := dc.reconcileNewRC(allRCs, newRC, deployment) + scaledUp, err := dc.reconcileNewReplicaSet(allRSs, newRS, deployment) if err != nil { return err } if scaledUp { // Update DeploymentStatus - return dc.updateDeploymentStatus(allRCs, newRC, deployment) + return dc.updateDeploymentStatus(allRSs, newRS, deployment) } // Scale down, if we can. - scaledDown, err := dc.reconcileOldRCs(allRCs, oldRCs, newRC, deployment, true) + scaledDown, err := dc.reconcileOldReplicaSets(allRSs, oldRSs, newRS, deployment, true) if err != nil { return err } if scaledDown { // Update DeploymentStatus - return dc.updateDeploymentStatus(allRCs, newRC, deployment) + return dc.updateDeploymentStatus(allRSs, newRS, deployment) } if deployment.Spec.RevisionHistoryLimit != nil { - // Cleanup old RCs - dc.cleanupOldRcs(oldRCs, deployment) + // Cleanup old replicas sets + dc.cleanupOldReplicaSets(oldRSs, deployment) } // Sync deployment status - return dc.syncDeploymentStatus(allRCs, newRC, deployment) + return dc.syncDeploymentStatus(allRSs, newRS, deployment) // TODO: raise an event, neither scaled up nor down. } // syncDeploymentStatus checks if the status is up-to-date and sync it if necessary -func (dc *DeploymentController) syncDeploymentStatus(allRCs []*api.ReplicationController, newRC *api.ReplicationController, deployment extensions.Deployment) error { - totalReplicas, updatedReplicas, availableReplicas, _, err := dc.calculateStatus(allRCs, newRC, deployment) +func (dc *DeploymentController) syncDeploymentStatus(allRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet, deployment extensions.Deployment) error { + totalReplicas, updatedReplicas, availableReplicas, _, err := dc.calculateStatus(allRSs, newRS, deployment) if err != nil { return err } if deployment.Status.Replicas != totalReplicas || deployment.Status.UpdatedReplicas != updatedReplicas || deployment.Status.AvailableReplicas != availableReplicas { - return dc.updateDeploymentStatus(allRCs, newRC, deployment) + return dc.updateDeploymentStatus(allRSs, newRS, deployment) } return nil } -// getNewRCAndMaybeFilteredOldRCs returns new RC and old RCs of the deployment. If ignoreNoPod is true, -// the returned old RCs won't include the ones with no pods; otherwise, all old RCs will be returned. -func (dc *DeploymentController) getNewRCAndMaybeFilteredOldRCs(deployment extensions.Deployment, ignoreNoPod bool) (*api.ReplicationController, []*api.ReplicationController, error) { - oldRCs, allOldRCs, err := dc.getOldRCs(deployment) +// getNewAndMaybeFilteredOldReplicaSets returns new replica set and old replica sets of the deployment. If ignoreNoPod is true, +// the returned old replica sets won't include the ones with no pods; otherwise, all old replica sets will be returned. +func (dc *DeploymentController) getNewAndMaybeFilteredOldReplicaSets(deployment extensions.Deployment, ignoreNoPod bool) (*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { + oldRSs, allOldRSs, err := dc.getOldReplicaSets(deployment) if err != nil { return nil, nil, err } - maxOldV := maxRevision(allOldRCs) + maxOldV := maxRevision(allOldRSs) - // Get new RC with the updated revision number - newRC, err := dc.getNewRC(deployment, maxOldV) + // Get new replica set with the updated revision number + newRS, err := dc.getNewReplicaSet(deployment, maxOldV) if err != nil { return nil, nil, err } - // Sync deployment's revision number with new RC - if newRC.Annotations != nil && len(newRC.Annotations[deploymentutil.RevisionAnnotation]) > 0 && - (deployment.Annotations == nil || deployment.Annotations[deploymentutil.RevisionAnnotation] != newRC.Annotations[deploymentutil.RevisionAnnotation]) { - if err = dc.updateDeploymentRevision(deployment, newRC.Annotations[deploymentutil.RevisionAnnotation]); err != nil { + // Sync deployment's revision number with new replica set + if newRS.Annotations != nil && len(newRS.Annotations[deploymentutil.RevisionAnnotation]) > 0 && + (deployment.Annotations == nil || deployment.Annotations[deploymentutil.RevisionAnnotation] != newRS.Annotations[deploymentutil.RevisionAnnotation]) { + if err = dc.updateDeploymentRevision(deployment, newRS.Annotations[deploymentutil.RevisionAnnotation]); err != nil { glog.V(4).Infof("Error: %v. Unable to update deployment revision, will retry later.", err) } } if !ignoreNoPod { - return newRC, allOldRCs, nil + return newRS, allOldRSs, nil } - return newRC, oldRCs, nil + return newRS, oldRSs, nil } -// getNewRCAndOldRCs returns new RC and old RCs of the deployment. -// Note that the returned old RCs don't include the ones with no pods. -func (dc *DeploymentController) getNewRCAndOldRCs(deployment extensions.Deployment) (*api.ReplicationController, []*api.ReplicationController, error) { - return dc.getNewRCAndMaybeFilteredOldRCs(deployment, true) +// getNewAndOldReplicaSets returns new replica set and old replica sets of the deployment. +// Note that the returned old replica sets don't include the ones with no pods. +func (dc *DeploymentController) getNewAndOldReplicaSets(deployment extensions.Deployment) (*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { + return dc.getNewAndMaybeFilteredOldReplicaSets(deployment, true) } -// getNewRCAndAllOldRCs returns new RC and old RCs of the deployment. -// Note that all old RCs are returned, include the ones with no pods. -func (dc *DeploymentController) getNewRCAndAllOldRCs(deployment extensions.Deployment) (*api.ReplicationController, []*api.ReplicationController, error) { - return dc.getNewRCAndMaybeFilteredOldRCs(deployment, false) +// getNewAndAllOldReplicaSets returns new replica set and old replica sets of the deployment. +// Note that all old replica sets are returned, include the ones with no pods. +func (dc *DeploymentController) getNewAndAllOldReplicaSets(deployment extensions.Deployment) (*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { + return dc.getNewAndMaybeFilteredOldReplicaSets(deployment, false) } -func maxRevision(allRCs []*api.ReplicationController) int64 { +func maxRevision(allRSs []*extensions.ReplicaSet) int64 { max := int64(0) - for _, rc := range allRCs { - if v, err := deploymentutil.Revision(rc); err != nil { - // Skip the RCs when it failed to parse their revision information - glog.V(4).Infof("Error: %v. Couldn't parse revision for rc %#v, deployment controller will skip it when reconciling revisions.", err, rc) + for _, rs := range allRSs { + if v, err := deploymentutil.Revision(rs); err != nil { + // Skip the replica sets when it failed to parse their revision information + glog.V(4).Infof("Error: %v. Couldn't parse revision for replica set %#v, deployment controller will skip it when reconciling revisions.", err, rs) } else if v > max { max = v } @@ -633,13 +635,13 @@ func maxRevision(allRCs []*api.ReplicationController) int64 { return max } -// lastRevision finds the second max revision number in all RCs (the last revision) -func lastRevision(allRCs []*api.ReplicationController) int64 { +// lastRevision finds the second max revision number in all replica sets (the last revision) +func lastRevision(allRSs []*extensions.ReplicaSet) int64 { max, secMax := int64(0), int64(0) - for _, rc := range allRCs { - if v, err := deploymentutil.Revision(rc); err != nil { - // Skip the RCs when it failed to parse their revision information - glog.V(4).Infof("Error: %v. Couldn't parse revision for rc %#v, deployment controller will skip it when reconciling revisions.", err, rc) + for _, rs := range allRSs { + if v, err := deploymentutil.Revision(rs); err != nil { + // Skip the replica sets when it failed to parse their revision information + glog.V(4).Infof("Error: %v. Couldn't parse revision for replica set %#v, deployment controller will skip it when reconciling revisions.", err, rs) } else if v >= max { secMax = max max = v @@ -650,120 +652,120 @@ func lastRevision(allRCs []*api.ReplicationController) int64 { return secMax } -// getOldRCs returns two sets of old RCs of the deployment. The first set of old RCs doesn't include -// the ones with no pods, and the second set of old RCs include all old RCs. -func (dc *DeploymentController) getOldRCs(deployment extensions.Deployment) ([]*api.ReplicationController, []*api.ReplicationController, error) { - return deploymentutil.GetOldRCsFromLists(deployment, dc.client, +// getOldReplicaSets returns two sets of old replica sets of the deployment. The first set of old replica sets doesn't include +// the ones with no pods, and the second set of old replica sets include all old replica sets. +func (dc *DeploymentController) getOldReplicaSets(deployment extensions.Deployment) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { + return deploymentutil.GetOldReplicaSetsFromLists(deployment, dc.client, func(namespace string, options api.ListOptions) (*api.PodList, error) { podList, err := dc.podStore.Pods(namespace).List(options.LabelSelector) return &podList, err }, - func(namespace string, options api.ListOptions) ([]api.ReplicationController, error) { - return dc.rcStore.ReplicationControllers(namespace).List(options.LabelSelector) + func(namespace string, options api.ListOptions) ([]extensions.ReplicaSet, error) { + return dc.rsStore.ReplicaSets(namespace).List(options.LabelSelector) }) } -// 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 int64) (*api.ReplicationController, error) { - // Calculate revision number for this new RC +// Returns a replica set that matches the intent of the given deployment. +// It creates a new replica set if required. +// The revision of the new replica set will be updated to maxOldRevision + 1 +func (dc *DeploymentController) getNewReplicaSet(deployment extensions.Deployment, maxOldRevision int64) (*extensions.ReplicaSet, error) { + // Calculate revision number for this new replica set newRevision := strconv.FormatInt(maxOldRevision+1, 10) - existingNewRC, err := deploymentutil.GetNewRCFromList(deployment, dc.client, - func(namespace string, options api.ListOptions) ([]api.ReplicationController, error) { - return dc.rcStore.ReplicationControllers(namespace).List(options.LabelSelector) + existingNewRS, err := deploymentutil.GetNewReplicaSetFromList(deployment, dc.client, + func(namespace string, options api.ListOptions) ([]extensions.ReplicaSet, error) { + return dc.rsStore.ReplicaSets(namespace).List(options.LabelSelector) }) if err != nil { return nil, err - } else if existingNewRC != nil { - // Set existing new RC's annotation - if setNewRCAnnotations(&deployment, existingNewRC, newRevision) { - return dc.client.Core().ReplicationControllers(deployment.ObjectMeta.Namespace).Update(existingNewRC) + } else if existingNewRS != nil { + // Set existing new replica set's annotation + if setNewReplicaSetAnnotations(&deployment, existingNewRS, newRevision) { + return dc.client.Extensions().ReplicaSets(deployment.ObjectMeta.Namespace).Update(existingNewRS) } - return existingNewRC, nil + return existingNewRS, nil } - // Check the rc expectations of deployment before creating a new rc + // Check the replica set expectations of the deployment before creating a new one. 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) { + if !dc.rsExpectations.SatisfiedExpectations(dKey) { dc.enqueueDeployment(&deployment) - return nil, fmt.Errorf("RC expectations not met yet before getting new RC\n") + return nil, fmt.Errorf("replica set expectations not met yet before getting new replica set\n") } - // new RC does not exist, create one. + // new ReplicaSet does not exist, create one. namespace := deployment.ObjectMeta.Namespace podTemplateSpecHash := podutil.GetPodTemplateSpecHash(deployment.Spec.Template) - newRCTemplate := deploymentutil.GetNewRCTemplate(deployment) - // Add pod-template-hash label to selector. - newRCSelector := labelsutil.CloneAndAddLabel(deployment.Spec.Selector, extensions.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash) + newRSTemplate := deploymentutil.GetNewReplicaSetTemplate(deployment) + // Add podTemplateHash label to selector. + newRSSelector := labelsutil.CloneSelectorAndAddLabel(deployment.Spec.Selector, extensions.DefaultDeploymentUniqueLabelKey, podTemplateSpecHash) - // Set RC expectations (1 rc should be created) + // Set ReplicaSet expectations (1 ReplicaSet 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{ + dc.rsExpectations.ExpectCreations(dKey, 1) + // Create new ReplicaSet + newRS := extensions.ReplicaSet{ ObjectMeta: api.ObjectMeta{ GenerateName: deployment.Name + "-", Namespace: namespace, }, - Spec: api.ReplicationControllerSpec{ + Spec: extensions.ReplicaSetSpec{ Replicas: 0, - Selector: newRCSelector, - Template: &newRCTemplate, + Selector: newRSSelector, + Template: &newRSTemplate, }, } - // Set new RC's annotation - setNewRCAnnotations(&deployment, &newRC, newRevision) - createdRC, err := dc.client.Core().ReplicationControllers(namespace).Create(&newRC) + // Set new replica set's annotation + setNewReplicaSetAnnotations(&deployment, &newRS, newRevision) + createdRS, err := dc.client.Extensions().ReplicaSets(namespace).Create(&newRS) if err != nil { - dc.rcExpectations.DeleteExpectations(dKey) - return nil, fmt.Errorf("error creating replication controller: %v", err) + dc.rsExpectations.DeleteExpectations(dKey) + return nil, fmt.Errorf("error creating replica set: %v", err) } err = dc.updateDeploymentRevision(deployment, newRevision) - return createdRC, err + return createdRS, err } -// setNewRCAnnotations sets new rc's annotations appropriately by updating its revision and -// copying required deployment annotations to it; it returns true if rc's annotation is changed. -func setNewRCAnnotations(deployment *extensions.Deployment, rc *api.ReplicationController, newRevision string) bool { +// 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 := copyDeploymentAnnotationsToRC(deployment, rc) - // Then, update RC's revision annotation - if rc.Annotations == nil { - rc.Annotations = make(map[string]string) + annotationChanged := copyDeploymentAnnotationsToReplicaSet(deployment, rs) + // Then, update replica set's revision annotation + if rs.Annotations == nil { + rs.Annotations = make(map[string]string) } - if rc.Annotations[deploymentutil.RevisionAnnotation] != newRevision { - rc.Annotations[deploymentutil.RevisionAnnotation] = newRevision + if rs.Annotations[deploymentutil.RevisionAnnotation] != newRevision { + rs.Annotations[deploymentutil.RevisionAnnotation] = newRevision annotationChanged = true - glog.V(4).Infof("updating RC %q's revision to %s - %+v\n", rc.Name, newRevision) + glog.V(4).Infof("updating replica set %q's revision to %s - %+v\n", rs.Name, newRevision) } return annotationChanged } -// copyDeploymentAnnotationsToRC copies deployment's annotations to rc's annotations, -// and returns true if rc's annotation is changed -func copyDeploymentAnnotationsToRC(deployment *extensions.Deployment, rc *api.ReplicationController) bool { - rcAnnotationsChanged := false - if rc.Annotations == nil { - rc.Annotations = make(map[string]string) +// 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 || rc.Annotations[k] == v { + if k == kubectl.LastAppliedConfigAnnotation || rs.Annotations[k] == v { continue } - rc.Annotations[k] = v - rcAnnotationsChanged = true + rs.Annotations[k] = v + rsAnnotationsChanged = true } - return rcAnnotationsChanged + return rsAnnotationsChanged } func (dc *DeploymentController) updateDeploymentRevision(deployment extensions.Deployment, revision string) error { @@ -775,14 +777,14 @@ func (dc *DeploymentController) updateDeploymentRevision(deployment extensions.D return err } -func (dc *DeploymentController) reconcileNewRC(allRCs []*api.ReplicationController, newRC *api.ReplicationController, deployment extensions.Deployment) (bool, error) { - if newRC.Spec.Replicas == deployment.Spec.Replicas { +func (dc *DeploymentController) reconcileNewReplicaSet(allRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet, deployment extensions.Deployment) (bool, error) { + if newRS.Spec.Replicas == deployment.Spec.Replicas { // Scaling not required. return false, nil } - if newRC.Spec.Replicas > deployment.Spec.Replicas { + if newRS.Spec.Replicas > deployment.Spec.Replicas { // Scale down. - _, err := dc.scaleRCAndRecordEvent(newRC, deployment.Spec.Replicas, deployment) + _, err := dc.scaleReplicaSetAndRecordEvent(newRS, deployment.Spec.Replicas, deployment) return true, err } // Check if we can scale up. @@ -794,7 +796,7 @@ func (dc *DeploymentController) reconcileNewRC(allRCs []*api.ReplicationControll maxSurge = util.GetValueFromPercent(maxSurge, deployment.Spec.Replicas) } // Find the total number of pods - currentPodCount := deploymentutil.GetReplicaCountForRCs(allRCs) + currentPodCount := deploymentutil.GetReplicaCountForReplicaSets(allRSs) maxTotalPods := deployment.Spec.Replicas + maxSurge if currentPodCount >= maxTotalPods { // Cannot scale up. @@ -803,15 +805,15 @@ func (dc *DeploymentController) reconcileNewRC(allRCs []*api.ReplicationControll // Scale up. scaleUpCount := maxTotalPods - currentPodCount // Do not exceed the number of desired replicas. - scaleUpCount = int(math.Min(float64(scaleUpCount), float64(deployment.Spec.Replicas-newRC.Spec.Replicas))) - newReplicasCount := newRC.Spec.Replicas + scaleUpCount - _, err = dc.scaleRCAndRecordEvent(newRC, newReplicasCount, deployment) + scaleUpCount = int(math.Min(float64(scaleUpCount), float64(deployment.Spec.Replicas-newRS.Spec.Replicas))) + newReplicasCount := newRS.Spec.Replicas + scaleUpCount + _, err = dc.scaleReplicaSetAndRecordEvent(newRS, newReplicasCount, deployment) return true, err } // Set expectationsCheck to false to bypass expectations check when testing -func (dc *DeploymentController) reconcileOldRCs(allRCs []*api.ReplicationController, oldRCs []*api.ReplicationController, newRC *api.ReplicationController, deployment extensions.Deployment, expectationsCheck bool) (bool, error) { - oldPodsCount := deploymentutil.GetReplicaCountForRCs(oldRCs) +func (dc *DeploymentController) reconcileOldReplicaSets(allRSs []*extensions.ReplicaSet, oldRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet, deployment extensions.Deployment, expectationsCheck bool) (bool, error) { + oldPodsCount := deploymentutil.GetReplicaCountForReplicaSets(oldRSs) if oldPodsCount == 0 { // Can't scale down further return false, nil @@ -823,13 +825,13 @@ func (dc *DeploymentController) reconcileOldRCs(allRCs []*api.ReplicationControl return false, fmt.Errorf("Couldn't get key for deployment %#v: %v", deployment, err) } if expectationsCheck && !dc.podExpectations.SatisfiedExpectations(dKey) { - glog.V(4).Infof("Pod expectations not met yet before reconciling old RCs\n") + glog.V(4).Infof("Pod expectations not met yet before reconciling old replica sets\n") return false, nil } minReadySeconds := deployment.Spec.MinReadySeconds - allPodsCount := deploymentutil.GetReplicaCountForRCs(allRCs) - newRCAvailablePodCount, err := deploymentutil.GetAvailablePodsForRCs(dc.client, []*api.ReplicationController{newRC}, minReadySeconds) + allPodsCount := deploymentutil.GetReplicaCountForReplicaSets(allRSs) + newRSAvailablePodCount, err := deploymentutil.GetAvailablePodsForReplicaSets(dc.client, []*extensions.ReplicaSet{newRS}, minReadySeconds) if err != nil { return false, fmt.Errorf("could not find available pods: %v", err) } @@ -843,14 +845,14 @@ func (dc *DeploymentController) reconcileOldRCs(allRCs []*api.ReplicationControl } // Check if we can scale down. We can scale down in the following 2 cases: - // * Some old rcs have unhealthy replicas, we could safely scale down those unhealthy replicas since that won't further + // * Some old replica sets have unhealthy replicas, we could safely scale down those unhealthy replicas since that won't further // increase unavailability. - // * New rc has scaled up and it's replicas becomes ready, then we can scale down old rcs in a further step. + // * New replica set has scaled up and it's replicas becomes ready, then we can scale down old replica sets in a further step. // - // maxScaledDown := allPodsCount - minAvailable - newRCPodsUnavailable + // maxScaledDown := allPodsCount - minAvailable - newReplicaSetPodsUnavailable // take into account not only maxUnavailable and any surge pods that have been created, but also unavailable pods from - // the newRC, so that the unavailable pods from the newRC would not make us scale down old RCs in a further step(that will - // increase unavailability). + // the newRS, so that the unavailable pods from the newRS would not make us scale down old replica sets in a further + // step(that will increase unavailability). // // Concrete example: // @@ -859,35 +861,35 @@ func (dc *DeploymentController) reconcileOldRCs(allRCs []*api.ReplicationControl // * 3 maxSurge (absolute number, not percent) // // case 1: - // * Deployment is updated, newRC is created with 3 replicas, oldRC is scaled down to 8, and newRC is scaled up to 5. - // * The new RC pods crashloop and never become available. - // * allPodsCount is 13. minAvailable is 8. newRCPodsUnavailable is 5. - // * A node fails and causes one of the oldRC pods to become unavailable. However, 13 - 8 - 5 = 0, so the oldRC won't be scaled down. + // * Deployment is updated, newRS is created with 3 replicas, oldRS is scaled down to 8, and newRS is scaled up to 5. + // * The new replica set pods crashloop and never become available. + // * allPodsCount is 13. minAvailable is 8. newRSPodsUnavailable is 5. + // * A node fails and causes one of the oldRS pods to become unavailable. However, 13 - 8 - 5 = 0, so the oldRS won't be scaled down. // * The user notices the crashloop and does kubectl rollout undo to rollback. - // * newRCPodsUnavailable is 1, since we rolled back to the good RC, so maxScaledDown = 13 - 8 - 1 = 4. 4 of the crashlooping pods will be scaled down. - // * The total number of pods will then be 9 and the newRC can be scaled up to 10. + // * newRSPodsUnavailable is 1, since we rolled back to the good replica set, so maxScaledDown = 13 - 8 - 1 = 4. 4 of the crashlooping pods will be scaled down. + // * The total number of pods will then be 9 and the newRS can be scaled up to 10. // // case 2: // Same example, but pushing a new pod template instead of rolling back (aka "roll over"): - // * The new RC created must start with 0 replicas because allPodsCount is already at 13. - // * However, newRCPodsUnavailable would also be 0, so the 2 old RCs could be scaled down by 5 (13 - 8 - 0), which would then - // allow the new RC to be scaled up by 5. + // * The new replica set created must start with 0 replicas because allPodsCount is already at 13. + // * However, newRSPodsUnavailable would also be 0, so the 2 old replica sets could be scaled down by 5 (13 - 8 - 0), which would then + // allow the new replica set to be scaled up by 5. minAvailable := deployment.Spec.Replicas - maxUnavailable - newRCUnavailablePodCount := newRC.Spec.Replicas - newRCAvailablePodCount - maxScaledDown := allPodsCount - minAvailable - newRCUnavailablePodCount + newRSUnavailablePodCount := newRS.Spec.Replicas - newRSAvailablePodCount + maxScaledDown := allPodsCount - minAvailable - newRSUnavailablePodCount if maxScaledDown <= 0 { return false, nil } // Clean up unhealthy replicas first, otherwise unhealthy replicas will block deployment // and cause timeout. See https://github.com/kubernetes/kubernetes/issues/16737 - cleanupCount, err := dc.cleanupUnhealthyReplicas(oldRCs, deployment, maxScaledDown) + cleanupCount, err := dc.cleanupUnhealthyReplicas(oldRSs, deployment, maxScaledDown) if err != nil { return false, nil } - // Scale down old rcs, need check maxUnavailable to ensure we can scale down - scaledDownCount, err := dc.scaleDownOldRCsForRollingUpdate(allRCs, oldRCs, deployment) + // Scale down old replica sets, need check maxUnavailable to ensure we can scale down + scaledDownCount, err := dc.scaleDownOldReplicaSetsForRollingUpdate(allRSs, oldRSs, deployment) if err != nil { return false, nil } @@ -900,33 +902,33 @@ func (dc *DeploymentController) reconcileOldRCs(allRCs []*api.ReplicationControl return totalScaledDown > 0, nil } -// cleanupUnhealthyReplicas will scale down old rcs with unhealthy replicas, so that all unhealthy replicas will be deleted. -func (dc *DeploymentController) cleanupUnhealthyReplicas(oldRCs []*api.ReplicationController, deployment extensions.Deployment, maxCleanupCount int) (int, error) { - sort.Sort(controller.ControllersByCreationTimestamp(oldRCs)) - // Safely scale down all old rcs with unhealthy replicas. ReplicationController/ReplicaSet will sort the pods in the order +// cleanupUnhealthyReplicas will scale down old replica sets with unhealthy replicas, so that all unhealthy replicas will be deleted. +func (dc *DeploymentController) cleanupUnhealthyReplicas(oldRSs []*extensions.ReplicaSet, deployment extensions.Deployment, maxCleanupCount int) (int, error) { + sort.Sort(controller.ReplicaSetsByCreationTimestamp(oldRSs)) + // Safely scale down all old replica sets with unhealthy replicas. Replica set will sort the pods in the order // such that not-ready < ready, unscheduled < scheduled, and pending < running. This ensures that unhealthy replicas will // been deleted first and won't increase unavailability. totalScaledDown := 0 - for _, targetRC := range oldRCs { + for _, targetRS := range oldRSs { if totalScaledDown >= maxCleanupCount { break } - if targetRC.Spec.Replicas == 0 { - // cannot scale down this RC. + if targetRS.Spec.Replicas == 0 { + // cannot scale down this replica set. continue } - readyPodCount, err := deploymentutil.GetAvailablePodsForRCs(dc.client, []*api.ReplicationController{targetRC}, 0) + readyPodCount, err := deploymentutil.GetAvailablePodsForReplicaSets(dc.client, []*extensions.ReplicaSet{targetRS}, 0) if err != nil { return totalScaledDown, fmt.Errorf("could not find available pods: %v", err) } - if targetRC.Spec.Replicas == readyPodCount { + if targetRS.Spec.Replicas == readyPodCount { // no unhealthy replicas found, no scaling required. continue } - scaledDownCount := int(math.Min(float64(maxCleanupCount-totalScaledDown), float64(targetRC.Spec.Replicas-readyPodCount))) - newReplicasCount := targetRC.Spec.Replicas - scaledDownCount - _, err = dc.scaleRCAndRecordEvent(targetRC, newReplicasCount, deployment) + scaledDownCount := int(math.Min(float64(maxCleanupCount-totalScaledDown), float64(targetRS.Spec.Replicas-readyPodCount))) + newReplicasCount := targetRS.Spec.Replicas - scaledDownCount + _, err = dc.scaleReplicaSetAndRecordEvent(targetRS, newReplicasCount, deployment) if err != nil { return totalScaledDown, err } @@ -935,9 +937,9 @@ func (dc *DeploymentController) cleanupUnhealthyReplicas(oldRCs []*api.Replicati return totalScaledDown, nil } -// scaleDownOldRCsForRollingUpdate scales down old rcs when deployment strategy is "RollingUpdate". +// scaleDownOldReplicaSetsForRollingUpdate scales down old replica sets when deployment strategy is "RollingUpdate". // Need check maxUnavailable to ensure availability -func (dc *DeploymentController) scaleDownOldRCsForRollingUpdate(allRCs []*api.ReplicationController, oldRCs []*api.ReplicationController, deployment extensions.Deployment) (int, error) { +func (dc *DeploymentController) scaleDownOldReplicaSetsForRollingUpdate(allRSs []*extensions.ReplicaSet, oldRSs []*extensions.ReplicaSet, deployment extensions.Deployment) (int, error) { maxUnavailable, isPercent, err := util.GetIntOrPercentValue(&deployment.Spec.Strategy.RollingUpdate.MaxUnavailable) if err != nil { return 0, fmt.Errorf("invalid value for MaxUnavailable: %v", err) @@ -949,7 +951,7 @@ func (dc *DeploymentController) scaleDownOldRCsForRollingUpdate(allRCs []*api.Re minAvailable := deployment.Spec.Replicas - maxUnavailable minReadySeconds := deployment.Spec.MinReadySeconds // Find the number of ready pods. - readyPodCount, err := deploymentutil.GetAvailablePodsForRCs(dc.client, allRCs, minReadySeconds) + readyPodCount, err := deploymentutil.GetAvailablePodsForReplicaSets(dc.client, allRSs, minReadySeconds) if err != nil { return 0, fmt.Errorf("could not find available pods: %v", err) } @@ -958,23 +960,23 @@ func (dc *DeploymentController) scaleDownOldRCsForRollingUpdate(allRCs []*api.Re return 0, nil } - sort.Sort(controller.ControllersByCreationTimestamp(oldRCs)) + sort.Sort(controller.ReplicaSetsByCreationTimestamp(oldRSs)) totalScaledDown := 0 totalScaleDownCount := readyPodCount - minAvailable - for _, targetRC := range oldRCs { + for _, targetRS := range oldRSs { if totalScaledDown >= totalScaleDownCount { // No further scaling required. break } - if targetRC.Spec.Replicas == 0 { - // cannot scale down this RC. + if targetRS.Spec.Replicas == 0 { + // cannot scale down this ReplicaSet. continue } // Scale down. - scaleDownCount := int(math.Min(float64(targetRC.Spec.Replicas), float64(totalScaleDownCount-totalScaledDown))) - newReplicasCount := targetRC.Spec.Replicas - scaleDownCount - _, err = dc.scaleRCAndRecordEvent(targetRC, newReplicasCount, deployment) + scaleDownCount := int(math.Min(float64(targetRS.Spec.Replicas), float64(totalScaleDownCount-totalScaledDown))) + newReplicasCount := targetRS.Spec.Replicas - scaleDownCount + _, err = dc.scaleReplicaSetAndRecordEvent(targetRS, newReplicasCount, deployment) if err != nil { return totalScaledDown, err } @@ -985,15 +987,15 @@ func (dc *DeploymentController) scaleDownOldRCsForRollingUpdate(allRCs []*api.Re return totalScaledDown, nil } -// scaleDownOldRCsForRecreate scales down old rcs when deployment strategy is "Recreate" -func (dc *DeploymentController) scaleDownOldRCsForRecreate(oldRCs []*api.ReplicationController, deployment extensions.Deployment) (bool, error) { +// scaleDownOldReplicaSetsForRecreate scales down old replica sets when deployment strategy is "Recreate" +func (dc *DeploymentController) scaleDownOldReplicaSetsForRecreate(oldRSs []*extensions.ReplicaSet, deployment extensions.Deployment) (bool, error) { scaled := false - for _, rc := range oldRCs { + for _, rs := range oldRSs { // Scaling not required. - if rc.Spec.Replicas == 0 { + if rs.Spec.Replicas == 0 { continue } - _, err := dc.scaleRCAndRecordEvent(rc, 0, deployment) + _, err := dc.scaleReplicaSetAndRecordEvent(rs, 0, deployment) if err != nil { return false, err } @@ -1002,34 +1004,34 @@ func (dc *DeploymentController) scaleDownOldRCsForRecreate(oldRCs []*api.Replica return scaled, nil } -// scaleUpNewRCForRecreate scales up new rc when deployment strategy is "Recreate" -func (dc *DeploymentController) scaleUpNewRCForRecreate(newRC *api.ReplicationController, deployment extensions.Deployment) (bool, error) { - if newRC.Spec.Replicas == deployment.Spec.Replicas { +// scaleUpNewReplicaSetForRecreate scales up new replica set when deployment strategy is "Recreate" +func (dc *DeploymentController) scaleUpNewReplicaSetForRecreate(newRS *extensions.ReplicaSet, deployment extensions.Deployment) (bool, error) { + if newRS.Spec.Replicas == deployment.Spec.Replicas { // Scaling not required. return false, nil } - _, err := dc.scaleRCAndRecordEvent(newRC, deployment.Spec.Replicas, deployment) + _, err := dc.scaleReplicaSetAndRecordEvent(newRS, deployment.Spec.Replicas, deployment) return true, err } -func (dc *DeploymentController) cleanupOldRcs(oldRCs []*api.ReplicationController, deployment extensions.Deployment) error { - diff := len(oldRCs) - *deployment.Spec.RevisionHistoryLimit +func (dc *DeploymentController) cleanupOldReplicaSets(oldRSs []*extensions.ReplicaSet, deployment extensions.Deployment) error { + diff := len(oldRSs) - *deployment.Spec.RevisionHistoryLimit if diff <= 0 { return nil } - sort.Sort(controller.ControllersByCreationTimestamp(oldRCs)) + sort.Sort(controller.ControllersByCreationTimestamp(oldRSs)) var errList []error // TODO: This should be parallelized. for i := 0; i < diff; i++ { - controller := oldRCs[i] - // Avoid delete rc with non-zero replica counts + controller := oldRSs[i] + // Avoid delete replica set with non-zero replica counts if controller.Spec.Replicas != 0 || controller.Generation > controller.Status.ObservedGeneration { continue } - if err := dc.client.Core().ReplicationControllers(controller.Namespace).Delete(controller.Name, nil); err != nil && !errors.IsNotFound(err) { - glog.V(2).Infof("Failed deleting old rc %v for deployment %v: %v", controller.Name, deployment.Name, err) + if err := dc.client.Extensions().ReplicaSets(rs.Namespace).Delete(rs.Name, nil); err != nil && !errors.IsNotFound(err) { + glog.V(2).Infof("Failed deleting old replica set %v for deployment %v: %v", rs.Name, deployment.Name, err) errList = append(errList, err) } } @@ -1037,8 +1039,8 @@ func (dc *DeploymentController) cleanupOldRcs(oldRCs []*api.ReplicationControlle return utilerrors.NewAggregate(errList) } -func (dc *DeploymentController) updateDeploymentStatus(allRCs []*api.ReplicationController, newRC *api.ReplicationController, deployment extensions.Deployment) error { - totalReplicas, updatedReplicas, availableReplicas, unavailableReplicas, err := dc.calculateStatus(allRCs, newRC, deployment) +func (dc *DeploymentController) updateDeploymentStatus(allRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet, deployment extensions.Deployment) error { + totalReplicas, updatedReplicas, availableReplicas, unavailableReplicas, err := dc.calculateStatus(allRSs, newRS, deployment) if err != nil { return err } @@ -1054,11 +1056,11 @@ func (dc *DeploymentController) updateDeploymentStatus(allRCs []*api.Replication return err } -func (dc *DeploymentController) calculateStatus(allRCs []*api.ReplicationController, newRC *api.ReplicationController, deployment extensions.Deployment) (totalReplicas, updatedReplicas, availableReplicas, unavailableReplicas int, err error) { - totalReplicas = deploymentutil.GetReplicaCountForRCs(allRCs) - updatedReplicas = deploymentutil.GetReplicaCountForRCs([]*api.ReplicationController{newRC}) +func (dc *DeploymentController) calculateStatus(allRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet, deployment extensions.Deployment) (totalReplicas, updatedReplicas, availableReplicas, unavailableReplicas int, err error) { + totalReplicas = deploymentutil.GetReplicaCountForReplicaSets(allRSs) + updatedReplicas = deploymentutil.GetReplicaCountForReplicaSets([]*extensions.ReplicaSet{newRS}) minReadySeconds := deployment.Spec.MinReadySeconds - availableReplicas, err = deploymentutil.GetAvailablePodsForRCs(dc.client, allRCs, minReadySeconds) + availableReplicas, err = deploymentutil.GetAvailablePodsForReplicaSets(dc.client, allRSs, minReadySeconds) if err != nil { err = fmt.Errorf("failed to count available pods: %v", err) return @@ -1067,22 +1069,22 @@ func (dc *DeploymentController) calculateStatus(allRCs []*api.ReplicationControl return } -func (dc *DeploymentController) scaleRCAndRecordEvent(rc *api.ReplicationController, newScale int, deployment extensions.Deployment) (*api.ReplicationController, error) { +func (dc *DeploymentController) scaleReplicaSetAndRecordEvent(rs *extensions.ReplicaSet, newScale int, deployment extensions.Deployment) (*extensions.ReplicaSet, error) { scalingOperation := "down" - if rc.Spec.Replicas < newScale { + if rs.Spec.Replicas < newScale { scalingOperation = "up" } - newRC, err := dc.scaleRC(rc, newScale) + newRS, err := dc.scaleReplicaSet(rs, newScale) if err == nil { - dc.eventRecorder.Eventf(&deployment, api.EventTypeNormal, "ScalingRC", "Scaled %s rc %s to %d", scalingOperation, rc.Name, newScale) + dc.eventRecorder.Eventf(&deployment, api.EventTypeNormal, "ScalingReplicaSet", "Scaled %s ReplicaSet %s to %d", scalingOperation, rs.Name, newScale) } - return newRC, err + return newRS, err } -func (dc *DeploymentController) scaleRC(rc *api.ReplicationController, newScale int) (*api.ReplicationController, error) { +func (dc *DeploymentController) scaleReplicaSet(rs *extensions.ReplicaSet, newScale int) (*extensions.ReplicaSet, error) { // TODO: Using client for now, update to use store when it is ready. - rc.Spec.Replicas = newScale - return dc.client.Core().ReplicationControllers(rc.ObjectMeta.Namespace).Update(rc) + rs.Spec.Replicas = newScale + return dc.client.Extensions().ReplicaSets(rs.ObjectMeta.Namespace).Update(rs) } func (dc *DeploymentController) updateDeployment(deployment *extensions.Deployment) (*extensions.Deployment, error) { @@ -1090,10 +1092,10 @@ func (dc *DeploymentController) updateDeployment(deployment *extensions.Deployme return dc.client.Extensions().Deployments(deployment.ObjectMeta.Namespace).Update(deployment) } -func (dc *DeploymentController) rollbackToTemplate(deployment *extensions.Deployment, rc *api.ReplicationController) (d *extensions.Deployment, performedRollback bool, err error) { - if !reflect.DeepEqual(deploymentutil.GetNewRCTemplate(*deployment), *rc.Spec.Template) { - glog.Infof("Rolling back deployment %s to template spec %+v", deployment.Name, rc.Spec.Template.Spec) - deploymentutil.SetFromRCTemplate(deployment, *rc.Spec.Template) +func (dc *DeploymentController) rollbackToTemplate(deployment *extensions.Deployment, rs *extensions.ReplicaSet) (d *extensions.Deployment, performedRollback bool, err error) { + if !reflect.DeepEqual(deploymentutil.GetNewReplicaSetTemplate(*deployment), *rs.Spec.Template) { + glog.Infof("Rolling back deployment %s to template spec %+v", deployment.Name, rs.Spec.Template.Spec) + deploymentutil.SetFromReplicaSetTemplate(deployment, *rs.Spec.Template) performedRollback = true } else { glog.V(4).Infof("Rolling back to a revision that contains the same template as current deployment %s, skipping rollback...", deployment.Name) diff --git a/pkg/controller/deployment/deployment_controller_test.go b/pkg/controller/deployment/deployment_controller_test.go index ea77b300c21..7c89b0175e9 100644 --- a/pkg/controller/deployment/deployment_controller_test.go +++ b/pkg/controller/deployment/deployment_controller_test.go @@ -34,7 +34,7 @@ import ( "k8s.io/kubernetes/pkg/util/intstr" ) -func TestDeploymentController_reconcileNewRC(t *testing.T) { +func TestDeploymentController_reconcileNewReplicaSet(t *testing.T) { tests := []struct { deploymentReplicas int maxSurge intstr.IntOrString @@ -87,16 +87,17 @@ func TestDeploymentController_reconcileNewRC(t *testing.T) { for i, test := range tests { t.Logf("executing scenario %d", i) - newRc := rc("foo-v2", test.newReplicas, nil) - oldRc := rc("foo-v2", test.oldReplicas, nil) - allRcs := []*api.ReplicationController{newRc, oldRc} + newRS := rs("foo-v2", test.newReplicas, nil) + oldRS := rs("foo-v2", test.oldReplicas, nil) + allRSs := []*extensions.ReplicaSet{newRS, oldRS} deployment := deployment("foo", test.deploymentReplicas, test.maxSurge, intstr.FromInt(0)) fake := fake.Clientset{} controller := &DeploymentController{ client: &fake, + expClient: fake.Extensions(), eventRecorder: &record.FakeRecorder{}, } - scaled, err := controller.reconcileNewRC(allRcs, newRc, deployment) + scaled, err := controller.reconcileNewReplicaSet(allRSs, newRS, deployment) if err != nil { t.Errorf("unexpected error: %v", err) continue @@ -115,21 +116,21 @@ func TestDeploymentController_reconcileNewRC(t *testing.T) { t.Errorf("expected 1 action during scale, got: %v", fake.Actions()) continue } - updated := fake.Actions()[0].(testclient.UpdateAction).GetObject().(*api.ReplicationController) + updated := fake.Actions()[0].(testclient.UpdateAction).GetObject().(*exp.ReplicaSet) if e, a := test.expectedNewReplicas, updated.Spec.Replicas; e != a { t.Errorf("expected update to %d replicas, got %d", e, a) } } } -func TestDeploymentController_reconcileOldRCs(t *testing.T) { +func TestDeploymentController_reconcileOldReplicaSets(t *testing.T) { tests := []struct { deploymentReplicas int maxUnavailable intstr.IntOrString oldReplicas int newReplicas int - readyPodsFromOldRC int - readyPodsFromNewRC int + readyPodsFromOldRS int + readyPodsFromNewRS int scaleExpected bool expectedOldReplicas int }{ @@ -138,8 +139,8 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { maxUnavailable: intstr.FromInt(0), oldReplicas: 10, newReplicas: 0, - readyPodsFromOldRC: 10, - readyPodsFromNewRC: 0, + readyPodsFromOldRS: 10, + readyPodsFromNewRS: 0, scaleExpected: false, }, { @@ -147,38 +148,38 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { maxUnavailable: intstr.FromInt(2), oldReplicas: 10, newReplicas: 0, - readyPodsFromOldRC: 10, - readyPodsFromNewRC: 0, + readyPodsFromOldRS: 10, + readyPodsFromNewRS: 0, scaleExpected: true, expectedOldReplicas: 8, }, - { // expect unhealthy replicas from old rcs been cleaned up + { // expect unhealthy replicas from old replica sets been cleaned up deploymentReplicas: 10, maxUnavailable: intstr.FromInt(2), oldReplicas: 10, newReplicas: 0, - readyPodsFromOldRC: 8, - readyPodsFromNewRC: 0, + readyPodsFromOldRS: 8, + readyPodsFromNewRS: 0, scaleExpected: true, expectedOldReplicas: 8, }, - { // expect 1 unhealthy replica from old rcs been cleaned up, and 1 ready pod been scaled down + { // expect 1 unhealthy replica from old replica sets been cleaned up, and 1 ready pod been scaled down deploymentReplicas: 10, maxUnavailable: intstr.FromInt(2), oldReplicas: 10, newReplicas: 0, - readyPodsFromOldRC: 9, - readyPodsFromNewRC: 0, + readyPodsFromOldRS: 9, + readyPodsFromNewRS: 0, scaleExpected: true, expectedOldReplicas: 8, }, - { // the unavailable pods from the newRC would not make us scale down old RCs in a further step + { // the unavailable pods from the newRS would not make us scale down old RSs in a further step deploymentReplicas: 10, maxUnavailable: intstr.FromInt(2), oldReplicas: 8, newReplicas: 2, - readyPodsFromOldRC: 8, - readyPodsFromNewRC: 0, + readyPodsFromOldRS: 8, + readyPodsFromNewRS: 0, scaleExpected: false, }, } @@ -187,10 +188,10 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { newSelector := map[string]string{"foo": "new"} oldSelector := map[string]string{"foo": "old"} - newRc := rc("foo-new", test.newReplicas, newSelector) - oldRc := rc("foo-old", test.oldReplicas, oldSelector) - oldRCs := []*api.ReplicationController{oldRc} - allRCs := []*api.ReplicationController{oldRc, newRc} + newRS := rs("foo-new", test.newReplicas, newSelector) + oldRS := rs("foo-old", test.oldReplicas, oldSelector) + oldRSs := []*exp.ReplicaSet{oldRS} + allRSs := []*exp.ReplicaSet{oldRS, newRS} deployment := deployment("foo", test.deploymentReplicas, intstr.FromInt(0), test.maxUnavailable) fakeClientset := fake.Clientset{} @@ -198,10 +199,10 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { switch action.(type) { case core.ListAction: podList := &api.PodList{} - for podIndex := 0; podIndex < test.readyPodsFromOldRC; podIndex++ { + for podIndex := 0; podIndex < test.readyPodsFromOldRS; podIndex++ { podList.Items = append(podList.Items, api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("%s-oldReadyPod-%d", oldRc.Name, podIndex), + Name: fmt.Sprintf("%s-oldReadyPod-%d", oldRS.Name, podIndex), Labels: oldSelector, }, Status: api.PodStatus{ @@ -214,10 +215,10 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { }, }) } - for podIndex := 0; podIndex < test.oldReplicas-test.readyPodsFromOldRC; podIndex++ { + for podIndex := 0; podIndex < test.oldReplicas-test.readyPodsFromOldRS; podIndex++ { podList.Items = append(podList.Items, api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("%s-oldUnhealthyPod-%d", oldRc.Name, podIndex), + Name: fmt.Sprintf("%s-oldUnhealthyPod-%d", oldRS.Name, podIndex), Labels: oldSelector, }, Status: api.PodStatus{ @@ -230,10 +231,10 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { }, }) } - for podIndex := 0; podIndex < test.readyPodsFromNewRC; podIndex++ { + for podIndex := 0; podIndex < test.readyPodsFromNewRS; podIndex++ { podList.Items = append(podList.Items, api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("%s-newReadyPod-%d", oldRc.Name, podIndex), + Name: fmt.Sprintf("%s-newReadyPod-%d", oldRS.Name, podIndex), Labels: newSelector, }, Status: api.PodStatus{ @@ -246,10 +247,10 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { }, }) } - for podIndex := 0; podIndex < test.oldReplicas-test.readyPodsFromOldRC; podIndex++ { + for podIndex := 0; podIndex < test.oldReplicas-test.readyPodsFromOldRS; podIndex++ { podList.Items = append(podList.Items, api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("%s-newUnhealthyPod-%d", oldRc.Name, podIndex), + Name: fmt.Sprintf("%s-newUnhealthyPod-%d", oldRS.Name, podIndex), Labels: newSelector, }, Status: api.PodStatus{ @@ -271,7 +272,7 @@ func TestDeploymentController_reconcileOldRCs(t *testing.T) { eventRecorder: &record.FakeRecorder{}, } - scaled, err := controller.reconcileOldRCs(allRCs, oldRCs, newRc, deployment, false) + scaled, err := controller.reconcileOldReplicaSets(allRSs, oldRSs, newRS, deployment, false) if err != nil { t.Errorf("unexpected error: %v", err) continue @@ -327,8 +328,8 @@ func TestDeploymentController_cleanupUnhealthyReplicas(t *testing.T) { for i, test := range tests { t.Logf("executing scenario %d", i) - oldRc := rc("foo-v2", test.oldReplicas, nil) - oldRCs := []*api.ReplicationController{oldRc} + oldRS := rs("foo-v2", test.oldReplicas, nil) + oldRSs := []*exp.ReplicaSet{oldRS} deployment := deployment("foo", 10, intstr.FromInt(2), intstr.FromInt(2)) fakeClientset := fake.Clientset{} fakeClientset.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) { @@ -338,7 +339,7 @@ func TestDeploymentController_cleanupUnhealthyReplicas(t *testing.T) { for podIndex := 0; podIndex < test.readyPods; podIndex++ { podList.Items = append(podList.Items, api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("%s-readyPod-%d", oldRc.Name, podIndex), + Name: fmt.Sprintf("%s-readyPod-%d", oldRS.Name, podIndex), }, Status: api.PodStatus{ Conditions: []api.PodCondition{ @@ -353,7 +354,7 @@ func TestDeploymentController_cleanupUnhealthyReplicas(t *testing.T) { for podIndex := 0; podIndex < test.unHealthyPods; podIndex++ { podList.Items = append(podList.Items, api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("%s-unHealthyPod-%d", oldRc.Name, podIndex), + Name: fmt.Sprintf("%s-unHealthyPod-%d", oldRS.Name, podIndex), }, Status: api.PodStatus{ Conditions: []api.PodCondition{ @@ -374,7 +375,7 @@ func TestDeploymentController_cleanupUnhealthyReplicas(t *testing.T) { client: &fakeClientset, eventRecorder: &record.FakeRecorder{}, } - cleanupCount, err := controller.cleanupUnhealthyReplicas(oldRCs, deployment, test.maxCleanupCount) + cleanupCount, err := controller.cleanupUnhealthyReplicas(oldRSs, deployment, test.maxCleanupCount) if err != nil { t.Errorf("unexpected error: %v", err) continue @@ -386,7 +387,7 @@ func TestDeploymentController_cleanupUnhealthyReplicas(t *testing.T) { } } -func TestDeploymentController_scaleDownOldRCsForRollingUpdate(t *testing.T) { +func TestDeploymentController_scaleDownOldReplicaSetsForRollingUpdate(t *testing.T) { tests := []struct { deploymentReplicas int maxUnavailable intstr.IntOrString @@ -428,9 +429,9 @@ func TestDeploymentController_scaleDownOldRCsForRollingUpdate(t *testing.T) { for i, test := range tests { t.Logf("executing scenario %d", i) - oldRc := rc("foo-v2", test.oldReplicas, nil) - allRcs := []*api.ReplicationController{oldRc} - oldRcs := []*api.ReplicationController{oldRc} + oldRS := rs("foo-v2", test.oldReplicas, nil) + allRSs := []*extensions.ReplicaSet{oldRS} + oldRSs := []*extensions.ReplicaSet{oldRS} deployment := deployment("foo", test.deploymentReplicas, intstr.FromInt(0), test.maxUnavailable) fakeClientset := fake.Clientset{} fakeClientset.AddReactor("list", "pods", func(action core.Action) (handled bool, ret runtime.Object, err error) { @@ -440,7 +441,8 @@ func TestDeploymentController_scaleDownOldRCsForRollingUpdate(t *testing.T) { for podIndex := 0; podIndex < test.readyPods; podIndex++ { podList.Items = append(podList.Items, api.Pod{ ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("%s-pod-%d", oldRc.Name, podIndex), + Name: fmt.Sprintf("%s-pod-%d", oldRS.Name, podIndex), + Labels: map[string]string{"foo": "bar"}, }, Status: api.PodStatus{ Conditions: []api.PodCondition{ @@ -458,9 +460,10 @@ func TestDeploymentController_scaleDownOldRCsForRollingUpdate(t *testing.T) { }) controller := &DeploymentController{ client: &fakeClientset, + expClient: fake.Extensions(), eventRecorder: &record.FakeRecorder{}, } - scaled, err := controller.scaleDownOldRCsForRollingUpdate(allRcs, oldRcs, deployment) + scaled, err := controller.scaleDownOldReplicaSetsForRollingUpdate(allRSs, oldRSs, deployment) if err != nil { t.Errorf("unexpected error: %v", err) continue @@ -492,42 +495,42 @@ func TestDeploymentController_scaleDownOldRCsForRollingUpdate(t *testing.T) { t.Errorf("expected an update action") continue } - updated := updateAction.GetObject().(*api.ReplicationController) + updated := updateAction.GetObject().(*exp.ReplicaSet) if e, a := test.expectedOldReplicas, updated.Spec.Replicas; e != a { t.Errorf("expected update to %d replicas, got %d", e, a) } } } -func TestDeploymentController_cleanupOldRCs(t *testing.T) { +func TestDeploymentController_cleanupOldReplicaSets(t *testing.T) { selector := map[string]string{"foo": "bar"} tests := []struct { - oldRCs []*api.ReplicationController + oldRSs []*extensions.ReplicaSet revisionHistoryLimit int expectedDeletions int }{ { - oldRCs: []*api.ReplicationController{ - rc("foo-1", 0, selector), - rc("foo-2", 0, selector), - rc("foo-3", 0, selector), + oldRSs: []*extensions.ReplicaSet{ + rs("foo-1", 0, selector), + rs("foo-2", 0, selector), + rs("foo-3", 0, selector), }, revisionHistoryLimit: 1, expectedDeletions: 2, }, { - oldRCs: []*api.ReplicationController{ - rc("foo-1", 0, selector), - rc("foo-2", 0, selector), + oldRSs: []*extensions.ReplicaSet{ + rs("foo-1", 0, selector), + rs("foo-2", 0, selector), }, revisionHistoryLimit: 0, expectedDeletions: 2, }, { - oldRCs: []*api.ReplicationController{ - rc("foo-1", 1, selector), - rc("foo-2", 1, selector), + oldRSs: []*extensions.ReplicaSet{ + rs("foo-1", 1, selector), + rs("foo-2", 1, selector), }, revisionHistoryLimit: 0, expectedDeletions: 0, @@ -539,14 +542,14 @@ func TestDeploymentController_cleanupOldRCs(t *testing.T) { controller := NewDeploymentController(fake, controller.NoResyncPeriodFunc) controller.eventRecorder = &record.FakeRecorder{} - controller.rcStoreSynced = alwaysReady + controller.rsStoreSynced = alwaysReady controller.podStoreSynced = alwaysReady - for _, rc := range test.oldRCs { - controller.rcStore.Add(rc) + for _, rs := range test.oldRSs { + controller.rsStore.Add(rs) } d := newDeployment(1, &tests[i].revisionHistoryLimit) - controller.cleanupOldRcs(test.oldRCs, *d) + controller.cleanupOldReplicaSets(test.oldRSs, *d) gotDeletions := 0 for _, action := range fake.Actions() { @@ -555,20 +558,20 @@ func TestDeploymentController_cleanupOldRCs(t *testing.T) { } } if gotDeletions != test.expectedDeletions { - t.Errorf("expect %v old rcs been deleted, but got %v", test.expectedDeletions, gotDeletions) + t.Errorf("expect %v old replica sets been deleted, but got %v", test.expectedDeletions, gotDeletions) continue } } } -func rc(name string, replicas int, selector map[string]string) *api.ReplicationController { - return &api.ReplicationController{ +func rs(name string, replicas int, selector map[string]string) *extensions.ReplicaSet { + return &extensions.ReplicaSet{ ObjectMeta: api.ObjectMeta{ Name: name, }, - Spec: api.ReplicationControllerSpec{ + Spec: exp.ReplicaSetSpec{ Replicas: replicas, - Selector: selector, + Selector: &exp.LabelSelector{MatchLabels: selector}, Template: &api.PodTemplateSpec{}, }, } @@ -609,7 +612,7 @@ func newDeployment(replicas int, revisionHistoryLimit *int) *exp.Deployment { RollingUpdate: &exp.RollingUpdateDeployment{}, }, Replicas: replicas, - Selector: map[string]string{"foo": "bar"}, + Selector: &exp.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{ @@ -640,13 +643,13 @@ func getKey(d *exp.Deployment, t *testing.T) string { } } -func newReplicationController(d *exp.Deployment, name string, replicas int) *api.ReplicationController { - return &api.ReplicationController{ +func newReplicaSet(d *exp.Deployment, name string, replicas int) *exp.ReplicaSet { + return &exp.ReplicaSet{ ObjectMeta: api.ObjectMeta{ Name: name, Namespace: api.NamespaceDefault, }, - Spec: api.ReplicationControllerSpec{ + Spec: exp.ReplicaSetSpec{ Replicas: replicas, Template: &d.Spec.Template, }, @@ -664,7 +667,7 @@ type fixture struct { client *fake.Clientset // Objects to put in the store. dStore []*exp.Deployment - rcStore []*api.ReplicationController + rsStore []*exp.ReplicaSet podStore []*api.Pod // Actions expected to happen on the client. Objects from here are also @@ -678,14 +681,14 @@ func (f *fixture) expectUpdateDeploymentAction(d *exp.Deployment) { f.objects.Items = append(f.objects.Items, d) } -func (f *fixture) expectCreateRCAction(rc *api.ReplicationController) { - f.actions = append(f.actions, core.NewCreateAction("replicationcontrollers", rc.Namespace, rc)) - f.objects.Items = append(f.objects.Items, rc) +func (f *fixture) expectCreateRSAction(rs *extensions.ReplicaSet) { + f.actions = append(f.actions, core.NewCreateAction("replicasets", rs.Namespace, rs)) + f.objects.Items = append(f.objects.Items, rs) } -func (f *fixture) expectUpdateRCAction(rc *api.ReplicationController) { - f.actions = append(f.actions, core.NewUpdateAction("replicationcontrollers", rc.Namespace, rc)) - f.objects.Items = append(f.objects.Items, rc) +func (f *fixture) expectUpdateRSAction(rs *extensions.ReplicaSet) { + f.actions = append(f.actions, core.NewUpdateAction("replicasets", rs.Namespace, rs)) + f.objects.Items = append(f.objects.Items, rs) } func (f *fixture) expectListPodAction(namespace string, opt api.ListOptions) { @@ -703,13 +706,13 @@ func (f *fixture) run(deploymentName string) { f.client = fake.NewSimpleClientset(f.objects) c := NewDeploymentController(f.client, controller.NoResyncPeriodFunc) c.eventRecorder = &record.FakeRecorder{} - c.rcStoreSynced = alwaysReady + c.rsStoreSynced = alwaysReady c.podStoreSynced = alwaysReady for _, d := range f.dStore { c.dStore.Store.Add(d) } - for _, rc := range f.rcStore { - c.rcStore.Store.Add(rc) + for _, rs := range f.rsStore { + c.rsStore.Store.Add(rs) } for _, pod := range f.podStore { c.podStore.Store.Add(pod) @@ -739,22 +742,22 @@ func (f *fixture) run(deploymentName string) { } } -func TestSyncDeploymentCreatesRC(t *testing.T) { +func TestSyncDeploymentCreatesReplicaSet(t *testing.T) { f := newFixture(t) d := newDeployment(1, nil) f.dStore = append(f.dStore, d) - // expect that one rc with zero replicas is created + // expect that one ReplicaSet with zero replicas is created // then is updated to 1 replica - rc := newReplicationController(d, "deploymentrc-4186632231", 0) - updatedRC := newReplicationController(d, "deploymentrc-4186632231", 1) + rs := newReplicaSet(d, "deploymentrs-4186632231", 0) + updatedRS := newReplicaSet(d, "deploymentrs-4186632231", 1) opt := newListOptions() - f.expectCreateRCAction(rc) + f.expectCreateRSAction(rs) f.expectUpdateDeploymentAction(d) - f.expectUpdateRCAction(updatedRC) - f.expectListPodAction(rc.Namespace, opt) + f.expectUpdateRSAction(updatedRS) + f.expectListPodAction(rs.Namespace, opt) f.expectUpdateDeploymentAction(d) f.run(getKey(d, t)) diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 61b4085919b..cb796e03b96 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -447,11 +447,20 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } switch t := object.(type) { case *api.ReplicationController: - return GetFirstPod(client, t.Namespace, t.Spec.Selector) + selector := labels.SelectorFromSet(t.Spec.Selector) + return GetFirstPod(client, t.Namespace, selector) case *extensions.Deployment: - return GetFirstPod(client, t.Namespace, t.Spec.Selector) + selector, err := extensions.LabelSelectorAsSelector(t.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("failed to convert label selector to selector: %v", err) + } + return GetFirstPod(client, t.Namespace, selector) case *extensions.Job: - return GetFirstPod(client, t.Namespace, t.Spec.Selector.MatchLabels) + selector, err := extensions.LabelSelectorAsSelector(t.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("failed to convert label selector to selector: %v", err) + } + return GetFirstPod(client, t.Namespace, selector) case *api.Pod: return t, nil default: @@ -469,12 +478,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } // GetFirstPod returns the first pod of an object from its namespace and selector -func GetFirstPod(client *client.Client, namespace string, selector map[string]string) (*api.Pod, error) { +func GetFirstPod(client *client.Client, namespace string, selector labels.Selector) (*api.Pod, error) { var pods *api.PodList for pods == nil || len(pods.Items) == 0 { var err error - labelSelector := labels.SelectorFromSet(selector) - options := api.ListOptions{LabelSelector: labelSelector} + options := api.ListOptions{LabelSelector: selector} if pods, err = client.Pods(namespace).List(options); err != nil { return nil, err } diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index a48dac48aa9..1c82fb7fa21 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -1612,7 +1612,7 @@ func (dd *DeploymentDescriber) Describe(namespace, name string) (string, error) fmt.Fprintf(out, "Namespace:\t%s\n", d.ObjectMeta.Namespace) fmt.Fprintf(out, "CreationTimestamp:\t%s\n", d.CreationTimestamp.Time.Format(time.RFC1123Z)) fmt.Fprintf(out, "Labels:\t%s\n", labels.FormatLabels(d.Labels)) - fmt.Fprintf(out, "Selector:\t%s\n", labels.FormatLabels(d.Spec.Selector)) + fmt.Fprintf(out, "Selector:\t%s\n", d.Spec.Selector) fmt.Fprintf(out, "Replicas:\t%d updated | %d total | %d available | %d unavailable\n", d.Status.UpdatedReplicas, d.Spec.Replicas, d.Status.AvailableReplicas, d.Status.UnavailableReplicas) fmt.Fprintf(out, "StrategyType:\t%s\n", d.Spec.Strategy.Type) fmt.Fprintf(out, "MinReadySeconds:\t%s\n", d.Spec.MinReadySeconds) @@ -1620,17 +1620,17 @@ func (dd *DeploymentDescriber) Describe(namespace, name string) (string, error) ru := d.Spec.Strategy.RollingUpdate fmt.Fprintf(out, "RollingUpdateStrategy:\t%s max unavailable, %s max surge\n", ru.MaxUnavailable.String(), ru.MaxSurge.String()) } - oldRCs, _, err := deploymentutil.GetOldRCs(*d, dd) + oldRSs, _, err := deploymentutil.GetOldReplicaSets(*d, dd) if err == nil { - fmt.Fprintf(out, "OldReplicationControllers:\t%s\n", printReplicationControllersByLabels(oldRCs)) + fmt.Fprintf(out, "OldReplicaSets:\t%s\n", printReplicaSetsByLabels(oldRSs)) } - newRC, err := deploymentutil.GetNewRC(*d, dd) + newRS, err := deploymentutil.GetNewReplicaSet(*d, dd) if err == nil { - var newRCs []*api.ReplicationController - if newRC != nil { - newRCs = append(newRCs, newRC) + var newRSs []*extensions.ReplicaSet + if newRS != nil { + newRSs = append(newRSs, newRS) } - fmt.Fprintf(out, "NewReplicationController:\t%s\n", printReplicationControllersByLabels(newRCs)) + fmt.Fprintf(out, "NewReplicaSet:\t%s\n", printReplicaSetsByLabels(newRSs)) } events, err := dd.Core().Events(namespace).Search(d) if err == nil && events != nil { @@ -1705,6 +1705,20 @@ func printReplicationControllersByLabels(matchingRCs []*api.ReplicationControlle return list } +func printReplicaSetsByLabels(matchingRSs []*extensions.ReplicaSet) string { + // Format the matching ReplicaSets into strings. + var rsStrings []string + for _, rs := range matchingRSs { + rsStrings = append(rsStrings, fmt.Sprintf("%s (%d/%d replicas created)", rs.Name, rs.Status.Replicas, rs.Spec.Replicas)) + } + + list := strings.Join(rsStrings, ", ") + if list == "" { + return "" + } + return list +} + func getPodStatusForController(c client.PodInterface, selector labels.Selector) (running, waiting, succeeded, failed int, err error) { options := api.ListOptions{LabelSelector: selector} rcPods, err := c.List(options) diff --git a/pkg/kubectl/run.go b/pkg/kubectl/run.go index 207e22c1853..c6a93185bc0 100644 --- a/pkg/kubectl/run.go +++ b/pkg/kubectl/run.go @@ -103,7 +103,7 @@ func (DeploymentV1Beta1) Generate(genericParams map[string]interface{}) (runtime }, Spec: extensions.DeploymentSpec{ Replicas: count, - Selector: labels, + Selector: &extensions.LabelSelector{MatchLabels: labels}, Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: labels, diff --git a/pkg/kubectl/run_test.go b/pkg/kubectl/run_test.go index d10abfdb2e9..e9b6e6465e5 100644 --- a/pkg/kubectl/run_test.go +++ b/pkg/kubectl/run_test.go @@ -656,7 +656,7 @@ func TestGenerateDeployment(t *testing.T) { }, Spec: extensions.DeploymentSpec{ Replicas: 3, - Selector: map[string]string{"foo": "bar", "baz": "blah"}, + Selector: &extensions.LabelSelector{MatchLabels: map[string]string{"foo": "bar", "baz": "blah"}}, Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{"foo": "bar", "baz": "blah"}, diff --git a/pkg/kubectl/scale.go b/pkg/kubectl/scale.go index 44214cecb8e..0d5edad82a3 100644 --- a/pkg/kubectl/scale.go +++ b/pkg/kubectl/scale.go @@ -342,7 +342,10 @@ func (scaler *DeploymentScaler) ScaleSimple(namespace, name string, precondition return err } } - scale := extensions.ScaleFromDeployment(deployment) + scale, err := extensions.ScaleFromDeployment(deployment) + if err != nil { + return ScaleError{ScaleUpdateFailure, deployment.ResourceVersion, err} + } scale.Spec.Replicas = int(newSize) if _, err := scaler.c.Scales(namespace).Update("Deployment", scale); err != nil { if errors.IsInvalid(err) { diff --git a/pkg/registry/deployment/etcd/etcd.go b/pkg/registry/deployment/etcd/etcd.go index f72f79a17ec..79f09afdca3 100644 --- a/pkg/registry/deployment/etcd/etcd.go +++ b/pkg/registry/deployment/etcd/etcd.go @@ -196,7 +196,11 @@ func (r *ScaleREST) Get(ctx api.Context, name string) (runtime.Object, error) { if err != nil { return nil, errors.NewNotFound(extensions.Resource("deployments/scale"), name) } - return extensions.ScaleFromDeployment(deployment), nil + scale, err := extensions.ScaleFromDeployment(deployment) + if err != nil { + return nil, errors.NewBadRequest(fmt.Sprintf("%v", err)) + } + return scale, nil } func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) { @@ -221,5 +225,9 @@ func (r *ScaleREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, if err != nil { return nil, false, errors.NewConflict(extensions.Resource("deployments/scale"), scale.Name, err) } - return extensions.ScaleFromDeployment(deployment), false, nil + newScale, err := extensions.ScaleFromDeployment(deployment) + if err != nil { + return nil, false, errors.NewBadRequest(fmt.Sprintf("%v", err)) + } + return newScale, false, nil } diff --git a/pkg/registry/deployment/etcd/etcd_test.go b/pkg/registry/deployment/etcd/etcd_test.go index 0d7171ca2ef..b866d722f83 100644 --- a/pkg/registry/deployment/etcd/etcd_test.go +++ b/pkg/registry/deployment/etcd/etcd_test.go @@ -50,7 +50,7 @@ func validNewDeployment() *extensions.Deployment { Namespace: namespace, }, Spec: extensions.DeploymentSpec{ - Selector: map[string]string{"a": "b"}, + Selector: &extensions.LabelSelector{MatchLabels: map[string]string{"a": "b"}}, Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: map[string]string{"a": "b"}, @@ -89,7 +89,7 @@ func TestCreate(t *testing.T) { // invalid (invalid selector) &extensions.Deployment{ Spec: extensions.DeploymentSpec{ - Selector: map[string]string{}, + Selector: &extensions.LabelSelector{MatchLabels: map[string]string{}}, Template: validDeployment.Spec.Template, }, }, @@ -127,7 +127,7 @@ func TestUpdate(t *testing.T) { }, func(obj runtime.Object) runtime.Object { object := obj.(*extensions.Deployment) - object.Spec.Selector = map[string]string{} + object.Spec.Selector = &extensions.LabelSelector{MatchLabels: map[string]string{}} return object }, ) diff --git a/pkg/util/deployment/deployment.go b/pkg/util/deployment/deployment.go index f3fcf04de04..e33a669fe4f 100644 --- a/pkg/util/deployment/deployment.go +++ b/pkg/util/deployment/deployment.go @@ -30,7 +30,7 @@ import ( ) const ( - // The revision annotation of a deployment's replication controllers which records its rollout sequence + // The revision annotation of a deployment's replica sets which records its rollout sequence RevisionAnnotation = "deployment.kubernetes.io/revision" // Here are the possible rollback event reasons @@ -39,112 +39,116 @@ const ( RollbackDone = "DeploymentRollback" ) -// GetOldRCs returns the old RCs targeted by the given Deployment; get PodList and RCList from client interface. -// Note that the first set of old RCs doesn't include the ones with no pods, and the second set of old RCs include all old RCs. -func GetOldRCs(deployment extensions.Deployment, c clientset.Interface) ([]*api.ReplicationController, []*api.ReplicationController, error) { - return GetOldRCsFromLists(deployment, c, +// GetOldReplicaSets returns the old replica sets targeted by the given Deployment; get PodList and ReplicaSetList from client interface. +// Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets. +func GetOldReplicaSets(deployment extensions.Deployment, c clientset.Interface) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { + return GetOldReplicaSetsFromLists(deployment, c, func(namespace string, options api.ListOptions) (*api.PodList, error) { return c.Core().Pods(namespace).List(options) }, - func(namespace string, options api.ListOptions) ([]api.ReplicationController, error) { - rcList, err := c.Core().ReplicationControllers(namespace).List(options) - return rcList.Items, err + func(namespace string, options api.ListOptions) ([]extensions.ReplicaSet, error) { + rsList, err := c.Extensions().ReplicaSets(namespace).List(options) + return rsList.Items, err }) } -// GetOldRCsFromLists returns two sets of old RCs targeted by the given Deployment; get PodList and RCList with input functions. -// Note that the first set of old RCs doesn't include the ones with no pods, and the second set of old RCs include all old RCs. -func GetOldRCsFromLists(deployment extensions.Deployment, c clientset.Interface, getPodList func(string, api.ListOptions) (*api.PodList, error), getRcList func(string, api.ListOptions) ([]api.ReplicationController, error)) ([]*api.ReplicationController, []*api.ReplicationController, error) { +// GetOldReplicaSetsFromLists returns two sets of old replica sets targeted by the given Deployment; get PodList and ReplicaSetList with input functions. +// Note that the first set of old replica sets doesn't include the ones with no pods, and the second set of old replica sets include all old replica sets. +func GetOldReplicaSetsFromLists(deployment extensions.Deployment, c clientset.Interface, getPodList func(string, api.ListOptions) (*api.PodList, error), getRcList func(string, api.ListOptions) ([]extensions.ReplicaSet, error)) ([]*extensions.ReplicaSet, []*extensions.ReplicaSet, error) { namespace := deployment.ObjectMeta.Namespace + selector, err := extensions.LabelSelectorAsSelector(deployment.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("failed to convert LabelSelector to Selector: %v", err) + } + // 1. Find all pods whose labels match deployment.Spec.Selector - selector := labels.SelectorFromSet(deployment.Spec.Selector) options := api.ListOptions{LabelSelector: selector} podList, err := getPodList(namespace, options) if err != nil { return nil, nil, fmt.Errorf("error listing pods: %v", err) } - // 2. Find the corresponding RCs for pods in podList. - // TODO: Right now we list all RCs and then filter. We should add an API for this. - oldRCs := map[string]api.ReplicationController{} - allOldRCs := map[string]api.ReplicationController{} - rcList, err := getRcList(namespace, options) + // 2. Find the corresponding replica sets for pods in podList. + // TODO: Right now we list all replica sets and then filter. We should add an API for this. + oldRSs := map[string]extensions.ReplicaSet{} + allOldRSs := map[string]extensions.ReplicaSet{} + rsList, err := getRcList(namespace, options) if err != nil { - return nil, nil, fmt.Errorf("error listing replication controllers: %v", err) + return nil, nil, fmt.Errorf("error listing replica sets: %v", err) } - newRCTemplate := GetNewRCTemplate(deployment) + newRSTemplate := GetNewReplicaSetTemplate(deployment) for _, pod := range podList.Items { podLabelsSelector := labels.Set(pod.ObjectMeta.Labels) - for _, rc := range rcList { - rcLabelsSelector := labels.SelectorFromSet(rc.Spec.Selector) - // Filter out RC that has the same pod template spec as the deployment - that is the new RC. - if api.Semantic.DeepEqual(rc.Spec.Template, &newRCTemplate) { + for _, rs := range rsList { + rsLabelsSelector := labels.SelectorFromSet(rs.Spec.Selector) + // Filter out replica set that has the same pod template spec as the deployment - that is the new replica set. + if api.Semantic.DeepEqual(rs.Spec.Template, &newRSTemplate) { continue } - allOldRCs[rc.ObjectMeta.Name] = rc - if rcLabelsSelector.Matches(podLabelsSelector) { - oldRCs[rc.ObjectMeta.Name] = rc + allOldRSs[rs.ObjectMeta.Name] = rs + if rsLabelsSelector.Matches(podLabelsSelector) { + oldRSs[rs.ObjectMeta.Name] = rs } } } - requiredRCs := []*api.ReplicationController{} - for key := range oldRCs { - value := oldRCs[key] - requiredRCs = append(requiredRCs, &value) + requiredRSs := []*extensions.ReplicaSet{} + for key := range oldRSs { + value := oldRSs[key] + requiredRSs = append(requiredRSs, &value) } - allRCs := []*api.ReplicationController{} - for key := range allOldRCs { - value := allOldRCs[key] - allRCs = append(allRCs, &value) + allRSs := []*extensions.ReplicaSet{} + for key := range allOldRSs { + value := allOldRSs[key] + allRSs = append(allRSs, &value) } - return requiredRCs, allRCs, nil + return requiredRSs, allRSs, nil } -// GetNewRC returns an RC that matches the intent of the given deployment; get RCList from client interface. -// Returns nil if the new RC doesnt exist yet. -func GetNewRC(deployment extensions.Deployment, c clientset.Interface) (*api.ReplicationController, error) { - return GetNewRCFromList(deployment, c, - func(namespace string, options api.ListOptions) ([]api.ReplicationController, error) { - rcList, err := c.Core().ReplicationControllers(namespace).List(options) - return rcList.Items, err +// GetNewReplicaSet returns a replica set that matches the intent of the given deployment; get ReplicaSetList from client interface. +// Returns nil if the new replica set doesnt exist yet. +func GetNewReplicaSet(deployment extensions.Deployment, c clientset.Interface) (*extensions.ReplicaSet, error) { + return GetNewReplicaSetFromList(deployment, c, + func(namespace string, options api.ListOptions) ([]extensions.ReplicaSet, error) { + rsList, err := c.Extensions().ReplicaSets(namespace).List(options) + return rsList.Items, err }) } -// GetNewRCFromList returns an RC that matches the intent of the given deployment; get RCList with the input function. -// Returns nil if the new RC doesnt exist yet. -func GetNewRCFromList(deployment extensions.Deployment, c clientset.Interface, getRcList func(string, api.ListOptions) ([]api.ReplicationController, error)) (*api.ReplicationController, error) { +// GetNewReplicaSetFromList returns a replica set that matches the intent of the given deployment; get ReplicaSetList with the input function. +// Returns nil if the new replica set doesnt exist yet. +func GetNewReplicaSetFromList(deployment extensions.Deployment, c clientset.Interface, getRcList func(string, api.ListOptions) ([]extensions.ReplicaSet, error)) (*extensions.ReplicaSet, error) { namespace := deployment.ObjectMeta.Namespace - rcList, err := getRcList(namespace, api.ListOptions{LabelSelector: labels.SelectorFromSet(deployment.Spec.Selector)}) + rsList, err := getRcList(namespace, api.ListOptions{LabelSelector: labels.SelectorFromSet(deployment.Spec.Selector)}) if err != nil { - return nil, fmt.Errorf("error listing replication controllers: %v", err) + return nil, fmt.Errorf("error listing ReplicaSets: %v", err) } - newRCTemplate := GetNewRCTemplate(deployment) + newRSTemplate := GetNewReplicaSetTemplate(deployment) - for i := range rcList { - if api.Semantic.DeepEqual(rcList[i].Spec.Template, &newRCTemplate) { - // This is the new RC. - return &rcList[i], nil + for i := range rsList { + if api.Semantic.DeepEqual(rsList[i].Spec.Template, &newRSTemplate) { + // This is the new ReplicaSet. + return &rsList[i], nil } } - // new RC does not exist. + // new ReplicaSet does not exist. return nil, nil } -// Returns the desired PodTemplateSpec for the new RC corresponding to the given RC. -func GetNewRCTemplate(deployment extensions.Deployment) api.PodTemplateSpec { - // newRC will have the same template as in deployment spec, plus a unique label in some cases. - newRCTemplate := api.PodTemplateSpec{ +// 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, } - newRCTemplate.ObjectMeta.Labels = labelsutil.CloneAndAddLabel( + newRSTemplate.ObjectMeta.Labels = labelsutil.CloneAndAddLabel( deployment.Spec.Template.ObjectMeta.Labels, extensions.DefaultDeploymentUniqueLabelKey, - podutil.GetPodTemplateSpecHash(newRCTemplate)) - return newRCTemplate + podutil.GetPodTemplateSpecHash(newRSTemplate)) + return newRSTemplate } -// SetTemplate sets the desired PodTemplateSpec from an RC template to the given deployment. -func SetFromRCTemplate(deployment *extensions.Deployment, template api.PodTemplateSpec) *extensions.Deployment { +// SetFromReplicaSetTemplate sets the desired PodTemplateSpec from a replica set template to the given deployment. +func SetFromReplicaSetTemplate(deployment *extensions.Deployment, template api.PodTemplateSpec) *extensions.Deployment { deployment.Spec.Template.ObjectMeta = template.ObjectMeta deployment.Spec.Template.Spec = template.Spec deployment.Spec.Template.ObjectMeta.Labels = labelsutil.CloneAndRemoveLabel( @@ -153,18 +157,18 @@ func SetFromRCTemplate(deployment *extensions.Deployment, template api.PodTempla return deployment } -// Returns the sum of Replicas of the given replication controllers. -func GetReplicaCountForRCs(replicationControllers []*api.ReplicationController) int { +// Returns the sum of Replicas of the given replica sets. +func GetReplicaCountForReplicaSets(replicationControllers []*extensions.ReplicaSet) int { totalReplicaCount := 0 - for _, rc := range replicationControllers { - totalReplicaCount += rc.Spec.Replicas + for _, rs := range replicaSets { + totalReplicaCount += rs.Spec.Replicas } return totalReplicaCount } -// Returns the number of available pods corresponding to the given RCs. -func GetAvailablePodsForRCs(c clientset.Interface, rcs []*api.ReplicationController, minReadySeconds int) (int, error) { - allPods, err := getPodsForRCs(c, rcs) +// Returns the number of available pods corresponding to the given replica sets. +func GetAvailablePodsForReplicaSets(c clientset.Interface, rss []*extensions.ReplicaSet, minReadySeconds int) (int, error) { + allPods, err := getPodsForReplicaSets(c, rss) if err != nil { return 0, err } @@ -195,12 +199,15 @@ func getReadyPodsCount(pods []api.Pod, minReadySeconds int) int { return readyPodCount } -func getPodsForRCs(c clientset.Interface, replicationControllers []*api.ReplicationController) ([]api.Pod, error) { +func getPodsForReplicaSets(c clientset.Interface, replicationControllers []*extensions.ReplicaSet) ([]api.Pod, error) { allPods := []api.Pod{} - for _, rc := range replicationControllers { - selector := labels.SelectorFromSet(rc.Spec.Selector) + for _, rs := range replicaSets { + selector, err := extensions.LabelSelectorAsSelector(rs.Spec.Selector) + if err != nil { + return nil, fmt.Errorf("failed to convert LabelSelector to Selector: %v", err) + } options := api.ListOptions{LabelSelector: selector} - podList, err := c.Core().Pods(rc.ObjectMeta.Namespace).List(options) + podList, err := c.Core().Pods(rs.ObjectMeta.Namespace).List(options) if err != nil { return allPods, fmt.Errorf("error listing pods: %v", err) } @@ -209,9 +216,9 @@ func getPodsForRCs(c clientset.Interface, replicationControllers []*api.Replicat return allPods, nil } -// Revision returns the revision number of the input RC -func Revision(rc *api.ReplicationController) (int64, error) { - v, ok := rc.Annotations[RevisionAnnotation] +// Revision returns the revision number of the input replica set +func Revision(rs *extensions.ReplicaSet) (int64, error) { + v, ok := rs.Annotations[RevisionAnnotation] if !ok { return 0, nil } diff --git a/pkg/util/deployment/deployment_test.go b/pkg/util/deployment/deployment_test.go index cd86d2bdae1..c1ae5583cad 100644 --- a/pkg/util/deployment/deployment_test.go +++ b/pkg/util/deployment/deployment_test.go @@ -82,13 +82,13 @@ func TestGetReadyPodsCount(t *testing.T) { } } -// generatePodFromRC creates a pod, with the input rc's selector and its template -func generatePodFromRC(rc api.ReplicationController) api.Pod { +// generatePodFromRS creates a pod, with the input ReplicaSet's selector and its template +func generatePodFromRS(rs extensions.ReplicaSet) api.Pod { return api.Pod{ ObjectMeta: api.ObjectMeta{ - Labels: rc.Spec.Selector, + Labels: rs.Labels, }, - Spec: rc.Spec.Template.Spec, + Spec: rs.Spec.Template.Spec, } } @@ -110,15 +110,15 @@ func generatePod(labels map[string]string, image string) api.Pod { } } -func generateRCWithLabel(labels map[string]string, image string) api.ReplicationController { - return api.ReplicationController{ +func generateRSWithLabel(labels map[string]string, image string) extensions.ReplicaSet { + return extensions.ReplicaSet{ ObjectMeta: api.ObjectMeta{ - Name: api.SimpleNameGenerator.GenerateName("rc"), + Name: api.SimpleNameGenerator.GenerateName("replicaset"), Labels: labels, }, - Spec: api.ReplicationControllerSpec{ + Spec: extensions.ReplicaSetSpec{ Replicas: 1, - Selector: labels, + Selector: &extensions.LabelSelector{MatchLabels: labels}, Template: &api.PodTemplateSpec{ Spec: api.PodSpec{ Containers: []api.Container{ @@ -135,17 +135,17 @@ func generateRCWithLabel(labels map[string]string, image string) api.Replication } } -// generateRC creates a replication controller, with the input deployment's template as its template -func generateRC(deployment extensions.Deployment) api.ReplicationController { - template := GetNewRCTemplate(deployment) - return api.ReplicationController{ +// generateRS creates a replica set, with the input deployment's template as its template +func generateRS(deployment extensions.Deployment) extensions.ReplicaSet { + template := GetNewReplicaSetTemplate(deployment) + return extensions.ReplicaSet{ ObjectMeta: api.ObjectMeta{ - Name: api.SimpleNameGenerator.GenerateName("rc"), + Name: api.SimpleNameGenerator.GenerateName("replicaset"), Labels: template.Labels, }, - Spec: api.ReplicationControllerSpec{ + Spec: extensions.ReplicaSetSpec{ Template: &template, - Selector: template.Labels, + Selector: &extensions.LabelSelector{MatchLabels: template.Labels}, }, } } @@ -160,7 +160,7 @@ func generateDeployment(image string) extensions.Deployment { }, Spec: extensions.DeploymentSpec{ Replicas: 1, - Selector: podLabels, + Selector: &extensions.LabelSelector{MatchLabels: podLabels}, Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ Labels: podLabels, @@ -186,32 +186,32 @@ func generateDeployment(image string) extensions.Deployment { func TestGetNewRC(t *testing.T) { newDeployment := generateDeployment("nginx") - newRC := generateRC(newDeployment) + newRC := generateRS(newDeployment) tests := []struct { test string - rcList api.ReplicationControllerList - expected *api.ReplicationController + rsList extensions.ReplicaSetList + expected *extensions.ReplicaSet }{ { - "No new RC", - api.ReplicationControllerList{ - Items: []api.ReplicationController{ - generateRC(generateDeployment("foo")), - generateRC(generateDeployment("bar")), + "No new ReplicaSet", + extensions.ReplicaSetList{ + Items: []extensions.ReplicaSet{ + generateRS(generateDeployment("foo")), + generateRS(generateDeployment("bar")), }, }, nil, }, { - "Has new RC", - api.ReplicationControllerList{ - Items: []api.ReplicationController{ - generateRC(generateDeployment("foo")), - generateRC(generateDeployment("bar")), - generateRC(generateDeployment("abc")), + "Has new ReplicaSet", + extensions.ReplicaSetList{ + Items: []extensions.ReplicaSet{ + generateRS(generateDeployment("foo")), + generateRS(generateDeployment("bar")), + generateRS(generateDeployment("abc")), newRC, - generateRC(generateDeployment("xyz")), + generateRS(generateDeployment("xyz")), }, }, &newRC, @@ -223,69 +223,69 @@ func TestGetNewRC(t *testing.T) { c := &simple.Client{ Request: simple.Request{ Method: "GET", - Path: testapi.Default.ResourcePath("replicationControllers", ns, ""), + Path: testapi.Default.ResourcePath("replicaSets", ns, ""), }, Response: simple.Response{ StatusCode: 200, - Body: &test.rcList, + Body: &test.rsList, }, } - rc, err := GetNewRC(newDeployment, c.Setup(t).Clientset) + rs, err := GetNewReplicaSet(newDeployment, c.Setup(t).Clientset) if err != nil { t.Errorf("In test case %s, got unexpected error %v", test.test, err) } - if !api.Semantic.DeepEqual(rc, test.expected) { - t.Errorf("In test case %s, expected %+v, got %+v", test.test, test.expected, rc) + if !api.Semantic.DeepEqual(rs, test.expected) { + t.Errorf("In test case %s, expected %+v, got %+v", test.test, test.expected, rs) } } } func TestGetOldRCs(t *testing.T) { newDeployment := generateDeployment("nginx") - newRC := generateRC(newDeployment) - newPod := generatePodFromRC(newRC) + newRS := generateRS(newDeployment) + newPod := generatePodFromRS(newRS) - // create 2 old deployments and related rcs/pods, with the same labels but different template + // create 2 old deployments and related replica sets/pods, with the same labels but different template oldDeployment := generateDeployment("nginx") oldDeployment.Spec.Template.Spec.Containers[0].Name = "nginx-old-1" - oldRC := generateRC(oldDeployment) - oldPod := generatePodFromRC(oldRC) + oldRS := generateRS(oldDeployment) + oldPod := generatePodFromRS(oldRS) oldDeployment2 := generateDeployment("nginx") oldDeployment2.Spec.Template.Spec.Containers[0].Name = "nginx-old-2" - oldRC2 := generateRC(oldDeployment2) - oldPod2 := generatePodFromRC(oldRC2) + oldRS2 := generateRS(oldDeployment2) + oldPod2 := generatePodFromRS(oldRS2) - // create 1 rc that existed before the deployment, with the same labels as the deployment - existedPod := generatePod(newDeployment.Spec.Selector, "foo") - existedRC := generateRCWithLabel(newDeployment.Spec.Selector, "foo") + // create 1 ReplicaSet that existed before the deployment, with the same labels as the deployment + existedPod := generatePod(newDeployment.Spec.Template.Labels, "foo") + existedRS := generateRSWithLabel(newDeployment.Spec.Template.Labels, "foo") tests := []struct { test string objs []runtime.Object - expected []*api.ReplicationController + expected []*extensions.ReplicaSet }{ { - "No old RCs", + "No old ReplicaSets", []runtime.Object{ &api.PodList{ Items: []api.Pod{ - generatePod(newDeployment.Spec.Selector, "foo"), - generatePod(newDeployment.Spec.Selector, "bar"), + generatePod(newDeployment.Spec.Template.Labels, "foo"), + generatePod(newDeployment.Spec.Template.Labels, "bar"), newPod, }, }, - &api.ReplicationControllerList{ - Items: []api.ReplicationController{ - generateRC(generateDeployment("foo")), - newRC, - generateRC(generateDeployment("bar")), + &extensions.ReplicaSetList{ + Items: []extensions.ReplicaSet{ + generateRS(generateDeployment("foo")), + newRS, + generateRS(generateDeployment("bar")), }, }, }, - []*api.ReplicationController{}, + []*extensions.ReplicaSet{}, }, { - "Has old RC", + "Has old ReplicaSet", []runtime.Object{ &api.PodList{ Items: []api.Pod{ @@ -294,51 +294,51 @@ func TestGetOldRCs(t *testing.T) { generatePod(map[string]string{"name": "bar"}, "bar"), generatePod(map[string]string{"name": "xyz"}, "xyz"), existedPod, - generatePod(newDeployment.Spec.Selector, "abc"), + generatePod(newDeployment.Spec.Template.Labels, "abc"), }, }, - &api.ReplicationControllerList{ - Items: []api.ReplicationController{ - oldRC2, - oldRC, - existedRC, - newRC, - generateRCWithLabel(map[string]string{"name": "xyz"}, "xyz"), - generateRCWithLabel(map[string]string{"name": "bar"}, "bar"), + &extensions.ReplicaSetList{ + Items: []extensions.ReplicaSet{ + oldRS2, + oldRS, + existedRS, + newRS, + generateRSWithLabel(map[string]string{"name": "xyz"}, "xyz"), + generateRSWithLabel(map[string]string{"name": "bar"}, "bar"), }, }, }, - []*api.ReplicationController{&oldRC, &oldRC2, &existedRC}, + []*extensions.ReplicaSet{&oldRS, &oldRS2, &existedRS}, }, } for _, test := range tests { - rcs, _, err := GetOldRCs(newDeployment, fake.NewSimpleClientset(test.objs...)) + rss, _, err := GetOldReplicaSets(newDeployment, fake.NewSimpleClientset(test.objs...)) if err != nil { t.Errorf("In test case %s, got unexpected error %v", test.test, err) } - if !equal(rcs, test.expected) { - t.Errorf("In test case %q, expected %v, got %v", test.test, test.expected, rcs) + if !equal(rss, test.expected) { + t.Errorf("In test case %q, expected %v, got %v", test.test, test.expected, rss) } } } -// equal compares the equality of two rc slices regardless of their ordering -func equal(rcs1, rcs2 []*api.ReplicationController) bool { - if reflect.DeepEqual(rcs1, rcs2) { +// equal compares the equality of two ReplicaSet slices regardless of their ordering +func equal(rss1, rss2 []*extensions.ReplicaSet) bool { + if reflect.DeepEqual(rss1, rss2) { return true } - if rcs1 == nil || rcs2 == nil || len(rcs1) != len(rcs2) { + if rss1 == nil || rss2 == nil || len(rss1) != len(rss2) { return false } count := 0 - for _, rc1 := range rcs1 { - for _, rc2 := range rcs2 { - if reflect.DeepEqual(rc1, rc2) { + for _, rs1 := range rss1 { + for _, rs2 := range rss2 { + if reflect.DeepEqual(rs1, rs2) { count++ break } } } - return count == len(rcs1) + return count == len(rss1) } diff --git a/pkg/util/labels/labels.go b/pkg/util/labels/labels.go index 6627d5e357a..f6eb596b5fa 100644 --- a/pkg/util/labels/labels.go +++ b/pkg/util/labels/labels.go @@ -18,6 +18,8 @@ package labels import ( "fmt" + + "k8s.io/kubernetes/pkg/apis/extensions" ) // Clones the given map and returns a new map with the given key and value added. @@ -51,3 +53,45 @@ func CloneAndRemoveLabel(labels map[string]string, labelKey string) map[string]s delete(newLabels, labelKey) return newLabels } + +// Clones the given selector and returns a new selector with the given key and value added. +// Returns the given selector, if labelKey is empty. +func CloneSelectorAndAddLabel(selector *extensions.LabelSelector, labelKey string, labelValue uint32) *extensions.LabelSelector { + if labelKey == "" { + // Dont need to add a label. + return selector + } + + // Clone. + newSelector := new(extensions.LabelSelector) + + // TODO(madhusudancs): Check if you can use deepCopy_extensions_LabelSelector here. + newSelector.MatchLabels = make(map[string]string) + if selector.MatchLabels != nil { + for key, val := range selector.MatchLabels { + newSelector.MatchLabels[key] = val + } + } + newSelector.MatchLabels[labelKey] = fmt.Sprintf("%d", labelValue) + + if selector.MatchExpressions != nil { + newMExps := make([]extensions.LabelSelectorRequirement, len(selector.MatchExpressions)) + for i, me := range selector.MatchExpressions { + newMExps[i].Key = me.Key + newMExps[i].Operator = me.Operator + if me.Values != nil { + newMExps[i].Values = make([]string, len(me.Values)) + for j, val := range me.Values { + newMExps[i].Values[j] = val + } + } else { + newMExps[i].Values = nil + } + } + newSelector.MatchExpressions = newMExps + } else { + newSelector.MatchExpressions = nil + } + + return newSelector +} diff --git a/test/e2e/deployment.go b/test/e2e/deployment.go index 70f01a39e3b..6b50c3f845c 100644 --- a/test/e2e/deployment.go +++ b/test/e2e/deployment.go @@ -47,7 +47,7 @@ var _ = Describe("Deployment [Feature:Deployment]", func() { It("RecreateDeployment should delete old pods and create new ones", func() { testRecreateDeployment(f) }) - It("deployment should delete old rcs", func() { + It("deployment should delete old replica sets", func() { testDeploymentCleanUpPolicy(f) }) It("deployment should support rollover", func() { @@ -59,22 +59,22 @@ var _ = Describe("Deployment [Feature:Deployment]", func() { It("deployment should support rollback", func() { testRollbackDeployment(f) }) - It("deployment should support rollback when there's RC with no revision", func() { - testRollbackDeploymentRCNoRevision(f) + It("deployment should support rollback when there's replica set with no revision", func() { + testRollbackDeploymentRSNoRevision(f) }) }) -func newRC(rcName string, replicas int, rcPodLabels map[string]string, imageName string, image string) *api.ReplicationController { - return &api.ReplicationController{ +func newReplicaSet(rsName string, replicas int, rsPodLabels map[string]string, imageName string, image string) *extensions.ReplicaSet { + return &extensions.ReplicaSet{ ObjectMeta: api.ObjectMeta{ - Name: rcName, + Name: rsName, }, - Spec: api.ReplicationControllerSpec{ + Spec: extensions.ReplicaSetSpec{ Replicas: replicas, - Selector: rcPodLabels, + Selector: &extensions.LabelSelector{MatchLabels: rsPodLabels}, Template: &api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ - Labels: rcPodLabels, + Labels: rsPodLabels, }, Spec: api.PodSpec{ Containers: []api.Container{ @@ -96,7 +96,7 @@ func newDeployment(deploymentName string, replicas int, podLabels map[string]str }, Spec: extensions.DeploymentSpec{ Replicas: replicas, - Selector: podLabels, + Selector: &extensions.LabelSelector{MatchLabels: podLabels}, Strategy: extensions.DeploymentStrategy{ Type: strategyType, }, @@ -126,27 +126,27 @@ func newDeploymentRollback(name string, annotations map[string]string, revision } } -// checkDeploymentRevision checks if the input deployment's and its new RC's revision and images are as expected. -func checkDeploymentRevision(c *clientset.Clientset, ns, deploymentName, revision, imageName, image string) (*extensions.Deployment, *api.ReplicationController) { +// checkDeploymentRevision checks if the input deployment's and its new replica set's revision and images are as expected. +func checkDeploymentRevision(c *clientset.Clientset, ns, deploymentName, revision, imageName, image string) (*extensions.Deployment, *extensions.ReplicaSet) { deployment, err := c.Extensions().Deployments(ns).Get(deploymentName) Expect(err).NotTo(HaveOccurred()) - // Check revision of the new RC of this deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // Check revision of the new replica set of this deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(newRC.Annotations).NotTo(Equal(nil)) - Expect(newRC.Annotations[deploymentutil.RevisionAnnotation]).Should(Equal(revision)) + Expect(newRS.Annotations).NotTo(Equal(nil)) + Expect(newRS.Annotations[deploymentutil.RevisionAnnotation]).Should(Equal(revision)) // Check revision of This deployment Expect(deployment.Annotations).NotTo(Equal(nil)) Expect(deployment.Annotations[deploymentutil.RevisionAnnotation]).Should(Equal(revision)) if len(imageName) > 0 { - // Check the image the new RC creates - Expect(newRC.Spec.Template.Spec.Containers[0].Name).Should(Equal(imageName)) - Expect(newRC.Spec.Template.Spec.Containers[0].Image).Should(Equal(image)) + // Check the image the new replica set creates + Expect(newRS.Spec.Template.Spec.Containers[0].Name).Should(Equal(imageName)) + Expect(newRS.Spec.Template.Spec.Containers[0].Image).Should(Equal(image)) // Check the image the deployment creates Expect(deployment.Spec.Template.Spec.Containers[0].Name).Should(Equal(imageName)) Expect(deployment.Spec.Template.Spec.Containers[0].Image).Should(Equal(image)) } - return deployment, newRC + return deployment, newRS } func testNewDeployment(f *Framework) { @@ -161,7 +161,7 @@ func testNewDeployment(f *Framework) { replicas := 1 Logf("Creating simple deployment %s", deploymentName) d := newDeployment(deploymentName, replicas, podLabels, "nginx", "nginx", extensions.RollingUpdateDeploymentStrategyType, nil) - d.Annotations = map[string]string{"test": "should-copy-to-RC", kubectl.LastAppliedConfigAnnotation: "should-not-copy-to-RC"} + d.Annotations = map[string]string{"test": "should-copy-to-replica-set", kubectl.LastAppliedConfigAnnotation: "should-not-copy-to-replica-set"} _, err := c.Extensions().Deployments(ns).Create(d) Expect(err).NotTo(HaveOccurred()) defer func() { @@ -169,10 +169,10 @@ func testNewDeployment(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) }() // Check that deployment is created fine. deployment, err := c.Extensions().Deployments(ns).Get(deploymentName) @@ -191,12 +191,12 @@ func testNewDeployment(f *Framework) { Expect(deployment.Status.UpdatedReplicas).Should(Equal(replicas)) // Check if it's updated to revision 1 correctly - _, newRC := checkDeploymentRevision(c, ns, deploymentName, "1", "nginx", "nginx") + _, newRS := checkDeploymentRevision(c, ns, deploymentName, "1", "nginx", "nginx") // Check other annotations - Expect(newRC.Annotations["test"]).Should(Equal("should-copy-to-RC")) - Expect(newRC.Annotations[kubectl.LastAppliedConfigAnnotation]).Should(Equal("")) - Expect(deployment.Annotations["test"]).Should(Equal("should-copy-to-RC")) - Expect(deployment.Annotations[kubectl.LastAppliedConfigAnnotation]).Should(Equal("should-not-copy-to-RC")) + Expect(newRS.Annotations["test"]).Should(Equal("should-copy-to-replica-set")) + Expect(newRS.Annotations[kubectl.LastAppliedConfigAnnotation]).Should(Equal("")) + Expect(deployment.Annotations["test"]).Should(Equal("should-copy-to-replica-set")) + Expect(deployment.Annotations[kubectl.LastAppliedConfigAnnotation]).Should(Equal("should-not-copy-to-replica-set")) } func testRollingUpdateDeployment(f *Framework) { @@ -207,18 +207,18 @@ func testRollingUpdateDeployment(f *Framework) { c := clientset.FromUnversionedClient(unversionedClient) // Create nginx pods. deploymentPodLabels := map[string]string{"name": "sample-pod"} - rcPodLabels := map[string]string{ + rsPodLabels := map[string]string{ "name": "sample-pod", "pod": "nginx", } - rcName := "nginx-controller" + rsName := "nginx-controller" replicas := 3 - _, err := c.Core().ReplicationControllers(ns).Create(newRC(rcName, replicas, rcPodLabels, "nginx", "nginx")) + _, err := c.Extensions().ReplicaSets(ns).Create(newReplicaSet(rsName, replicas, rsPodLabels, "nginx", "nginx")) Expect(err).NotTo(HaveOccurred()) defer func() { - Logf("deleting replication controller %s", rcName) - Expect(c.Core().ReplicationControllers(ns).Delete(rcName, nil)).NotTo(HaveOccurred()) + Logf("deleting replica set %s", rsName) + Expect(c.Extensions().ReplicaSets(ns).Delete(rsName, nil)).NotTo(HaveOccurred()) }() // Verify that the required pods have come up. err = verifyPods(unversionedClient, ns, "sample-pod", false, 3) @@ -237,10 +237,10 @@ func testRollingUpdateDeployment(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) }() err = waitForDeploymentStatus(c, ns, deploymentName, replicas, replicas-1, replicas+1, 0) @@ -258,24 +258,24 @@ func testRollingUpdateDeploymentEvents(f *Framework) { c := clientset.FromUnversionedClient(unversionedClient) // Create nginx pods. deploymentPodLabels := map[string]string{"name": "sample-pod-2"} - rcPodLabels := map[string]string{ + rsPodLabels := map[string]string{ "name": "sample-pod-2", "pod": "nginx", } - rcName := "nginx-controller" + rsName := "nginx-controller" replicas := 1 - rcRevision := "3546343826724305832" + rsRevision := "3546343826724305832" annotations := make(map[string]string) - annotations[deploymentutil.RevisionAnnotation] = rcRevision - rc := newRC(rcName, replicas, rcPodLabels, "nginx", "nginx") - rc.Annotations = annotations + annotations[deploymentutil.RevisionAnnotation] = rsRevision + rs := newRS(rsName, replicas, rsPodLabels, "nginx", "nginx") + rs.Annotations = annotations - _, err := c.Core().ReplicationControllers(ns).Create(rc) + _, err := c.Extensions().ReplicaSets(ns).Create(rs) Expect(err).NotTo(HaveOccurred()) defer func() { - Logf("deleting replication controller %s", rcName) - Expect(c.Core().ReplicationControllers(ns).Delete(rcName, nil)).NotTo(HaveOccurred()) + Logf("deleting replica set %s", rsName) + Expect(c.Extensions().ReplicaSets(ns).Delete(rsName, nil)).NotTo(HaveOccurred()) }() // Verify that the required pods have come up. err = verifyPods(unversionedClient, ns, "sample-pod-2", false, 1) @@ -294,10 +294,10 @@ func testRollingUpdateDeploymentEvents(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) }() err = waitForDeploymentStatus(c, ns, deploymentName, replicas, replicas-1, replicas+1, 0) @@ -311,13 +311,14 @@ func testRollingUpdateDeploymentEvents(f *Framework) { Logf("error in listing events: %s", err) Expect(err).NotTo(HaveOccurred()) } - // There should be 2 events, one to scale up the new RC and then to scale down the old RC. + // There should be 2 events, one to scale up the new ReplicaSet and then to scale down + // the old ReplicaSet. Expect(len(events.Items)).Should(Equal(2)) - newRC, err := deploymentutil.GetNewRC(*deployment, c) + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(newRC).NotTo(Equal(nil)) - Expect(events.Items[0].Message).Should(Equal(fmt.Sprintf("Scaled up rc %s to 1", newRC.Name))) - Expect(events.Items[1].Message).Should(Equal(fmt.Sprintf("Scaled down rc %s to 0", rcName))) + Expect(newRS).NotTo(Equal(nil)) + Expect(events.Items[0].Message).Should(Equal(fmt.Sprintf("Scaled up replica set %s to 1", newRS.Name))) + Expect(events.Items[1].Message).Should(Equal(fmt.Sprintf("Scaled down replica set %s to 0", rsName))) // Check if it's updated to revision 3546343826724305833 correctly checkDeploymentRevision(c, ns, deploymentName, "3546343826724305833", "redis", "redis") @@ -331,18 +332,18 @@ func testRecreateDeployment(f *Framework) { c := clientset.FromUnversionedClient(unversionedClient) // Create nginx pods. deploymentPodLabels := map[string]string{"name": "sample-pod-3"} - rcPodLabels := map[string]string{ + rsPodLabels := map[string]string{ "name": "sample-pod-3", "pod": "nginx", } - rcName := "nginx-controller" + rsName := "nginx-controller" replicas := 3 - _, err := c.Core().ReplicationControllers(ns).Create(newRC(rcName, replicas, rcPodLabels, "nginx", "nginx")) + _, err := c.Extensions().ReplicaSets(ns).Create(newRS(rsName, replicas, rsPodLabels, "nginx", "nginx")) Expect(err).NotTo(HaveOccurred()) defer func() { - Logf("deleting replication controller %s", rcName) - Expect(c.Core().ReplicationControllers(ns).Delete(rcName, nil)).NotTo(HaveOccurred()) + Logf("deleting replica set %s", rsName) + Expect(c.Extensions().ReplicaSets(ns).Delete(rsName, nil)).NotTo(HaveOccurred()) }() // Verify that the required pods have come up. err = verifyPods(unversionedClient, ns, "sample-pod-3", false, 3) @@ -361,10 +362,10 @@ func testRecreateDeployment(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) }() err = waitForDeploymentStatus(c, ns, deploymentName, replicas, 0, replicas, 0) @@ -383,13 +384,13 @@ func testRecreateDeployment(f *Framework) { Logf("error in listing events: %s", err) Expect(err).NotTo(HaveOccurred()) } - // There should be 2 events, one to scale up the new RC and then to scale down the old RC. + // There should be 2 events, one to scale up the new ReplicaSet and then to scale down the old ReplicaSet. Expect(len(events.Items)).Should(Equal(2)) - newRC, err := deploymentutil.GetNewRC(*deployment, c) + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(newRC).NotTo(Equal(nil)) - Expect(events.Items[0].Message).Should(Equal(fmt.Sprintf("Scaled down rc %s to 0", rcName))) - Expect(events.Items[1].Message).Should(Equal(fmt.Sprintf("Scaled up rc %s to 3", newRC.Name))) + Expect(newRS).NotTo(Equal(nil)) + Expect(events.Items[0].Message).Should(Equal(fmt.Sprintf("Scaled down replica set %s to 0", rsName))) + Expect(events.Items[1].Message).Should(Equal(fmt.Sprintf("Scaled up replica set %s to 3", newRS.Name))) // Check if it's updated to revision 1 correctly checkDeploymentRevision(c, ns, deploymentName, "1", "redis", "redis") @@ -402,15 +403,15 @@ func testDeploymentCleanUpPolicy(f *Framework) { c := clientset.FromUnversionedClient(unversionedClient) // Create nginx pods. deploymentPodLabels := map[string]string{"name": "cleanup-pod"} - rcPodLabels := map[string]string{ + rsPodLabels := map[string]string{ "name": "cleanup-pod", "pod": "nginx", } - rcName := "nginx-controller" + rsName := "nginx-controller" replicas := 1 revisionHistoryLimit := new(int) *revisionHistoryLimit = 0 - _, err := c.Core().ReplicationControllers(ns).Create(newRC(rcName, replicas, rcPodLabels, "nginx", "nginx")) + _, err := c.Extensions().ReplicaSets(ns).Create(newRS(rsName, replicas, rsPodLabels, "nginx", "nginx")) Expect(err).NotTo(HaveOccurred()) // Verify that the required pods have come up. @@ -430,13 +431,13 @@ func testDeploymentCleanUpPolicy(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) }() - err = waitForDeploymentOldRCsNum(c, ns, deploymentName, *revisionHistoryLimit) + err = waitForDeploymentOldRSsNum(c, ns, deploymentName, *revisionHistoryLimit) Expect(err).NotTo(HaveOccurred()) } @@ -450,21 +451,21 @@ func testRolloverDeployment(f *Framework) { c := clientset.FromUnversionedClient(unversionedClient) podName := "rollover-pod" deploymentPodLabels := map[string]string{"name": podName} - rcPodLabels := map[string]string{ + rsPodLabels := map[string]string{ "name": podName, "pod": "nginx", } - rcName := "nginx-controller" - rcReplicas := 4 - _, err := c.Core().ReplicationControllers(ns).Create(newRC(rcName, rcReplicas, rcPodLabels, "nginx", "nginx")) + rsName := "nginx-controller" + rsReplicas := 4 + _, err := c.Extensions().ReplicaSets(ns).Create(newRS(rsName, rsReplicas, rsPodLabels, "nginx", "nginx")) Expect(err).NotTo(HaveOccurred()) defer func() { - Logf("deleting replication controller %s", rcName) - Expect(c.Core().ReplicationControllers(ns).Delete(rcName, nil)).NotTo(HaveOccurred()) + Logf("deleting replica set %s", rsName) + Expect(c.Extensions().ReplicaSets(ns).Delete(rsName, nil)).NotTo(HaveOccurred()) }() // Verify that the required pods have come up. - err = verifyPods(unversionedClient, ns, podName, false, rcReplicas) + err = verifyPods(unversionedClient, ns, podName, false, rsReplicas) if err != nil { Logf("error in waiting for pods to come up: %s", err) Expect(err).NotTo(HaveOccurred()) @@ -490,22 +491,22 @@ func testRolloverDeployment(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) }() // Verify that the pods were scaled up and down as expected. We use events to verify that. deployment, err := c.Extensions().Deployments(ns).Get(deploymentName) Expect(err).NotTo(HaveOccurred()) - // Make sure the deployment starts to scale up and down RCs + // Make sure the deployment starts to scale up and down replica sets waitForPartialEvents(unversionedClient, ns, deployment, 2) // Check if it's updated to revision 1 correctly - _, newRC := checkDeploymentRevision(c, ns, deploymentName, "1", deploymentImageName, deploymentImage) + _, newRS := checkDeploymentRevision(c, ns, deploymentName, "1", deploymentImageName, deploymentImage) - // Before the deployment finishes, update the deployment to rollover the above 2 rcs and bring up redis pods. + // Before the deployment finishes, update the deployment to rollover the above 2 ReplicaSets and bring up redis pods. // If the deployment already finished here, the test would fail. When this happens, increase its minReadySeconds or replicas to prevent it. - Expect(newRC.Spec.Replicas).Should(BeNumerically("<", deploymentReplicas)) + Expect(newRS.Spec.Replicas).Should(BeNumerically("<", deploymentReplicas)) updatedDeploymentImage := "redis" newDeployment.Spec.Template.Spec.Containers[0].Name = updatedDeploymentImage newDeployment.Spec.Template.Spec.Containers[0].Image = updatedDeploymentImage @@ -544,10 +545,10 @@ func testPausedDeployment(f *Framework) { Expect(err).NotTo(HaveOccurred()) // Verify that there is no latest state realized for the new deployment. - rc, err := deploymentutil.GetNewRC(*deployment, c) + rs, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - if rc != nil { - err = fmt.Errorf("unexpected new rc/%s for deployment/%s", rc.Name, deployment.Name) + if rs != nil { + err = fmt.Errorf("unexpected new rs/%s for deployment/%s", rs.Name, deployment.Name) Expect(err).NotTo(HaveOccurred()) } @@ -557,27 +558,27 @@ func testPausedDeployment(f *Framework) { Expect(err).NotTo(HaveOccurred()) opts := api.ListOptions{LabelSelector: labels.Set(deployment.Spec.Selector).AsSelector()} - w, err := c.Core().ReplicationControllers(ns).Watch(opts) + w, err := c.Extensions().ReplicaSets(ns).Watch(opts) Expect(err).NotTo(HaveOccurred()) select { case <-w.ResultChan(): // this is it case <-time.After(time.Minute): - err = fmt.Errorf("expected a new rc to be created") + err = fmt.Errorf("expected a new replica set to be created") Expect(err).NotTo(HaveOccurred()) } - // Pause the deployment and delete the replication controller. + // Pause the deployment and delete the replica set. // The paused deployment shouldn't recreate a new one. deployment.Spec.Paused = true deployment.ResourceVersion = "" deployment, err = c.Extensions().Deployments(ns).Update(deployment) Expect(err).NotTo(HaveOccurred()) - newRC, err := deploymentutil.GetNewRC(*deployment, c) + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) deployment, err = c.Extensions().Deployments(ns).Get(deploymentName) Expect(err).NotTo(HaveOccurred()) @@ -586,10 +587,10 @@ func testPausedDeployment(f *Framework) { err = fmt.Errorf("deployment %q should be paused", deployment.Name) Expect(err).NotTo(HaveOccurred()) } - shouldBeNil, err := deploymentutil.GetNewRC(*deployment, c) + shouldBeNil, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) if shouldBeNil != nil { - err = fmt.Errorf("deployment %q shouldn't have a rc but there is %q", deployment.Name, shouldBeNil.Name) + err = fmt.Errorf("deployment %q shouldn't have a replica set but there is %q", deployment.Name, shouldBeNil.Name) Expect(err).NotTo(HaveOccurred()) } } @@ -618,14 +619,14 @@ func testRollbackDeployment(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) - oldRCs, _, err := deploymentutil.GetOldRCs(*deployment, c) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) + oldRSs, _, err := deploymentutil.GetOldReplicaSets(*deployment, c) Expect(err).NotTo(HaveOccurred()) - for _, oldRC := range oldRCs { - Expect(c.Core().ReplicationControllers(ns).Delete(oldRC.Name, nil)).NotTo(HaveOccurred()) + for _, oldRS := range oldRSs { + Expect(c.Extensions().ReplicaSets(ns).Delete(oldRS.Name, nil)).NotTo(HaveOccurred()) } }() // Check that deployment is created fine. @@ -689,36 +690,36 @@ func testRollbackDeployment(f *Framework) { checkDeploymentRevision(c, ns, deploymentName, "4", updatedDeploymentImageName, updatedDeploymentImage) } -// testRollbackDeploymentRCNoRevision tests that deployment supports rollback even when there's old RC without revision. -// An old RC without revision is created, and then a deployment is created (v1). The deployment shouldn't add revision -// annotation to the old RC. Then rollback the deployment to last revision, and it should fail and emit related event. +// testRollbackDeploymentRSNoRevision tests that deployment supports rollback even when there's old replica set without revision. +// An old replica set without revision is created, and then a deployment is created (v1). The deployment shouldn't add revision +// annotation to the old replica set. Then rollback the deployment to last revision, and it should fail and emit related event. // Then update the deployment to v2 and rollback it to v1 should succeed and emit related event, now the deployment // becomes v3. Then rollback the deployment to v10 (doesn't exist in history) should fail and emit related event. // Finally, rollback the deployment (v3) to v3 should be no-op and emit related event. -func testRollbackDeploymentRCNoRevision(f *Framework) { +func testRollbackDeploymentRSNoRevision(f *Framework) { ns := f.Namespace.Name unversionedClient := f.Client c := clientset.FromUnversionedClient(f.Client) podName := "nginx" deploymentPodLabels := map[string]string{"name": podName} - rcPodLabels := map[string]string{ + rsPodLabels := map[string]string{ "name": podName, "pod": "nginx", } - rcName := "nginx-controller" - rcReplicas := 0 - rc := newRC(rcName, rcReplicas, rcPodLabels, "nginx", "nginx") - rc.Annotations = make(map[string]string) - rc.Annotations["make"] = "difference" - _, err := c.Core().ReplicationControllers(ns).Create(rc) + rsName := "nginx-controller" + rsReplicas := 0 + rs := newRS(rsName, rsReplicas, rsPodLabels, "nginx", "nginx") + rs.Annotations = make(map[string]string) + rs.Annotations["make"] = "difference" + _, err := c.Extensions().ReplicaSets(ns).Create(rs) Expect(err).NotTo(HaveOccurred()) defer func() { - Logf("deleting replication controller %s", rcName) - Expect(c.Core().ReplicationControllers(ns).Delete(rcName, nil)).NotTo(HaveOccurred()) + Logf("deleting replica set %s", rsName) + Expect(c.Extensions().ReplicaSets(ns).Delete(rsName, nil)).NotTo(HaveOccurred()) }() - // Create a deployment to create nginx pods, which have different template than the rc created above. + // Create a deployment to create nginx pods, which have different template than the replica set created above. deploymentName, deploymentImageName := "nginx-deployment", "nginx" deploymentReplicas := 1 deploymentImage := "nginx" @@ -732,14 +733,14 @@ func testRollbackDeploymentRCNoRevision(f *Framework) { Expect(err).NotTo(HaveOccurred()) Logf("deleting deployment %s", deploymentName) Expect(c.Extensions().Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) - // TODO: remove this once we can delete rcs with deployment - newRC, err := deploymentutil.GetNewRC(*deployment, c) + // TODO: remove this once we can delete replica sets with deployment + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) Expect(err).NotTo(HaveOccurred()) - Expect(c.Core().ReplicationControllers(ns).Delete(newRC.Name, nil)).NotTo(HaveOccurred()) - oldRCs, _, err := deploymentutil.GetOldRCs(*deployment, c) + Expect(c.Extensions().ReplicaSets(ns).Delete(newRS.Name, nil)).NotTo(HaveOccurred()) + oldRSs, _, err := deploymentutil.GetOldReplicaSets(*deployment, c) Expect(err).NotTo(HaveOccurred()) - for _, oldRC := range oldRCs { - Expect(c.Core().ReplicationControllers(ns).Delete(oldRC.Name, nil)).NotTo(HaveOccurred()) + for _, oldRS := range oldRSs { + Expect(c.Extensions().ReplicaSets(ns).Delete(oldRS.Name, nil)).NotTo(HaveOccurred()) } }() // Check that deployment is created fine. @@ -761,9 +762,9 @@ func testRollbackDeploymentRCNoRevision(f *Framework) { // Check if it's updated to revision 1 correctly checkDeploymentRevision(c, ns, deploymentName, "1", deploymentImageName, deploymentImage) - // Check that the rc we created still doesn't contain revision information - rc, err = c.Core().ReplicationControllers(ns).Get(rcName) - Expect(rc.Annotations[deploymentutil.RevisionAnnotation]).Should(Equal("")) + // Check that the replica set we created still doesn't contain revision information + rs, err = c.Extensions().ReplicaSets(ns).Get(rsName) + Expect(rs.Annotations[deploymentutil.RevisionAnnotation]).Should(Equal("")) // Update the deploymentRollback to rollback to last revision // Since there's only 1 revision in history, it should stay as revision 1 diff --git a/test/e2e/kubectl.go b/test/e2e/kubectl.go index b2b7b7f1e3d..e13f3c2b55e 100644 --- a/test/e2e/kubectl.go +++ b/test/e2e/kubectl.go @@ -457,7 +457,7 @@ var _ = Describe("Kubectl client", func() { withStdinData("abcd1234\n"). execOrDie() Expect(runOutput).ToNot(ContainSubstring("stdin closed")) - runTestPod, err := util.GetFirstPod(c, ns, map[string]string{"run": "run-test-3"}) + runTestPod, err := util.GetFirstPod(c, ns, labels.SelectorFromSet(map[string]string{"run": "run-test-3"})) if err != nil { os.Exit(1) } diff --git a/test/e2e/util.go b/test/e2e/util.go index 274ced1d0fd..5d5f1f2849b 100644 --- a/test/e2e/util.go +++ b/test/e2e/util.go @@ -1529,8 +1529,10 @@ func (config *DeploymentConfig) create() error { }, Spec: extensions.DeploymentSpec{ Replicas: config.Replicas, - Selector: map[string]string{ - "name": config.Name, + Selector: &extensions.LabelSelector{ + MatchLabels: map[string]string{ + "name": config.Name, + }, }, Template: api.PodTemplateSpec{ ObjectMeta: api.ObjectMeta{ @@ -2064,43 +2066,43 @@ func waitForDeploymentStatus(c clientset.Interface, ns, deploymentName string, d if err != nil { return false, err } - oldRCs, _, err := deploymentutil.GetOldRCs(*deployment, c) + oldRSs, _, err := deploymentutil.GetOldReplicaSets(*deployment, c) if err != nil { return false, err } - newRC, err := deploymentutil.GetNewRC(*deployment, c) + newRS, err := deploymentutil.GetNewReplicaSet(*deployment, c) if err != nil { return false, err } - if newRC == nil { + if newRS == nil { // New RC hasnt been created yet. return false, nil } - allRCs := append(oldRCs, newRC) - totalCreated := deploymentutil.GetReplicaCountForRCs(allRCs) - totalAvailable, err := deploymentutil.GetAvailablePodsForRCs(c, allRCs, minReadySeconds) + allRSs := append(oldRSs, newRS) + totalCreated := deploymentutil.GetReplicaCountForReplicaSets(allRSs) + totalAvailable, err := deploymentutil.GetAvailablePodsForReplicaSets(c, allRSs, minReadySeconds) if err != nil { return false, err } if totalCreated > maxCreated { - logRCsOfDeployment(deploymentName, oldRCs, newRC) + logReplicaSetsOfDeployment(deploymentName, oldRSs, newRS) return false, fmt.Errorf("total pods created: %d, more than the max allowed: %d", totalCreated, maxCreated) } if totalAvailable < minAvailable { - logRCsOfDeployment(deploymentName, oldRCs, newRC) + logReplicaSetsOfDeployment(deploymentName, oldRSs, newRS) return false, fmt.Errorf("total pods available: %d, less than the min required: %d", totalAvailable, minAvailable) } if deployment.Status.Replicas == desiredUpdatedReplicas && deployment.Status.UpdatedReplicas == desiredUpdatedReplicas { - // Verify RCs. - if deploymentutil.GetReplicaCountForRCs(oldRCs) != 0 { - logRCsOfDeployment(deploymentName, oldRCs, newRC) - return false, fmt.Errorf("old RCs are not fully scaled down") + // Verify replica sets. + if deploymentutil.GetReplicaCountForReplicaSets(oldRSs) != 0 { + logReplicaSetsOfDeployment(deploymentName, oldRSs, newRS) + return false, fmt.Errorf("old replica sets are not fully scaled down") } - if deploymentutil.GetReplicaCountForRCs([]*api.ReplicationController{newRC}) != desiredUpdatedReplicas { - logRCsOfDeployment(deploymentName, oldRCs, newRC) - return false, fmt.Errorf("new RC is not fully scaled up") + if deploymentutil.GetReplicaCountForReplicaSets([]*extensions.ReplicaSet{newRS}) != desiredUpdatedReplicas { + logReplicaSetsOfDeployment(deploymentName, oldRSs, newRS) + return false, fmt.Errorf("new replica sets is not fully scaled up") } return true, nil } @@ -2109,26 +2111,25 @@ func waitForDeploymentStatus(c clientset.Interface, ns, deploymentName string, d } // Waits for the deployment to clean up old rcs. -func waitForDeploymentOldRCsNum(c *clientset.Clientset, ns, deploymentName string, desiredRCNum int) error { +func waitForDeploymentOldRSsNum(c *clientset.Clientset, ns, deploymentName string, desiredRSNum int) error { return wait.Poll(poll, 5*time.Minute, func() (bool, error) { - deployment, err := c.Extensions().Deployments(ns).Get(deploymentName) if err != nil { return false, err } - oldRCs, _, err := deploymentutil.GetOldRCs(*deployment, c) + oldRSs, _, err := deploymentutil.GetOldReplicaSets(*deployment, c) if err != nil { return false, err } - return len(oldRCs) == desiredRCNum, nil + return len(oldRSs) == desiredRSNum, nil }) } -func logRCsOfDeployment(deploymentName string, oldRCs []*api.ReplicationController, newRC *api.ReplicationController) { - for i := range oldRCs { - Logf("Old RCs (%d/%d) of deployment %s: %+v", i+1, len(oldRCs), deploymentName, oldRCs[i]) +func logReplicaSetsOfDeployment(deploymentName string, oldRSs []*extensions.ReplicaSet, newRS *extensions.ReplicaSet) { + for i := range oldRSs { + Logf("Old ReplicaSets (%d/%d) of deployment %s: %+v", i+1, len(oldRSs), deploymentName, oldRSs[i]) } - Logf("New RC of deployment %s: %+v", deploymentName, newRC) + Logf("New ReplicaSet of deployment %s: %+v", deploymentName, newRS) } // Waits for the number of events on the given object to reach a desired count.