From 79d8c9754d553b3ded6be6711e095feaf1beb401 Mon Sep 17 00:00:00 2001 From: Wojciech Tyczynski Date: Thu, 25 Aug 2016 11:29:47 +0200 Subject: [PATCH] Fix scale x->x in kubectl for ReplicationController --- pkg/kubectl/scale.go | 48 +++++++++++++++++++++++++++------------- pkg/kubectl/stop_test.go | 4 ++-- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/pkg/kubectl/scale.go b/pkg/kubectl/scale.go index 25b8a0ee5e..044be5706f 100644 --- a/pkg/kubectl/scale.go +++ b/pkg/kubectl/scale.go @@ -198,27 +198,45 @@ func (scaler *ReplicationControllerScaler) Scale(namespace, name string, newSize return err } if waitForReplicas != nil { - watchOptions := api.ListOptions{FieldSelector: fields.OneTermEqualSelector("metadata.name", name), ResourceVersion: updatedResourceVersion} - watcher, err := scaler.c.ReplicationControllers(namespace).Watch(watchOptions) + checkRC := func(rc *api.ReplicationController) bool { + if uint(rc.Spec.Replicas) != newSize { + // the size is changed by other party. Don't need to wait for the new change to complete. + return true + } + return rc.Status.ObservedGeneration >= rc.Generation && rc.Status.Replicas == rc.Spec.Replicas + } + // If number of replicas doesn't change, then the update may not event + // be sent to underlying databse (we don't send no-op changes). + // In such case, will have value of the most + // recent update (which may be far in the past) so we may get "too old + // RV" error from watch or potentially no ReplicationController events + // will be deliver, since it may already be in the expected state. + // To protect from these two, we first issue Get() to ensure that we + // are not already in the expected state. + currentRC, err := scaler.c.ReplicationControllers(namespace).Get(name) if err != nil { return err } - _, err = watch.Until(waitForReplicas.Timeout, watcher, func(event watch.Event) (bool, error) { - if event.Type != watch.Added && event.Type != watch.Modified { - return false, nil + if !checkRC(currentRC) { + watchOptions := api.ListOptions{ + FieldSelector: fields.OneTermEqualSelector("metadata.name", name), + ResourceVersion: updatedResourceVersion, } - - rc := event.Object.(*api.ReplicationController) - if uint(rc.Spec.Replicas) != newSize { - // the size is changed by other party. Don't need to wait for the new change to complete. - return true, nil + watcher, err := scaler.c.ReplicationControllers(namespace).Watch(watchOptions) + if err != nil { + return err } - return rc.Status.ObservedGeneration >= rc.Generation && rc.Status.Replicas == rc.Spec.Replicas, nil - }) - if err == wait.ErrWaitTimeout { - return fmt.Errorf("timed out waiting for %q to be synced", name) + _, err = watch.Until(waitForReplicas.Timeout, watcher, func(event watch.Event) (bool, error) { + if event.Type != watch.Added && event.Type != watch.Modified { + return false, nil + } + return checkRC(event.Object.(*api.ReplicationController)), nil + }) + if err == wait.ErrWaitTimeout { + return fmt.Errorf("timed out waiting for %q to be synced", name) + } + return err } - return err } return nil } diff --git a/pkg/kubectl/stop_test.go b/pkg/kubectl/stop_test.go index aa116f3c1f..3e2defb04a 100644 --- a/pkg/kubectl/stop_test.go +++ b/pkg/kubectl/stop_test.go @@ -70,7 +70,7 @@ func TestReplicationControllerStop(t *testing.T) { }, }, StopError: nil, - ExpectedActions: []string{"get", "list", "get", "update", "watch", "delete"}, + ExpectedActions: []string{"get", "list", "get", "update", "get", "delete"}, }, { Name: "NoOverlapping", @@ -108,7 +108,7 @@ func TestReplicationControllerStop(t *testing.T) { }, }, StopError: nil, - ExpectedActions: []string{"get", "list", "get", "update", "watch", "delete"}, + ExpectedActions: []string{"get", "list", "get", "update", "get", "delete"}, }, { Name: "OverlappingError",