From 124344a1a465dcdd5c67cb68b8badedb9dad43cf Mon Sep 17 00:00:00 2001 From: Janet Kuo Date: Wed, 6 Sep 2017 13:42:26 -0700 Subject: [PATCH] Move paused deployment e2e tests to integration --- .../deployment/util/deployment_util.go | 2 +- test/e2e/apps/deployment.go | 144 --------------- test/e2e/framework/deployment_util.go | 29 +-- test/integration/deployment/BUILD | 2 + .../integration/deployment/deployment_test.go | 171 ++++++++++++++++-- test/integration/deployment/util.go | 52 +++++- test/utils/deployment.go | 31 ++++ 7 files changed, 246 insertions(+), 185 deletions(-) diff --git a/pkg/controller/deployment/util/deployment_util.go b/pkg/controller/deployment/util/deployment_util.go index c9c11aa682f..4800497fda3 100644 --- a/pkg/controller/deployment/util/deployment_util.go +++ b/pkg/controller/deployment/util/deployment_util.go @@ -930,7 +930,7 @@ func IsSaturated(deployment *extensions.Deployment, rs *extensions.ReplicaSet) b // Returns error if polling timesout. func WaitForObservedDeployment(getDeploymentFunc func() (*extensions.Deployment, error), desiredGeneration int64, interval, timeout time.Duration) error { // TODO: This should take clientset.Interface when all code is updated to use clientset. Keeping it this way allows the function to be used by callers who have client.Interface. - return wait.Poll(interval, timeout, func() (bool, error) { + return wait.PollImmediate(interval, timeout, func() (bool, error) { deployment, err := getDeploymentFunc() if err != nil { return false, err diff --git a/test/e2e/apps/deployment.go b/test/e2e/apps/deployment.go index 79e723ef1f7..62c173a3d43 100644 --- a/test/e2e/apps/deployment.go +++ b/test/e2e/apps/deployment.go @@ -84,9 +84,6 @@ var _ = SIGDescribe("Deployment", func() { It("deployment should support rollover", func() { testRolloverDeployment(f) }) - It("paused deployment should be ignored by the controller", func() { - testPausedDeployment(f) - }) It("deployment should support rollback", func() { testRollbackDeployment(f) }) @@ -96,9 +93,6 @@ var _ = SIGDescribe("Deployment", func() { It("deployment should label adopted RSs and pods", func() { testDeploymentLabelAdopted(f) }) - It("paused deployment should be able to scale", func() { - testScalePausedDeployment(f) - }) It("scaled rollout deployment should not block on annotation check", func() { testScaledRolloutDeployment(f) }) @@ -523,93 +517,6 @@ func ensureReplicas(rs *extensions.ReplicaSet, replicas int32) { Expect(rs.Status.Replicas).Should(Equal(replicas)) } -// TODO: Can be moved to a unit test. -func testPausedDeployment(f *framework.Framework) { - ns := f.Namespace.Name - c := f.ClientSet - deploymentName := "test-paused-deployment" - podLabels := map[string]string{"name": NginxImageName} - d := framework.NewDeployment(deploymentName, 1, podLabels, NginxImageName, NginxImage, extensions.RollingUpdateDeploymentStrategyType) - d.Spec.Paused = true - tgps := int64(1) - d.Spec.Template.Spec.TerminationGracePeriodSeconds = &tgps - framework.Logf("Creating paused deployment %s", deploymentName) - _, err := c.Extensions().Deployments(ns).Create(d) - Expect(err).NotTo(HaveOccurred()) - // Check that deployment is created fine. - deployment, err := c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{}) - Expect(err).NotTo(HaveOccurred()) - - // Verify that there is no latest state realized for the new deployment. - rs, err := deploymentutil.GetNewReplicaSet(deployment, c.ExtensionsV1beta1()) - Expect(err).NotTo(HaveOccurred()) - Expect(rs).To(Equal(nilRs)) - - // Update the deployment to run - deployment, err = framework.UpdateDeploymentWithRetries(c, ns, d.Name, func(update *extensions.Deployment) { - update.Spec.Paused = false - }) - Expect(err).NotTo(HaveOccurred()) - - // Use observedGeneration to determine if the controller noticed the resume. - err = framework.WaitForObservedDeployment(c, ns, deploymentName, deployment.Generation) - Expect(err).NotTo(HaveOccurred()) - - selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector) - Expect(err).NotTo(HaveOccurred()) - - opts := metav1.ListOptions{LabelSelector: selector.String()} - w, err := c.Extensions().ReplicaSets(ns).Watch(opts) - Expect(err).NotTo(HaveOccurred()) - - select { - case <-w.ResultChan(): - // this is it - case <-time.After(time.Minute): - err = fmt.Errorf("expected a new replica set to be created") - Expect(err).NotTo(HaveOccurred()) - } - - // Pause the deployment and delete the replica set. - // The paused deployment shouldn't recreate a new one. - deployment, err = framework.UpdateDeploymentWithRetries(c, ns, d.Name, func(update *extensions.Deployment) { - update.Spec.Paused = true - }) - Expect(err).NotTo(HaveOccurred()) - - // Use observedGeneration to determine if the controller noticed the pause. - err = framework.WaitForObservedDeployment(c, ns, deploymentName, deployment.Generation) - Expect(err).NotTo(HaveOccurred()) - - // Update the deployment template - the new replicaset should stay the same - framework.Logf("Updating paused deployment %q", deploymentName) - newTGPS := int64(0) - deployment, err = framework.UpdateDeploymentWithRetries(c, ns, d.Name, func(update *extensions.Deployment) { - update.Spec.Template.Spec.TerminationGracePeriodSeconds = &newTGPS - }) - Expect(err).NotTo(HaveOccurred()) - - err = framework.WaitForObservedDeployment(c, ns, deploymentName, deployment.Generation) - Expect(err).NotTo(HaveOccurred()) - - framework.Logf("Looking for new replicaset for paused deployment %q (there should be none)", deploymentName) - newRS, err := deploymentutil.GetNewReplicaSet(deployment, c.ExtensionsV1beta1()) - Expect(err).NotTo(HaveOccurred()) - Expect(newRS).To(Equal(nilRs)) - - _, allOldRs, err := deploymentutil.GetOldReplicaSets(deployment, c.ExtensionsV1beta1()) - Expect(err).NotTo(HaveOccurred()) - if len(allOldRs) != 1 { - err = fmt.Errorf("expected an old replica set") - Expect(err).NotTo(HaveOccurred()) - } - framework.Logf("Comparing deployment diff with old replica set %q", allOldRs[0].Name) - if *allOldRs[0].Spec.Template.Spec.TerminationGracePeriodSeconds == newTGPS { - err = fmt.Errorf("TerminationGracePeriodSeconds on the replica set should be %d but is %d", tgps, newTGPS) - Expect(err).NotTo(HaveOccurred()) - } -} - // testRollbackDeployment tests that a deployment is created (revision 1) and updated (revision 2), and // then rollback to revision 1 (should update template to revision 1, and then update revision 1 to 3), // and then rollback to last revision. @@ -904,57 +811,6 @@ func testDeploymentLabelAdopted(f *framework.Framework) { Expect(int32(len(pods.Items))).Should(Equal(replicas)) } -// TODO: Can be moved to a unit test. -func testScalePausedDeployment(f *framework.Framework) { - ns := f.Namespace.Name - c := f.ClientSet - - podLabels := map[string]string{"name": NginxImageName} - replicas := int32(0) - - // Create a nginx deployment. - deploymentName := "nginx-deployment" - d := framework.NewDeployment(deploymentName, replicas, podLabels, NginxImageName, NginxImage, extensions.RollingUpdateDeploymentStrategyType) - framework.Logf("Creating deployment %q", deploymentName) - _, err := c.Extensions().Deployments(ns).Create(d) - Expect(err).NotTo(HaveOccurred()) - - // Check that deployment is created fine. - deployment, err := c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{}) - Expect(err).NotTo(HaveOccurred()) - - err = framework.WaitForObservedDeployment(c, ns, deploymentName, deployment.Generation) - Expect(err).NotTo(HaveOccurred()) - - framework.Logf("Waiting for deployment %q to have no running pods", deploymentName) - Expect(framework.WaitForDeploymentUpdatedReplicasLTE(c, ns, deploymentName, replicas, deployment.Generation)) - - // Pause the deployment and try to scale it. - framework.Logf("Pause deployment %q before scaling it up", deploymentName) - deployment, err = framework.UpdateDeploymentWithRetries(c, ns, d.Name, func(update *extensions.Deployment) { - update.Spec.Paused = true - }) - Expect(err).NotTo(HaveOccurred()) - - err = framework.WaitForObservedDeployment(c, ns, deploymentName, deployment.Generation) - Expect(err).NotTo(HaveOccurred()) - - // Scale the paused deployment. - framework.Logf("Scaling up the paused deployment %q", deploymentName) - newReplicas := int32(1) - deployment, err = framework.UpdateDeploymentWithRetries(c, ns, deployment.Name, func(update *extensions.Deployment) { - update.Spec.Replicas = &newReplicas - }) - Expect(err).NotTo(HaveOccurred()) - - err = framework.WaitForObservedDeployment(c, ns, deploymentName, deployment.Generation) - Expect(err).NotTo(HaveOccurred()) - - rs, err := deploymentutil.GetNewReplicaSet(deployment, c.ExtensionsV1beta1()) - Expect(err).NotTo(HaveOccurred()) - Expect(*(rs.Spec.Replicas)).Should(Equal(newReplicas)) -} - func testScaledRolloutDeployment(f *framework.Framework) { ns := f.Namespace.Name c := f.ClientSet diff --git a/test/e2e/framework/deployment_util.go b/test/e2e/framework/deployment_util.go index e4da5014d9a..5146f8d3178 100644 --- a/test/e2e/framework/deployment_util.go +++ b/test/e2e/framework/deployment_util.go @@ -35,29 +35,8 @@ import ( testutils "k8s.io/kubernetes/test/utils" ) -type updateDeploymentFunc func(d *extensions.Deployment) - -func UpdateDeploymentWithRetries(c clientset.Interface, namespace, name string, applyUpdate updateDeploymentFunc) (*extensions.Deployment, error) { - var deployment *extensions.Deployment - var updateErr error - pollErr := wait.PollImmediate(1*time.Second, 1*time.Minute, func() (bool, error) { - var err error - if deployment, err = c.Extensions().Deployments(namespace).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 = c.Extensions().Deployments(namespace).Update(deployment); err == nil { - Logf("Updating deployment %s", name) - return true, nil - } - updateErr = err - return false, nil - }) - if pollErr == wait.ErrWaitTimeout { - pollErr = fmt.Errorf("couldn't apply the provided updated to deployment %q: %v", name, updateErr) - } - return deployment, pollErr +func UpdateDeploymentWithRetries(c clientset.Interface, namespace, name string, applyUpdate testutils.UpdateDeploymentFunc) (*extensions.Deployment, error) { + return testutils.UpdateDeploymentWithRetries(c, namespace, name, applyUpdate, Logf) } // Waits for the deployment to clean up old rcs. @@ -90,9 +69,7 @@ func logReplicaSetsOfDeployment(deployment *extensions.Deployment, allOldRSs []* } func WaitForObservedDeployment(c clientset.Interface, ns, deploymentName string, desiredGeneration int64) error { - return deploymentutil.WaitForObservedDeployment(func() (*extensions.Deployment, error) { - return c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{}) - }, desiredGeneration, Poll, 1*time.Minute) + return testutils.WaitForObservedDeployment(c, ns, deploymentName, desiredGeneration) } func WaitForDeploymentWithCondition(c clientset.Interface, ns, deploymentName, reason string, condType extensions.DeploymentConditionType) error { diff --git a/test/integration/deployment/BUILD b/test/integration/deployment/BUILD index 8f1f8e8a9c4..1262c075011 100644 --- a/test/integration/deployment/BUILD +++ b/test/integration/deployment/BUILD @@ -18,6 +18,7 @@ go_test( "//pkg/controller/deployment/util:go_default_library", "//test/integration/framework:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", ], ) @@ -28,6 +29,7 @@ go_library( deps = [ "//pkg/api/v1/pod:go_default_library", "//pkg/controller/deployment:go_default_library", + "//pkg/controller/deployment/util:go_default_library", "//pkg/controller/replicaset:go_default_library", "//test/integration/framework:go_default_library", "//test/utils:go_default_library", diff --git a/test/integration/deployment/deployment_test.go b/test/integration/deployment/deployment_test.go index 7e6ef012d7a..0c59309e78c 100644 --- a/test/integration/deployment/deployment_test.go +++ b/test/integration/deployment/deployment_test.go @@ -22,6 +22,7 @@ import ( "testing" "k8s.io/api/core/v1" + "k8s.io/api/extensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" "k8s.io/kubernetes/test/integration/framework" @@ -39,9 +40,10 @@ func TestNewDeployment(t *testing.T) { tester.deployment.Spec.MinReadySeconds = 4 tester.deployment.Annotations = map[string]string{"test": "should-copy-to-replica-set", v1.LastAppliedConfigAnnotation: "should-not-copy-to-replica-set"} - deploy, err := c.Extensions().Deployments(ns.Name).Create(tester.deployment) + var err error + tester.deployment, err = c.ExtensionsV1beta1().Deployments(ns.Name).Create(tester.deployment) if err != nil { - t.Fatalf("failed to create deployment %s: %v", deploy.Name, err) + t.Fatalf("failed to create deployment %s: %v", tester.deployment.Name, err) } // Start informer and controllers @@ -52,24 +54,18 @@ func TestNewDeployment(t *testing.T) { go dc.Run(5, stopCh) // Wait for the Deployment to be updated to revision 1 - err = tester.waitForDeploymentRevisionAndImage("1", fakeImage) - if err != nil { - t.Fatalf("failed to wait for Deployment revision %s: %v", deploy.Name, err) - } + tester.waitForDeploymentRevisionAndImage("1", fakeImage) // Make sure the Deployment status becomes valid while manually marking Deployment pods as ready at the same time tester.waitForDeploymentStatusValidAndMarkPodsReady() // Check new RS annotations - newRS, err := deploymentutil.GetNewReplicaSet(deploy, c.ExtensionsV1beta1()) - if err != nil { - t.Fatalf("failed to get new ReplicaSet of Deployment %s: %v", deploy.Name, err) - } + newRS := tester.expectNewReplicaSet() if newRS.Annotations["test"] != "should-copy-to-replica-set" { - t.Errorf("expected new ReplicaSet annotations copied from Deployment %s, got: %v", deploy.Name, newRS.Annotations) + t.Errorf("expected new ReplicaSet annotations copied from Deployment %s, got: %v", tester.deployment.Name, newRS.Annotations) } if newRS.Annotations[v1.LastAppliedConfigAnnotation] != "" { - t.Errorf("expected new ReplicaSet last-applied annotation not copied from Deployment %s", deploy.Name) + t.Errorf("expected new ReplicaSet last-applied annotation not copied from Deployment %s", tester.deployment.Name) } } @@ -134,3 +130,154 @@ func TestDeploymentSelectorImmutability(t *testing.T) { t.Errorf("error message does not match, expected type: %s, expected detail: %s, got: %s", expectedErrType, expectedErrDetail, err.Error()) } } + +// Paused deployment should not start new rollout +func TestPausedDeployment(t *testing.T) { + s, closeFn, rm, dc, informers, c := dcSetup(t) + defer closeFn() + name := "test-paused-deployment" + ns := framework.CreateTestingNamespace(name, s, t) + defer framework.DeleteTestingNamespace(ns, s, t) + + replicas := int32(1) + tester := &deploymentTester{t: t, c: c, deployment: newDeployment(name, ns.Name, replicas)} + tester.deployment.Spec.Paused = true + tgps := int64(1) + tester.deployment.Spec.Template.Spec.TerminationGracePeriodSeconds = &tgps + + var err error + tester.deployment, err = c.ExtensionsV1beta1().Deployments(ns.Name).Create(tester.deployment) + if err != nil { + t.Fatalf("failed to create deployment %s: %v", tester.deployment.Name, err) + } + + // Start informer and controllers + stopCh := make(chan struct{}) + defer close(stopCh) + informers.Start(stopCh) + go rm.Run(5, stopCh) + go dc.Run(5, stopCh) + + // Verify that the paused deployment won't create new replica set. + tester.expectNoNewReplicaSet() + + // Resume the deployment + tester.deployment, err = tester.updateDeployment(resumeFn()) + if err != nil { + t.Errorf("failed to resume deployment %s: %v", tester.deployment.Name, err) + } + + // Wait for the controller to notice the resume. + tester.waitForObservedDeployment(tester.deployment.Generation) + + // Wait for the Deployment to be updated to revision 1 + tester.waitForDeploymentRevisionAndImage("1", fakeImage) + + // Make sure the Deployment status becomes valid while manually marking Deployment pods as ready at the same time + tester.waitForDeploymentStatusValidAndMarkPodsReady() + + // A new replicaset should be created. + tester.expectNewReplicaSet() + + // Pause the deployment. + // The paused deployment shouldn't trigger a new rollout. + tester.deployment, err = tester.updateDeployment(pauseFn()) + if err != nil { + t.Errorf("failed to pause deployment %s: %v", tester.deployment.Name, err) + } + + // Wait for the controller to notice the pause. + tester.waitForObservedDeployment(tester.deployment.Generation) + + // Update the deployment template + newTGPS := int64(0) + tester.deployment, err = tester.updateDeployment(func(update *v1beta1.Deployment) { + update.Spec.Template.Spec.TerminationGracePeriodSeconds = &newTGPS + }) + if err != nil { + t.Errorf("failed updating deployment %s: %v", tester.deployment.Name, err) + } + + // Wait for the controller to notice the rollout. + tester.waitForObservedDeployment(tester.deployment.Generation) + + // Verify that the paused deployment won't create new replica set. + tester.expectNoNewReplicaSet() + + _, allOldRs, err := deploymentutil.GetOldReplicaSets(tester.deployment, c.ExtensionsV1beta1()) + if err != nil { + t.Errorf("failed retrieving old replicasets of deployment %s: %v", tester.deployment.Name, err) + } + if len(allOldRs) != 1 { + t.Errorf("expected an old replica set, got %v", allOldRs) + } + if *allOldRs[0].Spec.Template.Spec.TerminationGracePeriodSeconds == newTGPS { + t.Errorf("TerminationGracePeriodSeconds on the replica set should be %d, got %d", tgps, newTGPS) + } +} + +// Paused deployment can be scaled +func TestScalePausedDeployment(t *testing.T) { + s, closeFn, rm, dc, informers, c := dcSetup(t) + defer closeFn() + name := "test-scale-paused-deployment" + ns := framework.CreateTestingNamespace(name, s, t) + defer framework.DeleteTestingNamespace(ns, s, t) + + replicas := int32(1) + tester := &deploymentTester{t: t, c: c, deployment: newDeployment(name, ns.Name, replicas)} + tgps := int64(1) + tester.deployment.Spec.Template.Spec.TerminationGracePeriodSeconds = &tgps + + var err error + tester.deployment, err = c.ExtensionsV1beta1().Deployments(ns.Name).Create(tester.deployment) + if err != nil { + t.Fatalf("failed to create deployment %s: %v", tester.deployment.Name, err) + } + + // Start informer and controllers + stopCh := make(chan struct{}) + defer close(stopCh) + informers.Start(stopCh) + go rm.Run(5, stopCh) + go dc.Run(5, stopCh) + + // Wait for the Deployment to be updated to revision 1 + tester.waitForDeploymentRevisionAndImage("1", fakeImage) + + // Make sure the Deployment status becomes valid while manually marking Deployment pods as ready at the same time + tester.waitForDeploymentStatusValidAndMarkPodsReady() + + // A new replicaset should be created. + tester.expectNewReplicaSet() + + // Pause the deployment. + tester.deployment, err = tester.updateDeployment(pauseFn()) + if err != nil { + t.Errorf("failed to pause deployment %s: %v", tester.deployment.Name, err) + } + + // Wait for the controller to notice the scale. + tester.waitForObservedDeployment(tester.deployment.Generation) + + // Scale the paused deployment. + newReplicas := int32(10) + tester.deployment, err = tester.updateDeployment(func(update *v1beta1.Deployment) { + update.Spec.Replicas = &newReplicas + }) + if err != nil { + t.Errorf("failed updating deployment %s: %v", tester.deployment.Name, err) + } + + // Wait for the controller to notice the scale. + tester.waitForObservedDeployment(tester.deployment.Generation) + + // Verify that the new replicaset is scaled. + rs := tester.expectNewReplicaSet() + if *rs.Spec.Replicas != newReplicas { + t.Errorf("expected new replicaset replicas = %d, got %d", newReplicas, *rs.Spec.Replicas) + } + + // Make sure the Deployment status becomes valid while manually marking Deployment pods as ready at the same time + tester.waitForDeploymentStatusValidAndMarkPodsReady() +} diff --git a/test/integration/deployment/util.go b/test/integration/deployment/util.go index 5180578b902..bd854751e68 100644 --- a/test/integration/deployment/util.go +++ b/test/integration/deployment/util.go @@ -30,6 +30,7 @@ import ( restclient "k8s.io/client-go/rest" podutil "k8s.io/kubernetes/pkg/api/v1/pod" "k8s.io/kubernetes/pkg/controller/deployment" + deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" "k8s.io/kubernetes/pkg/controller/replicaset" "k8s.io/kubernetes/test/integration/framework" testutil "k8s.io/kubernetes/test/utils" @@ -143,8 +144,10 @@ func addPodConditionReady(pod *v1.Pod, time metav1.Time) { } } -func (d *deploymentTester) waitForDeploymentRevisionAndImage(revision, image string) error { - return testutil.WaitForDeploymentRevisionAndImage(d.c, d.deployment.Namespace, d.deployment.Name, revision, image, d.t.Logf, pollInterval, pollTimeout) +func (d *deploymentTester) waitForDeploymentRevisionAndImage(revision, image string) { + if err := testutil.WaitForDeploymentRevisionAndImage(d.c, d.deployment.Namespace, d.deployment.Name, revision, image, d.t.Logf, pollInterval, pollTimeout); err != nil { + d.t.Fatalf("failed to wait for Deployment revision %s: %v", d.deployment.Name, err) + } } // markAllPodsReady manually updates all Deployment pods status to ready @@ -201,3 +204,48 @@ func (d *deploymentTester) waitForDeploymentStatusValidAndMarkPodsReady() { d.t.Fatalf("failed to wait for Deployment status %s: %v", d.deployment.Name, err) } } + +func (d *deploymentTester) updateDeployment(applyUpdate testutil.UpdateDeploymentFunc) (*v1beta1.Deployment, error) { + return testutil.UpdateDeploymentWithRetries(d.c, d.deployment.Namespace, d.deployment.Name, applyUpdate, d.t.Logf) +} + +func (d *deploymentTester) waitForObservedDeployment(desiredGeneration int64) { + if err := testutil.WaitForObservedDeployment(d.c, d.deployment.Namespace, d.deployment.Name, desiredGeneration); err != nil { + d.t.Fatalf("failed waiting for ObservedGeneration of deployment %s to become %d: %v", d.deployment.Name, desiredGeneration, err) + } +} + +func (d *deploymentTester) getNewReplicaSet() *v1beta1.ReplicaSet { + rs, err := deploymentutil.GetNewReplicaSet(d.deployment, d.c.ExtensionsV1beta1()) + if err != nil { + d.t.Fatalf("failed retrieving new replicaset of deployment %s: %v", d.deployment.Name, err) + } + return rs +} + +func (d *deploymentTester) expectNoNewReplicaSet() { + rs := d.getNewReplicaSet() + if rs != nil { + d.t.Fatalf("expected deployment %s not to create a new replicaset, got %v", d.deployment.Name, rs) + } +} + +func (d *deploymentTester) expectNewReplicaSet() *v1beta1.ReplicaSet { + rs := d.getNewReplicaSet() + if rs == nil { + d.t.Fatalf("expected deployment %s to create a new replicaset, got nil", d.deployment.Name) + } + return rs +} + +func pauseFn() func(update *v1beta1.Deployment) { + return func(update *v1beta1.Deployment) { + update.Spec.Paused = true + } +} + +func resumeFn() func(update *v1beta1.Deployment) { + return func(update *v1beta1.Deployment) { + update.Spec.Paused = false + } +} diff --git a/test/utils/deployment.go b/test/utils/deployment.go index abcff858d97..9d0ff50d49b 100644 --- a/test/utils/deployment.go +++ b/test/utils/deployment.go @@ -213,3 +213,34 @@ func containsImage(containers []v1.Container, imageName string) bool { } return false } + +type UpdateDeploymentFunc func(d *extensions.Deployment) + +func UpdateDeploymentWithRetries(c clientset.Interface, namespace, name string, applyUpdate UpdateDeploymentFunc, logf LogfFn) (*extensions.Deployment, error) { + var deployment *extensions.Deployment + var updateErr error + pollErr := wait.PollImmediate(1*time.Second, 1*time.Minute, func() (bool, error) { + var err error + if deployment, err = c.Extensions().Deployments(namespace).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 = c.Extensions().Deployments(namespace).Update(deployment); err == nil { + logf("Updating deployment %s", name) + return true, nil + } + updateErr = err + return false, nil + }) + if pollErr == wait.ErrWaitTimeout { + pollErr = fmt.Errorf("couldn't apply the provided updated to deployment %q: %v", name, updateErr) + } + return deployment, pollErr +} + +func WaitForObservedDeployment(c clientset.Interface, ns, deploymentName string, desiredGeneration int64) error { + return deploymentutil.WaitForObservedDeployment(func() (*extensions.Deployment, error) { + return c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{}) + }, desiredGeneration, 2*time.Second, 1*time.Minute) +}