Remove kubectl reapers

This commit is contained in:
Maciej Szulik 2018-05-17 17:27:44 +02:00
parent d7c40cf69e
commit 383872615d
No known key found for this signature in database
GPG Key ID: F15E55D276FA84C4
43 changed files with 189 additions and 1818 deletions

View File

@ -1468,7 +1468,7 @@ __EOF__
# Test that we can list this new CustomResource # Test that we can list this new CustomResource
kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" '' kube::test::get_object_assert foos "{{range.items}}{{$id_field}}:{{end}}" ''
# Compare "old" output with experimental output and ensure both are the same # Compare "old" output with experimental output and ensure both are the same
expected_output=$(kubectl get foos "${kube_flags[@]}") expected_output=$(kubectl get foos "${kube_flags[@]}" | awk 'NF{NF--};1')
actual_output=$(kubectl get foos --server-print=false "${kube_flags[@]}" | awk 'NF{NF--};1') actual_output=$(kubectl get foos --server-print=false "${kube_flags[@]}" | awk 'NF{NF--};1')
kube::test::if_has_string "${actual_output}" "${expected_output}" kube::test::if_has_string "${actual_output}" "${expected_output}"
@ -1480,6 +1480,9 @@ __EOF__
kubectl delete rc frontend "${kube_flags[@]}" kubectl delete rc frontend "${kube_flags[@]}"
kubectl delete ds bind "${kube_flags[@]}" kubectl delete ds bind "${kube_flags[@]}"
kubectl delete pod valid-pod "${kube_flags[@]}" kubectl delete pod valid-pod "${kube_flags[@]}"
set +o nounset
set +o errexit
} }
run_kubectl_get_tests() { run_kubectl_get_tests() {

View File

@ -12,7 +12,6 @@ go_test(
"autoscale_test.go", "autoscale_test.go",
"clusterrolebinding_test.go", "clusterrolebinding_test.go",
"configmap_test.go", "configmap_test.go",
"delete_test.go",
"deployment_test.go", "deployment_test.go",
"env_file_test.go", "env_file_test.go",
"generate_test.go", "generate_test.go",
@ -40,12 +39,9 @@ go_test(
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/api/testapi:go_default_library", "//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library", "//pkg/api/testing:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library", "//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/kubectl/util:go_default_library", "//pkg/kubectl/util:go_default_library",
"//pkg/util/pointer:go_default_library", "//pkg/util/pointer:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library",
@ -71,8 +67,6 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr: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/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library",
@ -92,7 +86,6 @@ go_library(
"clusterrolebinding.go", "clusterrolebinding.go",
"conditions.go", "conditions.go",
"configmap.go", "configmap.go",
"delete.go",
"deployment.go", "deployment.go",
"doc.go", "doc.go",
"env_file.go", "env_file.go",
@ -123,19 +116,15 @@ go_library(
"//pkg/api/pod:go_default_library", "//pkg/api/pod:go_default_library",
"//pkg/api/v1/pod:go_default_library", "//pkg/api/v1/pod:go_default_library",
"//pkg/apis/apps:go_default_library", "//pkg/apis/apps:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/core/v1:go_default_library", "//pkg/apis/core/v1:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions: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/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/core/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
"//pkg/controller/deployment/util:go_default_library", "//pkg/controller/deployment/util:go_default_library",
"//pkg/credentialprovider:go_default_library", "//pkg/credentialprovider:go_default_library",
"//pkg/kubectl/apps:go_default_library", "//pkg/kubectl/apps:go_default_library",
"//pkg/kubectl/cmd/scalejob:go_default_library",
"//pkg/kubectl/genericclioptions/resource:go_default_library", "//pkg/kubectl/genericclioptions/resource:go_default_library",
"//pkg/kubectl/util:go_default_library", "//pkg/kubectl/util:go_default_library",
"//pkg/kubectl/util/hash:go_default_library", "//pkg/kubectl/util/hash:go_default_library",
@ -171,7 +160,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch: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/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait: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/apimachinery/pkg/watch:go_default_library",

View File

@ -197,7 +197,6 @@ go_test(
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/create:go_default_library", "//pkg/kubectl/cmd/create:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
@ -214,7 +213,6 @@ go_test(
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/gopkg.in/yaml.v2:go_default_library", "//vendor/gopkg.in/yaml.v2:go_default_library",
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/policy/v1beta1:go_default_library", "//vendor/k8s.io/api/policy/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
@ -232,9 +230,9 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/util/strategicpatch/testing:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch/testing:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library", "//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/k8s.io/client-go/dynamic/fake:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest/fake:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library",
"//vendor/k8s.io/client-go/scale/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library",
"//vendor/k8s.io/client-go/tools/remotecommand:go_default_library", "//vendor/k8s.io/client-go/tools/remotecommand:go_default_library",
"//vendor/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library", "//vendor/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library",

View File

@ -43,7 +43,6 @@ import (
scaleclient "k8s.io/client-go/scale" scaleclient "k8s.io/client-go/scale"
oapi "k8s.io/kube-openapi/pkg/util/proto" oapi "k8s.io/kube-openapi/pkg/util/proto"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -82,7 +81,6 @@ type ApplyOptions struct {
Mapper meta.RESTMapper Mapper meta.RESTMapper
Scaler scaleclient.ScalesGetter Scaler scaleclient.ScalesGetter
DynamicClient dynamic.Interface DynamicClient dynamic.Interface
ClientSetFunc func() (internalclientset.Interface, error)
OpenAPISchema openapi.Resources OpenAPISchema openapi.Resources
Namespace string Namespace string
@ -215,7 +213,6 @@ func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.ShouldIncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, o.Prune) o.ShouldIncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, o.Prune)
o.OpenAPISchema, _ = f.OpenAPISchema() o.OpenAPISchema, _ = f.OpenAPISchema()
o.ClientSetFunc = f.ClientSet
o.Validator, err = f.Validator(cmdutil.GetFlagBool(cmd, "validate")) o.Validator, err = f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
o.Builder = f.NewBuilder() o.Builder = f.NewBuilder()
o.Mapper, err = f.ToRESTMapper() o.Mapper, err = f.ToRESTMapper()
@ -406,7 +403,6 @@ func (o *ApplyOptions) Run() error {
mapping: info.Mapping, mapping: info.Mapping,
helper: helper, helper: helper,
dynamicClient: o.DynamicClient, dynamicClient: o.DynamicClient,
clientsetFunc: o.ClientSetFunc,
overwrite: o.Overwrite, overwrite: o.Overwrite,
backOff: clockwork.NewRealClock(), backOff: clockwork.NewRealClock(),
force: o.DeleteOptions.ForceDeletion, force: o.DeleteOptions.ForceDeletion,
@ -414,7 +410,6 @@ func (o *ApplyOptions) Run() error {
timeout: o.DeleteOptions.Timeout, timeout: o.DeleteOptions.Timeout,
gracePeriod: o.DeleteOptions.GracePeriod, gracePeriod: o.DeleteOptions.GracePeriod,
openapiSchema: openapiSchema, openapiSchema: openapiSchema,
scaleClient: o.Scaler,
} }
patchBytes, patchedObject, err := patcher.patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut) patchBytes, patchedObject, err := patcher.patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut)
@ -491,7 +486,6 @@ func (o *ApplyOptions) Run() error {
p := pruner{ p := pruner{
mapper: o.Mapper, mapper: o.Mapper,
dynamicClient: o.DynamicClient, dynamicClient: o.DynamicClient,
clientsetFunc: o.ClientSetFunc,
labelSelector: o.Selector, labelSelector: o.Selector,
visitedUids: visitedUids, visitedUids: visitedUids,
@ -580,7 +574,6 @@ func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (n
type pruner struct { type pruner struct {
mapper meta.RESTMapper mapper meta.RESTMapper
dynamicClient dynamic.Interface dynamicClient dynamic.Interface
clientsetFunc func() (internalclientset.Interface, error)
visitedUids sets.String visitedUids sets.String
labelSelector string labelSelector string
@ -630,7 +623,7 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, includeUnini
} }
name := metadata.GetName() name := metadata.GetName()
if !p.dryRun { if !p.dryRun {
if err := p.delete(namespace, name, mapping, p.scaler); err != nil { if err := p.delete(namespace, name, mapping); err != nil {
return err return err
} }
} }
@ -644,44 +637,31 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, includeUnini
return nil return nil
} }
func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping, scaleClient scaleclient.ScalesGetter) error { func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.clientsetFunc, scaleClient) return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod)
} }
func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int, clientsetFunc func() (internalclientset.Interface, error), scaleClient scaleclient.ScalesGetter) error { func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int) error {
if !cascade { options := &metav1.DeleteOptions{}
return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, nil)
}
cs, err := clientsetFunc()
if err != nil {
return err
}
r, err := kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), cs, scaleClient)
if err != nil {
if _, ok := err.(*kubectl.NoSuchReaperError); !ok {
return err
}
return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, nil)
}
var options *metav1.DeleteOptions
if gracePeriod >= 0 { if gracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(gracePeriod)) options = metav1.NewDeleteOptions(int64(gracePeriod))
} }
if err := r.Stop(namespace, name, 2*time.Minute, options); err != nil { policy := metav1.DeletePropagationForeground
return err if !cascade {
policy = metav1.DeletePropagationOrphan
} }
return nil options.PropagationPolicy = &policy
return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, options)
} }
func (p *patcher) delete(namespace, name string) error { func (p *patcher) delete(namespace, name string) error {
return runDelete(namespace, name, p.mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.clientsetFunc, p.scaleClient) return runDelete(namespace, name, p.mapping, p.dynamicClient, p.cascade, p.gracePeriod)
} }
type patcher struct { type patcher struct {
mapping *meta.RESTMapping mapping *meta.RESTMapping
helper *resource.Helper helper *resource.Helper
dynamicClient dynamic.Interface dynamicClient dynamic.Interface
clientsetFunc func() (internalclientset.Interface, error)
overwrite bool overwrite bool
backOff clockwork.Clock backOff clockwork.Clock
@ -692,7 +672,6 @@ type patcher struct {
gracePeriod int gracePeriod int
openapiSchema openapi.Resources openapiSchema openapi.Resources
scaleClient scaleclient.ScalesGetter
} }
func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) { func (p *patcher) patchSimple(obj runtime.Object, modified []byte, source, namespace, name string, errOut io.Writer) ([]byte, runtime.Object, error) {
@ -790,17 +769,16 @@ func (p *patcher) patch(current runtime.Object, modified []byte, source, namespa
} }
func (p *patcher) deleteAndCreate(original runtime.Object, modified []byte, namespace, name string) ([]byte, runtime.Object, error) { func (p *patcher) deleteAndCreate(original runtime.Object, modified []byte, namespace, name string) ([]byte, runtime.Object, error) {
err := p.delete(namespace, name) if err := p.delete(namespace, name); err != nil {
if err != nil {
return modified, nil, err return modified, nil, err
} }
err = wait.PollImmediate(kubectl.Interval, p.timeout, func() (bool, error) { // TODO: use wait
if err := wait.PollImmediate(1*time.Second, p.timeout, func() (bool, error) {
if _, err := p.helper.Get(namespace, name, false); !errors.IsNotFound(err) { if _, err := p.helper.Get(namespace, name, false); !errors.IsNotFound(err) {
return false, err return false, err
} }
return true, nil return true, nil
}) }); err != nil {
if err != nil {
return modified, nil, err return modified, nil, err
} }
versionedObject, _, err := unstructured.UnstructuredJSONScheme.Decode(modified, nil, nil) versionedObject, _, err := unstructured.UnstructuredJSONScheme.Decode(modified, nil, nil)

View File

@ -31,19 +31,17 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
autoscalingv1 "k8s.io/api/autoscaling/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
kubeerr "k8s.io/apimachinery/pkg/api/errors" kubeerr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
sptest "k8s.io/apimachinery/pkg/util/strategicpatch/testing" sptest "k8s.io/apimachinery/pkg/util/strategicpatch/testing"
dynamicfakeclient "k8s.io/client-go/dynamic/fake"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
fakescale "k8s.io/client-go/scale/fake" clienttesting "k8s.io/client-go/testing"
testcore "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/testapi"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
@ -1211,18 +1209,18 @@ func checkPatchString(t *testing.T, req *http.Request) {
func TestForceApply(t *testing.T) { func TestForceApply(t *testing.T) {
initTestErrorHandler(t) initTestErrorHandler(t)
scheme := runtime.NewScheme()
nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC) nameRC, currentRC := readAndAnnotateReplicationController(t, filenameRC)
pathRC := "/namespaces/test/replicationcontrollers/" + nameRC pathRC := "/namespaces/test/replicationcontrollers/" + nameRC
pathRCList := "/namespaces/test/replicationcontrollers" pathRCList := "/namespaces/test/replicationcontrollers"
expected := map[string]int{ expected := map[string]int{
"getOk": 7, "getOk": 6,
"getNotFound": 1, "getNotFound": 1,
"getList": 1, "getList": 0,
"patch": 6, "patch": 6,
"delete": 1, "delete": 1,
"post": 1, "post": 1,
} }
scaleClientExpected := []string{"get", "update", "get", "get"}
for _, fn := range testingOpenAPISchemaFns { for _, fn := range testingOpenAPISchemaFns {
t.Run("test apply with --force", func(t *testing.T) { t.Run("test apply with --force", func(t *testing.T) {
@ -1282,10 +1280,6 @@ func TestForceApply(t *testing.T) {
} }
t.Fatalf("unexpected request: %#v after %v tries\n%#v", req.URL, counts["patch"], req) t.Fatalf("unexpected request: %#v after %v tries\n%#v", req.URL, counts["patch"], req)
return nil, nil return nil, nil
case strings.HasSuffix(p, pathRC) && m == "DELETE":
counts["delete"]++
deleted = true
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: ioutil.NopCloser(bytes.NewReader([]byte{}))}, nil
case strings.HasSuffix(p, pathRC) && m == "PUT": case strings.HasSuffix(p, pathRC) && m == "PUT":
counts["put"]++ counts["put"]++
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
@ -1303,43 +1297,18 @@ func TestForceApply(t *testing.T) {
} }
}), }),
} }
newReplicas := int32(3) fakeDynamicClient := dynamicfakeclient.NewSimpleDynamicClient(scheme)
scaleClient := &fakescale.FakeScaleClient{} fakeDynamicClient.PrependReactor("delete", "replicationcontrollers", func(action clienttesting.Action) (bool, runtime.Object, error) {
scaleClient.AddReactor("get", "replicationcontrollers", func(rawAction testcore.Action) (handled bool, ret runtime.Object, err error) { if deleteAction, ok := action.(clienttesting.DeleteAction); ok {
action := rawAction.(testcore.GetAction) if deleteAction.GetName() == nameRC {
if action.GetName() != "test-rc" { counts["delete"]++
return true, nil, fmt.Errorf("expected = test-rc, got = %s", action.GetName()) deleted = true
return true, nil, nil
} }
obj := &autoscalingv1.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: action.GetName(),
Namespace: action.GetNamespace(),
},
Spec: autoscalingv1.ScaleSpec{
Replicas: newReplicas,
},
} }
return true, obj, nil return false, nil, nil
}) })
scaleClient.AddReactor("update", "replicationcontrollers", func(rawAction testcore.Action) (handled bool, ret runtime.Object, err error) { tf.FakeDynamicClient = fakeDynamicClient
action := rawAction.(testcore.UpdateAction)
obj := action.GetObject().(*autoscalingv1.Scale)
if obj.Name != "test-rc" {
return true, nil, fmt.Errorf("expected = test-rc, got = %s", obj.Name)
}
newReplicas = obj.Spec.Replicas
return true, &autoscalingv1.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: obj.Name,
Namespace: action.GetNamespace(),
},
Spec: autoscalingv1.ScaleSpec{
Replicas: newReplicas,
},
}, nil
})
tf.ScaleGetter = scaleClient
tf.OpenAPISchemaFunc = fn tf.OpenAPISchemaFunc = fn
tf.Client = tf.UnstructuredClient tf.Client = tf.UnstructuredClient
tf.ClientConfigVal = &restclient.Config{} tf.ClientConfigVal = &restclient.Config{}
@ -1364,22 +1333,6 @@ func TestForceApply(t *testing.T) {
if errBuf.String() != "" { if errBuf.String() != "" {
t.Fatalf("unexpected error output: %s", errBuf.String()) t.Fatalf("unexpected error output: %s", errBuf.String())
} }
scale, err := scaleClient.Scales(tf.Namespace).Get(schema.GroupResource{Group: "", Resource: "replicationcontrollers"}, nameRC)
if err != nil {
t.Error(err)
}
if scale.Spec.Replicas != 0 {
t.Errorf("a scale subresource has unexpected number of replicas, got %d expected 0", scale.Spec.Replicas)
}
if len(scaleClient.Actions()) != len(scaleClientExpected) {
t.Fatalf("a fake scale client has unexpected amout of API calls, wanted = %d, got = %d", len(scaleClientExpected), len(scaleClient.Actions()))
}
for index, action := range scaleClient.Actions() {
if scaleClientExpected[index] != action.GetVerb() {
t.Errorf("unexpected API method called on a fake scale client, wanted = %s, got = %s at index = %d", scaleClientExpected[index], action.GetVerb(), index)
}
}
}) })
} }
} }

