diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/BUILD b/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/BUILD index b66f81736d7..e0f9badb8f6 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/BUILD +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/BUILD @@ -15,7 +15,6 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/rolling_updater.go b/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/rolling_updater.go index 2fed4128eae..ebb077be5cc 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/rolling_updater.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/rollingupdate/rolling_updater.go @@ -28,7 +28,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/wait" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -413,7 +412,7 @@ func (r *RollingUpdater) scaleDown(newRc, oldRc *corev1.ReplicationController, d // scalerScaleAndWait scales a controller using a Scaler and a real client. func (r *RollingUpdater) scaleAndWaitWithScaler(rc *corev1.ReplicationController, retry *scale.RetryParams, wait *scale.RetryParams) (*corev1.ReplicationController, error) { scaler := scale.NewScaler(r.scaleClient) - if err := scaler.Scale(rc.Namespace, rc.Name, uint(valOrZero(rc.Spec.Replicas)), &scale.ScalePrecondition{Size: -1, ResourceVersion: ""}, retry, wait, schema.GroupResource{Resource: "replicationcontrollers"}); err != nil { + if err := scaler.Scale(rc.Namespace, rc.Name, uint(valOrZero(rc.Spec.Replicas)), &scale.ScalePrecondition{Size: -1, ResourceVersion: ""}, retry, wait, corev1.SchemeGroupVersion.WithResource("replicationcontrollers")); err != nil { return nil, err } return r.rcClient.ReplicationControllers(rc.Namespace).Get(rc.Name, metav1.GetOptions{}) diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go index 1e2e5d71d1b..535fdb2c8ae 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/scale/scale.go @@ -227,7 +227,7 @@ func (o *ScaleOptions) RunScale() error { } mapping := info.ResourceMapping() - if err := o.scaler.Scale(info.Namespace, info.Name, uint(o.Replicas), precondition, retry, waitForReplicas, mapping.Resource.GroupResource()); err != nil { + if err := o.scaler.Scale(info.Namespace, info.Name, uint(o.Replicas), precondition, retry, waitForReplicas, mapping.Resource); err != nil { return err } diff --git a/staging/src/k8s.io/kubectl/pkg/scale/BUILD b/staging/src/k8s.io/kubectl/pkg/scale/BUILD index 476f6be570c..a3f914aaee2 100644 --- a/staging/src/k8s.io/kubectl/pkg/scale/BUILD +++ b/staging/src/k8s.io/kubectl/pkg/scale/BUILD @@ -9,8 +9,8 @@ go_library( deps = [ "//staging/src/k8s.io/api/autoscaling/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/client-go/scale:go_default_library", ], @@ -27,6 +27,7 @@ go_test( "//staging/src/k8s.io/apimachinery/pkg/apis/testapigroup/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", "//staging/src/k8s.io/client-go/scale:go_default_library", "//staging/src/k8s.io/client-go/scale/fake:go_default_library", "//staging/src/k8s.io/client-go/testing:go_default_library", diff --git a/staging/src/k8s.io/kubectl/pkg/scale/scale.go b/staging/src/k8s.io/kubectl/pkg/scale/scale.go index 6d4be8bc3fb..80597c8304b 100644 --- a/staging/src/k8s.io/kubectl/pkg/scale/scale.go +++ b/staging/src/k8s.io/kubectl/pkg/scale/scale.go @@ -23,8 +23,8 @@ import ( autoscalingv1 "k8s.io/api/autoscaling/v1" "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/wait" scaleclient "k8s.io/client-go/scale" ) @@ -35,10 +35,10 @@ type Scaler interface { // retries in the event of resource version mismatch (if retry is not nil), // and optionally waits until the status of the resource matches newSize (if wait is not nil) // TODO: Make the implementation of this watch-based (#56075) once #31345 is fixed. - Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, wait *RetryParams, gr schema.GroupResource) error + Scale(namespace, name string, newSize uint, preconditions *ScalePrecondition, retry, wait *RetryParams, gvr schema.GroupVersionResource) error // ScaleSimple does a simple one-shot attempt at scaling - not useful on its own, but // a necessary building block for Scale - ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint, gr schema.GroupResource) (updatedResourceVersion string, err error) + ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint, gvr schema.GroupVersionResource) (updatedResourceVersion string, err error) } // NewScaler get a scaler for a given resource @@ -77,9 +77,9 @@ func NewRetryParams(interval, timeout time.Duration) *RetryParams { } // ScaleCondition is a closure around Scale that facilitates retries via util.wait -func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name string, count uint, updatedResourceVersion *string, gr schema.GroupResource) wait.ConditionFunc { +func ScaleCondition(r Scaler, precondition *ScalePrecondition, namespace, name string, count uint, updatedResourceVersion *string, gvr schema.GroupVersionResource) wait.ConditionFunc { return func() (bool, error) { - rv, err := r.ScaleSimple(namespace, name, precondition, count, gr) + rv, err := r.ScaleSimple(namespace, name, precondition, count, gvr) if updatedResourceVersion != nil { *updatedResourceVersion = rv } @@ -113,23 +113,25 @@ type genericScaler struct { var _ Scaler = &genericScaler{} // ScaleSimple updates a scale of a given resource. It returns the resourceVersion of the scale if the update was successful. -func (s *genericScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint, gr schema.GroupResource) (updatedResourceVersion string, err error) { - scale := &autoscalingv1.Scale{ - ObjectMeta: metav1.ObjectMeta{Namespace: namespace, Name: name}, - } +func (s *genericScaler) ScaleSimple(namespace, name string, preconditions *ScalePrecondition, newSize uint, gvr schema.GroupVersionResource) (updatedResourceVersion string, err error) { if preconditions != nil { - var err error - scale, err = s.scaleNamespacer.Scales(namespace).Get(gr, name) + scale, err := s.scaleNamespacer.Scales(namespace).Get(gvr.GroupResource(), name) if err != nil { return "", err } - if err := preconditions.validate(scale); err != nil { + if err = preconditions.validate(scale); err != nil { return "", err } + scale.Spec.Replicas = int32(newSize) + updatedScale, err := s.scaleNamespacer.Scales(namespace).Update(gvr.GroupResource(), scale) + if err != nil { + return "", err + } + return updatedScale.ResourceVersion, nil } - scale.Spec.Replicas = int32(newSize) - updatedScale, err := s.scaleNamespacer.Scales(namespace).Update(gr, scale) + patch := []byte(fmt.Sprintf(`{"spec":{"replicas":%d}}`, newSize)) + updatedScale, err := s.scaleNamespacer.Scales(namespace).Patch(gvr, name, types.MergePatchType, patch) if err != nil { return "", err } @@ -138,17 +140,17 @@ func (s *genericScaler) ScaleSimple(namespace, name string, preconditions *Scale // Scale updates a scale of a given resource to a new size, with optional precondition check (if preconditions is not nil), // optional retries (if retry is not nil), and then optionally waits for the status to reach desired count. -func (s *genericScaler) Scale(namespace, resourceName string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams, gr schema.GroupResource) error { +func (s *genericScaler) Scale(namespace, resourceName string, newSize uint, preconditions *ScalePrecondition, retry, waitForReplicas *RetryParams, gvr schema.GroupVersionResource) error { if retry == nil { // make it try only once, immediately retry = &RetryParams{Interval: time.Millisecond, Timeout: time.Millisecond} } - cond := ScaleCondition(s, preconditions, namespace, resourceName, newSize, nil, gr) + cond := ScaleCondition(s, preconditions, namespace, resourceName, newSize, nil, gvr) if err := wait.PollImmediate(retry.Interval, retry.Timeout, cond); err != nil { return err } if waitForReplicas != nil { - return WaitForScaleHasDesiredReplicas(s.scaleNamespacer, gr, resourceName, namespace, newSize, waitForReplicas) + return WaitForScaleHasDesiredReplicas(s.scaleNamespacer, gvr.GroupResource(), resourceName, namespace, newSize, waitForReplicas) } return nil } diff --git a/staging/src/k8s.io/kubectl/pkg/scale/scale_test.go b/staging/src/k8s.io/kubectl/pkg/scale/scale_test.go index f15e94c2349..276ac5edd41 100644 --- a/staging/src/k8s.io/kubectl/pkg/scale/scale_test.go +++ b/staging/src/k8s.io/kubectl/pkg/scale/scale_test.go @@ -17,6 +17,7 @@ limitations under the License. package scale import ( + "encoding/json" "fmt" "testing" "time" @@ -27,24 +28,47 @@ import ( api "k8s.io/apimachinery/pkg/apis/testapigroup/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/scale" fakescale "k8s.io/client-go/scale/fake" testcore "k8s.io/client-go/testing" ) +var ( + rcgvr = schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "replicationcontrollers", + } + rsgvr = schema.GroupVersionResource{ + Group: "extensions", + Version: "v1beta1", + Resource: "replicasets", + } + deploygvr = schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "deployments", + } + stsgvr = schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "statefulsets", + } +) + func TestReplicationControllerScaleRetry(t *testing.T) { verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewConflict(api.Resource("Status"), "foo", nil), + "patch": kerrors.NewConflict(api.Resource("Status"), "foo", nil), } - scaleClientExpectedAction := []string{"get", "update", "get"} + scaleClientExpectedAction := []string{"patch", "get"} scaleClient := createFakeScaleClient("replicationcontrollers", "foo-v1", 2, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo-v1" namespace := metav1.NamespaceDefault - scaleFunc := ScaleCondition(scaler, &preconditions, namespace, name, count, nil, schema.GroupResource{Group: "", Resource: "replicationcontrollers"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, rcgvr) pass, err := scaleFunc() if pass { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -52,8 +76,8 @@ func TestReplicationControllerScaleRetry(t *testing.T) { if err != nil { t.Errorf("Did not expect an error on update conflict failure, got %v", err) } - preconditions = ScalePrecondition{3, ""} - scaleFunc = ScaleCondition(scaler, &preconditions, namespace, name, count, nil, schema.GroupResource{Group: "", Resource: "replicationcontrollers"}) + preconditions := ScalePrecondition{3, ""} + scaleFunc = ScaleCondition(scaler, &preconditions, namespace, name, count, nil, rcgvr) pass, err = scaleFunc() if err == nil { t.Errorf("Expected error on precondition failure") @@ -71,17 +95,16 @@ func TestReplicationControllerScaleRetry(t *testing.T) { func TestReplicationControllerScaleInvalid(t *testing.T) { verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), + "patch": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), } - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} scaleClient := createFakeScaleClient("replicationcontrollers", "foo-v1", 1, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo-v1" namespace := "default" - scaleFunc := ScaleCondition(scaler, &preconditions, namespace, name, count, nil, schema.GroupResource{Group: "", Resource: "replicationcontrollers"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, rcgvr) pass, err := scaleFunc() if pass { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -101,13 +124,12 @@ func TestReplicationControllerScaleInvalid(t *testing.T) { } func TestReplicationControllerScale(t *testing.T) { - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} scaleClient := createFakeScaleClient("replicationcontrollers", "foo-v1", 2, nil) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo-v1" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "", Resource: "replicationcontrollers"}) + err := scaler.Scale("default", name, count, nil, nil, nil, rcgvr) if err != nil { t.Fatalf("unexpected error occurred = %v while scaling the resource", err) @@ -130,7 +152,7 @@ func TestReplicationControllerScaleFailsPreconditions(t *testing.T) { preconditions := ScalePrecondition{2, ""} count := uint(3) name := "foo" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "", Resource: "replicationcontrollers"}) + err := scaler.Scale("default", name, count, &preconditions, nil, nil, rcgvr) if err == nil { t.Fatal("expected to get an error but none was returned") } @@ -147,17 +169,16 @@ func TestReplicationControllerScaleFailsPreconditions(t *testing.T) { func TestDeploymentScaleRetry(t *testing.T) { verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewConflict(api.Resource("Status"), "foo", nil), + "patch": kerrors.NewConflict(api.Resource("Status"), "foo", nil), } - scaleClientExpectedAction := []string{"get", "update", "get"} + scaleClientExpectedAction := []string{"patch", "get"} scaleClient := createFakeScaleClient("deployments", "foo", 2, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := &ScalePrecondition{-1, ""} count := uint(3) name := "foo" namespace := "default" - scaleFunc := ScaleCondition(scaler, preconditions, namespace, name, count, nil, schema.GroupResource{Group: "apps", Resource: "deployments"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, deploygvr) pass, err := scaleFunc() if pass != false { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -165,8 +186,8 @@ func TestDeploymentScaleRetry(t *testing.T) { 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, schema.GroupResource{Group: "apps", Resource: "deployments"}) + preconditions := &ScalePrecondition{3, ""} + scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count, nil, deploygvr) pass, err = scaleFunc() if err == nil { t.Error("Expected error on precondition failure") @@ -183,13 +204,12 @@ func TestDeploymentScaleRetry(t *testing.T) { } func TestDeploymentScale(t *testing.T) { - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} scaleClient := createFakeScaleClient("deployments", "foo", 2, nil) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "apps", Resource: "deployments"}) + err := scaler.Scale("default", name, count, nil, nil, nil, deploygvr) if err != nil { t.Fatal(err) } @@ -205,18 +225,17 @@ func TestDeploymentScale(t *testing.T) { } func TestDeploymentScaleInvalid(t *testing.T) { - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), + "patch": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), } scaleClient := createFakeScaleClient("deployments", "foo", 2, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo" namespace := "default" - scaleFunc := ScaleCondition(scaler, &preconditions, namespace, name, count, nil, schema.GroupResource{Group: "apps", Resource: "deployments"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, deploygvr) pass, err := scaleFunc() if pass { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -242,7 +261,7 @@ func TestDeploymentScaleFailsPreconditions(t *testing.T) { preconditions := ScalePrecondition{2, ""} count := uint(3) name := "foo" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "apps", Resource: "deployments"}) + err := scaler.Scale("default", name, count, &preconditions, nil, nil, deploygvr) if err == nil { t.Fatal("exptected to get an error but none was returned") } @@ -258,13 +277,12 @@ func TestDeploymentScaleFailsPreconditions(t *testing.T) { } func TestStatefulSetScale(t *testing.T) { - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} scaleClient := createFakeScaleClient("statefulsets", "foo", 2, nil) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "apps", Resource: "statefulset"}) + err := scaler.Scale("default", name, count, nil, nil, nil, stsgvr) if err != nil { t.Fatal(err) } @@ -280,18 +298,17 @@ func TestStatefulSetScale(t *testing.T) { } func TestStatefulSetScaleRetry(t *testing.T) { - scaleClientExpectedAction := []string{"get", "update", "get"} + scaleClientExpectedAction := []string{"patch", "get"} verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewConflict(api.Resource("Status"), "foo", nil), + "patch": kerrors.NewConflict(api.Resource("Status"), "foo", nil), } scaleClient := createFakeScaleClient("statefulsets", "foo", 2, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := &ScalePrecondition{-1, ""} count := uint(3) name := "foo" namespace := "default" - scaleFunc := ScaleCondition(scaler, preconditions, namespace, name, count, nil, schema.GroupResource{Group: "apps", Resource: "statefulsets"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, stsgvr) pass, err := scaleFunc() if pass != false { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -299,8 +316,8 @@ func TestStatefulSetScaleRetry(t *testing.T) { 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, schema.GroupResource{Group: "apps", Resource: "statefulsets"}) + preconditions := &ScalePrecondition{3, ""} + scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count, nil, stsgvr) pass, err = scaleFunc() if err == nil { t.Error("Expected error on precondition failure") @@ -317,18 +334,17 @@ func TestStatefulSetScaleRetry(t *testing.T) { } func TestStatefulSetScaleInvalid(t *testing.T) { - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), + "patch": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), } scaleClient := createFakeScaleClient("statefulsets", "foo", 2, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo" namespace := "default" - scaleFunc := ScaleCondition(scaler, &preconditions, namespace, name, count, nil, schema.GroupResource{Group: "apps", Resource: "statefulsets"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, stsgvr) pass, err := scaleFunc() if pass { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -354,7 +370,7 @@ func TestStatefulSetScaleFailsPreconditions(t *testing.T) { preconditions := ScalePrecondition{2, ""} count := uint(3) name := "foo" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "apps", Resource: "statefulsets"}) + err := scaler.Scale("default", name, count, &preconditions, nil, nil, stsgvr) if err == nil { t.Fatal("expected to get an error but none was returned") } @@ -370,13 +386,12 @@ func TestStatefulSetScaleFailsPreconditions(t *testing.T) { } func TestReplicaSetScale(t *testing.T) { - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} scaleClient := createFakeScaleClient("replicasets", "foo", 10, nil) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "extensions", Resource: "replicasets"}) + err := scaler.Scale("default", name, count, nil, nil, nil, rsgvr) if err != nil { t.Fatal(err) } @@ -393,17 +408,16 @@ func TestReplicaSetScale(t *testing.T) { func TestReplicaSetScaleRetry(t *testing.T) { verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewConflict(api.Resource("Status"), "foo", nil), + "patch": kerrors.NewConflict(api.Resource("Status"), "foo", nil), } - scaleClientExpectedAction := []string{"get", "update", "get"} + scaleClientExpectedAction := []string{"patch", "get"} scaleClient := createFakeScaleClient("replicasets", "foo", 2, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := &ScalePrecondition{-1, ""} count := uint(3) name := "foo" namespace := "default" - scaleFunc := ScaleCondition(scaler, preconditions, namespace, name, count, nil, schema.GroupResource{Group: "extensions", Resource: "replicasets"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, rsgvr) pass, err := scaleFunc() if pass != false { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -411,8 +425,8 @@ func TestReplicaSetScaleRetry(t *testing.T) { 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, schema.GroupResource{Group: "extensions", Resource: "replicasets"}) + preconditions := &ScalePrecondition{3, ""} + scaleFunc = ScaleCondition(scaler, preconditions, namespace, name, count, nil, rsgvr) pass, err = scaleFunc() if err == nil { t.Error("Expected error on precondition failure") @@ -430,17 +444,16 @@ func TestReplicaSetScaleRetry(t *testing.T) { func TestReplicaSetScaleInvalid(t *testing.T) { verbsOnError := map[string]*kerrors.StatusError{ - "update": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), + "patch": kerrors.NewInvalid(api.Kind("Status"), "foo", nil), } - scaleClientExpectedAction := []string{"get", "update"} + scaleClientExpectedAction := []string{"patch"} scaleClient := createFakeScaleClient("replicasets", "foo", 2, verbsOnError) scaler := NewScaler(scaleClient) - preconditions := ScalePrecondition{-1, ""} count := uint(3) name := "foo" namespace := "default" - scaleFunc := ScaleCondition(scaler, &preconditions, namespace, name, count, nil, schema.GroupResource{Group: "extensions", Resource: "replicasets"}) + scaleFunc := ScaleCondition(scaler, nil, namespace, name, count, nil, rsgvr) pass, err := scaleFunc() if pass { t.Errorf("Expected an update failure to return pass = false, got pass = %v", pass) @@ -466,7 +479,7 @@ func TestReplicaSetsGetterFailsPreconditions(t *testing.T) { preconditions := ScalePrecondition{2, ""} count := uint(3) name := "foo" - err := scaler.Scale("default", name, count, &preconditions, nil, nil, schema.GroupResource{Group: "extensions", Resource: "replicasets"}) + err := scaler.Scale("default", name, count, &preconditions, nil, nil, rsgvr) if err == nil { t.Fatal("expected to get an error but non was returned") } @@ -484,33 +497,44 @@ func TestReplicaSetsGetterFailsPreconditions(t *testing.T) { // TestGenericScaleSimple exercises GenericScaler.ScaleSimple method func TestGenericScaleSimple(t *testing.T) { // test data - scaleClient := createFakeScaleClient("deployments", "abc", 10, nil) + scaleClient := createFakeScaleClient("deployments", "abc", 5, nil) + // expected actions + scaleClientExpectedAction := []string{"patch", "get", "update", "get", "update", "get", "get", "update", "get"} // test scenarios scenarios := []struct { name string - precondition ScalePrecondition + precondition *ScalePrecondition newSize int - targetGR schema.GroupResource + targetGVR schema.GroupVersionResource resName string scaleGetter scale.ScalesGetter expectError bool }{ + // scenario 0: scale up the "abc" deployment without precondition + { + name: "scale up the \"abc\" deployment without precondition", + precondition: nil, + newSize: 10, + targetGVR: deploygvr, + resName: "abc", + scaleGetter: scaleClient, + }, // scenario 1: scale up the "abc" deployment { name: "scale up the \"abc\" deployment", - precondition: ScalePrecondition{10, ""}, + precondition: &ScalePrecondition{10, ""}, newSize: 20, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "abc", scaleGetter: scaleClient, }, // scenario 2: scale down the "abc" deployment { name: "scale down the \"abs\" deployment", - precondition: ScalePrecondition{20, ""}, + precondition: &ScalePrecondition{20, ""}, newSize: 5, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "abc", scaleGetter: scaleClient, }, @@ -518,9 +542,9 @@ func TestGenericScaleSimple(t *testing.T) { // note that the previous scenario (2) set the size to 5 { name: "precondition error, expected size is 1", - precondition: ScalePrecondition{1, ""}, + precondition: &ScalePrecondition{1, ""}, newSize: 5, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "abc", scaleGetter: scaleClient, expectError: true, @@ -528,18 +552,18 @@ func TestGenericScaleSimple(t *testing.T) { // scenario 4: precondition is not validated when the precondition size is set to -1 { name: "precondition is not validated when the size is set to -1", - precondition: ScalePrecondition{-1, ""}, + precondition: &ScalePrecondition{-1, ""}, newSize: 5, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "abc", scaleGetter: scaleClient, }, // scenario 5: precondition error, resource version mismatch { name: "precondition error, resource version mismatch", - precondition: ScalePrecondition{5, "v1"}, + precondition: &ScalePrecondition{5, "v1"}, newSize: 5, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "abc", scaleGetter: scaleClient, expectError: true, @@ -551,7 +575,7 @@ func TestGenericScaleSimple(t *testing.T) { t.Run(fmt.Sprintf("running scenario %d: %s", index+1, scenario.name), func(t *testing.T) { target := NewScaler(scenario.scaleGetter) - resVersion, err := target.ScaleSimple("default", scenario.resName, &scenario.precondition, uint(scenario.newSize), scenario.targetGR) + resVersion, err := target.ScaleSimple("default", scenario.resName, scenario.precondition, uint(scenario.newSize), scenario.targetGVR) if scenario.expectError && err == nil { t.Fatal("expected an error but was not returned") @@ -564,39 +588,61 @@ func TestGenericScaleSimple(t *testing.T) { } }) } + + // check actions + actions := scaleClient.Actions() + if len(actions) != len(scaleClientExpectedAction) { + t.Errorf("unexpected actions: %v, expected %d actions got %d", actions, len(scaleClientExpectedAction), len(actions)) + } + for i, verb := range scaleClientExpectedAction { + if actions[i].GetVerb() != verb { + t.Errorf("unexpected action: %+v, expected %s", actions[i].GetVerb(), verb) + } + } } // TestGenericScale exercises GenericScaler.Scale method func TestGenericScale(t *testing.T) { // test data - scaleClient := createFakeScaleClient("deployments", "abc", 10, nil) + scaleClient := createFakeScaleClient("deployments", "abc", 5, nil) + // expected actions + scaleClientExpectedAction := []string{"patch", "get", "update", "get", "get"} // test scenarios scenarios := []struct { name string - precondition ScalePrecondition + precondition *ScalePrecondition newSize int - targetGR schema.GroupResource + targetGVR schema.GroupVersionResource resName string scaleGetter scale.ScalesGetter waitForReplicas *RetryParams expectError bool }{ + // scenario 0: scale up the "abc" deployment without precondition + { + name: "scale up the \"abc\" deployment without precondition", + precondition: nil, + newSize: 10, + targetGVR: deploygvr, + resName: "abc", + scaleGetter: scaleClient, + }, // scenario 1: scale up the "abc" deployment { name: "scale up the \"abc\" deployment", - precondition: ScalePrecondition{10, ""}, + precondition: &ScalePrecondition{10, ""}, newSize: 20, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "abc", scaleGetter: scaleClient, }, //scenario 2: a resource name cannot be empty { name: "a resource name cannot be empty", - precondition: ScalePrecondition{10, ""}, + precondition: &ScalePrecondition{10, ""}, newSize: 20, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "", scaleGetter: scaleClient, expectError: true, @@ -604,9 +650,9 @@ func TestGenericScale(t *testing.T) { // scenario 3: wait for replicas error due to status.Replicas != spec.Replicas { name: "wait for replicas error due to status.Replicas != spec.Replicas", - precondition: ScalePrecondition{10, ""}, + precondition: &ScalePrecondition{10, ""}, newSize: 20, - targetGR: schema.GroupResource{Group: "apps", Resource: "deployments"}, + targetGVR: deploygvr, resName: "abc", scaleGetter: scaleClient, waitForReplicas: &RetryParams{time.Duration(5 * time.Second), time.Duration(5 * time.Second)}, @@ -619,7 +665,7 @@ func TestGenericScale(t *testing.T) { t.Run(scenario.name, func(t *testing.T) { target := NewScaler(scenario.scaleGetter) - err := target.Scale("default", scenario.resName, uint(scenario.newSize), &scenario.precondition, nil, scenario.waitForReplicas, scenario.targetGR) + err := target.Scale("default", scenario.resName, uint(scenario.newSize), scenario.precondition, nil, scenario.waitForReplicas, scenario.targetGVR) if scenario.expectError && err == nil { t.Fatal("expected an error but was not returned") @@ -629,6 +675,17 @@ func TestGenericScale(t *testing.T) { } }) } + + // check actions + actions := scaleClient.Actions() + if len(actions) != len(scaleClientExpectedAction) { + t.Errorf("unexpected actions: %v, expected %d actions got %d", actions, len(scaleClientExpectedAction), len(actions)) + } + for i, verb := range scaleClientExpectedAction { + if actions[i].GetVerb() != verb { + t.Errorf("unexpected action: %+v, expected %s", actions[i].GetVerb(), verb) + } + } } func createFakeScaleClient(resource string, resourceName string, replicas int, errorsOnVerb map[string]*kerrors.StatusError) *fakescale.FakeScaleClient { @@ -659,6 +716,7 @@ func createFakeScaleClient(resource string, resourceName string, replicas int, e } 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) @@ -679,5 +737,35 @@ func createFakeScaleClient(resource string, resourceName string, replicas int, e }, }, nil }) + + scaleClient.AddReactor("patch", resource, func(rawAction testcore.Action) (handled bool, ret runtime.Object, err error) { + action := rawAction.(testcore.PatchAction) + pt := action.GetPatchType() + if pt != types.MergePatchType { + return true, nil, fmt.Errorf("unexpected patch type: expected = %s, got = %s", types.MergePatchType, pt) + } + var scale autoscalingv1.Scale + err = json.Unmarshal(action.GetPatch(), &scale) + if err != nil { + return true, nil, fmt.Errorf("invalid patch: %s", err) + } + name := action.GetName() + if name != resourceName { + return true, nil, fmt.Errorf("expected = %s, got = %s", resourceName, name) + } + if anError, should := shouldReturnAnError("patch"); should { + return true, nil, anError + } + newReplicas = scale.Spec.Replicas + return true, &autoscalingv1.Scale{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: action.GetNamespace(), + }, + Spec: autoscalingv1.ScaleSpec{ + Replicas: newReplicas, + }, + }, nil + }) return scaleClient } diff --git a/test/e2e/framework/rc_util.go b/test/e2e/framework/rc_util.go index a0755b07834..1030b58035c 100644 --- a/test/e2e/framework/rc_util.go +++ b/test/e2e/framework/rc_util.go @@ -120,7 +120,7 @@ func DeleteRCAndWaitForGC(c clientset.Interface, ns, name string) error { // ScaleRC scales Replication Controller to be desired size. 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.SchemeGroupVersion.WithResource("replicationcontrollers")) } // RunRC Launches (and verifies correctness) of a Replication Controller diff --git a/test/e2e/framework/util.go b/test/e2e/framework/util.go index 46a0aef671f..49b6ee13abd 100644 --- a/test/e2e/framework/util.go +++ b/test/e2e/framework/util.go @@ -2183,10 +2183,10 @@ func ScaleResource( size uint, wait bool, kind schema.GroupKind, - gr schema.GroupResource, + gvr schema.GroupVersionResource, ) error { ginkgo.By(fmt.Sprintf("Scaling %v %s in namespace %s to %d", kind, name, ns, size)) - if err := testutils.ScaleResourceWithRetries(scalesGetter, ns, name, size, gr); err != nil { + if err := testutils.ScaleResourceWithRetries(scalesGetter, ns, name, size, gvr); err != nil { return fmt.Errorf("error while scaling RC %s to %d replicas: %v", name, size, err) } if !wait { diff --git a/test/e2e/scalability/load.go b/test/e2e/scalability/load.go index 0de7771c41b..0ba20251ad5 100644 --- a/test/e2e/scalability/load.go +++ b/test/e2e/scalability/load.go @@ -691,7 +691,7 @@ func scaleResource(wg *sync.WaitGroup, config testutils.RunObjectConfig, scaling newSize, true, config.GetKind(), - config.GetGroupResource(), + config.GetGroupVersionResource(), ), fmt.Sprintf("scaling %v %v", config.GetKind(), config.GetName())) diff --git a/test/utils/runners.go b/test/utils/runners.go index d3196364032..3c375a3991a 100644 --- a/test/utils/runners.go +++ b/test/utils/runners.go @@ -107,6 +107,7 @@ type RunObjectConfig interface { GetReplicas() int GetLabelValue(string) (string, bool) GetGroupResource() schema.GroupResource + GetGroupVersionResource() schema.GroupVersionResource } type RCConfig struct { @@ -298,6 +299,10 @@ func (config *DeploymentConfig) GetGroupResource() schema.GroupResource { return extensionsinternal.Resource("deployments") } +func (config *DeploymentConfig) GetGroupVersionResource() schema.GroupVersionResource { + return extensionsinternal.SchemeGroupVersion.WithResource("deployments") +} + func (config *DeploymentConfig) create() error { deployment := &apps.Deployment{ ObjectMeta: metav1.ObjectMeta{ @@ -374,6 +379,10 @@ func (config *ReplicaSetConfig) GetGroupResource() schema.GroupResource { return extensionsinternal.Resource("replicasets") } +func (config *ReplicaSetConfig) GetGroupVersionResource() schema.GroupVersionResource { + return extensionsinternal.SchemeGroupVersion.WithResource("replicasets") +} + func (config *ReplicaSetConfig) create() error { rs := &apps.ReplicaSet{ ObjectMeta: metav1.ObjectMeta{ @@ -446,6 +455,10 @@ func (config *JobConfig) GetGroupResource() schema.GroupResource { return batchinternal.Resource("jobs") } +func (config *JobConfig) GetGroupVersionResource() schema.GroupVersionResource { + return batchinternal.SchemeGroupVersion.WithResource("jobs") +} + func (config *JobConfig) create() error { job := &batch.Job{ ObjectMeta: metav1.ObjectMeta{ @@ -522,6 +535,10 @@ func (config *RCConfig) GetGroupResource() schema.GroupResource { return api.Resource("replicationcontrollers") } +func (config *RCConfig) GetGroupVersionResource() schema.GroupVersionResource { + return api.SchemeGroupVersion.WithResource("replicationcontrollers") +} + func (config *RCConfig) GetClient() clientset.Interface { return config.Client } diff --git a/test/utils/update_resources.go b/test/utils/update_resources.go index c98f5eae7aa..eab599c3ff6 100644 --- a/test/utils/update_resources.go +++ b/test/utils/update_resources.go @@ -45,17 +45,17 @@ func RetryErrorCondition(condition wait.ConditionFunc) wait.ConditionFunc { } } -func ScaleResourceWithRetries(scalesGetter scaleclient.ScalesGetter, namespace, name string, size uint, gr schema.GroupResource) error { +func ScaleResourceWithRetries(scalesGetter scaleclient.ScalesGetter, namespace, name string, size uint, gvr schema.GroupVersionResource) error { scaler := scale.NewScaler(scalesGetter) preconditions := &scale.ScalePrecondition{ Size: -1, ResourceVersion: "", } waitForReplicas := scale.NewRetryParams(waitRetryInterval, waitRetryTimeout) - cond := RetryErrorCondition(scale.ScaleCondition(scaler, preconditions, namespace, name, size, nil, gr)) + cond := RetryErrorCondition(scale.ScaleCondition(scaler, preconditions, namespace, name, size, nil, gvr)) err := wait.PollImmediate(updateRetryInterval, updateRetryTimeout, cond) if err == nil { - err = scale.WaitForScaleHasDesiredReplicas(scalesGetter, gr, name, namespace, size, waitForReplicas) + err = scale.WaitForScaleHasDesiredReplicas(scalesGetter, gvr.GroupResource(), name, namespace, size, waitForReplicas) } if err != nil { return fmt.Errorf("Error while scaling %s to %d replicas: %v", name, size, err)