diff --git a/pkg/client/unversioned/conditions.go b/pkg/client/unversioned/conditions.go index 7c1994638f9..a65c8475085 100644 --- a/pkg/client/unversioned/conditions.go +++ b/pkg/client/unversioned/conditions.go @@ -79,15 +79,23 @@ func ReplicaSetHasDesiredReplicas(rsClient extensionsclient.ReplicaSetsGetter, r } } -// StatefulSetHasDesiredReplicas returns a conditon that checks the number of statefulset replicas +// StatefulSetHasDesiredReplicas returns a condition that checks the number of StatefulSet replicas func StatefulSetHasDesiredReplicas(ssClient appsclient.StatefulSetsGetter, ss *apps.StatefulSet) wait.ConditionFunc { - // TODO: Differentiate between 0 statefulset pods and a really quick scale down using generation. + // If we're given a StatefulSet where the status lags the spec, it either means that the + // StatefulSet is stale, or that the StatefulSet manager hasn't noticed the update yet. + // Polling status.Replicas is not safe in the latter case. + desiredGeneration := ss.Generation return func() (bool, error) { ss, err := ssClient.StatefulSets(ss.Namespace).Get(ss.Name, metav1.GetOptions{}) if err != nil { return false, err } - return ss.Status.Replicas == ss.Spec.Replicas, nil + // There's a chance a concurrent update modifies the Spec.Replicas causing this check to + // pass, or, after this check has passed, a modification causes the StatefulSet manager to + // create more pods. This will not be an issue once we've implemented graceful delete for + // StatefulSet, but till then concurrent stop operations on the same StatefulSet might have + // unintended side effects. + return ss.Status.ObservedGeneration != nil && *ss.Status.ObservedGeneration >= desiredGeneration && ss.Status.Replicas == ss.Spec.Replicas, nil } } diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index ce9878673a1..11d890b89b5 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -1,3 +1,5 @@ +package(default_visibility = ["//visibility:public"]) + licenses(["notice"]) load( @@ -6,115 +8,6 @@ load( "go_test", ) -go_library( - name = "go_default_library", - srcs = [ - "apply.go", - "autoscale.go", - "bash_comp_utils.go", - "cluster.go", - "clusterrolebinding.go", - "configmap.go", - "deployment.go", - "doc.go", - "env_file.go", - "explain.go", - "generate.go", - "history.go", - "interfaces.go", - "kubectl.go", - "namespace.go", - "pdb.go", - "proxy_server.go", - "quota.go", - "resource_filter.go", - "rolebinding.go", - "rollback.go", - "rolling_updater.go", - "rollout_status.go", - "run.go", - "scale.go", - "secret.go", - "secret_for_docker_registry.go", - "secret_for_tls.go", - "service.go", - "service_basic.go", - "serviceaccount.go", - "sorting_printer.go", - "stop.go", - "versioned_client.go", - ], - tags = ["automanaged"], - visibility = [ - "//build/visible_to:COMMON_testing", - "//build/visible_to:pkg_kubectl_CONSUMERS", - ], - deps = [ - "//federation/apis/federation/v1beta1:go_default_library", - "//pkg/api:go_default_library", - "//pkg/api/util:go_default_library", - "//pkg/api/v1:go_default_library", - "//pkg/api/v1/pod:go_default_library", - "//pkg/apis/apps:go_default_library", - "//pkg/apis/apps/v1beta1:go_default_library", - "//pkg/apis/autoscaling:go_default_library", - "//pkg/apis/batch:go_default_library", - "//pkg/apis/batch/v1:go_default_library", - "//pkg/apis/batch/v2alpha1:go_default_library", - "//pkg/apis/extensions:go_default_library", - "//pkg/apis/extensions/v1beta1:go_default_library", - "//pkg/apis/policy:go_default_library", - "//pkg/apis/rbac:go_default_library", - "//pkg/client/clientset_generated/clientset:go_default_library", - "//pkg/client/clientset_generated/clientset/typed/apps/v1beta1:go_default_library", - "//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library", - "//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library", - "//pkg/client/clientset_generated/internalclientset:go_default_library", - "//pkg/client/clientset_generated/internalclientset/typed/apps/internalversion:go_default_library", - "//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library", - "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", - "//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library", - "//pkg/client/retry:go_default_library", - "//pkg/client/unversioned:go_default_library", - "//pkg/controller:go_default_library", - "//pkg/controller/daemon:go_default_library", - "//pkg/controller/deployment/util:go_default_library", - "//pkg/credentialprovider:go_default_library", - "//pkg/kubectl/resource:go_default_library", - "//pkg/kubectl/util:go_default_library", - "//pkg/printers:go_default_library", - "//pkg/printers/internalversion:go_default_library", - "//pkg/util:go_default_library", - "//pkg/util/slice:go_default_library", - "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", - "//vendor/github.com/golang/glog:go_default_library", - "//vendor/github.com/spf13/cobra:go_default_library", - "//vendor/github.com/spf13/pflag:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", - "//vendor/k8s.io/client-go/rest:go_default_library", - "//vendor/k8s.io/client-go/util/integer:go_default_library", - "//vendor/k8s.io/client-go/util/jsonpath:go_default_library", - "//vendor/vbom.ml/util/sortorder:go_default_library", - ], -) - go_test( name = "go_default_test", srcs = [ @@ -181,10 +74,116 @@ go_test( ], ) +go_library( + name = "go_default_library", + srcs = [ + "apply.go", + "autoscale.go", + "bash_comp_utils.go", + "cluster.go", + "clusterrolebinding.go", + "configmap.go", + "deployment.go", + "doc.go", + "env_file.go", + "explain.go", + "generate.go", + "history.go", + "interfaces.go", + "kubectl.go", + "namespace.go", + "pdb.go", + "proxy_server.go", + "quota.go", + "resource_filter.go", + "rolebinding.go", + "rollback.go", + "rolling_updater.go", + "rollout_status.go", + "run.go", + "scale.go", + "secret.go", + "secret_for_docker_registry.go", + "secret_for_tls.go", + "service.go", + "service_basic.go", + "serviceaccount.go", + "sorting_printer.go", + "stop.go", + "versioned_client.go", + ], + tags = ["automanaged"], + deps = [ + "//federation/apis/federation/v1beta1:go_default_library", + "//pkg/api:go_default_library", + "//pkg/api/util:go_default_library", + "//pkg/api/v1:go_default_library", + "//pkg/api/v1/pod:go_default_library", + "//pkg/apis/apps:go_default_library", + "//pkg/apis/apps/v1beta1:go_default_library", + "//pkg/apis/autoscaling:go_default_library", + "//pkg/apis/batch:go_default_library", + "//pkg/apis/batch/v1:go_default_library", + "//pkg/apis/batch/v2alpha1:go_default_library", + "//pkg/apis/extensions:go_default_library", + "//pkg/apis/extensions/v1beta1:go_default_library", + "//pkg/apis/policy:go_default_library", + "//pkg/apis/rbac:go_default_library", + "//pkg/client/clientset_generated/clientset:go_default_library", + "//pkg/client/clientset_generated/clientset/typed/apps/v1beta1:go_default_library", + "//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library", + "//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library", + "//pkg/client/clientset_generated/internalclientset:go_default_library", + "//pkg/client/clientset_generated/internalclientset/typed/apps/internalversion:go_default_library", + "//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library", + "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", + "//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library", + "//pkg/client/retry:go_default_library", + "//pkg/client/unversioned:go_default_library", + "//pkg/controller:go_default_library", + "//pkg/controller/daemon:go_default_library", + "//pkg/controller/deployment/util:go_default_library", + "//pkg/credentialprovider:go_default_library", + "//pkg/kubectl/resource:go_default_library", + "//pkg/kubectl/util:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/printers/internalversion:go_default_library", + "//pkg/util:go_default_library", + "//pkg/util/slice:go_default_library", + "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/spf13/cobra:go_default_library", + "//vendor/github.com/spf13/pflag:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/util/integer:go_default_library", + "//vendor/k8s.io/client-go/util/jsonpath:go_default_library", + "//vendor/vbom.ml/util/sortorder:go_default_library", + ], +) + filegroup( name = "package-srcs", srcs = glob(["**"]), tags = ["automanaged"], + visibility = ["//visibility:private"], ) filegroup( @@ -199,7 +198,4 @@ filegroup( "//pkg/kubectl/util:all-srcs", ], tags = ["automanaged"], - visibility = [ - "//build/visible_to:pkg_kubectl_CONSUMERS", - ], ) diff --git a/pkg/kubectl/stop.go b/pkg/kubectl/stop.go index ccae1ce6684..62b85c4a210 100644 --- a/pkg/kubectl/stop.go +++ b/pkg/kubectl/stop.go @@ -37,7 +37,6 @@ import ( batchclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion" coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" - "k8s.io/kubernetes/pkg/controller" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" "k8s.io/kubernetes/pkg/util" ) @@ -345,34 +344,6 @@ func (reaper *StatefulSetReaper) Stop(namespace, name string, timeout time.Durat return err } - // TODO: This shouldn't be needed, see corresponding TODO in StatefulSetHasDesiredReplicas. - // StatefulSet should track generation number. - pods := reaper.podClient.Pods(namespace) - selector, _ := metav1.LabelSelectorAsSelector(ss.Spec.Selector) - options := metav1.ListOptions{LabelSelector: selector.String()} - podList, err := pods.List(options) - if err != nil { - return err - } - - errList := []error{} - for i := range podList.Items { - pod := &podList.Items[i] - controllerRef := controller.GetControllerOf(pod) - // Ignore Pod if it's an orphan or owned by someone else. - if controllerRef == nil || controllerRef.UID != ss.UID { - continue - } - if err := pods.Delete(pod.Name, gracePeriod); err != nil { - if !errors.IsNotFound(err) { - errList = append(errList, err) - } - } - } - if len(errList) > 0 { - return utilerrors.NewAggregate(errList) - } - // TODO: Cleanup volumes? We don't want to accidentally delete volumes from // stop, so just leave this up to the statefulset. falseVar := false