View File

@ -27,9 +27,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
kubectlwait "k8s.io/kubernetes/pkg/kubectl/cmd/wait" kubectlwait "k8s.io/kubernetes/pkg/kubectl/cmd/wait"
@ -103,8 +101,6 @@ type DeleteOptions struct {
ForceDeletion bool ForceDeletion bool
WaitForDeletion bool WaitForDeletion bool
Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
GracePeriod int GracePeriod int
Timeout time.Duration Timeout time.Duration
@ -128,15 +124,9 @@ func NewCmdDelete(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra
Example: delete_example, Example: delete_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
o := deleteFlags.ToOptions(nil, streams) o := deleteFlags.ToOptions(nil, streams)
if err := o.Complete(f, args, cmd); err != nil { cmdutil.CheckErr(o.Complete(f, args, cmd))
cmdutil.CheckErr(err) cmdutil.CheckErr(o.Validate(cmd))
} cmdutil.CheckErr(o.RunDelete())
if err := o.Validate(cmd); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error()))
}
if err := o.RunDelete(); err != nil {
cmdutil.CheckErr(err)
}
}, },
SuggestFor: []string{"rm"}, SuggestFor: []string{"rm"},
} }
@ -178,8 +168,6 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Co
o.WaitForDeletion = b o.WaitForDeletion = b
} }
o.Reaper = f.Reaper
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
r := f.NewBuilder(). r := f.NewBuilder().
Unstructured(). Unstructured().
@ -234,62 +222,9 @@ func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
} }
func (o *DeleteOptions) RunDelete() error { func (o *DeleteOptions) RunDelete() error {
// By default use a reaper to delete all related resources.
if o.Cascade {
// TODO(juanvallejo): although o.Result can be accessed from the options
// it is also passed here so that callers of this method outside of the "delete"
// command do not have to tack it to the "delete" options as well.
// Find a cleaner way to approach this.
return o.ReapResult(o.Result, true, false)
}
return o.DeleteResult(o.Result) return o.DeleteResult(o.Result)
} }
func (o *DeleteOptions) ReapResult(r *resource.Result, isDefaultDelete, quiet bool) error {
found := 0
if o.IgnoreNotFound {
r = r.IgnoreErrors(errors.IsNotFound)
}
err := r.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
found++
reaper, err := o.Reaper(info.Mapping)
if err != nil {
// If there is no reaper for this resources and the user didn't explicitly ask for stop.
if kubectl.IsNoSuchReaperError(err) && isDefaultDelete {
// No client side reaper found. Let the server do cascading deletion.
return o.cascadingDeleteResource(info)
}
return cmdutil.AddSourceToErr("reaping", info.Source, err)
}
var options *metav1.DeleteOptions
if o.GracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(o.GracePeriod))
}
if err := reaper.Stop(info.Namespace, info.Name, o.Timeout, options); err != nil {
return cmdutil.AddSourceToErr("stopping", info.Source, err)
}
if o.WaitForDeletion {
if err := waitForObjectDeletion(info, o.Timeout); err != nil {
return cmdutil.AddSourceToErr("stopping", info.Source, err)
}
}
if !quiet {
o.PrintObj(info)
}
return nil
})
if err != nil {
return err
}
if found == 0 {
fmt.Fprintf(o.Out, "No resources found\n")
}
return nil
}
func (o *DeleteOptions) DeleteResult(r *resource.Result) error { func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
found := 0 found := 0
if o.IgnoreNotFound { if o.IgnoreNotFound {
@ -301,12 +236,14 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
} }
found++ found++
// if we're here, it means that cascade=false (not the default), so we should orphan as requested
options := &metav1.DeleteOptions{} options := &metav1.DeleteOptions{}
if o.GracePeriod >= 0 { if o.GracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(o.GracePeriod)) options = metav1.NewDeleteOptions(int64(o.GracePeriod))
} }
policy := metav1.DeletePropagationOrphan policy := metav1.DeletePropagationForeground
if !o.Cascade {
policy = metav1.DeletePropagationOrphan
}
options.PropagationPolicy = &policy options.PropagationPolicy = &policy
return o.deleteResource(info, options) return o.deleteResource(info, options)
}) })
@ -349,11 +286,6 @@ func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
return err return err
} }
func (o *DeleteOptions) cascadingDeleteResource(info *resource.Info) error {
policy := metav1.DeletePropagationForeground
return o.deleteResource(info, &metav1.DeleteOptions{PropagationPolicy: &policy})
}
func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav1.DeleteOptions) error { func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav1.DeleteOptions) error {
if err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions); err != nil { if err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions); err != nil {
return cmdutil.AddSourceToErr("deleting", info.Source, err) return cmdutil.AddSourceToErr("deleting", info.Source, err)
@ -386,24 +318,3 @@ func (o *DeleteOptions) PrintObj(info *resource.Info) {
// understandable output by default // understandable output by default
fmt.Fprintf(o.Out, "%s \"%s\" %s\n", kindString, info.Name, operation) fmt.Fprintf(o.Out, "%s \"%s\" %s\n", kindString, info.Name, operation)
} }
// objectDeletionWaitInterval is the interval to wait between checks for deletion.
var objectDeletionWaitInterval = time.Second
// waitForObjectDeletion refreshes the object, waiting until it is deleted, a timeout is reached, or
// an error is encountered. It checks once a second.
func waitForObjectDeletion(info *resource.Info, timeout time.Duration) error {
copied := *info
info = &copied
// TODO: refactor Reaper so that we can pass the "wait" option into it, and then check for UID change.
return wait.PollImmediate(objectDeletionWaitInterval, timeout, func() (bool, error) {
switch err := info.Get(); {
case err == nil:
return false, nil
case errors.IsNotFound(err):
return true, nil
default:
return false, err
}
})
}

View File

@ -23,19 +23,15 @@ import (
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource" "k8s.io/kubernetes/pkg/kubectl/genericclioptions/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
@ -259,34 +255,10 @@ func TestDeleteObject(t *testing.T) {
} }
} }
type fakeReaper struct {
namespace, name string
timeout time.Duration
deleteOptions *metav1.DeleteOptions
err error
}
func (r *fakeReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
r.namespace, r.name = namespace, name
r.timeout = timeout
r.deleteOptions = gracePeriod
return r.err
}
type fakeReaperFactory struct {
cmdutil.Factory
reaper kubectl.Reaper
}
func (f *fakeReaperFactory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
return f.reaper, nil
}
func TestDeleteObjectGraceZero(t *testing.T) { func TestDeleteObjectGraceZero(t *testing.T) {
initTestErrorHandler(t) initTestErrorHandler(t)
pods, _, _ := testData() pods, _, _ := testData()
objectDeletionWaitInterval = time.Millisecond
count := 0 count := 0
tf := cmdtesting.NewTestFactory() tf := cmdtesting.NewTestFactory()
defer tf.Cleanup() defer tf.Cleanup()
@ -318,10 +290,8 @@ func TestDeleteObjectGraceZero(t *testing.T) {
} }
tf.Namespace = "test" tf.Namespace = "test"
reaper := &fakeReaper{}
fake := &fakeReaperFactory{Factory: tf, reaper: reaper}
streams, _, buf, errBuf := genericclioptions.NewTestIOStreams() streams, _, buf, errBuf := genericclioptions.NewTestIOStreams()
cmd := NewCmdDelete(fake, streams) cmd := NewCmdDelete(tf, streams)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", "name")
cmd.Flags().Set("grace-period", "0") cmd.Flags().Set("grace-period", "0")
cmd.Run(cmd, []string{"pods/nginx"}) cmd.Run(cmd, []string{"pods/nginx"})
@ -330,10 +300,7 @@ func TestDeleteObjectGraceZero(t *testing.T) {
if buf.String() != "pod/nginx\n" { if buf.String() != "pod/nginx\n" {
t.Errorf("unexpected output: %s\n---\n%s", buf.String(), errBuf.String()) t.Errorf("unexpected output: %s\n---\n%s", buf.String(), errBuf.String())
} }
if reaper.deleteOptions == nil || reaper.deleteOptions.GracePeriodSeconds == nil || *reaper.deleteOptions.GracePeriodSeconds != 1 { if count != 0 {
t.Errorf("unexpected reaper options: %#v", reaper)
}
if count != 4 {
t.Errorf("unexpected calls to GET: %d", count) t.Errorf("unexpected calls to GET: %d", count)
} }
} }

View File

@ -43,7 +43,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
@ -594,7 +593,7 @@ func (o *DrainOptions) evictPods(pods []corev1.Pod, policyGroupVersion string, g
} }
} }
podArray := []corev1.Pod{pod} podArray := []corev1.Pod{pod}
_, err = o.waitForDelete(podArray, kubectl.Interval, time.Duration(math.MaxInt64), true, getPodFn) _, err = o.waitForDelete(podArray, 1*time.Second, time.Duration(math.MaxInt64), true, getPodFn)
if err == nil { if err == nil {
doneCh <- true doneCh <- true
} else { } else {
@ -640,7 +639,7 @@ func (o *DrainOptions) deletePods(pods []corev1.Pod, getPodFn func(namespace, na
return err return err
} }
} }
_, err := o.waitForDelete(pods, kubectl.Interval, globalTimeout, false, getPodFn) _, err := o.waitForDelete(pods, 1*time.Second, globalTimeout, false, getPodFn)
return err return err
} }

View File

