kubectl rolling-update support for same image

This commit is contained in:
Jeff Lowdermilk 2016-04-21 15:20:32 -07:00
parent e73606b974
commit 1baa473ef2
8 changed files with 64 additions and 20 deletions

View File

@ -1668,6 +1668,7 @@ _kubectl_rolling-update()
flags_with_completion+=("-f") flags_with_completion+=("-f")
flags_completion+=("__handle_filename_extension_flag json|yaml|yml") flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
flags+=("--image=") flags+=("--image=")
flags+=("--image-pull-policy=")
flags+=("--include-extended-apis") flags+=("--include-extended-apis")
flags+=("--no-headers") flags+=("--no-headers")
flags+=("--output=") flags+=("--output=")

View File

@ -42,6 +42,10 @@ existing replication controller and overwrite at least one (common) label in its
\fB\-\-image\fP="" \fB\-\-image\fP=""
Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with \-\-filename/\-f Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with \-\-filename/\-f
.PP
\fB\-\-image\-pull\-policy\fP=""
Explicit policy for when to pull container images. Required when \-\-image is same as existing image, ignored otherwise.
.PP .PP
\fB\-\-include\-extended\-apis\fP=true \fB\-\-include\-extended\-apis\fP=true
If true, include definitions of new APIs via calls to the API server. [default true] If true, include definitions of new APIs via calls to the API server. [default true]

View File

@ -78,6 +78,7 @@ kubectl rolling-update frontend-v1 frontend-v2 --rollback
--dry-run[=false]: If true, print out the changes that would be made, but don't actually make them. --dry-run[=false]: If true, print out the changes that would be made, but don't actually make them.
-f, --filename=[]: Filename or URL to file to use to create the new replication controller. -f, --filename=[]: Filename or URL to file to use to create the new replication controller.
--image="": Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f --image="": Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f
--image-pull-policy="": Explicit policy for when to pull container images. Required when --image is same as existing image, ignored otherwise.
--include-extended-apis[=true]: If true, include definitions of new APIs via calls to the API server. [default true] --include-extended-apis[=true]: If true, include definitions of new APIs via calls to the API server. [default true]
--no-headers[=false]: When using the default output, don't print headers. --no-headers[=false]: When using the default output, don't print headers.
-o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md]. -o, --output="": Output format. One of: json|yaml|wide|name|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md].
@ -126,7 +127,7 @@ kubectl rolling-update frontend-v1 frontend-v2 --rollback
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra on 5-Apr-2016 ###### Auto generated by spf13/cobra on 21-Apr-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS --> <!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rolling-update.md?pixel)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rolling-update.md?pixel)]()

View File

@ -26,6 +26,9 @@ options:
- name: image - name: image
usage: | usage: |
Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f
- name: image-pull-policy
usage: |
Explicit policy for when to pull container images. Required when --image is same as existing image, ignored otherwise.
- name: include-extended-apis - name: include-extended-apis
default_value: "true" default_value: "true"
usage: | usage: |

View File

@ -164,6 +164,7 @@ ignore-daemonsets
ignore-not-found ignore-not-found
image-gc-high-threshold image-gc-high-threshold
image-gc-low-threshold image-gc-low-threshold
image-pull-policy
include-extended-apis include-extended-apis
input-dirs input-dirs
insecure-bind-address insecure-bind-address

View File

