mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #6996 from ironcladlou/rolling-updater-cleanup-policy
Add cleanup policy to RollingUpdater
This commit is contained in:
commit
7d3c15dd4d
@ -134,7 +134,15 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
|
|||||||
if newRc.Spec.Replicas == 0 {
|
if newRc.Spec.Replicas == 0 {
|
||||||
newRc.Spec.Replicas = oldRc.Spec.Replicas
|
newRc.Spec.Replicas = oldRc.Spec.Replicas
|
||||||
}
|
}
|
||||||
err = updater.Update(out, oldRc, newRc, period, interval, timeout)
|
err = updater.Update(&kubectl.RollingUpdaterConfig{
|
||||||
|
Out: out,
|
||||||
|
OldRc: oldRc,
|
||||||
|
NewRc: newRc,
|
||||||
|
UpdatePeriod: period,
|
||||||
|
Interval: interval,
|
||||||
|
Timeout: timeout,
|
||||||
|
CleanupPolicy: kubectl.DeleteRollingUpdateCleanupPolicy,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,38 @@ type RollingUpdater struct {
|
|||||||
ns string
|
ns string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RollingUpdaterConfig is the configuration for a rolling deployment process.
|
||||||
|
type RollingUpdaterConfig struct {
|
||||||
|
// Out is a writer for progress output.
|
||||||
|
Out io.Writer
|
||||||
|
// OldRC is an existing controller to be replaced.
|
||||||
|
OldRc *api.ReplicationController
|
||||||
|
// NewRc is a controller that will take ownership of updated pods (will be
|
||||||
|
// created if needed).
|
||||||
|
NewRc *api.ReplicationController
|
||||||
|
// UpdatePeriod is the time to wait between individual pod updates.
|
||||||
|
UpdatePeriod time.Duration
|
||||||
|
// Interval is the time to wait between polling controller status after
|
||||||
|
// update.
|
||||||
|
Interval time.Duration
|
||||||
|
// Timeout is the time to wait for controller updates before giving up.
|
||||||
|
Timeout time.Duration
|
||||||
|
// CleanupPolicy defines the cleanup action to take after the deployment is
|
||||||
|
// complete.
|
||||||
|
CleanupPolicy RollingUpdaterCleanupPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
// RollingUpdaterCleanupPolicy is a cleanup action to take after the
|
||||||
|
// deployment is complete.
|
||||||
|
type RollingUpdaterCleanupPolicy string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DeleteRollingUpdateCleanupPolicy means delete the old controller.
|
||||||
|
DeleteRollingUpdateCleanupPolicy RollingUpdaterCleanupPolicy = "Delete"
|
||||||
|
// PreserveRollingUpdateCleanupPolicy means keep the old controller.
|
||||||
|
PreserveRollingUpdateCleanupPolicy RollingUpdaterCleanupPolicy = "Preserve"
|
||||||
|
)
|
||||||
|
|
||||||
// NewRollingUpdater creates a RollingUpdater from a client
|
// NewRollingUpdater creates a RollingUpdater from a client
|
||||||
func NewRollingUpdater(namespace string, c RollingUpdaterClient) *RollingUpdater {
|
func NewRollingUpdater(namespace string, c RollingUpdaterClient) *RollingUpdater {
|
||||||
return &RollingUpdater{
|
return &RollingUpdater{
|
||||||
@ -49,20 +81,25 @@ const (
|
|||||||
desiredReplicasAnnotation = kubectlAnnotationPrefix + "desired-replicas"
|
desiredReplicasAnnotation = kubectlAnnotationPrefix + "desired-replicas"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update all pods for a ReplicationController (oldRc) by creating a new controller (newRc)
|
// Update all pods for a ReplicationController (oldRc) by creating a new
|
||||||
// with 0 replicas, and synchronously resizing oldRc,newRc by 1 until oldRc has 0 replicas
|
// controller (newRc) with 0 replicas, and synchronously resizing oldRc,newRc
|
||||||
// and newRc has the original # of desired replicas. oldRc is then deleted.
|
// by 1 until oldRc has 0 replicas and newRc has the original # of desired
|
||||||
// If an update from newRc to oldRc is already in progress, we attempt to drive it to completion.
|
// replicas. Cleanup occurs based on a RollingUpdaterCleanupPolicy.
|
||||||
// If an error occurs at any step of the update, the error will be returned.
|
|
||||||
// 'out' writer for progress output
|
|
||||||
// 'oldRc' existing controller to be replaced
|
|
||||||
// 'newRc' controller that will take ownership of updated pods (will be created if needed)
|
|
||||||
// 'updatePeriod' time to wait between individual pod updates
|
|
||||||
// 'interval' time to wait between polling controller status after update
|
|
||||||
// 'timeout' time to wait for controller updates before giving up
|
|
||||||
//
|
//
|
||||||
// TODO: make this handle performing a rollback of a partially completed rollout.
|
// If an update from newRc to oldRc is already in progress, we attempt to
|
||||||
func (r *RollingUpdater) Update(out io.Writer, oldRc, newRc *api.ReplicationController, updatePeriod, interval, timeout time.Duration) error {
|
// drive it to completion. If an error occurs at any step of the update, the
|
||||||
|
// error will be returned.
|
||||||
|
//
|
||||||
|
// TODO: make this handle performing a rollback of a partially completed
|
||||||
|
// rollout.
|
||||||
|
func (r *RollingUpdater) Update(config *RollingUpdaterConfig) error {
|
||||||
|
out := config.Out
|
||||||
|
oldRc := config.OldRc
|
||||||
|
newRc := config.NewRc
|
||||||
|
updatePeriod := config.UpdatePeriod
|
||||||
|
interval := config.Interval
|
||||||
|
timeout := config.Timeout
|
||||||
|
|
||||||
oldName := oldRc.ObjectMeta.Name
|
oldName := oldRc.ObjectMeta.Name
|
||||||
newName := newRc.ObjectMeta.Name
|
newName := newRc.ObjectMeta.Name
|
||||||
retry := &RetryParams{interval, timeout}
|
retry := &RetryParams{interval, timeout}
|
||||||
@ -156,9 +193,17 @@ func (r *RollingUpdater) Update(out io.Writer, oldRc, newRc *api.ReplicationCont
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// delete old rc
|
|
||||||
fmt.Fprintf(out, "Update succeeded. Deleting %s\n", oldName)
|
switch config.CleanupPolicy {
|
||||||
return r.c.DeleteReplicationController(r.ns, oldName)
|
case DeleteRollingUpdateCleanupPolicy:
|
||||||
|
// delete old rc
|
||||||
|
fmt.Fprintf(out, "Update succeeded. Deleting %s\n", oldName)
|
||||||
|
return r.c.DeleteReplicationController(r.ns, oldName)
|
||||||
|
case PreserveRollingUpdateCleanupPolicy:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RollingUpdater) getExistingNewRc(sourceId, name string) (rc *api.ReplicationController, existing bool, err error) {
|
func (r *RollingUpdater) getExistingNewRc(sourceId, name string) (rc *api.ReplicationController, existing bool, err error) {
|
||||||
|
@ -19,12 +19,14 @@ package kubectl
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
||||||
)
|
)
|
||||||
|
|
||||||
type updaterFake struct {
|
type updaterFake struct {
|
||||||
@ -257,7 +259,16 @@ Update succeeded. Deleting foo-v1
|
|||||||
"default",
|
"default",
|
||||||
}
|
}
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
if err := updater.Update(&buffer, test.oldRc, test.newRc, 0, time.Millisecond, time.Millisecond); err != nil {
|
config := &RollingUpdaterConfig{
|
||||||
|
Out: &buffer,
|
||||||
|
OldRc: test.oldRc,
|
||||||
|
NewRc: test.newRc,
|
||||||
|
UpdatePeriod: 0,
|
||||||
|
Interval: time.Millisecond,
|
||||||
|
Timeout: time.Millisecond,
|
||||||
|
CleanupPolicy: DeleteRollingUpdateCleanupPolicy,
|
||||||
|
}
|
||||||
|
if err := updater.Update(config); err != nil {
|
||||||
t.Errorf("Update failed: %v", err)
|
t.Errorf("Update failed: %v", err)
|
||||||
}
|
}
|
||||||
if buffer.String() != test.output {
|
if buffer.String() != test.output {
|
||||||
@ -299,10 +310,101 @@ Update succeeded. Deleting foo-v1
|
|||||||
updater := RollingUpdater{NewRollingUpdaterClient(fakeClientFor("default", responses)), "default"}
|
updater := RollingUpdater{NewRollingUpdaterClient(fakeClientFor("default", responses)), "default"}
|
||||||
|
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
if err := updater.Update(&buffer, rc, rcExisting, 0, time.Millisecond, time.Millisecond); err != nil {
|
config := &RollingUpdaterConfig{
|
||||||
|
Out: &buffer,
|
||||||
|
OldRc: rc,
|
||||||
|
NewRc: rcExisting,
|
||||||
|
UpdatePeriod: 0,
|
||||||
|
Interval: time.Millisecond,
|
||||||
|
Timeout: time.Millisecond,
|
||||||
|
CleanupPolicy: DeleteRollingUpdateCleanupPolicy,
|
||||||
|
}
|
||||||
|
if err := updater.Update(config); err != nil {
|
||||||
t.Errorf("Update failed: %v", err)
|
t.Errorf("Update failed: %v", err)
|
||||||
}
|
}
|
||||||
if buffer.String() != output {
|
if buffer.String() != output {
|
||||||
t.Errorf("Output was not as expected. Expected:\n%s\nGot:\n%s", output, buffer.String())
|
t.Errorf("Output was not as expected. Expected:\n%s\nGot:\n%s", output, buffer.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRollingUpdater_preserveCleanup ensures that the old controller isn't
|
||||||
|
// deleted following a successful deployment.
|
||||||
|
func TestRollingUpdater_preserveCleanup(t *testing.T) {
|
||||||
|
rc := oldRc(2)
|
||||||
|
rcExisting := newRc(1, 3)
|
||||||
|
|
||||||
|
updater := &RollingUpdater{
|
||||||
|
ns: "default",
|
||||||
|
c: &rollingUpdaterClientImpl{
|
||||||
|
GetReplicationControllerFn: func(namespace, name string) (*api.ReplicationController, error) {
|
||||||
|
switch name {
|
||||||
|
case rc.Name:
|
||||||
|
return rc, nil
|
||||||
|
case rcExisting.Name:
|
||||||
|
return rcExisting, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unexpected get call for %s/%s", namespace, name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
UpdateReplicationControllerFn: func(namespace string, rc *api.ReplicationController) (*api.ReplicationController, error) {
|
||||||
|
return rc, nil
|
||||||
|
},
|
||||||
|
CreateReplicationControllerFn: func(namespace string, rc *api.ReplicationController) (*api.ReplicationController, error) {
|
||||||
|
t.Fatalf("unexpected call to create %s/rc:%#v", namespace, rc)
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
DeleteReplicationControllerFn: func(namespace, name string) error {
|
||||||
|
t.Fatalf("unexpected call to delete %s/%s", namespace, name)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
ControllerHasDesiredReplicasFn: func(rc *api.ReplicationController) wait.ConditionFunc {
|
||||||
|
return func() (done bool, err error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
config := &RollingUpdaterConfig{
|
||||||
|
Out: ioutil.Discard,
|
||||||
|
OldRc: rc,
|
||||||
|
NewRc: rcExisting,
|
||||||
|
UpdatePeriod: 0,
|
||||||
|
Interval: time.Millisecond,
|
||||||
|
Timeout: time.Millisecond,
|
||||||
|
CleanupPolicy: PreserveRollingUpdateCleanupPolicy,
|
||||||
|
}
|
||||||
|
err := updater.Update(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// rollingUpdaterClientImpl is a dynamic RollingUpdaterClient.
|
||||||
|
type rollingUpdaterClientImpl struct {
|
||||||
|
GetReplicationControllerFn func(namespace, name string) (*api.ReplicationController, error)
|
||||||
|
UpdateReplicationControllerFn func(namespace string, rc *api.ReplicationController) (*api.ReplicationController, error)
|
||||||
|
CreateReplicationControllerFn func(namespace string, rc *api.ReplicationController) (*api.ReplicationController, error)
|
||||||
|
DeleteReplicationControllerFn func(namespace, name string) error
|
||||||
|
ControllerHasDesiredReplicasFn func(rc *api.ReplicationController) wait.ConditionFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rollingUpdaterClientImpl) GetReplicationController(namespace, name string) (*api.ReplicationController, error) {
|
||||||
|
return c.GetReplicationControllerFn(namespace, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rollingUpdaterClientImpl) UpdateReplicationController(namespace string, rc *api.ReplicationController) (*api.ReplicationController, error) {
|
||||||
|
return c.UpdateReplicationControllerFn(namespace, rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rollingUpdaterClientImpl) CreateReplicationController(namespace string, rc *api.ReplicationController) (*api.ReplicationController, error) {
|
||||||
|
return c.CreateReplicationControllerFn(namespace, rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rollingUpdaterClientImpl) DeleteReplicationController(namespace, name string) error {
|
||||||
|
return c.DeleteReplicationControllerFn(namespace, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *rollingUpdaterClientImpl) ControllerHasDesiredReplicas(rc *api.ReplicationController) wait.ConditionFunc {
|
||||||
|
return c.ControllerHasDesiredReplicasFn(rc)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user