@ -21,6 +21,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -158,7 +159,6 @@ func (o *ReplaceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
//Replace will create a resource if it doesn't exist already, so ignore not found error //Replace will create a resource if it doesn't exist already, so ignore not found error
deleteOpts.IgnoreNotFound = true deleteOpts.IgnoreNotFound = true
deleteOpts.Reaper = f.Reaper
if o.PrintFlags.OutputFormat != nil { if o.PrintFlags.OutputFormat != nil {
deleteOpts.Output = *o.PrintFlags.OutputFormat deleteOpts.Output = *o.PrintFlags.OutputFormat
} }
@ -273,25 +273,20 @@ func (o *ReplaceOptions) forceReplace() error {
return err return err
} }
var err error if err := o.DeleteOptions.DeleteResult(r); err != nil {
return err
// By default use a reaper to delete all related resources.
if o.DeleteOptions.Cascade {
glog.Warningf("\"cascade\" is set, kubectl will delete and re-create all resources managed by this resource (e.g. Pods created by a ReplicationController). Consider using \"kubectl rolling-update\" if you want to update a ReplicationController together with its Pods.")
err = o.DeleteOptions.ReapResult(r, o.DeleteOptions.Cascade, false)
} else {
err = o.DeleteOptions.DeleteResult(r)
} }
timeout := o.DeleteOptions.Timeout
if timeout == 0 { if timeout == 0 {
timeout = kubectl.Timeout timeout = 5 * time.Minute
} }
err = r.Visit(func(info *resource.Info, err error) error { err := r.Visit(func(info *resource.Info, err error) error {
if err != nil { if err != nil {
return err return err
} }
return wait.PollImmediate(kubectl.Interval, timeout, func() (bool, error) { return wait.PollImmediate(1*time.Second, timeout, func() (bool, error) {
if err := info.Get(); !errors.IsNotFound(err) { if err := info.Get(); !errors.IsNotFound(err) {
return false, err return false, err
} }

View File

@ -227,7 +227,6 @@ func (o *RunOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
deleteOpts.IgnoreNotFound = true deleteOpts.IgnoreNotFound = true
deleteOpts.WaitForDeletion = false deleteOpts.WaitForDeletion = false
deleteOpts.GracePeriod = -1 deleteOpts.GracePeriod = -1
deleteOpts.Reaper = f.Reaper
o.DeleteOptions = deleteOpts o.DeleteOptions = deleteOpts
@ -459,14 +458,7 @@ func (o *RunOptions) removeCreatedObjects(f cmdutil.Factory, createdObjects []*R
ResourceNames(obj.Mapping.Resource.Resource+"."+obj.Mapping.Resource.Group, name). ResourceNames(obj.Mapping.Resource.Resource+"."+obj.Mapping.Resource.Group, name).
Flatten(). Flatten().
Do() Do()
// Note: we pass in "true" for the "quiet" parameter because if err := o.DeleteOptions.DeleteResult(r); err != nil {
// ReadResult will only print one thing based on the "quiet"
// flag, and that's the "pod xxx deleted" message. If they
// asked for us to remove the pod (via --rm) then telling them
// its been deleted is unnecessary since that's what they asked
// for. We should only print something if the "rm" fails.
err = o.DeleteOptions.ReapResult(r, true, true)
if err != nil {
return err return err
} }
} }

View File

@ -214,11 +214,11 @@ func (o *ScaleOptions) RunScale() error {
} }
precondition := &kubectl.ScalePrecondition{Size: o.CurrentReplicas, ResourceVersion: o.ResourceVersion} precondition := &kubectl.ScalePrecondition{Size: o.CurrentReplicas, ResourceVersion: o.ResourceVersion}
retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout) retry := kubectl.NewRetryParams(1*time.Second, 5*time.Minute)
var waitForReplicas *kubectl.RetryParams var waitForReplicas *kubectl.RetryParams
if o.Timeout != 0 { if o.Timeout != 0 {
waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout) waitForReplicas = kubectl.NewRetryParams(1*time.Second, timeout)
} }
counter := 0 counter := 0

View File

@ -105,8 +105,6 @@ type ObjectMappingFactory interface {
type BuilderFactory interface { type BuilderFactory interface {
// ScaleClient gives you back scale getter // ScaleClient gives you back scale getter
ScaleClient() (scaleclient.ScalesGetter, error) ScaleClient() (scaleclient.ScalesGetter, error)
// Returns a Reaper for gracefully shutting down resources.
Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error)
} }
type factory struct { type factory struct {

View File

@ -19,10 +19,8 @@ limitations under the License.
package util package util
import ( import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
scaleclient "k8s.io/client-go/scale" scaleclient "k8s.io/client-go/scale"
"k8s.io/kubernetes/pkg/kubectl"
) )
type ring2Factory struct { type ring2Factory struct {
@ -56,20 +54,3 @@ func (f *ring2Factory) ScaleClient() (scaleclient.ScalesGetter, error) {
return scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil return scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil
} }
func (f *ring2Factory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
clientset, clientsetErr := f.clientAccessFactory.ClientSet()
if clientsetErr != nil {
return nil, clientsetErr
}
scaler, err := f.ScaleClient()
if err != nil {
return nil, err
}
reaper, reaperErr := kubectl.ReaperFor(mapping.GroupVersionKind.GroupKind(), clientset, scaler)
if kubectl.IsNoSuchReaperError(reaperErr) {
return nil, reaperErr
}
return reaper, reaperErr
}

View File

@ -1,504 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
import (
"fmt"
"strings"
"time"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/util/wait"
scaleclient "k8s.io/client-go/scale"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
appsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion"
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"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/scalejob"
)
const (
Interval = time.Second * 1
Timeout = time.Minute * 5
)
// A Reaper terminates an object as gracefully as possible.
type Reaper interface {
// Stop a given object within a namespace. timeout is how long we'll
// wait for the termination to be successful. gracePeriod is time given
// to an API object for it to delete itself cleanly (e.g., pod
// shutdown). It may or may not be supported by the API object.
Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error
}
type NoSuchReaperError struct {
kind schema.GroupKind
}
func (n *NoSuchReaperError) Error() string {
return fmt.Sprintf("no reaper has been implemented for %v", n.kind)
}
func IsNoSuchReaperError(err error) bool {
_, ok := err.(*NoSuchReaperError)
return ok
}
func ReaperFor(kind schema.GroupKind, c internalclientset.Interface, sc scaleclient.ScalesGetter) (Reaper, error) {
switch kind {
case api.Kind("ReplicationController"):
return &ReplicationControllerReaper{c.Core(), Interval, Timeout, sc}, nil
case extensions.Kind("ReplicaSet"), apps.Kind("ReplicaSet"):
return &ReplicaSetReaper{c.Extensions(), Interval, Timeout, sc, schema.GroupResource{Group: kind.Group, Resource: "replicasets"}}, nil
case extensions.Kind("DaemonSet"), apps.Kind("DaemonSet"):
return &DaemonSetReaper{c.Extensions(), Interval, Timeout}, nil
case api.Kind("Pod"):
return &PodReaper{c.Core()}, nil
case batch.Kind("Job"):
return &JobReaper{c.Batch(), c.Core(), Interval, Timeout}, nil
case apps.Kind("StatefulSet"):
return &StatefulSetReaper{c.Apps(), c.Core(), Interval, Timeout, sc}, nil
case extensions.Kind("Deployment"), apps.Kind("Deployment"):
return &DeploymentReaper{c.Extensions(), c.Extensions(), Interval, Timeout, sc, schema.GroupResource{Group: kind.Group, Resource: "deployments"}}, nil
}
return nil, &NoSuchReaperError{kind}
}
func ReaperForReplicationController(rcClient coreclient.ReplicationControllersGetter, scaleClient scaleclient.ScalesGetter, timeout time.Duration) (Reaper, error) {
return &ReplicationControllerReaper{rcClient, Interval, timeout, scaleClient}, nil
}
type ReplicationControllerReaper struct {
client coreclient.ReplicationControllersGetter
pollInterval, timeout time.Duration
scaleClient scaleclient.ScalesGetter
}
type ReplicaSetReaper struct {
client extensionsclient.ReplicaSetsGetter
pollInterval, timeout time.Duration
scaleClient scaleclient.ScalesGetter
gr schema.GroupResource
}
type DaemonSetReaper struct {
client extensionsclient.DaemonSetsGetter
pollInterval, timeout time.Duration
}
type JobReaper struct {
client batchclient.JobsGetter
podClient coreclient.PodsGetter
pollInterval, timeout time.Duration
}
type DeploymentReaper struct {
dClient extensionsclient.DeploymentsGetter
rsClient extensionsclient.ReplicaSetsGetter
pollInterval, timeout time.Duration
scaleClient scaleclient.ScalesGetter
gr schema.GroupResource
}
type PodReaper struct {
client coreclient.PodsGetter
}
type StatefulSetReaper struct {
client appsclient.StatefulSetsGetter
podClient coreclient.PodsGetter
pollInterval, timeout time.Duration
scaleClient scaleclient.ScalesGetter
}
// getOverlappingControllers finds rcs that this controller overlaps, as well as rcs overlapping this controller.
func getOverlappingControllers(rcClient coreclient.ReplicationControllerInterface, rc *api.ReplicationController) ([]api.ReplicationController, error) {
rcs, err := rcClient.List(metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("error getting replication controllers: %v", err)
}
var matchingRCs []api.ReplicationController
rcLabels := labels.Set(rc.Spec.Selector)
for _, controller := range rcs.Items {
newRCLabels := labels.Set(controller.Spec.Selector)
if labels.SelectorFromSet(newRCLabels).Matches(rcLabels) || labels.SelectorFromSet(rcLabels).Matches(newRCLabels) {
matchingRCs = append(matchingRCs, controller)
}
}
return matchingRCs, nil
}
func (reaper *ReplicationControllerReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
rc := reaper.client.ReplicationControllers(namespace)
scaler := NewScaler(reaper.scaleClient)
ctrl, err := rc.Get(name, metav1.GetOptions{})
if err != nil {
return err
}
if timeout == 0 {
timeout = Timeout + time.Duration(10*ctrl.Spec.Replicas)*time.Second
}
// The rc manager will try and detect all matching rcs for a pod's labels,
// and only sync the oldest one. This means if we have a pod with labels
// [(k1: v1), (k2: v2)] and two rcs: rc1 with selector [(k1=v1)], and rc2 with selector [(k1=v1),(k2=v2)],
// the rc manager will sync the older of the two rcs.
//
// If there are rcs with a superset of labels, eg:
// deleting: (k1=v1), superset: (k2=v2, k1=v1)
// - It isn't safe to delete the rc because there could be a pod with labels
// (k1=v1) that isn't managed by the superset rc. We can't scale it down
// either, because there could be a pod (k2=v2, k1=v1) that it deletes
// causing a fight with the superset rc.
// If there are rcs with a subset of labels, eg:
// deleting: (k2=v2, k1=v1), subset: (k1=v1), superset: (k2=v2, k1=v1, k3=v3)
// - Even if it's safe to delete this rc without a scale down because all it's pods
// are being controlled by the subset rc the code returns an error.
// In theory, creating overlapping controllers is user error, so the loop below
// tries to account for this logic only in the common case, where we end up
// with multiple rcs that have an exact match on selectors.
overlappingCtrls, err := getOverlappingControllers(rc, ctrl)
if err != nil {
return fmt.Errorf("error getting replication controllers: %v", err)
}
exactMatchRCs := []api.ReplicationController{}
overlapRCs := []string{}
for _, overlappingRC := range overlappingCtrls {
if len(overlappingRC.Spec.Selector) == len(ctrl.Spec.Selector) {
exactMatchRCs = append(exactMatchRCs, overlappingRC)
} else {
overlapRCs = append(overlapRCs, overlappingRC.Name)
}
}
if len(overlapRCs) > 0 {
return fmt.Errorf(
"Detected overlapping controllers for rc %v: %v, please manage deletion individually with --cascade=false.",
ctrl.Name, strings.Join(overlapRCs, ","))
}
if len(exactMatchRCs) == 1 {
// No overlapping controllers.
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
waitForReplicas := NewRetryParams(reaper.pollInterval, timeout)
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForReplicas, schema.GroupResource{Resource: "replicationcontrollers"}); err != nil && !errors.IsNotFound(err) {
return err
}
}
// Using a background deletion policy because the replication controller
// has already been scaled down.
policy := metav1.DeletePropagationBackground
deleteOptions := &metav1.DeleteOptions{PropagationPolicy: &policy}
return rc.Delete(name, deleteOptions)
}
// TODO(madhusudancs): Implement it when controllerRef is implemented - https://github.com/kubernetes/kubernetes/issues/2210
// getOverlappingReplicaSets finds ReplicaSets that this ReplicaSet overlaps, as well as ReplicaSets overlapping this ReplicaSet.
func getOverlappingReplicaSets(c extensionsclient.ReplicaSetInterface, rs *extensions.ReplicaSet) ([]extensions.ReplicaSet, []extensions.ReplicaSet, error) {
var overlappingRSs, exactMatchRSs []extensions.ReplicaSet
return overlappingRSs, exactMatchRSs, nil
}
func (reaper *ReplicaSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
rsc := reaper.client.ReplicaSets(namespace)
scaler := NewScaler(reaper.scaleClient)
rs, err := rsc.Get(name, metav1.GetOptions{})
if err != nil {
return err
}
if timeout == 0 {
timeout = Timeout + time.Duration(10*rs.Spec.Replicas)*time.Second
}
// The ReplicaSet controller will try and detect all matching ReplicaSets
// for a pod's labels, and only sync the oldest one. This means if we have
// a pod with labels [(k1: v1), (k2: v2)] and two ReplicaSets: rs1 with
// selector [(k1=v1)], and rs2 with selector [(k1=v1),(k2=v2)], the
// ReplicaSet controller will sync the older of the two ReplicaSets.
//
// If there are ReplicaSets with a superset of labels, eg:
// deleting: (k1=v1), superset: (k2=v2, k1=v1)
// - It isn't safe to delete the ReplicaSet because there could be a pod
// with labels (k1=v1) that isn't managed by the superset ReplicaSet.
// We can't scale it down either, because there could be a pod
// (k2=v2, k1=v1) that it deletes causing a fight with the superset
// ReplicaSet.
// If there are ReplicaSets with a subset of labels, eg:
// deleting: (k2=v2, k1=v1), subset: (k1=v1), superset: (k2=v2, k1=v1, k3=v3)
// - Even if it's safe to delete this ReplicaSet without a scale down because
// all it's pods are being controlled by the subset ReplicaSet the code
// returns an error.
// In theory, creating overlapping ReplicaSets is user error, so the loop below
// tries to account for this logic only in the common case, where we end up
// with multiple ReplicaSets that have an exact match on selectors.
// TODO(madhusudancs): Re-evaluate again when controllerRef is implemented -
// https://github.com/kubernetes/kubernetes/issues/2210
overlappingRSs, exactMatchRSs, err := getOverlappingReplicaSets(rsc, rs)
if err != nil {
return fmt.Errorf("error getting ReplicaSets: %v", err)
}
if len(overlappingRSs) > 0 {
var names []string
for _, overlappingRS := range overlappingRSs {
names = append(names, overlappingRS.Name)
}
return fmt.Errorf(
"Detected overlapping ReplicaSets for ReplicaSet %v: %v, please manage deletion individually with --cascade=false.",
rs.Name, strings.Join(names, ","))
}
if len(exactMatchRSs) == 0 {
// No overlapping ReplicaSets.
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
waitForReplicas := NewRetryParams(reaper.pollInterval, timeout)
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForReplicas, reaper.gr); err != nil && !errors.IsNotFound(err) {
return err
}
}
// Using a background deletion policy because the replica set has already
// been scaled down.
policy := metav1.DeletePropagationBackground
deleteOptions := &metav1.DeleteOptions{PropagationPolicy: &policy}
return rsc.Delete(name, deleteOptions)
}
func (reaper *DaemonSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
ds, err := reaper.client.DaemonSets(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return err
}
// We set the nodeSelector to a random label. This label is nearly guaranteed
// to not be set on any node so the DameonSetController will start deleting
// daemon pods. Once it's done deleting the daemon pods, it's safe to delete
// the DaemonSet.
ds.Spec.Template.Spec.NodeSelector = map[string]string{
string(uuid.NewUUID()): string(uuid.NewUUID()),
}
// force update to avoid version conflict
ds.ResourceVersion = ""
if ds, err = reaper.client.DaemonSets(namespace).Update(ds); err != nil {
return err
}
// Wait for the daemon set controller to kill all the daemon pods.
if err := wait.Poll(reaper.pollInterval, reaper.timeout, func() (bool, error) {
updatedDS, err := reaper.client.DaemonSets(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return false, nil
}
return updatedDS.Status.CurrentNumberScheduled+updatedDS.Status.NumberMisscheduled == 0, nil
}); err != nil {
return err
}
// Using a background deletion policy because the daemon set has already
// been scaled down.
policy := metav1.DeletePropagationBackground
deleteOptions := &metav1.DeleteOptions{PropagationPolicy: &policy}
return reaper.client.DaemonSets(namespace).Delete(name, deleteOptions)
}
func (reaper *StatefulSetReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
statefulsets := reaper.client.StatefulSets(namespace)
scaler := NewScaler(reaper.scaleClient)
ss, err := statefulsets.Get(name, metav1.GetOptions{})
if err != nil {
return err
}
if timeout == 0 {
numReplicas := ss.Spec.Replicas
// See discussion of this behavior here:
// https://github.com/kubernetes/kubernetes/pull/46468#discussion_r118589512
timeout = Timeout + time.Duration(10*numReplicas)*time.Second
}
retry := NewRetryParams(reaper.pollInterval, reaper.timeout)
waitForStatefulSet := NewRetryParams(reaper.pollInterval, timeout)
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForStatefulSet, apps.Resource("statefulsets")); err != nil && !errors.IsNotFound(err) {
return err
}
// TODO: Cleanup volumes? We don't want to accidentally delete volumes from
// stop, so just leave this up to the statefulset.
// Using a background deletion policy because the stateful set has already
// been scaled down.
policy := metav1.DeletePropagationBackground
deleteOptions := &metav1.DeleteOptions{PropagationPolicy: &policy}
return statefulsets.Delete(name, deleteOptions)
}
func (reaper *JobReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
jobs := reaper.client.Jobs(namespace)
pods := reaper.podClient.Pods(namespace)
scaler := &scalejob.JobPsuedoScaler{
JobsClient: reaper.client,
}
job, err := jobs.Get(name, metav1.GetOptions{})
if err != nil {
return err
}
if timeout == 0 {
// we will never have more active pods than job.Spec.Parallelism
parallelism := *job.Spec.Parallelism
timeout = Timeout + time.Duration(10*parallelism)*time.Second
}
// TODO: handle overlapping jobs
retry := &scalejob.RetryParams{Interval: reaper.pollInterval, Timeout: reaper.timeout}
waitForJobs := &scalejob.RetryParams{Interval: reaper.pollInterval, Timeout: reaper.timeout}
if err = scaler.Scale(namespace, name, 0, nil, retry, waitForJobs); err != nil && !errors.IsNotFound(err) {
return err
}
// at this point only dead pods are left, that should be removed
selector, _ := metav1.LabelSelectorAsSelector(job.Spec.Selector)
options := metav1.ListOptions{LabelSelector: selector.String()}
podList, err := pods.List(options)
if err != nil {
return err
}
errList := []error{}
for _, pod := range podList.Items {
if err := pods.Delete(pod.Name, gracePeriod); err != nil {
// ignores the error when the pod isn't found
if !errors.IsNotFound(err) {
errList = append(errList, err)
}
}
}
if len(errList) > 0 {
return utilerrors.NewAggregate(errList)
}
// once we have all the pods removed we can safely remove the job itself.
policy := metav1.DeletePropagationBackground
deleteOptions := &metav1.DeleteOptions{PropagationPolicy: &policy}
return jobs.Delete(name, deleteOptions)
}
func (reaper *DeploymentReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
deployments := reaper.dClient.Deployments(namespace)
rsReaper := &ReplicaSetReaper{reaper.rsClient, reaper.pollInterval, reaper.timeout, reaper.scaleClient, schema.GroupResource{Group: reaper.gr.Group, Resource: "replicasets"}}
deployment, err := reaper.updateDeploymentWithRetries(namespace, name, func(d *extensions.Deployment) {
// set deployment's history and scale to 0
// TODO replace with patch when available: https://github.com/kubernetes/kubernetes/issues/20527
rhl := int32(0)
d.Spec.RevisionHistoryLimit = &rhl
d.Spec.Replicas = 0
d.Spec.Paused = true
})
if err != nil {
return err
}
if deployment.Initializers != nil {
policy := metav1.DeletePropagationBackground
deleteOptions := &metav1.DeleteOptions{PropagationPolicy: &policy}
return deployments.Delete(name, deleteOptions)
}
// Use observedGeneration to determine if the deployment controller noticed the pause.
if err := deploymentutil.WaitForObservedDeploymentInternal(func() (*extensions.Deployment, error) {
return deployments.Get(name, metav1.GetOptions{})
}, deployment.Generation, 1*time.Second, 1*time.Minute); err != nil {
return err
}
// 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
}
errList := []error{}
for _, rs := range rss {
if err := rsReaper.Stop(rs.Namespace, rs.Name, timeout, gracePeriod); err != nil {
if errors.IsNotFound(err) {
continue
}
errList = append(errList, err)
}
}
if len(errList) > 0 {
return utilerrors.NewAggregate(errList)
}
// Delete deployment at the end.
// Note: We delete deployment at the end so that if removing RSs fails, we at least have the deployment to retry.
policy := metav1.DeletePropagationBackground
deleteOptions := &metav1.DeleteOptions{PropagationPolicy: &policy}
return deployments.Delete(name, deleteOptions)
}
type updateDeploymentFunc func(d *extensions.Deployment)
func (reaper *DeploymentReaper) updateDeploymentWithRetries(namespace, name string, applyUpdate updateDeploymentFunc) (deployment *extensions.Deployment, err error) {
deployments := reaper.dClient.Deployments(namespace)
err = wait.Poll(10*time.Millisecond, 1*time.Minute, func() (bool, error) {
if deployment, err = deployments.Get(name, metav1.GetOptions{}); err != nil {
return false, err
}
// Apply the update, then attempt to push it to the apiserver.
applyUpdate(deployment)
if deployment, err = deployments.Update(deployment); err == nil {
return true, nil
}
// Retry only on update conflict.
if errors.IsConflict(err) {
return false, nil
}
return false, err
})
return deployment, err
}
func (reaper *PodReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *metav1.DeleteOptions) error {
pods := reaper.client.Pods(namespace)
_, err := pods.Get(name, metav1.GetOptions{})
if err != nil {
return err
}
return pods.Delete(name, gracePeriod)
}

View File

@ -1,837 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
import (
"fmt"
"reflect"
"strings"
"testing"
"time"
autoscalingv1 "k8s.io/api/autoscaling/v1"
"k8s.io/apimachinery/pkg/api/errors"
kerrors "k8s.io/apimachinery/pkg/api/errors"
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"
fakescale "k8s.io/client-go/scale/fake"
testcore "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
)
func TestReplicationControllerStop(t *testing.T) {
name := "foo"
ns := "default"
tests := []struct {
Name string
Objs []runtime.Object
ScaledDown bool
StopError error
ExpectedActions []string
ScaleClientExpectedAction []string
}{
{
Name: "OnlyOneRC",
Objs: []runtime.Object{
&api.ReplicationControllerList{ // LIST
Items: []api.ReplicationController{
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1"}},
},
},
},
},
ScaledDown: true,
StopError: nil,
ExpectedActions: []string{"get", "list", "delete"},
ScaleClientExpectedAction: []string{"get", "update", "get", "get"},
},
{
Name: "NoOverlapping",
Objs: []runtime.Object{
&api.ReplicationControllerList{ // LIST
Items: []api.ReplicationController{
{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k3": "v3"}},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1"}},
},
},
},
},
ScaledDown: true,
StopError: nil,
ExpectedActions: []string{"get", "list", "delete"},
ScaleClientExpectedAction: []string{"get", "update", "get", "get"},
},
{
Name: "OverlappingError",
Objs: []runtime.Object{
&api.ReplicationControllerList{ // LIST
Items: []api.ReplicationController{
{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1", "k2": "v2"}},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1"}},
},
},
},
},
ScaledDown: false, // scale resource was not scaled down due to overlapping controllers
StopError: fmt.Errorf("Detected overlapping controllers for rc foo: baz, please manage deletion individually with --cascade=false."),
ExpectedActions: []string{"get", "list"},
},
{
Name: "OverlappingButSafeDelete",
Objs: []runtime.Object{
&api.ReplicationControllerList{ // LIST
Items: []api.ReplicationController{
{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"}},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "zaz",
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1"}},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1", "k2": "v2"}},
},
},
},
},
ScaledDown: false, // scale resource was not scaled down due to overlapping controllers
StopError: fmt.Errorf("Detected overlapping controllers for rc foo: baz,zaz, please manage deletion individually with --cascade=false."),
ExpectedActions: []string{"get", "list"},
},
{
Name: "TwoExactMatchRCs",
Objs: []runtime.Object{
&api.ReplicationControllerList{ // LIST
Items: []api.ReplicationController{
{
ObjectMeta: metav1.ObjectMeta{
Name: "zaz",
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1"}},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: api.ReplicationControllerSpec{
Replicas: 0,
Selector: map[string]string{"k1": "v1"}},
},
},
},
},
ScaledDown: false, // scale resource was not scaled down because there is still an additional replica
StopError: nil,
ExpectedActions: []string{"get", "list", "delete"},
},
}
for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
copiedForWatch := test.Objs[0].DeepCopyObject()
scaleClient := createFakeScaleClient("replicationcontrollers", "foo", 3, nil)
fake := fake.NewSimpleClientset(test.Objs...)
fakeWatch := watch.NewFake()
fake.PrependWatchReactor("replicationcontrollers", testcore.DefaultWatchReactor(fakeWatch, nil))
go func() {
fakeWatch.Add(copiedForWatch)
}()
reaper := ReplicationControllerReaper{fake.Core(), time.Millisecond, time.Millisecond, scaleClient}
err := reaper.Stop(ns, name, 0, nil)
if !reflect.DeepEqual(err, test.StopError) {
t.Fatalf("unexpected error: %v", err)
}
actions := fake.Actions()
if len(actions) != len(test.ExpectedActions) {
t.Fatalf("unexpected actions: %v, expected %d actions got %d", actions, len(test.ExpectedActions), len(actions))
}
for i, verb := range test.ExpectedActions {
if actions[i].GetResource().GroupResource() != api.Resource("replicationcontrollers") {
t.Errorf("unexpected action: %+v, expected %s-replicationController", actions[i], verb)
}
if actions[i].GetVerb() != verb {
t.Errorf("unexpected action: %+v, expected %s-replicationController", actions[i], verb)
}
}
if test.ScaledDown {
scale, err := scaleClient.Scales(ns).Get(schema.GroupResource{Group: "", Resource: "replicationcontrollers"}, name)
if err != nil {
t.Error(err)
}
if scale.Spec.Replicas != 0 {
t.Errorf("a scale subresource has unexpected number of replicas, got %d expected 0", scale.Spec.Replicas)
}
actions := scaleClient.Actions()
if len(actions) != len(test.ScaleClientExpectedAction) {
t.Errorf("unexpected actions: %v, expected %d actions got %d", actions, len(test.ScaleClientExpectedAction), len(actions))
}
for i, verb := range test.ScaleClientExpectedAction {
if actions[i].GetVerb() != verb {
t.Errorf("unexpected action: %+v, expected %s", actions[i].GetVerb(), verb)
}
}
}
})
}
}
func TestReplicaSetStop(t *testing.T) {
name := "foo"
ns := "default"
tests := []struct {
Name string
Objs []runtime.Object
DiscoveryResources []*metav1.APIResourceList
PathsResources map[string]runtime.Object
ScaledDown bool
StopError error
ExpectedActions []string
ScaleClientExpectedAction []string
}{
{
Name: "OnlyOneRS",
Objs: []runtime.Object{
&extensions.ReplicaSetList{ // LIST
TypeMeta: metav1.TypeMeta{
APIVersion: extensions.SchemeGroupVersion.String(),
},
Items: []extensions.ReplicaSet{
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.ReplicaSetSpec{
Replicas: 0,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}},
},
},
},
},
},
ScaledDown: true,
StopError: nil,
ExpectedActions: []string{"get", "delete"},
ScaleClientExpectedAction: []string{"get", "update", "get", "get"},
},
{
Name: "NoOverlapping",
Objs: []runtime.Object{
&extensions.ReplicaSetList{ // LIST
Items: []extensions.ReplicaSet{
{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: ns,
},
Spec: extensions.ReplicaSetSpec{
Replicas: 0,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"k3": "v3"}},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.ReplicaSetSpec{
Replicas: 0,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}},
},
},
},
},
},
ScaledDown: true,
StopError: nil,
ExpectedActions: []string{"get", "delete"},
ScaleClientExpectedAction: []string{"get", "update", "get", "get"},
},
// TODO: Implement tests for overlapping replica sets, similar to replication controllers,
// when the overlapping checks are implemented for replica sets.
}
for _, test := range tests {
fake := fake.NewSimpleClientset(test.Objs...)
scaleClient := createFakeScaleClient("replicasets", "foo", 3, nil)
reaper := ReplicaSetReaper{fake.Extensions(), time.Millisecond, time.Millisecond, scaleClient, schema.GroupResource{Group: "extensions", Resource: "replicasets"}}
err := reaper.Stop(ns, name, 0, nil)
if !reflect.DeepEqual(err, test.StopError) {
t.Errorf("%s unexpected error: %v", test.Name, err)
continue
}
actions := fake.Actions()
if len(actions) != len(test.ExpectedActions) {
t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions))
continue
}
for i, verb := range test.ExpectedActions {
if actions[i].GetResource().GroupResource() != extensions.Resource("replicasets") {
t.Errorf("%s unexpected action: %+v, expected %s-replicaSet", test.Name, actions[i], verb)
}
if actions[i].GetVerb() != verb {
t.Errorf("%s unexpected action: %+v, expected %s-replicaSet", test.Name, actions[i], verb)
}
}
if test.ScaledDown {
scale, err := scaleClient.Scales(ns).Get(schema.GroupResource{Group: "extensions", Resource: "replicasets"}, name)
if err != nil {
t.Error(err)
}
if scale.Spec.Replicas != 0 {
t.Errorf("a scale subresource has unexpected number of replicas, got %d expected 0", scale.Spec.Replicas)
}
actions := scaleClient.Actions()
if len(actions) != len(test.ScaleClientExpectedAction) {
t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ScaleClientExpectedAction), len(actions))
}
for i, verb := range test.ScaleClientExpectedAction {
if actions[i].GetVerb() != verb {
t.Errorf("%s unexpected action: %+v, expected %s", test.Name, actions[i].GetVerb(), verb)
}
}
}
}
}
func TestJobStop(t *testing.T) {
name := "foo"
ns := "default"
zero := int32(0)
tests := []struct {
Name string
Objs []runtime.Object
StopError error
ExpectedActions []string
}{
{
Name: "OnlyOneJob",
Objs: []runtime.Object{
&batch.JobList{ // LIST
Items: []batch.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: batch.JobSpec{
Parallelism: &zero,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"k1": "v1"},
},
},
},
},
},
},
StopError: nil,
ExpectedActions: []string{"get:jobs", "get:jobs", "update:jobs",
"get:jobs", "get:jobs", "list:pods", "delete:jobs"},
},
{
Name: "JobWithDeadPods",
Objs: []runtime.Object{
&batch.JobList{ // LIST
Items: []batch.Job{
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: batch.JobSpec{
Parallelism: &zero,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"k1": "v1"},
},
},
},
},
},
&api.PodList{ // LIST
Items: []api.Pod{
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: ns,
Labels: map[string]string{"k1": "v1"},
},
},
},
},
},
StopError: nil,
ExpectedActions: []string{"get:jobs", "get:jobs", "update:jobs",
"get:jobs", "get:jobs", "list:pods", "delete:pods", "delete:jobs"},
},
}
for _, test := range tests {
fake := fake.NewSimpleClientset(test.Objs...)
reaper := JobReaper{fake.Batch(), fake.Core(), time.Millisecond, time.Millisecond}
err := reaper.Stop(ns, name, 0, nil)
if !reflect.DeepEqual(err, test.StopError) {
t.Errorf("%s unexpected error: %v", test.Name, err)
continue
}
actions := fake.Actions()
if len(actions) != len(test.ExpectedActions) {
t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions))
continue
}
for i, expAction := range test.ExpectedActions {
action := strings.Split(expAction, ":")
if actions[i].GetVerb() != action[0] {
t.Errorf("%s unexpected verb: %+v, expected %s", test.Name, actions[i], expAction)
}
if actions[i].GetResource().Resource != action[1] {
t.Errorf("%s unexpected resource: %+v, expected %s", test.Name, actions[i], expAction)
}
}
}
}
func TestDeploymentStop(t *testing.T) {
name := "foo"
ns := "default"
deployment := extensions.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
UID: uuid.NewUUID(),
Namespace: ns,
},
Spec: extensions.DeploymentSpec{
Replicas: 0,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}},
},
Status: extensions.DeploymentStatus{
Replicas: 0,
},
}
trueVar := true
tests := []struct {
Name string
Objs []runtime.Object
ScaledDown bool
StopError error
ExpectedActions []string
ScaleClientExpectedAction []string
}{
{
Name: "SimpleDeployment",
Objs: []runtime.Object{
&extensions.Deployment{ // GET
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.DeploymentSpec{
Replicas: 0,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}},
},
Status: extensions.DeploymentStatus{
Replicas: 0,
},
},
},
StopError: nil,
ExpectedActions: []string{"get:deployments", "update:deployments",
"get:deployments", "list:replicasets", "delete:deployments"},
},
{
Name: "Deployment with single replicaset",
Objs: []runtime.Object{
&deployment, // GET
&extensions.ReplicaSetList{ // LIST
Items: []extensions.ReplicaSet{
// ReplicaSet owned by this Deployment.
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
Labels: map[string]string{"k1": "v1"},
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: extensions.SchemeGroupVersion.String(),
Kind: "Deployment",
Name: deployment.Name,
UID: deployment.UID,
Controller: &trueVar,
},
},
},
Spec: extensions.ReplicaSetSpec{},
},
// ReplicaSet owned by something else (should be ignored).
{
ObjectMeta: metav1.ObjectMeta{
Name: "rs2",
Namespace: ns,
Labels: map[string]string{"k1": "v1"},
OwnerReferences: []metav1.OwnerReference{
{
APIVersion: extensions.SchemeGroupVersion.String(),
Kind: "Deployment",
Name: "somethingelse",
UID: uuid.NewUUID(),
Controller: &trueVar,
},
},
},
Spec: extensions.ReplicaSetSpec{},
},
},
},
},
ScaledDown: true,
StopError: nil,
ExpectedActions: []string{"get:deployments", "update:deployments",
"get:deployments", "list:replicasets", "get:replicasets",
"delete:replicasets", "delete:deployments"},
ScaleClientExpectedAction: []string{"get", "update", "get", "get"},
},
}
for _, test := range tests {
scaleClient := createFakeScaleClient("deployments", "foo", 3, nil)
fake := fake.NewSimpleClientset(test.Objs...)
reaper := DeploymentReaper{fake.Extensions(), fake.Extensions(), time.Millisecond, time.Millisecond, scaleClient, schema.GroupResource{Group: "extensions", Resource: "deployments"}}
err := reaper.Stop(ns, name, 0, nil)
if !reflect.DeepEqual(err, test.StopError) {
t.Errorf("%s unexpected error: %v", test.Name, err)
continue
}
actions := fake.Actions()
if len(actions) != len(test.ExpectedActions) {
t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ExpectedActions), len(actions))
continue
}
for i, expAction := range test.ExpectedActions {
action := strings.Split(expAction, ":")
if actions[i].GetVerb() != action[0] {
t.Errorf("%s unexpected verb: %+v, expected %s", test.Name, actions[i], expAction)
}
if actions[i].GetResource().Resource != action[1] {
t.Errorf("%s unexpected resource: %+v, expected %s", test.Name, actions[i], expAction)
}
if len(action) == 3 && actions[i].GetSubresource() != action[2] {
t.Errorf("%s unexpected subresource: %+v, expected %s", test.Name, actions[i], expAction)
}
}
if test.ScaledDown {
scale, err := scaleClient.Scales(ns).Get(schema.GroupResource{Group: "extensions", Resource: "replicaset"}, name)
if err != nil {
t.Error(err)
}
if scale.Spec.Replicas != 0 {
t.Errorf("a scale subresource has unexpected number of replicas, got %d expected 0", scale.Spec.Replicas)
}
actions := scaleClient.Actions()
if len(actions) != len(test.ScaleClientExpectedAction) {
t.Errorf("%s unexpected actions: %v, expected %d actions got %d", test.Name, actions, len(test.ScaleClientExpectedAction), len(actions))
}
for i, verb := range test.ScaleClientExpectedAction {
if actions[i].GetVerb() != verb {
t.Errorf("%s unexpected action: %+v, expected %s", test.Name, actions[i].GetVerb(), verb)
}
}
}
}
}
type noSuchPod struct {
coreclient.PodInterface
}
func (c *noSuchPod) Get(name string, options metav1.GetOptions) (*api.Pod, error) {
return nil, fmt.Errorf("%s does not exist", name)
}
type noDeletePod struct {
coreclient.PodInterface
}
func (c *noDeletePod) Delete(name string, o *metav1.DeleteOptions) error {
return fmt.Errorf("I'm afraid I can't do that, Dave")
}
type reaperFake struct {
*fake.Clientset
noSuchPod, noDeletePod bool
}
func (c *reaperFake) Core() coreclient.CoreInterface {
return &reaperCoreFake{c.Clientset.Core(), c.noSuchPod, c.noDeletePod}
}
type reaperCoreFake struct {
coreclient.CoreInterface
noSuchPod, noDeletePod bool
}
func (c *reaperCoreFake) Pods(namespace string) coreclient.PodInterface {
pods := c.CoreInterface.Pods(namespace)
if c.noSuchPod {
return &noSuchPod{pods}
}
if c.noDeletePod {
return &noDeletePod{pods}
}
return pods
}
func newPod() *api.Pod {
return &api.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}}
}
func TestSimpleStop(t *testing.T) {
tests := []struct {
fake *reaperFake
kind schema.GroupKind
actions []testcore.Action
expectError bool
test string
}{
{
fake: &reaperFake{
Clientset: fake.NewSimpleClientset(newPod()),
},
kind: api.Kind("Pod"),
actions: []testcore.Action{
testcore.NewGetAction(api.Resource("pods").WithVersion(""), metav1.NamespaceDefault, "foo"),
testcore.NewDeleteAction(api.Resource("pods").WithVersion(""), metav1.NamespaceDefault, "foo"),
},
expectError: false,
test: "stop pod succeeds",
},
{
fake: &reaperFake{
Clientset: fake.NewSimpleClientset(),
noSuchPod: true,
},
kind: api.Kind("Pod"),
actions: []testcore.Action{},
expectError: true,
test: "stop pod fails, no pod",
},
{
fake: &reaperFake{
Clientset: fake.NewSimpleClientset(newPod()),
noDeletePod: true,
},
kind: api.Kind("Pod"),
actions: []testcore.Action{
testcore.NewGetAction(api.Resource("pods").WithVersion(""), metav1.NamespaceDefault, "foo"),
},
expectError: true,
test: "stop pod fails, can't delete",
},
}
for _, test := range tests {
fake := test.fake
reaper, err := ReaperFor(test.kind, fake, nil)
if err != nil {
t.Errorf("unexpected error: %v (%s)", err, test.test)
}
err = reaper.Stop("default", "foo", 0, nil)
if err != nil && !test.expectError {
t.Errorf("unexpected error: %v (%s)", err, test.test)
}
if err == nil {
if test.expectError {
t.Errorf("unexpected non-error: %v (%s)", err, test.test)
}
}
actions := fake.Actions()
if len(test.actions) != len(actions) {
t.Errorf("unexpected actions: %v; expected %v (%s)", actions, test.actions, test.test)
}
for i, action := range actions {
testAction := test.actions[i]
if action != testAction {
t.Errorf("unexpected action: %#v; expected %v (%s)", action, testAction, test.test)
}
}
}
}
func TestDeploymentNotFoundError(t *testing.T) {
name := "foo"
ns := "default"
deployment := &extensions.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.DeploymentSpec{
Replicas: 0,
Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"k1": "v1"}},
},
Status: extensions.DeploymentStatus{
Replicas: 0,
},
}
fake := fake.NewSimpleClientset(
deployment,
&extensions.ReplicaSetList{Items: []extensions.ReplicaSet{
{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
},
Spec: extensions.ReplicaSetSpec{},
},
},
},
)
fake.AddReactor("get", "replicasets", func(action testcore.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.NewNotFound(api.Resource("replicaset"), "doesn't-matter")
})
reaper := DeploymentReaper{fake.Extensions(), fake.Extensions(), time.Millisecond, time.Millisecond, nil, schema.GroupResource{}}
if err := reaper.Stop(ns, name, 0, nil); err != nil {
t.Fatalf("unexpected error: %#v", err)
}
}
func createFakeScaleClient(resource string, resourceName string, replicas int, errorsOnVerb map[string]*kerrors.StatusError) *fakescale.FakeScaleClient {
shouldReturnAnError := func(verb string) (*kerrors.StatusError, bool) {
if anError, anErrorExists := errorsOnVerb[verb]; anErrorExists {
return anError, true
}
return &kerrors.StatusError{}, false
}
newReplicas := int32(replicas)
scaleClient := &fakescale.FakeScaleClient{}
scaleClient.AddReactor("get", resource, func(rawAction testcore.Action) (handled bool, ret runtime.Object, err error) {
action := rawAction.(testcore.GetAction)
if action.GetName() != resourceName {
return true, nil, fmt.Errorf("expected = %s, got = %s", resourceName, action.GetName())
}
if anError, should := shouldReturnAnError("get"); should {
return true, nil, anError
}
obj := &autoscalingv1.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: action.GetName(),
Namespace: action.GetNamespace(),
},
Spec: autoscalingv1.ScaleSpec{
Replicas: newReplicas,
},
}
return true, obj, nil
})
scaleClient.AddReactor("update", resource, func(rawAction testcore.Action) (handled bool, ret runtime.Object, err error) {
action := rawAction.(testcore.UpdateAction)
obj := action.GetObject().(*autoscalingv1.Scale)
if obj.Name != resourceName {
return true, nil, fmt.Errorf("expected = %s, got = %s", resourceName, obj.Name)
}
if anError, should := shouldReturnAnError("update"); should {
return true, nil, anError
}
newReplicas = obj.Spec.Replicas
return true, &autoscalingv1.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: obj.Name,
Namespace: action.GetNamespace(),
},
Spec: autoscalingv1.ScaleSpec{
Replicas: newReplicas,
},
}, nil
})
return scaleClient
}

