mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Move paused deployment e2e tests to integration
This commit is contained in:
parent
4b63c1fb90
commit
124344a1a4
@ -930,7 +930,7 @@ func IsSaturated(deployment *extensions.Deployment, rs *extensions.ReplicaSet) b
|
|||||||
// Returns error if polling timesout.
|
// Returns error if polling timesout.
|
||||||
func WaitForObservedDeployment(getDeploymentFunc func() (*extensions.Deployment, error), desiredGeneration int64, interval, timeout time.Duration) error {
|
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.
|
// 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()
|
deployment, err := getDeploymentFunc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -84,9 +84,6 @@ var _ = SIGDescribe("Deployment", func() {
|
|||||||
It("deployment should support rollover", func() {
|
It("deployment should support rollover", func() {
|
||||||
testRolloverDeployment(f)
|
testRolloverDeployment(f)
|
||||||
})
|
})
|
||||||
It("paused deployment should be ignored by the controller", func() {
|
|
||||||
testPausedDeployment(f)
|
|
||||||
})
|
|
||||||
It("deployment should support rollback", func() {
|
It("deployment should support rollback", func() {
|
||||||
testRollbackDeployment(f)
|
testRollbackDeployment(f)
|
||||||
})
|
})
|
||||||
@ -96,9 +93,6 @@ var _ = SIGDescribe("Deployment", func() {
|
|||||||
It("deployment should label adopted RSs and pods", func() {
|
It("deployment should label adopted RSs and pods", func() {
|
||||||
testDeploymentLabelAdopted(f)
|
testDeploymentLabelAdopted(f)
|
||||||
})
|
})
|
||||||
It("paused deployment should be able to scale", func() {
|
|
||||||
testScalePausedDeployment(f)
|
|
||||||
})
|
|
||||||
It("scaled rollout deployment should not block on annotation check", func() {
|
It("scaled rollout deployment should not block on annotation check", func() {
|
||||||
testScaledRolloutDeployment(f)
|
testScaledRolloutDeployment(f)
|
||||||
})
|
})
|
||||||
@ -523,93 +517,6 @@ func ensureReplicas(rs *extensions.ReplicaSet, replicas int32) {
|
|||||||
Expect(rs.Status.Replicas).Should(Equal(replicas))
|
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
|
// 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),
|
// then rollback to revision 1 (should update template to revision 1, and then update revision 1 to 3),
|
||||||
// and then rollback to last revision.
|
// and then rollback to last revision.
|
||||||
@ -904,57 +811,6 @@ func testDeploymentLabelAdopted(f *framework.Framework) {
|
|||||||
Expect(int32(len(pods.Items))).Should(Equal(replicas))
|
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) {
|
func testScaledRolloutDeployment(f *framework.Framework) {
|
||||||
ns := f.Namespace.Name
|
ns := f.Namespace.Name
|
||||||
c := f.ClientSet
|
c := f.ClientSet
|
||||||
|
@ -35,29 +35,8 @@ import (
|
|||||||
testutils "k8s.io/kubernetes/test/utils"
|
testutils "k8s.io/kubernetes/test/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type updateDeploymentFunc func(d *extensions.Deployment)
|
func UpdateDeploymentWithRetries(c clientset.Interface, namespace, name string, applyUpdate testutils.UpdateDeploymentFunc) (*extensions.Deployment, error) {
|
||||||
|
return testutils.UpdateDeploymentWithRetries(c, namespace, name, applyUpdate, Logf)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Waits for the deployment to clean up old rcs.
|
// 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 {
|
func WaitForObservedDeployment(c clientset.Interface, ns, deploymentName string, desiredGeneration int64) error {
|
||||||
return deploymentutil.WaitForObservedDeployment(func() (*extensions.Deployment, error) {
|
return testutils.WaitForObservedDeployment(c, ns, deploymentName, desiredGeneration)
|
||||||
return c.Extensions().Deployments(ns).Get(deploymentName, metav1.GetOptions{})
|
|
||||||
}, desiredGeneration, Poll, 1*time.Minute)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func WaitForDeploymentWithCondition(c clientset.Interface, ns, deploymentName, reason string, condType extensions.DeploymentConditionType) error {
|
func WaitForDeploymentWithCondition(c clientset.Interface, ns, deploymentName, reason string, condType extensions.DeploymentConditionType) error {
|
||||||
|
@ -18,6 +18,7 @@ go_test(
|
|||||||
"//pkg/controller/deployment/util:go_default_library",
|
"//pkg/controller/deployment/util:go_default_library",
|
||||||
"//test/integration/framework:go_default_library",
|
"//test/integration/framework:go_default_library",
|
||||||
"//vendor/k8s.io/api/core/v1: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",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -28,6 +29,7 @@ go_library(
|
|||||||
deps = [
|
deps = [
|
||||||
"//pkg/api/v1/pod:go_default_library",
|
"//pkg/api/v1/pod:go_default_library",
|
||||||
"//pkg/controller/deployment:go_default_library",
|
"//pkg/controller/deployment:go_default_library",
|
||||||
|
"//pkg/controller/deployment/util:go_default_library",
|
||||||
"//pkg/controller/replicaset:go_default_library",
|
"//pkg/controller/replicaset:go_default_library",
|
||||||
"//test/integration/framework:go_default_library",
|
"//test/integration/framework:go_default_library",
|
||||||
"//test/utils:go_default_library",
|
"//test/utils:go_default_library",
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
|
"k8s.io/api/extensions/v1beta1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
@ -39,9 +40,10 @@ func TestNewDeployment(t *testing.T) {
|
|||||||
tester.deployment.Spec.MinReadySeconds = 4
|
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"}
|
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 {
|
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
|
// Start informer and controllers
|
||||||
@ -52,24 +54,18 @@ func TestNewDeployment(t *testing.T) {
|
|||||||
go dc.Run(5, stopCh)
|
go dc.Run(5, stopCh)
|
||||||
|
|
||||||
// Wait for the Deployment to be updated to revision 1
|
// Wait for the Deployment to be updated to revision 1
|
||||||
err = tester.waitForDeploymentRevisionAndImage("1", fakeImage)
|
tester.waitForDeploymentRevisionAndImage("1", fakeImage)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to wait for Deployment revision %s: %v", deploy.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the Deployment status becomes valid while manually marking Deployment pods as ready at the same time
|
// Make sure the Deployment status becomes valid while manually marking Deployment pods as ready at the same time
|
||||||
tester.waitForDeploymentStatusValidAndMarkPodsReady()
|
tester.waitForDeploymentStatusValidAndMarkPodsReady()
|
||||||
|
|
||||||
// Check new RS annotations
|
// Check new RS annotations
|
||||||
newRS, err := deploymentutil.GetNewReplicaSet(deploy, c.ExtensionsV1beta1())
|
newRS := tester.expectNewReplicaSet()
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to get new ReplicaSet of Deployment %s: %v", deploy.Name, err)
|
|
||||||
}
|
|
||||||
if newRS.Annotations["test"] != "should-copy-to-replica-set" {
|
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] != "" {
|
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())
|
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()
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ import (
|
|||||||
restclient "k8s.io/client-go/rest"
|
restclient "k8s.io/client-go/rest"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
"k8s.io/kubernetes/pkg/controller/deployment"
|
"k8s.io/kubernetes/pkg/controller/deployment"
|
||||||
|
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
|
||||||
"k8s.io/kubernetes/pkg/controller/replicaset"
|
"k8s.io/kubernetes/pkg/controller/replicaset"
|
||||||
"k8s.io/kubernetes/test/integration/framework"
|
"k8s.io/kubernetes/test/integration/framework"
|
||||||
testutil "k8s.io/kubernetes/test/utils"
|
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 {
|
func (d *deploymentTester) waitForDeploymentRevisionAndImage(revision, image string) {
|
||||||
return testutil.WaitForDeploymentRevisionAndImage(d.c, d.deployment.Namespace, d.deployment.Name, revision, image, d.t.Logf, pollInterval, pollTimeout)
|
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
|
// 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)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -213,3 +213,34 @@ func containsImage(containers []v1.Container, imageName string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
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)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user