@ -97,6 +97,7 @@ func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.MarkFlagRequired("image") cmd.MarkFlagRequired("image")
cmd.Flags().String("deployment-label-key", "deployment", "The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when --image is specified, ignored otherwise") cmd.Flags().String("deployment-label-key", "deployment", "The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when --image is specified, ignored otherwise")
cmd.Flags().String("container", "", "Container name which will have its image upgraded. Only relevant when --image is specified, ignored otherwise. Required when using --image on a multi-container pod") cmd.Flags().String("container", "", "Container name which will have its image upgraded. Only relevant when --image is specified, ignored otherwise. Required when using --image on a multi-container pod")
cmd.Flags().String("image-pull-policy", "", "Explicit policy for when to pull container images. Required when --image is same as existing image, ignored otherwise.")
cmd.Flags().Bool("dry-run", false, "If true, print out the changes that would be made, but don't actually make them.") cmd.Flags().Bool("dry-run", false, "If true, print out the changes that would be made, but don't actually make them.")
cmd.Flags().Bool("rollback", false, "If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout") cmd.Flags().Bool("rollback", false, "If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout")
cmdutil.AddValidateFlags(cmd) cmdutil.AddValidateFlags(cmd)
@ -150,6 +151,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
deploymentKey := cmdutil.GetFlagString(cmd, "deployment-label-key") deploymentKey := cmdutil.GetFlagString(cmd, "deployment-label-key")
filename := "" filename := ""
image := cmdutil.GetFlagString(cmd, "image") image := cmdutil.GetFlagString(cmd, "image")
pullPolicy := cmdutil.GetFlagString(cmd, "image-pull-policy")
oldName := args[0] oldName := args[0]
rollback := cmdutil.GetFlagBool(cmd, "rollback") rollback := cmdutil.GetFlagBool(cmd, "rollback")
period := cmdutil.GetFlagDuration(cmd, "update-period") period := cmdutil.GetFlagDuration(cmd, "update-period")
@ -233,8 +235,8 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
} }
} }
// If the --image option is specified, we need to create a new rc with at least one different selector // If the --image option is specified, we need to create a new rc with at least one different selector
// than the old rc. This selector is the hash of the rc, which will differ because the new rc has a // than the old rc. This selector is the hash of the rc, with a suffix to provide uniqueness for
// different image. // same-image updates.
if len(image) != 0 { if len(image) != 0 {
codec := api.Codecs.LegacyCodec(client.APIVersion()) codec := api.Codecs.LegacyCodec(client.APIVersion())
keepOldName = len(args) == 1 keepOldName = len(args) == 1
@ -248,10 +250,21 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
} }
fmt.Fprintf(out, "Found existing update in progress (%s), resuming.\n", newRc.Name) fmt.Fprintf(out, "Found existing update in progress (%s), resuming.\n", newRc.Name)
} else { } else {
if oldRc.Spec.Template.Spec.Containers[0].Image == image { config := &kubectl.NewControllerConfig{
return cmdutil.UsageError(cmd, "Specified --image must be distinct from existing container image") Namespace: cmdNamespace,
OldName: oldName,
NewName: newName,
Image: image,
Container: container,
DeploymentKey: deploymentKey,
} }
newRc, err = kubectl.CreateNewControllerFromCurrentController(client, codec, cmdNamespace, oldName, newName, image, container, deploymentKey) if oldRc.Spec.Template.Spec.Containers[0].Image == image {
if len(pullPolicy) == 0 {
return cmdutil.UsageError(cmd, "--image-pull-policy (Always|Never|IfNotPresent) must be provided when --image is the same as existing container image")
}
config.PullPolicy = api.PullPolicy(pullPolicy)
}
newRc, err = kubectl.CreateNewControllerFromCurrentController(client, codec, config)
if err != nil { if err != nil {
return err return err
} }
@ -262,6 +275,8 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
if err != nil { if err != nil {
return err return err
} }
// If new image is same as old, the hash may not be distinct, so add a suffix.
oldHash += "-orig"
oldRc, err = kubectl.UpdateExistingReplicationController(client, oldRc, cmdNamespace, newRc.Name, deploymentKey, oldHash, out) oldRc, err = kubectl.UpdateExistingReplicationController(client, oldRc, cmdNamespace, newRc.Name, deploymentKey, oldHash, out)
if err != nil { if err != nil {
return err return err

View File

@ -504,19 +504,28 @@ func LoadExistingNextReplicationController(c client.ReplicationControllersNamesp
return newRc, err return newRc, err
} }
func CreateNewControllerFromCurrentController(c client.Interface, codec runtime.Codec, namespace, oldName, newName, image, container, deploymentKey string) (*api.ReplicationController, error) { type NewControllerConfig struct {
Namespace string
OldName, NewName string
Image string
Container string
DeploymentKey string
PullPolicy api.PullPolicy
}
func CreateNewControllerFromCurrentController(c client.Interface, codec runtime.Codec, cfg *NewControllerConfig) (*api.ReplicationController, error) {
containerIndex := 0 containerIndex := 0
// load the old RC into the "new" RC // load the old RC into the "new" RC
newRc, err := c.ReplicationControllers(namespace).Get(oldName) newRc, err := c.ReplicationControllers(cfg.Namespace).Get(cfg.OldName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(container) != 0 { if len(cfg.Container) != 0 {
containerFound := false containerFound := false
for i, c := range newRc.Spec.Template.Spec.Containers { for i, c := range newRc.Spec.Template.Spec.Containers {
if c.Name == container { if c.Name == cfg.Container {
containerIndex = i containerIndex = i
containerFound = true containerFound = true
break break
@ -524,31 +533,34 @@ func CreateNewControllerFromCurrentController(c client.Interface, codec runtime.
} }
if !containerFound { if !containerFound {
return nil, fmt.Errorf("container %s not found in pod", container) return nil, fmt.Errorf("container %s not found in pod", cfg.Container)
} }
} }
if len(newRc.Spec.Template.Spec.Containers) > 1 && len(container) == 0 { if len(newRc.Spec.Template.Spec.Containers) > 1 && len(cfg.Container) == 0 {
return nil, goerrors.New("Must specify container to update when updating a multi-container pod") return nil, goerrors.New("Must specify container to update when updating a multi-container pod")
} }
if len(newRc.Spec.Template.Spec.Containers) == 0 { if len(newRc.Spec.Template.Spec.Containers) == 0 {
return nil, goerrors.New(fmt.Sprintf("Pod has no containers! (%v)", newRc)) return nil, goerrors.New(fmt.Sprintf("Pod has no containers! (%v)", newRc))
} }
newRc.Spec.Template.Spec.Containers[containerIndex].Image = image newRc.Spec.Template.Spec.Containers[containerIndex].Image = cfg.Image
if len(cfg.PullPolicy) != 0 {
newRc.Spec.Template.Spec.Containers[containerIndex].ImagePullPolicy = cfg.PullPolicy
}
newHash, err := api.HashObject(newRc, codec) newHash, err := api.HashObject(newRc, codec)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(newName) == 0 { if len(cfg.NewName) == 0 {
newName = fmt.Sprintf("%s-%s", newRc.Name, newHash) cfg.NewName = fmt.Sprintf("%s-%s", newRc.Name, newHash)
} }
newRc.Name = newName newRc.Name = cfg.NewName
newRc.Spec.Selector[deploymentKey] = newHash newRc.Spec.Selector[cfg.DeploymentKey] = newHash
newRc.Spec.Template.Labels[deploymentKey] = newHash newRc.Spec.Template.Labels[cfg.DeploymentKey] = newHash
// Clear resource version after hashing so that identical updates get different hashes. // Clear resource version after hashing so that identical updates get different hashes.
newRc.ResourceVersion = "" newRc.ResourceVersion = ""
return newRc, nil return newRc, nil

View File

@ -1090,12 +1090,19 @@ func TestRollingUpdater_multipleContainersInPod(t *testing.T) {
test.newRc.Spec.Template.Labels[test.deploymentKey] = deploymentHash test.newRc.Spec.Template.Labels[test.deploymentKey] = deploymentHash
test.newRc.Name = fmt.Sprintf("%s-%s", test.newRc.Name, deploymentHash) test.newRc.Name = fmt.Sprintf("%s-%s", test.newRc.Name, deploymentHash)
updatedRc, err := CreateNewControllerFromCurrentController(fake, codec, "", test.oldRc.ObjectMeta.Name, test.newRc.ObjectMeta.Name, test.image, test.container, test.deploymentKey) config := &NewControllerConfig{
OldName: test.oldRc.ObjectMeta.Name,
NewName: test.newRc.ObjectMeta.Name,
Image: test.image,
Container: test.container,
DeploymentKey: test.deploymentKey,
}
updatedRc, err := CreateNewControllerFromCurrentController(fake, codec, config)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(updatedRc, test.newRc) { if !reflect.DeepEqual(updatedRc, test.newRc) {
t.Errorf("expected:\n%v\ngot:\n%v\n", test.newRc, updatedRc) t.Errorf("expected:\n%#v\ngot:\n%#v\n", test.newRc, updatedRc)
} }
} }
} }