View File

@ -21,10 +21,14 @@ import (
"testing" "testing"
"time" "time"
autoscalingv1 "k8s.io/api/autoscaling/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors" kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/scale" "k8s.io/client-go/scale"
fakescale "k8s.io/client-go/scale/fake"
testcore "k8s.io/client-go/testing"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
@ -626,3 +630,54 @@ func TestGenericScale(t *testing.T) {
}) })
} }
} }
func createFakeScaleClient(resource string, resourceName string, replicas int, errorsOnVerb map[string]*kerrors.StatusError) *fakescale.FakeScaleClient {
shouldReturnAnError := func(verb string) (*kerrors.StatusError, bool) {
if anError, anErrorExists := errorsOnVerb[verb]; anErrorExists {
return anError, true
}
return &kerrors.StatusError{}, false
}
newReplicas := int32(replicas)
scaleClient := &fakescale.FakeScaleClient{}
scaleClient.AddReactor("get", resource, func(rawAction testcore.Action) (handled bool, ret runtime.Object, err error) {
action := rawAction.(testcore.GetAction)
if action.GetName() != resourceName {
return true, nil, fmt.Errorf("expected = %s, got = %s", resourceName, action.GetName())
}
if anError, should := shouldReturnAnError("get"); should {
return true, nil, anError
}
obj := &autoscalingv1.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: action.GetName(),
Namespace: action.GetNamespace(),
},
Spec: autoscalingv1.ScaleSpec{
Replicas: newReplicas,
},
}
return true, obj, nil
})
scaleClient.AddReactor("update", resource, func(rawAction testcore.Action) (handled bool, ret runtime.Object, err error) {
action := rawAction.(testcore.UpdateAction)
obj := action.GetObject().(*autoscalingv1.Scale)
if obj.Name != resourceName {
return true, nil, fmt.Errorf("expected = %s, got = %s", resourceName, obj.Name)
}
if anError, should := shouldReturnAnError("update"); should {
return true, nil, anError
}
newReplicas = obj.Spec.Replicas
return true, &autoscalingv1.Scale{
ObjectMeta: metav1.ObjectMeta{
Name: obj.Name,
Namespace: action.GetNamespace(),
},
Spec: autoscalingv1.ScaleSpec{
Replicas: newReplicas,
},
}, nil
})
return scaleClient
}

