From 4d018182c4fe418d89455c82e8309e3afa37369b Mon Sep 17 00:00:00 2001 From: yuexiao-wang Date: Thu, 9 Nov 2017 13:43:32 +0800 Subject: [PATCH 1/4] add unit test for statefulset Signed-off-by: yuexiao-wang --- pkg/kubectl/scale_test.go | 264 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) diff --git a/pkg/kubectl/scale_test.go b/pkg/kubectl/scale_test.go index 85c77a1ffbf..96250d8fe1e 100644 --- a/pkg/kubectl/scale_test.go +++ b/pkg/kubectl/scale_test.go @@ -23,10 +23,12 @@ import ( kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" testcore "k8s.io/client-go/testing" + "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/fake" + 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" @@ -785,3 +787,265 @@ func TestValidateDeployment(t *testing.T) { } } } + +type ErrorStatefulSets struct { + appsclient.StatefulSetInterface + conflict bool + invalid bool +} + +func (c *ErrorStatefulSets) Update(statefulSet *apps.StatefulSet) (*apps.StatefulSet, error) { + switch { + case c.invalid: + return nil, kerrors.NewInvalid(api.Kind(statefulSet.Kind), statefulSet.Name, nil) + case c.conflict: + return nil, kerrors.NewConflict(api.Resource(statefulSet.Kind), statefulSet.Name, nil) + } + return nil, errors.New("statefulSet update failure") +} + +func (c *ErrorStatefulSets) Get(name string, options metav1.GetOptions) (*apps.StatefulSet, error) { + return &apps.StatefulSet{ + Spec: apps.StatefulSetSpec{ + Replicas: 0, + }, + }, nil +} + +type ErrorStatefulSetClient struct { + appsclient.StatefulSetsGetter + conflict bool + invalid bool +} + +func (c *ErrorStatefulSetClient) StatefulSets(namespace string) appsclient.StatefulSetInterface { + return &ErrorStatefulSets{ + StatefulSetInterface: c.StatefulSetsGetter.StatefulSets(namespace), + invalid: c.invalid, + conflict: c.conflict, + } +} + +func statefulSet() *apps.StatefulSet { + return &apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: metav1.NamespaceDefault, + Name: "foo", + }, + } +} + +func TestStatefulSetScale(t *testing.T) { + fake := fake.NewSimpleClientset(statefulSet()) + scaler := StatefulSetScaler{fake.Apps()} + preconditions := ScalePrecondition{-1, ""} + count := uint(3) + name := "foo" + scaler.Scale("default", name, count, &preconditions, nil, nil) + + actions := fake.Actions() + if len(actions) != 2 { + t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions) + } + + if action, ok := actions[0].(testcore.GetAction); !ok || action.GetResource().GroupResource() != apps.Resource("statefulsets") || action.GetName() != name { + t.Errorf("unexpected action: %v, expected get-statefulsets %s", actions[0], name) + } + if action, ok := actions[1].(testcore.UpdateAction); !ok || action.GetResource().GroupResource() != apps.Resource("statefulsets") || action.GetObject().(*apps.StatefulSet).Spec.Replicas != int32(count) { + t.Errorf("unexpected action %v, expected update-statefulset with replicas = %d", actions[1], count) + } +} + +func TestStatefulSetScaleRetry(t *testing.T) { + fake := &ErrorStatefulSetClient{StatefulSetsGetter: fake.NewSimpleClientset().Apps(), conflict: true} + scaler := &StatefulSetScaler{fake} + preconditions := &ScalePrecondition{-1, ""} + count := uint(3) + name := "foo" + namespace := "default" + + scaleFunc := ScaleCondition(scaler, preconditions, namespace, name, count, nil) + pass, err := scaleFunc() + if pass != false { + t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) + } + if err != nil { + t.Errorf("Did not expect an error on update failure, got %v", err) + } + preconditions = &ScalePrecondition{3, ""} + scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count, nil) + pass, err = scaleFunc() + if err == nil { + t.Error("Expected error on precondition failure") + } +} + +func TestStatefulSetScaleInvalid(t *testing.T) { + fake := &ErrorStatefulSetClient{StatefulSetsGetter: fake.NewSimpleClientset().Apps(), invalid: true} + scaler := StatefulSetScaler{fake} + preconditions := ScalePrecondition{-1, ""} + count := uint(3) + name := "foo" + namespace := "default" + + scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count, nil) + pass, err := scaleFunc() + if pass { + t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) + } + e, ok := err.(ScaleError) + if err == nil || !ok || e.FailureType != ScaleUpdateFailure { + t.Errorf("Expected error on invalid update failure, got %v", err) + } +} + +func TestStatefulSetScaleFailsPreconditions(t *testing.T) { + fake := fake.NewSimpleClientset(&apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: metav1.NamespaceDefault, + Name: "foo", + }, + Spec: apps.StatefulSetSpec{ + Replicas: 10, + }, + }) + scaler := StatefulSetScaler{fake.Apps()} + preconditions := ScalePrecondition{2, ""} + count := uint(3) + name := "foo" + scaler.Scale("default", name, count, &preconditions, nil, nil) + + actions := fake.Actions() + if len(actions) != 1 { + t.Errorf("unexpected actions: %v, expected 1 actions (get)", actions) + } + if action, ok := actions[0].(testcore.GetAction); !ok || action.GetResource().GroupResource() != apps.Resource("statefulsets") || action.GetName() != name { + t.Errorf("unexpected action: %v, expected get-statefulset %s", actions[0], name) + } +} + +func TestValidateStatefulSet(t *testing.T) { + zero, ten, twenty := int32(0), int32(10), int32(20) + tests := []struct { + preconditions ScalePrecondition + statefulset apps.StatefulSet + expectError bool + test string + }{ + { + preconditions: ScalePrecondition{-1, ""}, + expectError: false, + test: "defaults", + }, + { + preconditions: ScalePrecondition{-1, ""}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: apps.StatefulSetSpec{ + Replicas: ten, + }, + }, + expectError: false, + test: "defaults 2", + }, + { + preconditions: ScalePrecondition{0, ""}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: apps.StatefulSetSpec{ + Replicas: zero, + }, + }, + expectError: false, + test: "size matches", + }, + { + preconditions: ScalePrecondition{-1, "foo"}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: apps.StatefulSetSpec{ + Replicas: ten, + }, + }, + expectError: false, + test: "resource version matches", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: apps.StatefulSetSpec{ + Replicas: ten, + }, + }, + expectError: false, + test: "both match", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: apps.StatefulSetSpec{ + Replicas: twenty, + }, + }, + expectError: true, + test: "size different", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + }, + expectError: true, + test: "no replicas", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "bar", + }, + Spec: apps.StatefulSetSpec{ + Replicas: ten, + }, + }, + expectError: true, + test: "version different", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + statefulset: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "bar", + }, + Spec: apps.StatefulSetSpec{ + Replicas: twenty, + }, + }, + expectError: true, + test: "both different", + }, + } + for _, test := range tests { + err := test.preconditions.ValidateStatefulSet(&test.statefulset) + if err != nil && !test.expectError { + t.Errorf("unexpected error: %v (%s)", err, test.test) + } + if err == nil && test.expectError { + t.Errorf("expected an error: %v (%s)", err, test.test) + } + } +} From b6c47305a24f5e4e894660829e049624b1736898 Mon Sep 17 00:00:00 2001 From: yuexiao-wang Date: Sat, 11 Nov 2017 14:51:41 +0800 Subject: [PATCH 2/4] add unit test for replicaset Signed-off-by: yuexiao-wang --- pkg/kubectl/scale_test.go | 261 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/pkg/kubectl/scale_test.go b/pkg/kubectl/scale_test.go index 96250d8fe1e..f4e7a3b275d 100644 --- a/pkg/kubectl/scale_test.go +++ b/pkg/kubectl/scale_test.go @@ -1049,3 +1049,264 @@ func TestValidateStatefulSet(t *testing.T) { } } } + +type ErrorReplicaSets struct { + extensionsclient.ReplicaSetInterface + conflict bool + invalid bool +} + +func (c *ErrorReplicaSets) Update(replicaSets *extensions.ReplicaSet) (*extensions.ReplicaSet, error) { + switch { + case c.invalid: + return nil, kerrors.NewInvalid(api.Kind(replicaSets.Kind), replicaSets.Name, nil) + case c.conflict: + return nil, kerrors.NewConflict(api.Resource(replicaSets.Kind), replicaSets.Name, nil) + } + return nil, errors.New("replicaSets update failure") +} + +func (c *ErrorReplicaSets) Get(name string, options metav1.GetOptions) (*extensions.ReplicaSet, error) { + return &extensions.ReplicaSet{ + Spec: extensions.ReplicaSetSpec{ + Replicas: 0, + }, + }, nil +} + +type ErrorReplicaSetClient struct { + extensionsclient.ReplicaSetsGetter + conflict bool + invalid bool +} + +func (c *ErrorReplicaSetClient) ReplicaSets(namespace string) extensionsclient.ReplicaSetInterface { + return &ErrorReplicaSets{ + ReplicaSetInterface: c.ReplicaSetsGetter.ReplicaSets(namespace), + invalid: c.invalid, + conflict: c.conflict, + } +} + +func replicaSet() *extensions.ReplicaSet { + return &extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: metav1.NamespaceDefault, + Name: "foo", + }, + } +} + +func TestReplicaSetScale(t *testing.T) { + fake := fake.NewSimpleClientset(replicaSet()) + scaler := ReplicaSetScaler{fake.Extensions()} + preconditions := ScalePrecondition{-1, ""} + count := uint(3) + name := "foo" + scaler.Scale("default", name, count, &preconditions, nil, nil) + + actions := fake.Actions() + if len(actions) != 2 { + t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions) + } + if action, ok := actions[0].(testcore.GetAction); !ok || action.GetResource().GroupResource() != extensions.Resource("replicasets") || action.GetName() != name { + t.Errorf("unexpected action: %v, expected get-replicationSet %s", actions[0], name) + } + if action, ok := actions[1].(testcore.UpdateAction); !ok || action.GetResource().GroupResource() != extensions.Resource("replicasets") || action.GetObject().(*extensions.ReplicaSet).Spec.Replicas != int32(count) { + t.Errorf("unexpected action %v, expected update-replicaSet with replicas = %d", actions[1], count) + } +} + +func TestReplicaSetScaleRetry(t *testing.T) { + fake := &ErrorReplicaSetClient{ReplicaSetsGetter: fake.NewSimpleClientset().Extensions(), conflict: true} + scaler := &ReplicaSetScaler{fake} + preconditions := &ScalePrecondition{-1, ""} + count := uint(3) + name := "foo" + namespace := "default" + + scaleFunc := ScaleCondition(scaler, preconditions, namespace, name, count, nil) + pass, err := scaleFunc() + if pass != false { + t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) + } + if err != nil { + t.Errorf("Did not expect an error on update failure, got %v", err) + } + preconditions = &ScalePrecondition{3, ""} + scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count, nil) + pass, err = scaleFunc() + if err == nil { + t.Error("Expected error on precondition failure") + } +} + +func TestReplicaSetScaleInvalid(t *testing.T) { + fake := &ErrorReplicaSetClient{ReplicaSetsGetter: fake.NewSimpleClientset().Extensions(), invalid: true} + scaler := ReplicaSetScaler{fake} + preconditions := ScalePrecondition{-1, ""} + count := uint(3) + name := "foo" + namespace := "default" + + scaleFunc := ScaleCondition(&scaler, &preconditions, namespace, name, count, nil) + pass, err := scaleFunc() + if pass { + t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) + } + e, ok := err.(ScaleError) + if err == nil || !ok || e.FailureType != ScaleUpdateFailure { + t.Errorf("Expected error on invalid update failure, got %v", err) + } +} + +func TestReplicaSetsGetterFailsPreconditions(t *testing.T) { + fake := fake.NewSimpleClientset(&extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: metav1.NamespaceDefault, + Name: "foo", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: 10, + }, + }) + scaler := ReplicaSetScaler{fake.Extensions()} + preconditions := ScalePrecondition{2, ""} + count := uint(3) + name := "foo" + scaler.Scale("default", name, count, &preconditions, nil, nil) + + actions := fake.Actions() + if len(actions) != 1 { + t.Errorf("unexpected actions: %v, expected 1 actions (get)", actions) + } + if action, ok := actions[0].(testcore.GetAction); !ok || action.GetResource().GroupResource() != extensions.Resource("replicasets") || action.GetName() != name { + t.Errorf("unexpected action: %v, expected get-replicaSets %s", actions[0], name) + } +} + +func TestValidateReplicaSets(t *testing.T) { + zero, ten, twenty := int32(0), int32(10), int32(20) + tests := []struct { + preconditions ScalePrecondition + replicaSets extensions.ReplicaSet + expectError bool + test string + }{ + { + preconditions: ScalePrecondition{-1, ""}, + expectError: false, + test: "defaults", + }, + { + preconditions: ScalePrecondition{-1, ""}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: ten, + }, + }, + expectError: false, + test: "defaults 2", + }, + { + preconditions: ScalePrecondition{0, ""}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: zero, + }, + }, + expectError: false, + test: "size matches", + }, + { + preconditions: ScalePrecondition{-1, "foo"}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: ten, + }, + }, + expectError: false, + test: "resource version matches", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: ten, + }, + }, + expectError: false, + test: "both match", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: twenty, + }, + }, + expectError: true, + test: "size different", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "foo", + }, + }, + expectError: true, + test: "no replicas", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "bar", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: ten, + }, + }, + expectError: true, + test: "version different", + }, + { + preconditions: ScalePrecondition{10, "foo"}, + replicaSets: extensions.ReplicaSet{ + ObjectMeta: metav1.ObjectMeta{ + ResourceVersion: "bar", + }, + Spec: extensions.ReplicaSetSpec{ + Replicas: twenty, + }, + }, + expectError: true, + test: "both different", + }, + } + for _, test := range tests { + err := test.preconditions.ValidateReplicaSet(&test.replicaSets) + if err != nil && !test.expectError { + t.Errorf("unexpected error: %v (%s)", err, test.test) + } + if err == nil && test.expectError { + t.Errorf("expected an error: %v (%s)", err, test.test) + } + } +} From 2cfdfd9f8f7ef2ff4b281eb9117a4d825e943dcc Mon Sep 17 00:00:00 2001 From: yuexiao-wang Date: Sat, 11 Nov 2017 15:00:43 +0800 Subject: [PATCH 3/4] Use Error with no value format and fix typo error messages Signed-off-by: yuexiao-wang --- pkg/kubectl/scale_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/kubectl/scale_test.go b/pkg/kubectl/scale_test.go index f4e7a3b275d..ba177982f20 100644 --- a/pkg/kubectl/scale_test.go +++ b/pkg/kubectl/scale_test.go @@ -323,7 +323,7 @@ func TestJobScaleRetry(t *testing.T) { scaleFunc = ScaleCondition(&scaler, &preconditions, namespace, name, count, nil) pass, err = scaleFunc() if err == nil { - t.Errorf("Expected error on precondition failure") + t.Error("Expected error on precondition failure") } } @@ -349,7 +349,7 @@ func TestJobScale(t *testing.T) { t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions) } if action, ok := actions[0].(testcore.GetAction); !ok || action.GetResource().GroupResource() != batch.Resource("jobs") || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name) + t.Errorf("unexpected action: %v, expected get-job %s", actions[0], name) } if action, ok := actions[1].(testcore.UpdateAction); !ok || action.GetResource().GroupResource() != batch.Resource("jobs") || *action.GetObject().(*batch.Job).Spec.Parallelism != int32(count) { t.Errorf("unexpected action %v, expected update-job with parallelism = %d", actions[1], count) @@ -585,7 +585,7 @@ func TestDeploymentScaleRetry(t *testing.T) { scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count, nil) pass, err = scaleFunc() if err == nil { - t.Errorf("Expected error on precondition failure") + t.Error("Expected error on precondition failure") } } @@ -611,7 +611,7 @@ func TestDeploymentScale(t *testing.T) { t.Errorf("unexpected actions: %v, expected 2 actions (get, update)", actions) } if action, ok := actions[0].(testcore.GetAction); !ok || action.GetResource().GroupResource() != extensions.Resource("deployments") || action.GetName() != name { - t.Errorf("unexpected action: %v, expected get-replicationController %s", actions[0], name) + t.Errorf("unexpected action: %v, expected get-deployment %s", actions[0], name) } if action, ok := actions[1].(testcore.UpdateAction); !ok || action.GetResource().GroupResource() != extensions.Resource("deployments") || action.GetObject().(*extensions.Deployment).Spec.Replicas != int32(count) { t.Errorf("unexpected action %v, expected update-deployment with replicas = %d", actions[1], count) From dbca4f25c4ad8b23a42c917c46d45f0ae7e12ead Mon Sep 17 00:00:00 2001 From: yuexiao-wang Date: Sat, 11 Nov 2017 15:03:54 +0800 Subject: [PATCH 4/4] update BUILD Signed-off-by: yuexiao-wang --- pkg/kubectl/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index c1de97568d5..c76b9d86d5e 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -40,11 +40,13 @@ go_test( "//pkg/api/legacyscheme:go_default_library", "//pkg/api/testapi:go_default_library", "//pkg/api/testing:go_default_library", + "//pkg/apis/apps:go_default_library", "//pkg/apis/batch: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/fake:go_default_library", + "//pkg/client/clientset_generated/internalclientset/typed/apps/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",