From 518f08aa7ce0d3091529cfb11f39a70cf2829769 Mon Sep 17 00:00:00 2001 From: "Madhusudan.C.S" Date: Tue, 19 Jan 2016 16:40:18 -0800 Subject: [PATCH] Move Deployments to ReplicaSets and switch the Deployment selector to the new LabelSelector. Update the Deployments' API types, defaulting code, conversions, helpers and validation to use ReplicaSets instead of ReplicationControllers and LabelSelector instead of map[string]string for selectors. Also update the Deployment controller, registry, kubectl subcommands, client listers package and e2e tests to use ReplicaSets and LabelSelector for Deployments. --- examples/extensions/deployment.yaml | 3 +- pkg/apis/extensions/helpers.go | 11 +- pkg/apis/extensions/types.go | 4 +- pkg/apis/extensions/v1beta1/conversion.go | 13 +- pkg/apis/extensions/v1beta1/defaults.go | 4 +- pkg/apis/extensions/v1beta1/types.go | 2 +- pkg/apis/extensions/validation/validation.go | 19 +- .../extensions/validation/validation_test.go | 12 +- pkg/client/cache/listers.go | 21 +- .../deployment/deployment_controller.go | 572 +++++++++--------- .../deployment/deployment_controller_test.go | 181 +++--- pkg/kubectl/cmd/util/factory.go | 20 +- pkg/kubectl/describe.go | 30 +- pkg/kubectl/run.go | 2 +- pkg/kubectl/run_test.go | 2 +- pkg/kubectl/scale.go | 5 +- pkg/registry/deployment/etcd/etcd.go | 12 +- pkg/registry/deployment/etcd/etcd_test.go | 6 +- pkg/util/deployment/deployment.go | 159 ++--- pkg/util/deployment/deployment_test.go | 158 ++--- pkg/util/labels/labels.go | 44 ++ test/e2e/deployment.go | 255 ++++---- test/e2e/kubectl.go | 2 +- test/e2e/util.go | 51 +- 24 files changed, 853 insertions(+), 735 deletions(-) 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.