View File

@ -29,14 +29,12 @@ go_library(
"//pkg/apis/batch:go_default_library", "//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library", "//pkg/apis/core:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/controller/daemon:go_default_library", "//pkg/controller/daemon:go_default_library",
"//pkg/controller/deployment/util:go_default_library", "//pkg/controller/deployment/util:go_default_library",
"//pkg/controller/job:go_default_library", "//pkg/controller/job:go_default_library",
"//pkg/controller/nodelifecycle:go_default_library", "//pkg/controller/nodelifecycle:go_default_library",
"//pkg/controller/replicaset:go_default_library", "//pkg/controller/replicaset:go_default_library",
"//pkg/controller/replication:go_default_library", "//pkg/controller/replication:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/master/ports:go_default_library", "//pkg/master/ports:go_default_library",
"//pkg/scheduler/schedulercache:go_default_library", "//pkg/scheduler/schedulercache:go_default_library",
"//pkg/util/pointer:go_default_library", "//pkg/util/pointer:go_default_library",
@ -67,7 +65,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/wait: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/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/scale:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library",
], ],
) )

View File

@ -33,7 +33,6 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
batchinternal "k8s.io/kubernetes/pkg/apis/batch" batchinternal "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/controller/job" "k8s.io/kubernetes/pkg/controller/job"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
) )
@ -207,11 +206,7 @@ var _ = SIGDescribe("CronJob", func() {
By("Deleting the job") By("Deleting the job")
job := cronJob.Status.Active[0] job := cronJob.Status.Active[0]
reaper, err := kubectl.ReaperFor(batchinternal.Kind("Job"), f.InternalClientset, f.ScalesGetter) framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(f.ClientSet, batchinternal.Kind("Job"), f.Namespace.Name, job.Name))
Expect(err).NotTo(HaveOccurred())
timeout := 1 * time.Minute
err = reaper.Stop(f.Namespace.Name, job.Name, timeout, metav1.NewDeleteOptions(0))
Expect(err).NotTo(HaveOccurred())
By("Ensuring job was deleted") By("Ensuring job was deleted")
_, err = framework.GetJob(f.ClientSet, f.Namespace.Name, job.Name) _, err = framework.GetJob(f.ClientSet, f.Namespace.Name, job.Name)

View File

@ -36,7 +36,6 @@ import (
podutil "k8s.io/kubernetes/pkg/api/v1/pod" podutil "k8s.io/kubernetes/pkg/api/v1/pod"
extensionsinternal "k8s.io/kubernetes/pkg/apis/extensions" extensionsinternal "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/controller/daemon" "k8s.io/kubernetes/pkg/controller/daemon"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/scheduler/schedulercache" "k8s.io/kubernetes/pkg/scheduler/schedulercache"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
@ -69,11 +68,8 @@ var _ = SIGDescribe("Daemon set [Serial]", func() {
Expect(err).NotTo(HaveOccurred(), "unable to dump DaemonSets") Expect(err).NotTo(HaveOccurred(), "unable to dump DaemonSets")
if daemonsets != nil && len(daemonsets.Items) > 0 { if daemonsets != nil && len(daemonsets.Items) > 0 {
for _, ds := range daemonsets.Items { for _, ds := range daemonsets.Items {
By(fmt.Sprintf("Deleting DaemonSet %q with reaper", ds.Name)) By(fmt.Sprintf("Deleting DaemonSet %q", ds.Name))
dsReaper, err := kubectl.ReaperFor(extensionsinternal.Kind("DaemonSet"), f.InternalClientset, f.ScalesGetter) framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(f.ClientSet, extensionsinternal.Kind("DaemonSet"), f.Namespace.Name, ds.Name))
Expect(err).NotTo(HaveOccurred())
err = dsReaper.Stop(f.Namespace.Name, ds.Name, 0, nil)
Expect(err).NotTo(HaveOccurred())
err = wait.PollImmediate(dsRetryPeriod, dsRetryTimeout, checkRunningOnNoNodes(f, &ds)) err = wait.PollImmediate(dsRetryPeriod, dsRetryTimeout, checkRunningOnNoNodes(f, &ds))
Expect(err).NotTo(HaveOccurred(), "error waiting for daemon pod to be reaped") Expect(err).NotTo(HaveOccurred(), "error waiting for daemon pod to be reaped")
} }

View File

@ -36,11 +36,8 @@ import (
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
scaleclient "k8s.io/client-go/scale"
appsinternal "k8s.io/kubernetes/pkg/apis/apps" appsinternal "k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/kubectl"
utilpointer "k8s.io/kubernetes/pkg/util/pointer" utilpointer "k8s.io/kubernetes/pkg/util/pointer"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
testutil "k8s.io/kubernetes/test/utils" testutil "k8s.io/kubernetes/test/utils"
@ -160,17 +157,12 @@ func newDeploymentRollback(name string, annotations map[string]string, revision
} }
} }
func stopDeployment(c clientset.Interface, internalClient internalclientset.Interface, scaleClient scaleclient.ScalesGetter, ns, deploymentName string) { func stopDeployment(c clientset.Interface, ns, deploymentName string) {
deployment, err := c.AppsV1().Deployments(ns).Get(deploymentName, metav1.GetOptions{}) deployment, err := c.AppsV1().Deployments(ns).Get(deploymentName, metav1.GetOptions{})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
framework.Logf("Deleting deployment %s", deploymentName) framework.Logf("Deleting deployment %s", deploymentName)
reaper, err := kubectl.ReaperFor(appsinternal.Kind("Deployment"), internalClient, scaleClient) framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(c, appsinternal.Kind("Deployment"), ns, deployment.Name))
Expect(err).NotTo(HaveOccurred())
timeout := 1 * time.Minute
err = reaper.Stop(ns, deployment.Name, timeout, metav1.NewDeleteOptions(0))
Expect(err).NotTo(HaveOccurred())
framework.Logf("Ensuring deployment %s was deleted", deploymentName) framework.Logf("Ensuring deployment %s was deleted", deploymentName)
_, err = c.AppsV1().Deployments(ns).Get(deployment.Name, metav1.GetOptions{}) _, err = c.AppsV1().Deployments(ns).Get(deployment.Name, metav1.GetOptions{})
@ -203,7 +195,6 @@ func stopDeployment(c clientset.Interface, internalClient internalclientset.Inte
func testDeleteDeployment(f *framework.Framework) { func testDeleteDeployment(f *framework.Framework) {
ns := f.Namespace.Name ns := f.Namespace.Name
c := f.ClientSet c := f.ClientSet
internalClient := f.InternalClientset
deploymentName := "test-new-deployment" deploymentName := "test-new-deployment"
podLabels := map[string]string{"name": NginxImageName} podLabels := map[string]string{"name": NginxImageName}
@ -226,7 +217,7 @@ func testDeleteDeployment(f *framework.Framework) {
newRS, err := deploymentutil.GetNewReplicaSet(deployment, c.AppsV1()) newRS, err := deploymentutil.GetNewReplicaSet(deployment, c.AppsV1())
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(newRS).NotTo(Equal(nilRs)) Expect(newRS).NotTo(Equal(nilRs))
stopDeployment(c, internalClient, f.ScalesGetter, ns, deploymentName) stopDeployment(c, ns, deploymentName)
} }
func testRollingUpdateDeployment(f *framework.Framework) { func testRollingUpdateDeployment(f *framework.Framework) {

View File

@ -24,7 +24,6 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
batchinternal "k8s.io/kubernetes/pkg/apis/batch" batchinternal "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
@ -111,11 +110,7 @@ var _ = SIGDescribe("Job", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
By("delete a job") By("delete a job")
reaper, err := kubectl.ReaperFor(batchinternal.Kind("Job"), f.InternalClientset, f.ScalesGetter) framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(f.ClientSet, batchinternal.Kind("Job"), f.Namespace.Name, job.Name))
Expect(err).NotTo(HaveOccurred())
timeout := 1 * time.Minute
err = reaper.Stop(f.Namespace.Name, job.Name, timeout, metav1.NewDeleteOptions(0))
Expect(err).NotTo(HaveOccurred())
By("Ensuring job was deleted") By("Ensuring job was deleted")
_, err = framework.GetJob(f.ClientSet, f.Namespace.Name, job.Name) _, err = framework.GetJob(f.ClientSet, f.Namespace.Name, job.Name)

View File

@ -348,7 +348,7 @@ var _ = framework.KubeDescribe("Cluster size autoscaler scalability [Slow]", fun
timeToWait := 5 * time.Minute timeToWait := 5 * time.Minute
podsConfig := reserveMemoryRCConfig(f, "unschedulable-pod", unschedulablePodReplicas, totalMemReservation, timeToWait) podsConfig := reserveMemoryRCConfig(f, "unschedulable-pod", unschedulablePodReplicas, totalMemReservation, timeToWait)
framework.RunRC(*podsConfig) // Ignore error (it will occur because pods are unschedulable) framework.RunRC(*podsConfig) // Ignore error (it will occur because pods are unschedulable)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, podsConfig.Name) defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, podsConfig.Name)
// Ensure that no new nodes have been added so far. // Ensure that no new nodes have been added so far.
Expect(framework.NumberOfReadyNodes(f.ClientSet)).To(Equal(nodeCount)) Expect(framework.NumberOfReadyNodes(f.ClientSet)).To(Equal(nodeCount))
@ -418,7 +418,7 @@ func simpleScaleUpTestWithTolerance(f *framework.Framework, config *scaleUpTestC
} }
timeTrack(start, fmt.Sprintf("Scale up to %v", config.expectedResult.nodes)) timeTrack(start, fmt.Sprintf("Scale up to %v", config.expectedResult.nodes))
return func() error { return func() error {
return framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, config.extraPods.Name) return framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, config.extraPods.Name)
} }
} }
@ -501,7 +501,7 @@ func createHostPortPodsWithMemory(f *framework.Framework, id string, replicas, p
err := framework.RunRC(*config) err := framework.RunRC(*config)
framework.ExpectNoError(err) framework.ExpectNoError(err)
return func() error { return func() error {
return framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, id) return framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, id)
} }
} }
@ -541,7 +541,7 @@ func distributeLoad(f *framework.Framework, namespace string, id string, podDist
framework.ExpectNoError(framework.RunRC(*rcConfig)) framework.ExpectNoError(framework.RunRC(*rcConfig))
framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, f.ClientSet)) framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, f.ClientSet))
return func() error { return func() error {
return framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, id) return framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, id)
} }
} }

