Move paused deployment e2e tests to integration

This commit is contained in:
Janet Kuo 2017-09-06 13:42:26 -07:00
parent 4b63c1fb90
commit 124344a1a4
7 changed files with 246 additions and 185 deletions

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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",

View File

@ -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()
}

View File

@ -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
}
}

View File

@ -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)
}