diff --git a/pkg/controller/deployment/util/deployment_util.go b/pkg/controller/deployment/util/deployment_util.go index 296dba29acf..2320de1f9ea 100644 --- a/pkg/controller/deployment/util/deployment_util.go +++ b/pkg/controller/deployment/util/deployment_util.go @@ -593,6 +593,35 @@ func ListReplicaSets(deployment *extensions.Deployment, getRSList rsListFunc) ([ return owned, nil } +// ListReplicaSets returns a slice of RSes the given deployment targets. +// Note that this does NOT attempt to reconcile ControllerRef (adopt/orphan), +// because only the controller itself should do that. +// However, it does filter out anything whose ControllerRef doesn't match. +// TODO: Remove the duplicate. +func ListReplicaSetsInternal(deployment *internalextensions.Deployment, getRSList func(string, metav1.ListOptions) ([]*internalextensions.ReplicaSet, error)) ([]*internalextensions.ReplicaSet, error) { + // TODO: Right now we list replica sets by their labels. We should list them by selector, i.e. the replica set's selector + // should be a superset of the deployment's selector, see https://github.com/kubernetes/kubernetes/issues/19830. + namespace := deployment.Namespace + selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) + if err != nil { + return nil, err + } + options := metav1.ListOptions{LabelSelector: selector.String()} + all, err := getRSList(namespace, options) + if err != nil { + return all, err + } + // Only include those whose ControllerRef matches the Deployment. + owned := make([]*internalextensions.ReplicaSet, 0, len(all)) + for _, rs := range all { + controllerRef := controller.GetControllerOf(rs) + if controllerRef != nil && controllerRef.UID == deployment.UID { + owned = append(owned, rs) + } + } + return owned, nil +} + // ListPods returns a list of pods the given deployment targets. // This needs a list of ReplicaSets for the Deployment, // which can be found with ListReplicaSets(). diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index 386dd98aada..98923897872 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -152,6 +152,7 @@ go_test( "//vendor:k8s.io/apimachinery/pkg/runtime/schema", "//vendor:k8s.io/apimachinery/pkg/util/intstr", "//vendor:k8s.io/apimachinery/pkg/util/sets", + "//vendor:k8s.io/apimachinery/pkg/util/uuid", "//vendor:k8s.io/apimachinery/pkg/watch", "//vendor:k8s.io/client-go/rest", "//vendor:k8s.io/client-go/rest/fake", diff --git a/pkg/kubectl/stop.go b/pkg/kubectl/stop.go index c46d1039f50..70cea6deba0 100644 --- a/pkg/kubectl/stop.go +++ b/pkg/kubectl/stop.go @@ -420,7 +420,6 @@ func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gra func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error { deployments := reaper.dClient.Deployments(namespace) - replicaSets := reaper.rsClient.ReplicaSets(namespace) rsReaper := &ReplicaSetReaper{reaper.rsClient, reaper.pollInterval, reaper.timeout} deployment, err := reaper.updateDeploymentWithRetries(namespace, name, func(d *extensions.Deployment) { @@ -441,20 +440,26 @@ func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Durati return err } - // Stop all replica sets. - selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) + // Stop all replica sets belonging to this Deployment. + rss, err := deploymentutil.ListReplicaSetsInternal(deployment, + func(namespace string, options metav1.ListOptions) ([]*extensions.ReplicaSet, error) { + rsList, err := reaper.rsClient.ReplicaSets(namespace).List(options) + if err != nil { + return nil, err + } + rss := make([]*extensions.ReplicaSet, 0, len(rsList.Items)) + for i := range rsList.Items { + rss = append(rss, &rsList.Items[i]) + } + return rss, nil + }) if err != nil { return err } - options := metav1.ListOptions{LabelSelector: selector.String()} - rsList, err := replicaSets.List(options) - if err != nil { - return err - } errList := []error{} - for _, rc := range rsList.Items { - if err := rsReaper.Stop(rc.Namespace, rc.Name, timeout, gracePeriod); err != nil { + for _, rs := range rss { + if err := rsReaper.Stop(rs.Namespace, rs.Name, timeout, gracePeriod); err != nil { scaleGetErr, ok := err.(ScaleError) if errors.IsNotFound(err) || (ok && errors.IsNotFound(scaleGetErr.ActualError)) { continue diff --git a/pkg/kubectl/stop_test.go b/pkg/kubectl/stop_test.go index 3f45ad8b507..95a8b243472 100644 --- a/pkg/kubectl/stop_test.go +++ b/pkg/kubectl/stop_test.go @@ -27,6 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/watch" testcore "k8s.io/client-go/testing" "k8s.io/kubernetes/pkg/api" @@ -429,6 +430,7 @@ func TestDeploymentStop(t *testing.T) { deployment := extensions.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: name, + UID: uuid.NewUUID(), Namespace: ns, }, Spec: extensions.DeploymentSpec{ @@ -440,6 +442,7 @@ func TestDeploymentStop(t *testing.T) { }, } template := deploymentutil.GetNewReplicaSetTemplateInternal(&deployment) + trueVar := true tests := []struct { Name string Objs []runtime.Object @@ -478,6 +481,15 @@ func TestDeploymentStop(t *testing.T) { Name: name, Namespace: ns, Labels: map[string]string{"k1": "v1"}, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: extensions.SchemeGroupVersion.String(), + Kind: "ReplicaSet", + Name: deployment.Name, + UID: deployment.UID, + Controller: &trueVar, + }, + }, }, Spec: extensions.ReplicaSetSpec{ Template: template,