View File

@ -169,7 +169,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
It("shouldn't increase cluster size if pending pod is too large [Feature:ClusterSizeAutoscalingScaleUp]", func() { It("shouldn't increase cluster size if pending pod is too large [Feature:ClusterSizeAutoscalingScaleUp]", func() {
By("Creating unschedulable pod") By("Creating unschedulable pod")
ReserveMemory(f, "memory-reservation", 1, int(1.1*float64(memAllocatableMb)), false, defaultTimeout) ReserveMemory(f, "memory-reservation", 1, int(1.1*float64(memAllocatableMb)), false, defaultTimeout)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
By("Waiting for scale up hoping it won't happen") By("Waiting for scale up hoping it won't happen")
// Verify that the appropriate event was generated // Verify that the appropriate event was generated
@ -196,7 +196,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
simpleScaleUpTest := func(unready int) { simpleScaleUpTest := func(unready int) {
ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second) ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
// Verify that cluster size is increased // Verify that cluster size is increased
framework.ExpectNoError(WaitForClusterSizeFuncWithUnready(f.ClientSet, framework.ExpectNoError(WaitForClusterSizeFuncWithUnready(f.ClientSet,
@ -269,7 +269,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
By("Schedule bunch of pods beyond point of filling default pool but do not request any GPUs") By("Schedule bunch of pods beyond point of filling default pool but do not request any GPUs")
ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second) ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
// Verify that cluster size is increased // Verify that cluster size is increased
framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
@ -296,7 +296,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
defer disableAutoscaler(gpuPoolName, 0, 1) defer disableAutoscaler(gpuPoolName, 0, 1)
Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1)) Expect(len(getPoolNodes(f, gpuPoolName))).Should(Equal(1))
framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "gpu-pod-rc") framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "gpu-pod-rc")
framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
func(size int) bool { return size == nodeCount }, scaleDownTimeout)) func(size int) bool { return size == nodeCount }, scaleDownTimeout))
@ -319,7 +319,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
By("Schedule more pods than can fit and wait for cluster to scale-up") By("Schedule more pods than can fit and wait for cluster to scale-up")
ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second) ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, 1*time.Second)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
status, err = waitForScaleUpStatus(c, func(s *scaleUpStatus) bool { status, err = waitForScaleUpStatus(c, func(s *scaleUpStatus) bool {
return s.status == caOngoingScaleUpStatus return s.status == caOngoingScaleUpStatus
@ -362,7 +362,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
By("Reserving 0.1x more memory than the cluster holds to trigger scale up") By("Reserving 0.1x more memory than the cluster holds to trigger scale up")
totalMemoryReservation := int(1.1 * float64(nodeCount*memAllocatableMb+extraMemMb)) totalMemoryReservation := int(1.1 * float64(nodeCount*memAllocatableMb+extraMemMb))
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
ReserveMemory(f, "memory-reservation", 100, totalMemoryReservation, false, defaultTimeout) ReserveMemory(f, "memory-reservation", 100, totalMemoryReservation, false, defaultTimeout)
// Verify, that cluster size is increased // Verify, that cluster size is increased
@ -386,7 +386,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
It("should increase cluster size if pods are pending due to host port conflict [Feature:ClusterSizeAutoscalingScaleUp]", func() { It("should increase cluster size if pods are pending due to host port conflict [Feature:ClusterSizeAutoscalingScaleUp]", func() {
scheduling.CreateHostPortPods(f, "host-port", nodeCount+2, false) scheduling.CreateHostPortPods(f, "host-port", nodeCount+2, false)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "host-port") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "host-port")
framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
func(size int) bool { return size >= nodeCount+2 }, scaleUpTimeout)) func(size int) bool { return size >= nodeCount+2 }, scaleUpTimeout))
@ -401,12 +401,12 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
} }
By("starting a pod with anti-affinity on each node") By("starting a pod with anti-affinity on each node")
framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels)) framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels))
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "some-pod") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "some-pod")
framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c)) framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
By("scheduling extra pods with anti-affinity to existing ones") By("scheduling extra pods with anti-affinity to existing ones")
framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, newPods, "extra-pod", labels, labels)) framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, newPods, "extra-pod", labels, labels))
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "extra-pod") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "extra-pod")
framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c)) framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
framework.ExpectNoError(framework.WaitForReadyNodes(c, nodeCount+newPods, scaleUpTimeout)) framework.ExpectNoError(framework.WaitForReadyNodes(c, nodeCount+newPods, scaleUpTimeout))
@ -420,14 +420,14 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
"anti-affinity": "yes", "anti-affinity": "yes",
} }
framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels)) framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels))
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "some-pod") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "some-pod")
By("waiting for all pods before triggering scale up") By("waiting for all pods before triggering scale up")
framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c)) framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
By("creating a pod requesting EmptyDir") By("creating a pod requesting EmptyDir")
framework.ExpectNoError(runVolumeAntiAffinityPods(f, f.Namespace.Name, newPods, "extra-pod", labels, labels, emptyDirVolumes)) framework.ExpectNoError(runVolumeAntiAffinityPods(f, f.Namespace.Name, newPods, "extra-pod", labels, labels, emptyDirVolumes))
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "extra-pod") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "extra-pod")
framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c)) framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
framework.ExpectNoError(framework.WaitForReadyNodes(c, nodeCount+newPods, scaleUpTimeout)) framework.ExpectNoError(framework.WaitForReadyNodes(c, nodeCount+newPods, scaleUpTimeout))
@ -484,7 +484,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
} }
framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels)) framework.ExpectNoError(runAntiAffinityPods(f, f.Namespace.Name, pods, "some-pod", labels, labels))
defer func() { defer func() {
framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "some-pod") framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "some-pod")
glog.Infof("RC and pods not using volume deleted") glog.Infof("RC and pods not using volume deleted")
}() }()
@ -497,7 +497,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
volumes := buildVolumes(pv, pvc) volumes := buildVolumes(pv, pvc)
framework.ExpectNoError(runVolumeAntiAffinityPods(f, f.Namespace.Name, newPods, pvcPodName, labels, labels, volumes)) framework.ExpectNoError(runVolumeAntiAffinityPods(f, f.Namespace.Name, newPods, pvcPodName, labels, labels, volumes))
defer func() { defer func() {
framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, pvcPodName) framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, pvcPodName)
framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c)) framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
}() }()
@ -602,7 +602,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
defer removeLabels(registeredNodes) defer removeLabels(registeredNodes)
framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c)) framework.ExpectNoError(waitForAllCaPodsReadyInNamespace(f, c))
framework.ExpectNoError(framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "node-selector")) framework.ExpectNoError(framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "node-selector"))
}) })
It("should scale up correct target pool [Feature:ClusterSizeAutoscalingScaleUp]", func() { It("should scale up correct target pool [Feature:ClusterSizeAutoscalingScaleUp]", func() {
@ -620,7 +620,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
extraPods := extraNodes + 1 extraPods := extraNodes + 1
totalMemoryReservation := int(float64(extraPods) * 1.5 * float64(memAllocatableMb)) totalMemoryReservation := int(float64(extraPods) * 1.5 * float64(memAllocatableMb))
By(fmt.Sprintf("Creating rc with %v pods too big to fit default-pool but fitting extra-pool", extraPods)) By(fmt.Sprintf("Creating rc with %v pods too big to fit default-pool but fitting extra-pool", extraPods))
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
ReserveMemory(f, "memory-reservation", extraPods, totalMemoryReservation, false, defaultTimeout) ReserveMemory(f, "memory-reservation", extraPods, totalMemoryReservation, false, defaultTimeout)
// Apparently GKE master is restarted couple minutes after the node pool is added // Apparently GKE master is restarted couple minutes after the node pool is added
@ -759,7 +759,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
By("Run a scale-up test") By("Run a scale-up test")
ReserveMemory(f, "memory-reservation", 1, 100, false, 1*time.Second) ReserveMemory(f, "memory-reservation", 1, 100, false, 1*time.Second)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
// Verify that cluster size is increased // Verify that cluster size is increased
framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet, framework.ExpectNoError(WaitForClusterSizeFunc(f.ClientSet,
@ -872,7 +872,7 @@ var _ = SIGDescribe("Cluster size autoscaling [Slow]", func() {
framework.TestUnderTemporaryNetworkFailure(c, "default", ntb, testFunction) framework.TestUnderTemporaryNetworkFailure(c, "default", ntb, testFunction)
} else { } else {
ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, defaultTimeout) ReserveMemory(f, "memory-reservation", 100, nodeCount*memAllocatableMb, false, defaultTimeout)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, "memory-reservation") defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, "memory-reservation")
time.Sleep(scaleUpTimeout) time.Sleep(scaleUpTimeout)
currentNodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet) currentNodes := framework.GetReadySchedulableNodesOrDie(f.ClientSet)
framework.Logf("Currently available nodes: %v, nodes available at the start of test: %v, disabled nodes: %v", len(currentNodes.Items), len(nodes.Items), nodesToBreakCount) framework.Logf("Currently available nodes: %v, nodes available at the start of test: %v, disabled nodes: %v", len(currentNodes.Items), len(nodes.Items), nodesToBreakCount)
@ -1076,7 +1076,7 @@ func runDrainTest(f *framework.Framework, migSizes map[string]int, namespace str
labelMap := map[string]string{"test_id": testID} labelMap := map[string]string{"test_id": testID}
framework.ExpectNoError(runReplicatedPodOnEachNode(f, nodes.Items, namespace, podsPerNode, "reschedulable-pods", labelMap, 0)) framework.ExpectNoError(runReplicatedPodOnEachNode(f, nodes.Items, namespace, podsPerNode, "reschedulable-pods", labelMap, 0))
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, namespace, "reschedulable-pods") defer framework.DeleteRCAndWaitForGC(f.ClientSet, namespace, "reschedulable-pods")
By("Create a PodDisruptionBudget") By("Create a PodDisruptionBudget")
minAvailable := intstr.FromInt(numPods - pdbSize) minAvailable := intstr.FromInt(numPods - pdbSize)
@ -1523,7 +1523,7 @@ func reserveMemory(f *framework.Framework, id string, replicas, megabytes int, e
framework.ExpectNoError(err) framework.ExpectNoError(err)
} }
return func() error { return func() error {
return framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, id) return framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, id)
} }
} }
framework.Failf("Failed to reserve memory within timeout") framework.Failf("Failed to reserve memory within timeout")
@ -1929,7 +1929,7 @@ func runReplicatedPodOnEachNode(f *framework.Framework, nodes []v1.Node, namespa
func runReplicatedPodOnEachNodeWithCleanup(f *framework.Framework, nodes []v1.Node, namespace string, podsPerNode int, id string, labels map[string]string, memRequest int64) (func(), error) { func runReplicatedPodOnEachNodeWithCleanup(f *framework.Framework, nodes []v1.Node, namespace string, podsPerNode int, id string, labels map[string]string, memRequest int64) (func(), error) {
err := runReplicatedPodOnEachNode(f, nodes, namespace, podsPerNode, id, labels, memRequest) err := runReplicatedPodOnEachNode(f, nodes, namespace, podsPerNode, id, labels, memRequest)
return func() { return func() {
framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, namespace, id) framework.DeleteRCAndWaitForGC(f.ClientSet, namespace, id)
}, err }, err
} }

