mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
First part of improved rolling update, allow dynamic next replication controller generation.
This commit is contained in:
parent
e8b28c59c6
commit
217e6ff0d0
@ -405,11 +405,20 @@ _kubectl_rolling-update()
|
||||
flags_with_completion=()
|
||||
flags_completion=()
|
||||
|
||||
flags+=("--deployment-label-key=")
|
||||
flags+=("--dry-run")
|
||||
flags+=("--filename=")
|
||||
two_word_flags+=("-f")
|
||||
flags+=("--help")
|
||||
flags+=("-h")
|
||||
flags+=("--image=")
|
||||
flags+=("--no-headers")
|
||||
flags+=("--output=")
|
||||
two_word_flags+=("-o")
|
||||
flags+=("--output-version=")
|
||||
flags+=("--poll-interval=")
|
||||
flags+=("--template=")
|
||||
two_word_flags+=("-t")
|
||||
flags+=("--timeout=")
|
||||
flags+=("--update-period=")
|
||||
|
||||
|
@ -66,4 +66,4 @@ kubectl
|
||||
* [kubectl update](kubectl_update.md) - Update a resource by filename or stdin.
|
||||
* [kubectl version](kubectl_version.md) - Print the client and server version information.
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.406236586 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.641615471 +0000 UTC
|
||||
|
@ -50,4 +50,4 @@ kubectl api-versions
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.405815046 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.640579424 +0000 UTC
|
||||
|
@ -50,4 +50,4 @@ kubectl cluster-info
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.405639667 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.640382538 +0000 UTC
|
||||
|
@ -63,4 +63,4 @@ kubectl config SUBCOMMAND
|
||||
* [kubectl config use-context](kubectl_config_use-context.md) - Sets the current-context in a kubeconfig file
|
||||
* [kubectl config view](kubectl_config_view.md) - displays Merged kubeconfig settings or a specified kubeconfig file.
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.405466384 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.640154327 +0000 UTC
|
||||
|
@ -65,4 +65,4 @@ $ kubectl config set-cluster e2e --insecure-skip-tls-verify=true
|
||||
### SEE ALSO
|
||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.404357726 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.638291301 +0000 UTC
|
||||
|
@ -58,4 +58,4 @@ $ kubectl config set-context gce --user=cluster-admin
|
||||
### SEE ALSO
|
||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.40472172 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.638842283 +0000 UTC
|
||||
|
@ -78,4 +78,4 @@ $ kubectl set-credentials cluster-admin --client-certificate=~/.kube/admin.crt -
|
||||
### SEE ALSO
|
||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.40454463 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.638596132 +0000 UTC
|
||||
|
@ -52,4 +52,4 @@ kubectl config set PROPERTY_NAME PROPERTY_VALUE
|
||||
### SEE ALSO
|
||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.404916515 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.639475859 +0000 UTC
|
||||
|
@ -51,4 +51,4 @@ kubectl config unset PROPERTY_NAME
|
||||
### SEE ALSO
|
||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.405094144 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.639702273 +0000 UTC
|
||||
|
@ -50,4 +50,4 @@ kubectl config use-context CONTEXT_NAME
|
||||
### SEE ALSO
|
||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.405277784 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.639928664 +0000 UTC
|
||||
|
@ -73,4 +73,4 @@ $ kubectl config view -o template --template='{{range .users}}{{ if eq .name "e2
|
||||
### SEE ALSO
|
||||
* [kubectl config](kubectl_config.md) - config modifies kubeconfig files
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.404151238 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.638011058 +0000 UTC
|
||||
|
@ -63,4 +63,4 @@ $ cat pod.json | kubectl create -f -
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.400230448 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.633893126 +0000 UTC
|
||||
|
@ -81,4 +81,4 @@ $ kubectl delete pods --all
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.400618492 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.634491315 +0000 UTC
|
||||
|
@ -63,4 +63,4 @@ $ kubectl describe pods/nginx
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 21:21:05.485819349 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.633584776 +0000 UTC
|
||||
|
@ -64,4 +64,4 @@ $ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.401622652 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.636226358 +0000 UTC
|
||||
|
@ -82,4 +82,4 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.403631642 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.637486796 +0000 UTC
|
||||
|
@ -85,4 +85,4 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.399795546 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.633184922 +0000 UTC
|
||||
|
@ -81,4 +81,4 @@ $ kubectl label pods foo bar-
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.403876136 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.637727935 +0000 UTC
|
||||
|
@ -62,4 +62,4 @@ $ kubectl log -f 123456-7890 ruby-container
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.400998066 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.634970712 +0000 UTC
|
||||
|
@ -53,4 +53,4 @@ kubectl namespace [namespace]
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.400806182 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.634703492 +0000 UTC
|
||||
|
@ -68,4 +68,4 @@ $ kubectl port-forward -p mypod 0:5000
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.40181203 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.636454335 +0000 UTC
|
||||
|
@ -65,4 +65,4 @@ $ kubectl proxy --api-prefix=k8s-api
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.402034031 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.636689051 +0000 UTC
|
||||
|
@ -68,4 +68,4 @@ $ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.401412855 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.635916597 +0000 UTC
|
||||
|
@ -23,14 +23,25 @@ $ kubectl rolling-update frontend-v1 -f frontend-v2.json
|
||||
|
||||
// Update pods of frontend-v1 using JSON data passed into stdin.
|
||||
$ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -
|
||||
|
||||
// Update the pods of frontend-v1 to frontend-v2 by just changing the image
|
||||
$ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2
|
||||
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
--deployment-label-key="deployment": The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when --image is specified, ignored otherwise
|
||||
--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 controller.
|
||||
-h, --help=false: help for rolling-update
|
||||
--image="": Image to upgrade the controller to. Can not be used with --filename/-f
|
||||
--no-headers=false: When using the default output, don't print headers.
|
||||
-o, --output="": Output format. One of: json|yaml|template|templatefile.
|
||||
--output-version="": Output the formatted object with the given version (default api-version).
|
||||
--poll-interval="3s": Time delay between polling controller status after update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]
|
||||
--timeout="5m0s": Max time to wait for a controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
--update-period="1m0s": Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
```
|
||||
@ -68,4 +79,4 @@ $ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.401210692 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-24 04:16:19.186748349 +0000 UTC
|
||||
|
@ -78,4 +78,4 @@ $ kubectl run-container nginx --image=nginx --overrides='{ "apiVersion": "v1beta
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.402296924 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.636970945 +0000 UTC
|
||||
|
@ -72,4 +72,4 @@ $ kubectl stop -f path/to/resources
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.402507426 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.637194082 +0000 UTC
|
||||
|
@ -67,4 +67,4 @@ $ kubectl update pods my-pod --patch='{ "apiVersion": "v1beta1", "desiredState":
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.400435658 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.634205944 +0000 UTC
|
||||
|
@ -51,4 +51,4 @@ kubectl version
|
||||
### SEE ALSO
|
||||
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
|
||||
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 00:47:55.405995955 +0000 UTC
|
||||
###### Auto generated by spf13/cobra at 2015-04-23 23:29:05.640847018 +0000 UTC
|
||||
|
@ -22,6 +22,14 @@ existing controller and overwrite at least one (common) label in its replicaSele
|
||||
|
||||
|
||||
.SH OPTIONS
|
||||
.PP
|
||||
\fB\-\-deployment\-label\-key\fP="deployment"
|
||||
The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when \-\-image is specified, ignored otherwise
|
||||
|
||||
.PP
|
||||
\fB\-\-dry\-run\fP=false
|
||||
If true, print out the changes that would be made, but don't actually make them.
|
||||
|
||||
.PP
|
||||
\fB\-f\fP, \fB\-\-filename\fP=""
|
||||
Filename or URL to file to use to create the new controller.
|
||||
@ -30,10 +38,31 @@ existing controller and overwrite at least one (common) label in its replicaSele
|
||||
\fB\-h\fP, \fB\-\-help\fP=false
|
||||
help for rolling\-update
|
||||
|
||||
.PP
|
||||
\fB\-\-image\fP=""
|
||||
Image to upgrade the controller to. Can not be used with \-\-filename/\-f
|
||||
|
||||
.PP
|
||||
\fB\-\-no\-headers\fP=false
|
||||
When using the default output, don't print headers.
|
||||
|
||||
.PP
|
||||
\fB\-o\fP, \fB\-\-output\fP=""
|
||||
Output format. One of: json|yaml|template|templatefile.
|
||||
|
||||
.PP
|
||||
\fB\-\-output\-version\fP=""
|
||||
Output the formatted object with the given version (default api\-version).
|
||||
|
||||
.PP
|
||||
\fB\-\-poll\-interval\fP="3s"
|
||||
Time delay between polling controller status after update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
|
||||
.PP
|
||||
\fB\-t\fP, \fB\-\-template\fP=""
|
||||
Template string or path to template file to use when \-o=template or \-o=templatefile. The template format is golang templates [
|
||||
\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]
|
||||
|
||||
.PP
|
||||
\fB\-\-timeout\fP="5m0s"
|
||||
Max time to wait for a controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
||||
@ -156,6 +185,10 @@ $ kubectl rolling\-update frontend\-v1 \-f frontend\-v2.json
|
||||
// Update pods of frontend\-v1 using JSON data passed into stdin.
|
||||
$ cat frontend\-v2.json | kubectl rolling\-update frontend\-v1 \-f \-
|
||||
|
||||
// Update the pods of frontend\-v1 to frontend\-v2 by just changing the image
|
||||
$ kubectl rolling\-update frontend\-v1 frontend\-v2 \-\-image=image:v2
|
||||
|
||||
|
||||
.fi
|
||||
.RE
|
||||
|
||||
|
@ -17,14 +17,22 @@ limitations under the License.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
|
||||
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@ -41,7 +49,11 @@ existing controller and overwrite at least one (common) label in its replicaSele
|
||||
$ kubectl rolling-update frontend-v1 -f frontend-v2.json
|
||||
|
||||
// Update pods of frontend-v1 using JSON data passed into stdin.
|
||||
$ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -`
|
||||
$ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -
|
||||
|
||||
// Update the pods of frontend-v1 to frontend-v2 by just changing the image
|
||||
$ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2
|
||||
`
|
||||
)
|
||||
|
||||
func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
@ -61,64 +73,125 @@ func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
|
||||
cmd.Flags().String("poll-interval", pollInterval, `Time delay between polling controller status after update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
|
||||
cmd.Flags().String("timeout", timeout, `Max time to wait for a controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
|
||||
cmd.Flags().StringP("filename", "f", "", "Filename or URL to file to use to create the new controller.")
|
||||
cmd.Flags().String("image", "", "Image to upgrade the controller to. Can not be used with --filename/-f")
|
||||
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().Bool("dry-run", false, "If true, print out the changes that would be made, but don't actually make them.")
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func validateArguments(cmd *cobra.Command, args []string) (deploymentKey, filename, image, oldName string, err error) {
|
||||
deploymentKey = cmdutil.GetFlagString(cmd, "deployment-label-key")
|
||||
filename = cmdutil.GetFlagString(cmd, "filename")
|
||||
image = cmdutil.GetFlagString(cmd, "image")
|
||||
|
||||
if len(deploymentKey) == 0 {
|
||||
return "", "", "", "", cmdutil.UsageError(cmd, "--deployment-label-key can not be empty")
|
||||
}
|
||||
if len(filename) == 0 && len(image) == 0 {
|
||||
return "", "", "", "", cmdutil.UsageError(cmd, "Must specify --filename or --image for new controller")
|
||||
}
|
||||
if len(filename) != 0 && len(image) != 0 {
|
||||
return "", "", "", "", cmdutil.UsageError(cmd, "--filename and --image can not both be specified")
|
||||
}
|
||||
if len(image) > 0 && len(args) < 2 {
|
||||
return "", "", "", "", cmdutil.UsageError(cmd, "You must specify a name for your new controller")
|
||||
}
|
||||
if len(args) < 1 {
|
||||
return "", "", "", "", cmdutil.UsageError(cmd, "Must specify the controller to update")
|
||||
}
|
||||
|
||||
return deploymentKey, filename, image, args[0], nil
|
||||
}
|
||||
|
||||
func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
|
||||
if os.Args[1] == "rollingupdate" {
|
||||
printDeprecationWarning("rolling-update", "rollingupdate")
|
||||
}
|
||||
|
||||
filename := cmdutil.GetFlagString(cmd, "filename")
|
||||
if len(filename) == 0 {
|
||||
return cmdutil.UsageError(cmd, "Must specify filename for new controller")
|
||||
deploymentKey, filename, image, oldName, err := validateArguments(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
period := cmdutil.GetFlagDuration(cmd, "update-period")
|
||||
interval := cmdutil.GetFlagDuration(cmd, "poll-interval")
|
||||
timeout := cmdutil.GetFlagDuration(cmd, "timeout")
|
||||
if len(args) != 1 {
|
||||
return cmdutil.UsageError(cmd, "Must specify the controller to update")
|
||||
}
|
||||
oldName := args[0]
|
||||
dryrun := cmdutil.GetFlagBool(cmd, "dry-run")
|
||||
|
||||
cmdNamespace, err := f.DefaultNamespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapper, typer := f.Object()
|
||||
// TODO: use resource.Builder instead
|
||||
obj, err := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
||||
NamespaceParam(cmdNamespace).RequireNamespace().
|
||||
FilenameParam(filename).
|
||||
Do().
|
||||
Object()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRc, ok := obj.(*api.ReplicationController)
|
||||
if !ok {
|
||||
return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename)
|
||||
}
|
||||
newName := newRc.Name
|
||||
if oldName == newName {
|
||||
return cmdutil.UsageError(cmd, "%s cannot have the same name as the existing ReplicationController %s",
|
||||
filename, oldName)
|
||||
}
|
||||
|
||||
client, err := f.Client()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updater := kubectl.NewRollingUpdater(newRc.Namespace, kubectl.NewRollingUpdaterClient(client))
|
||||
|
||||
// fetch rc
|
||||
oldRc, err := client.ReplicationControllers(newRc.Namespace).Get(oldName)
|
||||
oldRc, err := client.ReplicationControllers(cmdNamespace).Get(oldName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mapper, typer := f.Object()
|
||||
var newRc *api.ReplicationController
|
||||
|
||||
if len(filename) != 0 {
|
||||
obj, err := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
|
||||
NamespaceParam(cmdNamespace).RequireNamespace().
|
||||
FilenameParam(filename).
|
||||
Do().
|
||||
Object()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ok bool
|
||||
newRc, ok = obj.(*api.ReplicationController)
|
||||
if !ok {
|
||||
return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename)
|
||||
}
|
||||
}
|
||||
if len(image) != 0 {
|
||||
newName := args[1]
|
||||
var err error
|
||||
// load the old RC into the "new" RC
|
||||
if newRc, err = client.ReplicationControllers(cmdNamespace).Get(oldName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(newRc.Spec.Template.Spec.Containers) > 1 {
|
||||
// TODO: support multi-container image update.
|
||||
return errors.New("Image update is not supported for multi-container pods")
|
||||
}
|
||||
if len(newRc.Spec.Template.Spec.Containers) == 0 {
|
||||
return cmdutil.UsageError(cmd, "Pod has no containers! (%v)", newRc)
|
||||
}
|
||||
newRc.Spec.Template.Spec.Containers[0].Image = image
|
||||
|
||||
newHash, err := hashObject(newRc, client.Codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newRc.Name = newName
|
||||
|
||||
newRc.Spec.Selector[deploymentKey] = newHash
|
||||
newRc.Spec.Template.Labels[deploymentKey] = newHash
|
||||
newRc.ResourceVersion = ""
|
||||
|
||||
if _, found := oldRc.Spec.Selector[deploymentKey]; !found {
|
||||
if err := addDeploymentKeyToReplicationController(oldRc, client, deploymentKey, cmdNamespace, out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
newName := newRc.Name
|
||||
if oldName == newName {
|
||||
return cmdutil.UsageError(cmd, "%s cannot have the same name as the existing ReplicationController %s",
|
||||
filename, oldName)
|
||||
}
|
||||
|
||||
updater := kubectl.NewRollingUpdater(newRc.Namespace, kubectl.NewRollingUpdaterClient(client))
|
||||
|
||||
var hasLabel bool
|
||||
for key, oldValue := range oldRc.Spec.Selector {
|
||||
if newValue, ok := newRc.Spec.Selector[key]; ok && newValue != oldValue {
|
||||
@ -134,6 +207,18 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
|
||||
if newRc.Spec.Replicas == 0 {
|
||||
newRc.Spec.Replicas = oldRc.Spec.Replicas
|
||||
}
|
||||
if dryrun {
|
||||
oldRcData := &bytes.Buffer{}
|
||||
if err := f.PrintObject(cmd, oldRc, oldRcData); err != nil {
|
||||
return err
|
||||
}
|
||||
newRcData := &bytes.Buffer{}
|
||||
if err := f.PrintObject(cmd, newRc, newRcData); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "Rolling from:\n%s\nTo:\n%s\n", string(oldRcData.Bytes()), string(newRcData.Bytes()))
|
||||
return nil
|
||||
}
|
||||
err = updater.Update(&kubectl.RollingUpdaterConfig{
|
||||
Out: out,
|
||||
OldRc: oldRc,
|
||||
@ -150,3 +235,68 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
|
||||
fmt.Fprintf(out, "%s\n", newName)
|
||||
return nil
|
||||
}
|
||||
|
||||
func hashObject(obj runtime.Object, codec runtime.Codec) (string, error) {
|
||||
data, err := codec.Encode(obj)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%x", md5.Sum(data)), nil
|
||||
}
|
||||
|
||||
const MaxRetries = 3
|
||||
|
||||
func addDeploymentKeyToReplicationController(oldRc *api.ReplicationController, client *client.Client, deploymentKey, namespace string, out io.Writer) error {
|
||||
oldHash, err := hashObject(oldRc, client.Codec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Update all labels to include the new hash, so they are correctly adopted
|
||||
// TODO: extract the code from the label command and re-use it here.
|
||||
podList, err := client.Pods(namespace).List(labels.SelectorFromSet(oldRc.Spec.Selector), fields.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for ix := range podList.Items {
|
||||
pod := &podList.Items[ix]
|
||||
if pod.Labels == nil {
|
||||
pod.Labels = map[string]string{
|
||||
deploymentKey: oldHash,
|
||||
}
|
||||
} else {
|
||||
pod.Labels[deploymentKey] = oldHash
|
||||
}
|
||||
err = nil
|
||||
delay := 3
|
||||
for i := 0; i < MaxRetries; i++ {
|
||||
_, err = client.Pods(namespace).Update(pod)
|
||||
if err != nil {
|
||||
fmt.Fprint(out, "Error updating pod (%v), retrying after %d seconds", err, delay)
|
||||
time.Sleep(time.Second * time.Duration(delay))
|
||||
delay *= delay
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if oldRc.Spec.Selector == nil {
|
||||
oldRc.Spec.Selector = map[string]string{}
|
||||
}
|
||||
if oldRc.Spec.Template.Labels == nil {
|
||||
oldRc.Spec.Template.Labels = map[string]string{}
|
||||
}
|
||||
oldRc.Spec.Selector[deploymentKey] = oldHash
|
||||
oldRc.Spec.Template.Labels[deploymentKey] = oldHash
|
||||
|
||||
if _, err := client.ReplicationControllers(namespace).Update(oldRc); err != nil {
|
||||
return err
|
||||
}
|
||||
// Note there is still a race here, if a pod was created during the update phase.
|
||||
// It's unlikely, but it could happen, and if it does, we'll create extra pods.
|
||||
// TODO: Clean up orphaned pods here.
|
||||
|
||||
return nil
|
||||
}
|
||||
|
177
pkg/kubectl/cmd/rollingupdate_test.go
Normal file
177
pkg/kubectl/cmd/rollingupdate_test.go
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func TestValidateArgs(t *testing.T) {
|
||||
f, _, _ := NewAPIFactory()
|
||||
|
||||
tests := []struct {
|
||||
flags map[string]string
|
||||
args []string
|
||||
expectErr bool
|
||||
testName string
|
||||
}{
|
||||
{
|
||||
expectErr: true,
|
||||
testName: "nothing",
|
||||
},
|
||||
{
|
||||
flags: map[string]string{},
|
||||
args: []string{"foo"},
|
||||
expectErr: true,
|
||||
testName: "no file, no image",
|
||||
},
|
||||
{
|
||||
flags: map[string]string{
|
||||
"filename": "bar.yaml",
|
||||
},
|
||||
args: []string{"foo"},
|
||||
testName: "valid file example",
|
||||
},
|
||||
{
|
||||
flags: map[string]string{
|
||||
"image": "foo:v2",
|
||||
},
|
||||
args: []string{"foo"},
|
||||
expectErr: true,
|
||||
testName: "missing second image name",
|
||||
},
|
||||
{
|
||||
flags: map[string]string{
|
||||
"image": "foo:v2",
|
||||
},
|
||||
args: []string{"foo", "foo-v2"},
|
||||
testName: "valid image example",
|
||||
},
|
||||
{
|
||||
flags: map[string]string{
|
||||
"image": "foo:v2",
|
||||
"filename": "bar.yaml",
|
||||
},
|
||||
args: []string{"foo", "foo-v2"},
|
||||
expectErr: true,
|
||||
testName: "both filename and image example",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
out := &bytes.Buffer{}
|
||||
cmd := NewCmdRollingUpdate(f, out)
|
||||
|
||||
if test.flags != nil {
|
||||
for key, val := range test.flags {
|
||||
cmd.Flags().Set(key, val)
|
||||
}
|
||||
}
|
||||
_, _, _, _, err := validateArguments(cmd, test.args)
|
||||
if err != nil && !test.expectErr {
|
||||
t.Errorf("unexpected error: %v (%s)", err, test.testName)
|
||||
}
|
||||
if err == nil && test.expectErr {
|
||||
t.Error("unexpected non-error (%s)", test.testName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddDeploymentHash(t *testing.T) {
|
||||
buf := &bytes.Buffer{}
|
||||
f, tf, codec := NewAPIFactory()
|
||||
rc := &api.ReplicationController{
|
||||
ObjectMeta: api.ObjectMeta{Name: "rc"},
|
||||
Spec: api.ReplicationControllerSpec{
|
||||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Template: &api.PodTemplateSpec{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
podList := &api.PodList{
|
||||
Items: []api.Pod{
|
||||
{ObjectMeta: api.ObjectMeta{Name: "foo"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "bar"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "baz"}},
|
||||
},
|
||||
}
|
||||
|
||||
seen := util.StringSet{}
|
||||
updatedRc := false
|
||||
tf.Client = &client.FakeRESTClient{
|
||||
Codec: codec,
|
||||
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
|
||||
switch p, m := req.URL.Path, req.Method; {
|
||||
case p == "/api/v1beta3/namespaces/default/pods" && m == "GET":
|
||||
if req.URL.RawQuery != "labelSelector=foo%3Dbar" {
|
||||
t.Errorf("Unexpected query string: %s", req.URL.RawQuery)
|
||||
}
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, podList)}, nil
|
||||
case p == "/api/v1beta3/namespaces/default/pods/foo" && m == "PUT":
|
||||
seen.Insert("foo")
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[0])}, nil
|
||||
case p == "/api/v1beta3/namespaces/default/pods/bar" && m == "PUT":
|
||||
seen.Insert("bar")
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[1])}, nil
|
||||
case p == "/api/v1beta3/namespaces/default/pods/baz" && m == "PUT":
|
||||
seen.Insert("baz")
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, &podList.Items[2])}, nil
|
||||
case p == "/api/v1beta3/namespaces/default/replicationcontrollers/rc" && m == "PUT":
|
||||
updatedRc = true
|
||||
return &http.Response{StatusCode: 200, Body: objBody(codec, rc)}, nil
|
||||
default:
|
||||
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
|
||||
return nil, nil
|
||||
}
|
||||
}),
|
||||
}
|
||||
tf.ClientConfig = &client.Config{Version: latest.Version}
|
||||
tf.Namespace = "test"
|
||||
|
||||
client, err := f.Client()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
if err := addDeploymentKeyToReplicationController(rc, client, "hash", api.NamespaceDefault, buf); err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
for _, pod := range podList.Items {
|
||||
if !seen.Has(pod.Name) {
|
||||
t.Errorf("Missing update for pod: %s", pod.Name)
|
||||
}
|
||||
}
|
||||
if !updatedRc {
|
||||
t.Errorf("Failed to update replication controller with new labels")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user