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:
Michail Kargakis 2016-01-21 11:12:58 +01:00
parent a14d0fd641
commit 436d2677f9
6 changed files with 110 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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