View File

@ -414,9 +414,9 @@ func (rc *ResourceConsumer) CleanUp() {
// Wait some time to ensure all child goroutines are finished. // Wait some time to ensure all child goroutines are finished.
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
kind := rc.kind.GroupKind() kind := rc.kind.GroupKind()
framework.ExpectNoError(framework.DeleteResourceAndPods(rc.clientSet, rc.internalClientset, rc.scaleClient, kind, rc.nsName, rc.name)) framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(rc.clientSet, kind, rc.nsName, rc.name))
framework.ExpectNoError(rc.clientSet.CoreV1().Services(rc.nsName).Delete(rc.name, nil)) framework.ExpectNoError(rc.clientSet.CoreV1().Services(rc.nsName).Delete(rc.name, nil))
framework.ExpectNoError(framework.DeleteResourceAndPods(rc.clientSet, rc.internalClientset, rc.scaleClient, api.Kind("ReplicationController"), rc.nsName, rc.controllerName)) framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(rc.clientSet, api.Kind("ReplicationController"), rc.nsName, rc.controllerName))
framework.ExpectNoError(rc.clientSet.CoreV1().Services(rc.nsName).Delete(rc.controllerName, nil)) framework.ExpectNoError(rc.clientSet.CoreV1().Services(rc.nsName).Delete(rc.controllerName, nil))
} }

View File

@ -210,6 +210,17 @@ func WaitForJobFailure(c clientset.Interface, ns, jobName string, timeout time.D
}) })
} }
// WaitForJobGone uses c to wait for up to timeout for the Job named jobName in namespace ns to be removed.
func WaitForJobGone(c clientset.Interface, ns, jobName string, timeout time.Duration) error {
return wait.Poll(Poll, timeout, func() (bool, error) {
_, err := c.BatchV1().Jobs(ns).Get(jobName, metav1.GetOptions{})
if errors.IsNotFound(err) {
return true, nil
}
return false, err
})
}
// CheckForAllJobPodsRunning uses c to check in the Job named jobName in ns is running. If the returned error is not // CheckForAllJobPodsRunning uses c to check in the Job named jobName in ns is running. If the returned error is not
// nil the returned bool is true if the Job is running. // nil the returned bool is true if the Job is running.
func CheckForAllJobPodsRunning(c clientset.Interface, ns, jobName string, parallelism int32) (bool, error) { func CheckForAllJobPodsRunning(c clientset.Interface, ns, jobName string, parallelism int32) (bool, error) {

View File

@ -31,7 +31,6 @@ import (
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
scaleclient "k8s.io/client-go/scale" scaleclient "k8s.io/client-go/scale"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
testutils "k8s.io/kubernetes/test/utils" testutils "k8s.io/kubernetes/test/utils"
) )
@ -153,10 +152,6 @@ func DeleteRCAndWaitForGC(c clientset.Interface, ns, name string) error {
return DeleteResourceAndWaitForGC(c, api.Kind("ReplicationController"), ns, name) return DeleteResourceAndWaitForGC(c, api.Kind("ReplicationController"), ns, name)
} }
func DeleteRCAndPods(clientset clientset.Interface, internalClientset internalclientset.Interface, scaleClient scaleclient.ScalesGetter, ns, name string) error {
return DeleteResourceAndPods(clientset, internalClientset, scaleClient, api.Kind("ReplicationController"), ns, name)
}
func ScaleRC(clientset clientset.Interface, scalesGetter scaleclient.ScalesGetter, ns, name string, size uint, wait bool) error { func ScaleRC(clientset clientset.Interface, scalesGetter scaleclient.ScalesGetter, ns, name string, size uint, wait bool) error {
return ScaleResource(clientset, scalesGetter, ns, name, size, wait, api.Kind("ReplicationController"), api.Resource("replicationcontrollers")) return ScaleResource(clientset, scalesGetter, ns, name, size, wait, api.Kind("ReplicationController"), api.Resource("replicationcontrollers"))
} }

View File

@ -47,7 +47,6 @@ import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
scaleclient "k8s.io/client-go/scale"
) )
const ( const (
@ -1261,8 +1260,8 @@ func StartServeHostnameService(c clientset.Interface, internalClient internalcli
return podNames, serviceIP, nil return podNames, serviceIP, nil
} }
func StopServeHostnameService(clientset clientset.Interface, internalClientset internalclientset.Interface, scaleClient scaleclient.ScalesGetter, ns, name string) error { func StopServeHostnameService(clientset clientset.Interface, ns, name string) error {
if err := DeleteRCAndPods(clientset, internalClientset, scaleClient, ns, name); err != nil { if err := DeleteRCAndWaitForGC(clientset, ns, name); err != nil {
return err return err
} }
if err := clientset.CoreV1().Services(ns).Delete(name, nil); err != nil { if err := clientset.CoreV1().Services(ns).Delete(name, nil); err != nil {

View File

@ -109,8 +109,6 @@ type TestContextType struct {
DisableLogDump bool DisableLogDump bool
// Path to the GCS artifacts directory to dump logs from nodes. Logexporter gets enabled if this is non-empty. // Path to the GCS artifacts directory to dump logs from nodes. Logexporter gets enabled if this is non-empty.
LogexporterGCSPath string LogexporterGCSPath string
// If the garbage collector is enabled in the kube-apiserver and kube-controller-manager.
GarbageCollectorEnabled bool
// featureGates is a map of feature names to bools that enable or disable alpha/experimental features. // featureGates is a map of feature names to bools that enable or disable alpha/experimental features.
FeatureGates map[string]bool FeatureGates map[string]bool
// Node e2e specific test context // Node e2e specific test context
@ -276,7 +274,6 @@ func RegisterClusterFlags() {
flag.StringVar(&TestContext.IngressUpgradeImage, "ingress-upgrade-image", "", "Image to upgrade to if doing an upgrade test for ingress.") flag.StringVar(&TestContext.IngressUpgradeImage, "ingress-upgrade-image", "", "Image to upgrade to if doing an upgrade test for ingress.")
flag.StringVar(&TestContext.GCEUpgradeScript, "gce-upgrade-script", "", "Script to use to upgrade a GCE cluster.") flag.StringVar(&TestContext.GCEUpgradeScript, "gce-upgrade-script", "", "Script to use to upgrade a GCE cluster.")
flag.BoolVar(&TestContext.CleanStart, "clean-start", false, "If true, purge all namespaces except default and system before running tests. This serves to Cleanup test namespaces from failed/interrupted e2e runs in a long-lived cluster.") flag.BoolVar(&TestContext.CleanStart, "clean-start", false, "If true, purge all namespaces except default and system before running tests. This serves to Cleanup test namespaces from failed/interrupted e2e runs in a long-lived cluster.")
flag.BoolVar(&TestContext.GarbageCollectorEnabled, "garbage-collector-enabled", true, "Set to true if the garbage collector is enabled in the kube-apiserver and kube-controller-manager, then some tests will rely on the garbage collector to delete dependent resources.")
} }
// Register flags specific to the node e2e test suite. // Register flags specific to the node e2e test suite.

View File

@ -3042,50 +3042,6 @@ func getReplicasFromRuntimeObject(obj runtime.Object) (int32, error) {
} }
} }
// DeleteResourceAndPods deletes a given resource and all pods it spawned
func DeleteResourceAndPods(clientset clientset.Interface, internalClientset internalclientset.Interface, scaleClient scaleclient.ScalesGetter, kind schema.GroupKind, ns, name string) error {
By(fmt.Sprintf("deleting %v %s in namespace %s", kind, name, ns))
rtObject, err := getRuntimeObjectForKind(clientset, kind, ns, name)
if err != nil {
if apierrs.IsNotFound(err) {
Logf("%v %s not found: %v", kind, name, err)
return nil
}
return err
}
selector, err := getSelectorFromRuntimeObject(rtObject)
if err != nil {
return err
}
ps, err := testutils.NewPodStore(clientset, ns, selector, fields.Everything())
if err != nil {
return err
}
defer ps.Stop()
startTime := time.Now()
if err := testutils.DeleteResourceUsingReaperWithRetries(internalClientset, kind, ns, name, nil, scaleClient); err != nil {
return fmt.Errorf("error while stopping %v: %s: %v", kind, name, err)
}
deleteTime := time.Since(startTime)
Logf("Deleting %v %s took: %v", kind, name, deleteTime)
err = waitForPodsInactive(ps, 100*time.Millisecond, 10*time.Minute)
if err != nil {
return fmt.Errorf("error while waiting for pods to become inactive %s: %v", name, err)
}
terminatePodTime := time.Since(startTime) - deleteTime
Logf("Terminating %v %s pods took: %v", kind, name, terminatePodTime)
// this is to relieve namespace controller's pressure when deleting the
// namespace after a test.
err = waitForPodsGone(ps, 100*time.Millisecond, 10*time.Minute)
if err != nil {
return fmt.Errorf("error while waiting for pods gone %s: %v", name, err)
}
gcPodTime := time.Since(startTime) - terminatePodTime
Logf("Garbage collecting %v %s pods took: %v", kind, name, gcPodTime)
return nil
}
// DeleteResourceAndWaitForGC deletes only given resource and waits for GC to delete the pods. // DeleteResourceAndWaitForGC deletes only given resource and waits for GC to delete the pods.
func DeleteResourceAndWaitForGC(c clientset.Interface, kind schema.GroupKind, ns, name string) error { func DeleteResourceAndWaitForGC(c clientset.Interface, kind schema.GroupKind, ns, name string) error {
By(fmt.Sprintf("deleting %v %s in namespace %s, will wait for the garbage collector to delete the pods", kind, name, ns)) By(fmt.Sprintf("deleting %v %s in namespace %s, will wait for the garbage collector to delete the pods", kind, name, ns))

View File

@ -1545,8 +1545,11 @@ metadata:
Expect(runOutput).To(ContainSubstring("abcd1234")) Expect(runOutput).To(ContainSubstring("abcd1234"))
Expect(runOutput).To(ContainSubstring("stdin closed")) Expect(runOutput).To(ContainSubstring("stdin closed"))
err := framework.WaitForJobGone(c, ns, jobName, 10*time.Second)
Expect(err).NotTo(HaveOccurred())
By("verifying the job " + jobName + " was deleted") By("verifying the job " + jobName + " was deleted")
_, err := c.BatchV1().Jobs(ns).Get(jobName, metav1.GetOptions{}) _, err = c.BatchV1().Jobs(ns).Get(jobName, metav1.GetOptions{})
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(apierrs.IsNotFound(err)).To(BeTrue()) Expect(apierrs.IsNotFound(err)).To(BeTrue())
}) })

View File

@ -161,7 +161,7 @@ var _ = SIGDescribe("Proxy", func() {
CreatedPods: &pods, CreatedPods: &pods,
} }
Expect(framework.RunRC(cfg)).NotTo(HaveOccurred()) Expect(framework.RunRC(cfg)).NotTo(HaveOccurred())
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, cfg.Name) defer framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, cfg.Name)
Expect(framework.WaitForEndpoint(f.ClientSet, f.Namespace.Name, service.Name)).NotTo(HaveOccurred()) Expect(framework.WaitForEndpoint(f.ClientSet, f.Namespace.Name, service.Name)).NotTo(HaveOccurred())

View File

