extensions: support paused deployments
This commit adds support for paused deployments so a user can choose when to run a deployment that exists in the system instead of having the deployment controller automatically reconciling it after every change or sync interval.
This commit is contained in:
		| @@ -228,6 +228,10 @@ type DeploymentSpec struct { | ||||
| 	// Value of this key is hash of DeploymentSpec.PodTemplateSpec. | ||||
| 	// No label is added if this is set to empty string. | ||||
| 	UniqueLabelKey string `json:"uniqueLabelKey,omitempty"` | ||||
|  | ||||
| 	// Indicates that the deployment is paused and will not be processed by the | ||||
| 	// deployment controller. | ||||
| 	Paused bool `json:"paused,omitempty"` | ||||
| } | ||||
|  | ||||
| const ( | ||||
|   | ||||
| @@ -262,6 +262,7 @@ func Convert_extensions_DeploymentSpec_To_v1beta1_DeploymentSpec(in *extensions. | ||||
| 	} | ||||
| 	out.UniqueLabelKey = new(string) | ||||
| 	*out.UniqueLabelKey = in.UniqueLabelKey | ||||
| 	out.Paused = in.Paused | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @@ -289,6 +290,7 @@ func Convert_v1beta1_DeploymentSpec_To_extensions_DeploymentSpec(in *DeploymentS | ||||
| 	if in.UniqueLabelKey != nil { | ||||
| 		out.UniqueLabelKey = *in.UniqueLabelKey | ||||
| 	} | ||||
| 	out.Paused = in.Paused | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -213,6 +213,10 @@ type DeploymentSpec struct { | ||||
| 	// Value of this key is hash of DeploymentSpec.PodTemplateSpec. | ||||
| 	// No label is added if this is set to empty string. | ||||
| 	UniqueLabelKey *string `json:"uniqueLabelKey,omitempty"` | ||||
|  | ||||
| 	// Indicates that the deployment is paused and will not be processed by the | ||||
| 	// deployment controller. | ||||
| 	Paused bool `json:"paused,omitempty"` | ||||
| } | ||||
|  | ||||
| const ( | ||||
|   | ||||
| @@ -410,6 +410,11 @@ func (dc *DeploymentController) syncDeployment(key string) error { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if d.Spec.Paused { | ||||
| 		// Ignore paused deployments | ||||
| 		glog.V(4).Infof("Ignoring paused deployment %s/%s", d.Namespace, d.Name) | ||||
| 		return nil | ||||
| 	} | ||||
| 	switch d.Spec.Strategy.Type { | ||||
| 	case extensions.RecreateDeploymentStrategyType: | ||||
| 		return dc.syncRecreateDeployment(d) | ||||
|   | ||||
| @@ -75,21 +75,6 @@ func validNewDeployment() *extensions.Deployment { | ||||
|  | ||||
| var validDeployment = *validNewDeployment() | ||||
|  | ||||
| func validNewScale() *extensions.Scale { | ||||
| 	return &extensions.Scale{ | ||||
| 		ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespace}, | ||||
| 		Spec: extensions.ScaleSpec{ | ||||
| 			Replicas: validDeployment.Spec.Replicas, | ||||
| 		}, | ||||
| 		Status: extensions.ScaleStatus{ | ||||
| 			Replicas: validDeployment.Status.Replicas, | ||||
| 			Selector: validDeployment.Spec.Template.Labels, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var validScale = *validNewScale() | ||||
|  | ||||
| func TestCreate(t *testing.T) { | ||||
| 	storage, server := newStorage(t) | ||||
| 	defer server.Terminate(t) | ||||
| @@ -192,6 +177,21 @@ func TestWatch(t *testing.T) { | ||||
| 	) | ||||
| } | ||||
|  | ||||
| func validNewScale() *extensions.Scale { | ||||
| 	return &extensions.Scale{ | ||||
| 		ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespace}, | ||||
| 		Spec: extensions.ScaleSpec{ | ||||
| 			Replicas: validDeployment.Spec.Replicas, | ||||
| 		}, | ||||
| 		Status: extensions.ScaleStatus{ | ||||
| 			Replicas: validDeployment.Status.Replicas, | ||||
| 			Selector: validDeployment.Spec.Template.Labels, | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var validScale = *validNewScale() | ||||
|  | ||||
| func TestScaleGet(t *testing.T) { | ||||
| 	storage, server := newStorage(t) | ||||
| 	defer server.Terminate(t) | ||||
| @@ -204,10 +204,10 @@ func TestScaleGet(t *testing.T) { | ||||
|  | ||||
| 	expect := &validScale | ||||
| 	obj, err := storage.Scale.Get(ctx, name) | ||||
| 	scale := obj.(*extensions.Scale) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error: %v", err) | ||||
| 	} | ||||
| 	scale := obj.(*extensions.Scale) | ||||
| 	if e, a := expect, scale; !api.Semantic.DeepDerivative(e, a) { | ||||
| 		t.Errorf("unexpected scale: %s", util.ObjectDiff(e, a)) | ||||
| 	} | ||||
| @@ -216,6 +216,7 @@ func TestScaleGet(t *testing.T) { | ||||
| func TestScaleUpdate(t *testing.T) { | ||||
| 	storage, server := newStorage(t) | ||||
| 	defer server.Terminate(t) | ||||
|  | ||||
| 	ctx := api.WithNamespace(api.NewContext(), namespace) | ||||
| 	key := etcdtest.AddPrefix("/deployments/" + namespace + "/" + name) | ||||
| 	if err := storage.Deployment.Storage.Set(ctx, key, &validDeployment, nil, 0); err != nil { | ||||
| @@ -232,12 +233,11 @@ func TestScaleUpdate(t *testing.T) { | ||||
| 	if _, _, err := storage.Scale.Update(ctx, &update); err != nil { | ||||
| 		t.Fatalf("unexpected error: %v", err) | ||||
| 	} | ||||
| 	obj, err := storage.Scale.Get(ctx, name) | ||||
| 	obj, err := storage.Deployment.Get(ctx, name) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	deployment := obj.(*extensions.Scale) | ||||
| 	deployment := obj.(*extensions.Deployment) | ||||
| 	if deployment.Spec.Replicas != replicas { | ||||
| 		t.Errorf("wrong replicas count expected: %d got: %d", replicas, deployment.Spec.Replicas) | ||||
| 	} | ||||
|   | ||||
| @@ -18,9 +18,11 @@ package e2e | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"time" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/api" | ||||
| 	"k8s.io/kubernetes/pkg/apis/extensions" | ||||
| 	"k8s.io/kubernetes/pkg/labels" | ||||
| 	deploymentutil "k8s.io/kubernetes/pkg/util/deployment" | ||||
| 	"k8s.io/kubernetes/pkg/util/intstr" | ||||
|  | ||||
| @@ -46,6 +48,9 @@ var _ = Describe("Deployment", func() { | ||||
| 	It("deployment should support rollover [Flaky]", func() { | ||||
| 		testRolloverDeployment(f) | ||||
| 	}) | ||||
| 	It("paused deployment should be ignored by the controller", func() { | ||||
| 		testPausedDeployment(f) | ||||
| 	}) | ||||
| }) | ||||
|  | ||||
| func newRC(rcName string, replicas int, rcPodLabels map[string]string, imageName string, image string) *api.ReplicationController { | ||||
| @@ -390,3 +395,74 @@ func testRolloverDeployment(f *Framework) { | ||||
| 	newRC, err = deploymentutil.GetNewRC(*deployment, c) | ||||
| 	Expect(newRC.Spec.Template.Spec.Containers[0].Image).Should(Equal(updatedDeploymentImage)) | ||||
| } | ||||
|  | ||||
| func testPausedDeployment(f *Framework) { | ||||
| 	ns := f.Namespace.Name | ||||
| 	c := f.Client | ||||
| 	deploymentName := "nginx" | ||||
| 	podLabels := map[string]string{"name": "nginx"} | ||||
| 	d := newDeployment(deploymentName, 1, podLabels, "nginx", "nginx", extensions.RollingUpdateDeploymentStrategyType) | ||||
| 	d.Spec.Paused = true | ||||
| 	Logf("Creating paused deployment %s", deploymentName) | ||||
| 	_, err := c.Deployments(ns).Create(d) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| 	defer func() { | ||||
| 		_, err := c.Deployments(ns).Get(deploymentName) | ||||
| 		Expect(err).NotTo(HaveOccurred()) | ||||
| 		Logf("deleting deployment %s", deploymentName) | ||||
| 		Expect(c.Deployments(ns).Delete(deploymentName, nil)).NotTo(HaveOccurred()) | ||||
| 	}() | ||||
| 	// Check that deployment is created fine. | ||||
| 	deployment, err := c.Deployments(ns).Get(deploymentName) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	// Verify that there is no latest state realized for the new deployment. | ||||
| 	rc, err := deploymentutil.GetNewRC(*deployment, c) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| 	if rc != nil { | ||||
| 		err = fmt.Errorf("unexpected new rc/%s for deployment/%s", rc.Name, deployment.Name) | ||||
| 		Expect(err).NotTo(HaveOccurred()) | ||||
| 	} | ||||
|  | ||||
| 	// Update the deployment to run | ||||
| 	deployment.Spec.Paused = false | ||||
| 	deployment, err = c.Deployments(ns).Update(deployment) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	opts := api.ListOptions{LabelSelector: labels.Set(deployment.Spec.Selector).AsSelector()} | ||||
| 	w, err := c.ReplicationControllers(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 rc to be created") | ||||
| 		Expect(err).NotTo(HaveOccurred()) | ||||
| 	} | ||||
|  | ||||
| 	// Pause the deployment and delete the replication controller. | ||||
| 	// The paused deployment shouldn't recreate a new one. | ||||
| 	deployment.Spec.Paused = true | ||||
| 	deployment.ResourceVersion = "" | ||||
| 	deployment, err = c.Deployments(ns).Update(deployment) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	newRC, err := deploymentutil.GetNewRC(*deployment, c) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| 	Expect(c.ReplicationControllers(ns).Delete(newRC.Name)).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	deployment, err = c.Deployments(ns).Get(deploymentName) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
|  | ||||
| 	if !deployment.Spec.Paused { | ||||
| 		err = fmt.Errorf("deployment %q should be paused", deployment.Name) | ||||
| 		Expect(err).NotTo(HaveOccurred()) | ||||
| 	} | ||||
| 	shouldBeNil, err := deploymentutil.GetNewRC(*deployment, c) | ||||
| 	Expect(err).NotTo(HaveOccurred()) | ||||
| 	if shouldBeNil != nil { | ||||
| 		err = fmt.Errorf("deployment %q shouldn't have a rc but there is %q", deployment.Name, shouldBeNil.Name) | ||||
| 		Expect(err).NotTo(HaveOccurred()) | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Michail Kargakis
					Michail Kargakis