@ -339,7 +339,7 @@ var _ = SIGDescribe("Services", func() {
// Stop service 1 and make sure it is gone. // Stop service 1 and make sure it is gone.
By("stopping service1") By("stopping service1")
framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, "service1")) framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, ns, "service1"))
By("verifying service1 is not up") By("verifying service1 is not up")
framework.ExpectNoError(framework.VerifyServeHostnameServiceDown(cs, host, svc1IP, servicePort)) framework.ExpectNoError(framework.VerifyServeHostnameServiceDown(cs, host, svc1IP, servicePort))
@ -373,13 +373,13 @@ var _ = SIGDescribe("Services", func() {
svc2 := "service2" svc2 := "service2"
defer func() { defer func() {
framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, svc1)) framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, ns, svc1))
}() }()
podNames1, svc1IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService(svc1), ns, numPods) podNames1, svc1IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService(svc1), ns, numPods)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer func() { defer func() {
framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, svc2)) framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, ns, svc2))
}() }()
podNames2, svc2IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService(svc2), ns, numPods) podNames2, svc2IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService(svc2), ns, numPods)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -426,7 +426,7 @@ var _ = SIGDescribe("Services", func() {
numPods, servicePort := 3, 80 numPods, servicePort := 3, 80
defer func() { defer func() {
framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, "service1")) framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, ns, "service1"))
}() }()
podNames1, svc1IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService("service1"), ns, numPods) podNames1, svc1IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService("service1"), ns, numPods)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -453,7 +453,7 @@ var _ = SIGDescribe("Services", func() {
// Create a new service and check if it's not reusing IP. // Create a new service and check if it's not reusing IP.
defer func() { defer func() {
framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, "service2")) framework.ExpectNoError(framework.StopServeHostnameService(f.ClientSet, ns, "service2"))
}() }()
podNames2, svc2IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService("service2"), ns, numPods) podNames2, svc2IP, err := framework.StartServeHostnameService(cs, internalClientset, getServeHostnameService("service2"), ns, numPods)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -1753,7 +1753,7 @@ var _ = SIGDescribe("ESIPP [Slow] [DisabledForLargeClusters]", func() {
framework.Logf("Health checking %s, http://%s%s, expectedSuccess %v", nodes.Items[n].Name, ipPort, path, expectedSuccess) framework.Logf("Health checking %s, http://%s%s, expectedSuccess %v", nodes.Items[n].Name, ipPort, path, expectedSuccess)
Expect(jig.TestHTTPHealthCheckNodePort(publicIP, healthCheckNodePort, path, framework.KubeProxyEndpointLagTimeout, expectedSuccess, threshold)).NotTo(HaveOccurred()) Expect(jig.TestHTTPHealthCheckNodePort(publicIP, healthCheckNodePort, path, framework.KubeProxyEndpointLagTimeout, expectedSuccess, threshold)).NotTo(HaveOccurred())
} }
framework.ExpectNoError(framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, namespace, serviceName)) framework.ExpectNoError(framework.DeleteRCAndWaitForGC(f.ClientSet, namespace, serviceName))
} }
}) })
@ -1968,7 +1968,7 @@ func execAffinityTestForNonLBService(f *framework.Framework, cs clientset.Interf
_, _, err := framework.StartServeHostnameService(cs, f.InternalClientset, svc, ns, numPods) _, _, err := framework.StartServeHostnameService(cs, f.InternalClientset, svc, ns, numPods)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer func() { defer func() {
framework.StopServeHostnameService(cs, f.InternalClientset, f.ScalesGetter, ns, serviceName) framework.StopServeHostnameService(cs, ns, serviceName)
}() }()
jig := framework.NewServiceTestJig(cs, serviceName) jig := framework.NewServiceTestJig(cs, serviceName)
svc, err = jig.Client.CoreV1().Services(ns).Get(serviceName, metav1.GetOptions{}) svc, err = jig.Client.CoreV1().Services(ns).Get(serviceName, metav1.GetOptions{})
@ -2023,7 +2023,7 @@ func execAffinityTestForLBService(f *framework.Framework, cs clientset.Interface
svc = jig.WaitForLoadBalancerOrFail(ns, serviceName, framework.LoadBalancerCreateTimeoutDefault) svc = jig.WaitForLoadBalancerOrFail(ns, serviceName, framework.LoadBalancerCreateTimeoutDefault)
jig.SanityCheckService(svc, v1.ServiceTypeLoadBalancer) jig.SanityCheckService(svc, v1.ServiceTypeLoadBalancer)
defer func() { defer func() {
framework.StopServeHostnameService(cs, f.InternalClientset, f.ScalesGetter, ns, serviceName) framework.StopServeHostnameService(cs, ns, serviceName)
lb := cloudprovider.GetLoadBalancerName(svc) lb := cloudprovider.GetLoadBalancerName(svc)
framework.Logf("cleaning gce resource for %s", lb) framework.Logf("cleaning gce resource for %s", lb)
framework.CleanupServiceGCEResources(cs, lb, framework.TestContext.CloudConfig.Region, framework.TestContext.CloudConfig.Zone) framework.CleanupServiceGCEResources(cs, lb, framework.TestContext.CloudConfig.Region, framework.TestContext.CloudConfig.Zone)

View File

@ -334,7 +334,7 @@ var _ = SIGDescribe("kubelet", func() {
} }
By("Deleting the RC") By("Deleting the RC")
framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, rcName) framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, rcName)
// Check that the pods really are gone by querying /runningpods on the // Check that the pods really are gone by querying /runningpods on the
// node. The /runningpods handler checks the container runtime (or its // node. The /runningpods handler checks the container runtime (or its
// cache) and returns a list of running pods. Some possible causes of // cache) and returns a list of running pods. Some possible causes of

View File

@ -118,7 +118,7 @@ func runResourceTrackingTest(f *framework.Framework, podsPerNode int, nodeNames
verifyCPULimits(expectedCPU, cpuSummary) verifyCPULimits(expectedCPU, cpuSummary)
By("Deleting the RC") By("Deleting the RC")
framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, rcName) framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, rcName)
} }
func verifyMemoryLimits(c clientset.Interface, expected framework.ResourceUsagePerContainer, actual framework.ResourceUsagePerNode) { func verifyMemoryLimits(c clientset.Interface, expected framework.ResourceUsagePerContainer, actual framework.ResourceUsagePerNode) {

View File

@ -116,10 +116,8 @@ func (dtc *DensityTestConfig) deleteConfigMaps(testPhase *timer.Phase) {
func (dtc *DensityTestConfig) deleteDaemonSets(numberOfClients int, testPhase *timer.Phase) { func (dtc *DensityTestConfig) deleteDaemonSets(numberOfClients int, testPhase *timer.Phase) {
defer testPhase.End() defer testPhase.End()
for i := range dtc.DaemonConfigs { for i := range dtc.DaemonConfigs {
framework.ExpectNoError(framework.DeleteResourceAndPods( framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(
dtc.ClientSets[i%numberOfClients], dtc.ClientSets[i%numberOfClients],
dtc.InternalClientsets[i%numberOfClients],
dtc.ScaleClients[i%numberOfClients],
extensions.Kind("DaemonSet"), extensions.Kind("DaemonSet"),
dtc.DaemonConfigs[i].Namespace, dtc.DaemonConfigs[i].Namespace,
dtc.DaemonConfigs[i].Name, dtc.DaemonConfigs[i].Name,
@ -320,15 +318,9 @@ func cleanupDensityTest(dtc DensityTestConfig, testPhaseDurations *timer.TestPha
name := dtc.Configs[i].GetName() name := dtc.Configs[i].GetName()
namespace := dtc.Configs[i].GetNamespace() namespace := dtc.Configs[i].GetNamespace()
kind := dtc.Configs[i].GetKind() kind := dtc.Configs[i].GetKind()
if framework.TestContext.GarbageCollectorEnabled && kindSupportsGarbageCollector(kind) {
By(fmt.Sprintf("Cleaning up only the %v, garbage collector will clean up the pods", kind)) By(fmt.Sprintf("Cleaning up only the %v, garbage collector will clean up the pods", kind))
err := framework.DeleteResourceAndWaitForGC(dtc.ClientSets[i%numberOfClients], kind, namespace, name) err := framework.DeleteResourceAndWaitForGC(dtc.ClientSets[i%numberOfClients], kind, namespace, name)
framework.ExpectNoError(err) framework.ExpectNoError(err)
} else {
By(fmt.Sprintf("Cleaning up the %v and pods", kind))
err := framework.DeleteResourceAndPods(dtc.ClientSets[i%numberOfClients], dtc.InternalClientsets[i%numberOfClients], dtc.ScaleClients[i%numberOfClients], kind, namespace, name)
framework.ExpectNoError(err)
}
} }
podCleanupPhase.End() podCleanupPhase.End()
@ -922,7 +914,3 @@ func createRunningPodFromRC(wg *sync.WaitGroup, c clientset.Interface, name, ns,
framework.ExpectNoError(framework.WaitForControlledPodsRunning(c, ns, name, api.Kind("ReplicationController"))) framework.ExpectNoError(framework.WaitForControlledPodsRunning(c, ns, name, api.Kind("ReplicationController")))
framework.Logf("Found pod '%s' running", name) framework.Logf("Found pod '%s' running", name)
} }
func kindSupportsGarbageCollector(kind schema.GroupKind) bool {
return kind != extensions.Kind("Deployment") && kind != batch.Kind("Job")
}

View File

@ -286,10 +286,8 @@ var _ = SIGDescribe("Load capacity", func() {
} }
daemonConfig.Run() daemonConfig.Run()
defer func(config *testutils.DaemonConfig) { defer func(config *testutils.DaemonConfig) {
framework.ExpectNoError(framework.DeleteResourceAndPods( framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(
f.ClientSet, f.ClientSet,
f.InternalClientset,
f.ScalesGetter,
extensions.Kind("DaemonSet"), extensions.Kind("DaemonSet"),
config.Namespace, config.Namespace,
config.Name, config.Name,
@ -694,15 +692,9 @@ func deleteResource(wg *sync.WaitGroup, config testutils.RunObjectConfig, deleti
defer wg.Done() defer wg.Done()
sleepUpTo(deletingTime) sleepUpTo(deletingTime)
if framework.TestContext.GarbageCollectorEnabled && config.GetKind() != extensions.Kind("Deployment") {
framework.ExpectNoError(framework.DeleteResourceAndWaitForGC( framework.ExpectNoError(framework.DeleteResourceAndWaitForGC(
config.GetClient(), config.GetKind(), config.GetNamespace(), config.GetName()), config.GetClient(), config.GetKind(), config.GetNamespace(), config.GetName()),
fmt.Sprintf("deleting %v %s", config.GetKind(), config.GetName())) fmt.Sprintf("deleting %v %s", config.GetKind(), config.GetName()))
} else {
framework.ExpectNoError(framework.DeleteResourceAndPods(
config.GetClient(), config.GetInternalClient(), config.GetScalesGetter(), config.GetKind(), config.GetNamespace(), config.GetName()),
fmt.Sprintf("deleting %v %s", config.GetKind(), config.GetName()))
}
} }
func CreateNamespaces(f *framework.Framework, namespaceCount int, namePrefix string, testPhase *timer.Phase) ([]*v1.Namespace, error) { func CreateNamespaces(f *framework.Framework, namespaceCount int, namePrefix string, testPhase *timer.Phase) ([]*v1.Namespace, error) {

View File

@ -93,7 +93,7 @@ var _ = framework.KubeDescribe("EquivalenceCache [Serial]", func() {
err := CreateNodeSelectorPods(f, rcName, 2, nodeSelector, false) err := CreateNodeSelectorPods(f, rcName, 2, nodeSelector, false)
return err return err
}, ns, rcName, false) }, ns, rcName, false)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, rcName) defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, rcName)
// the first replica pod is scheduled, and the second pod will be rejected. // the first replica pod is scheduled, and the second pod will be rejected.
verifyResult(cs, 1, 1, ns) verifyResult(cs, 1, 1, ns)
}) })
@ -141,7 +141,7 @@ var _ = framework.KubeDescribe("EquivalenceCache [Serial]", func() {
}, },
} }
rc := getRCWithInterPodAffinity(affinityRCName, labelsMap, replica, affinity, imageutils.GetPauseImageName()) rc := getRCWithInterPodAffinity(affinityRCName, labelsMap, replica, affinity, imageutils.GetPauseImageName())
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, affinityRCName) defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, affinityRCName)
// RC should be running successfully // RC should be running successfully
// TODO: WaitForSchedulerAfterAction() can on be used to wait for failure event, // TODO: WaitForSchedulerAfterAction() can on be used to wait for failure event,
@ -167,7 +167,7 @@ var _ = framework.KubeDescribe("EquivalenceCache [Serial]", func() {
It("validates pod anti-affinity works properly when new replica pod is scheduled", func() { It("validates pod anti-affinity works properly when new replica pod is scheduled", func() {
By("Launching two pods on two distinct nodes to get two node names") By("Launching two pods on two distinct nodes to get two node names")
CreateHostPortPods(f, "host-port", 2, true) CreateHostPortPods(f, "host-port", 2, true)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, "host-port") defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, "host-port")
podList, err := cs.CoreV1().Pods(ns).List(metav1.ListOptions{}) podList, err := cs.CoreV1().Pods(ns).List(metav1.ListOptions{})
framework.ExpectNoError(err) framework.ExpectNoError(err)
Expect(len(podList.Items)).To(Equal(2)) Expect(len(podList.Items)).To(Equal(2))
@ -218,7 +218,7 @@ var _ = framework.KubeDescribe("EquivalenceCache [Serial]", func() {
} }
rc := getRCWithInterPodAffinityNodeSelector(labelRCName, labelsMap, replica, affinity, rc := getRCWithInterPodAffinityNodeSelector(labelRCName, labelsMap, replica, affinity,
imageutils.GetPauseImageName(), map[string]string{k: v}) imageutils.GetPauseImageName(), map[string]string{k: v})
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, labelRCName) defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, labelRCName)
WaitForSchedulerAfterAction(f, func() error { WaitForSchedulerAfterAction(f, func() error {
_, err := cs.CoreV1().ReplicationControllers(ns).Create(rc) _, err := cs.CoreV1().ReplicationControllers(ns).Create(rc)

View File

@ -71,7 +71,7 @@ var _ = SIGDescribe("SchedulerPredicates [Serial]", func() {
rc, err := cs.CoreV1().ReplicationControllers(ns).Get(RCName, metav1.GetOptions{}) rc, err := cs.CoreV1().ReplicationControllers(ns).Get(RCName, metav1.GetOptions{})
if err == nil && *(rc.Spec.Replicas) != 0 { if err == nil && *(rc.Spec.Replicas) != 0 {
By("Cleaning up the replication controller") By("Cleaning up the replication controller")
err := framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, RCName) err := framework.DeleteRCAndWaitForGC(f.ClientSet, ns, RCName)
framework.ExpectNoError(err) framework.ExpectNoError(err)
} }
}) })

View File

@ -153,7 +153,7 @@ var _ = SIGDescribe("SchedulerPriorities [Serial]", func() {
// Cleanup the replication controller when we are done. // Cleanup the replication controller when we are done.
defer func() { defer func() {
// Resize the replication controller to zero to get rid of pods. // Resize the replication controller to zero to get rid of pods.
if err := framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, rc.Name); err != nil { if err := framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, rc.Name); err != nil {
framework.Logf("Failed to cleanup replication controller %v: %v.", rc.Name, err) framework.Logf("Failed to cleanup replication controller %v: %v.", rc.Name, err)
} }
}() }()

View File

@ -56,7 +56,7 @@ var _ = SIGDescribe("Rescheduler [Serial]", func() {
It("should ensure that critical pod is scheduled in case there is no resources available", func() { It("should ensure that critical pod is scheduled in case there is no resources available", func() {
By("reserving all available cpu") By("reserving all available cpu")
err := reserveAllCpu(f, "reserve-all-cpu", totalMillicores) err := reserveAllCpu(f, "reserve-all-cpu", totalMillicores)
defer framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, ns, "reserve-all-cpu") defer framework.DeleteRCAndWaitForGC(f.ClientSet, ns, "reserve-all-cpu")
framework.ExpectNoError(err) framework.ExpectNoError(err)
By("creating a new instance of Dashboard and waiting for Dashboard to be scheduled") By("creating a new instance of Dashboard and waiting for Dashboard to be scheduled")

View File

@ -224,7 +224,7 @@ func SpreadRCOrFail(f *framework.Framework, replicaCount int32, image string) {
// Cleanup the replication controller when we are done. // Cleanup the replication controller when we are done.
defer func() { defer func() {
// Resize the replication controller to zero to get rid of pods. // Resize the replication controller to zero to get rid of pods.
if err := framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, controller.Name); err != nil { if err := framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, controller.Name); err != nil {
framework.Logf("Failed to cleanup replication controller %v: %v.", controller.Name, err) framework.Logf("Failed to cleanup replication controller %v: %v.", controller.Name, err)
} }
}() }()

View File

@ -374,7 +374,7 @@ func testNoWrappedVolumeRace(f *framework.Framework, volumes []v1.Volume, volume
Expect(err).NotTo(HaveOccurred(), "error creating replication controller") Expect(err).NotTo(HaveOccurred(), "error creating replication controller")
defer func() { defer func() {
err := framework.DeleteRCAndPods(f.ClientSet, f.InternalClientset, f.ScalesGetter, f.Namespace.Name, rcName) err := framework.DeleteRCAndWaitForGC(f.ClientSet, f.Namespace.Name, rcName)
framework.ExpectNoError(err) framework.ExpectNoError(err)
}() }()

View File

@ -25,13 +25,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
scaleclient "k8s.io/client-go/scale"
appsinternal "k8s.io/kubernetes/pkg/apis/apps" appsinternal "k8s.io/kubernetes/pkg/apis/apps"
batchinternal "k8s.io/kubernetes/pkg/apis/batch" batchinternal "k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
extensionsinternal "k8s.io/kubernetes/pkg/apis/extensions" extensionsinternal "k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl"
) )
func deleteResource(c clientset.Interface, kind schema.GroupKind, namespace, name string, options *metav1.DeleteOptions) error { func deleteResource(c clientset.Interface, kind schema.GroupKind, namespace, name string, options *metav1.DeleteOptions) error {
@ -72,21 +69,3 @@ func DeleteResourceWithRetries(c clientset.Interface, kind schema.GroupKind, nam
} }
return RetryWithExponentialBackOff(deleteFunc) return RetryWithExponentialBackOff(deleteFunc)
} }
func DeleteResourceUsingReaperWithRetries(c internalclientset.Interface, kind schema.GroupKind, namespace, name string, options *metav1.DeleteOptions, scaleClient scaleclient.ScalesGetter) error {
reaper, err := kubectl.ReaperFor(kind, c, scaleClient)
if err != nil {
return err
}
deleteFunc := func() (bool, error) {
err := reaper.Stop(namespace, name, 0, options)
if err == nil || apierrs.IsNotFound(err) {
return true, nil
}
if IsRetryableAPIError(err) {
return false, nil
}
return false, fmt.Errorf("Failed to delete object with non-retriable error: %v", err)
}
return RetryWithExponentialBackOff(deleteFunc)
}