Add 'kubectl set image'

pull/6/head
Janet Kuo 2016-05-10 17:26:39 -07:00
parent 28333a0041
commit 4332472bde
33 changed files with 979 additions and 91 deletions

View File

@ -53,6 +53,7 @@ docs/man/man1/kubectl-rollout-undo.1
docs/man/man1/kubectl-rollout.1
docs/man/man1/kubectl-run.1
docs/man/man1/kubectl-scale.1
docs/man/man1/kubectl-set-image.1
docs/man/man1/kubectl-set.1
docs/man/man1/kubectl-stop.1
docs/man/man1/kubectl-taint.1
@ -109,6 +110,7 @@ docs/user-guide/kubectl/kubectl_rollout_undo.md
docs/user-guide/kubectl/kubectl_run.md
docs/user-guide/kubectl/kubectl_scale.md
docs/user-guide/kubectl/kubectl_set.md
docs/user-guide/kubectl/kubectl_set_image.md
docs/user-guide/kubectl/kubectl_taint.md
docs/user-guide/kubectl/kubectl_uncordon.md
docs/user-guide/kubectl/kubectl_version.md

View File

@ -478,10 +478,78 @@ _kubectl_get()
noun_aliases+=("thirdpartyresources")
}
_kubectl_set_image()
{
last_command="kubectl_set_image"
commands=()
flags=()
two_word_flags=()
flags_with_completion=()
flags_completion=()
flags+=("--all")
flags+=("--filename=")
flags_with_completion+=("--filename")
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
two_word_flags+=("-f")
flags_with_completion+=("-f")
flags_completion+=("__handle_filename_extension_flag json|yaml|yml")
flags+=("--local")
flags+=("--no-headers")
flags+=("--output=")
two_word_flags+=("-o")
flags+=("--output-version=")
flags+=("--record")
flags+=("--recursive")
flags+=("-R")
flags+=("--selector=")
two_word_flags+=("-l")
flags+=("--show-all")
flags+=("-a")
flags+=("--show-labels")
flags+=("--sort-by=")
flags+=("--template=")
flags_with_completion+=("--template")
flags_completion+=("_filedir")
flags+=("--alsologtostderr")
flags+=("--api-version=")
flags+=("--as=")
flags+=("--certificate-authority=")
flags+=("--client-certificate=")
flags+=("--client-key=")
flags+=("--cluster=")
flags+=("--context=")
flags+=("--insecure-skip-tls-verify")
flags+=("--kubeconfig=")
flags+=("--log-backtrace-at=")
flags+=("--log-dir=")
flags+=("--log-flush-frequency=")
flags+=("--logtostderr")
flags+=("--match-server-version")
flags+=("--namespace=")
flags_with_completion+=("--namespace")
flags_completion+=("__kubectl_get_namespaces")
flags+=("--password=")
flags+=("--server=")
two_word_flags+=("-s")
flags+=("--stderrthreshold=")
flags+=("--token=")
flags+=("--user=")
flags+=("--username=")
flags+=("--v=")
flags+=("--vmodule=")
must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}
_kubectl_set()
{
last_command="kubectl_set"
commands=()
commands+=("image")
flags=()
two_word_flags=()

View File

@ -27,7 +27,7 @@ An autoscaler can automatically increase or decrease number of pods deployed wit
.PP
\fB\-\-dry\-run\fP=false
If true, only print the object that would be sent, without creating it.
If true, only print the object that would be sent, without sending it.
.PP
\fB\-f\fP, \fB\-\-filename\fP=[]

View File

@ -40,7 +40,7 @@ Possible resources include (case insensitive):
.PP
\fB\-\-dry\-run\fP=false
If true, only print the object that would be sent, without creating it.
If true, only print the object that would be sent, without sending it.
.PP
\fB\-\-external\-ip\fP=""

View File

@ -32,7 +32,7 @@ existing replication controller and overwrite at least one (common) label in its
.PP
\fB\-\-dry\-run\fP=false
If true, print out the changes that would be made, but don't actually make them.
If true, only print the object that would be sent, without sending it.
.PP
\fB\-f\fP, \fB\-\-filename\fP=[]

View File

@ -0,0 +1,206 @@
.TH "KUBERNETES" "1" " kubernetes User Manuals" "Eric Paris" "Jan 2015" ""
.SH NAME
.PP
kubectl set image \- Update image of a pod template
.SH SYNOPSIS
.PP
\fBkubectl set image\fP [OPTIONS]
.SH DESCRIPTION
.PP
Update existing container image(s) of resources.
.PP
Possible resources include (case insensitive):
pod (po), replicationcontroller (rc), deployment, daemonset (ds), job, replicaset (rs)
.SH OPTIONS
.PP
\fB\-\-all\fP=false
select all resources in the namespace of the specified resource types
.PP
\fB\-f\fP, \fB\-\-filename\fP=[]
Filename, directory, or URL to a file identifying the resource to get from a server.
.PP
\fB\-\-local\fP=false
If true, set image will NOT contact api\-server but run locally.
.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|wide|name|go\-template=...|go\-template\-file=...|jsonpath=...|jsonpath\-file=... See golang template [
\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]] and jsonpath template [
\[la]http://releases.k8s.io/HEAD/docs/user-guide/jsonpath.md\[ra]].
.PP
\fB\-\-output\-version\fP=""
Output the formatted object with the given group version (for ex: 'extensions/v1beta1').
.PP
\fB\-\-record\fP=false
Record current kubectl command in the resource annotation.
.PP
\fB\-R\fP, \fB\-\-recursive\fP=false
If true, process directory recursively.
.PP
\fB\-l\fP, \fB\-\-selector\fP=""
Selector (label query) to filter on
.PP
\fB\-a\fP, \fB\-\-show\-all\fP=false
When printing, show all resources (default hide terminated pods.)
.PP
\fB\-\-show\-labels\fP=false
When printing, show all labels as the last column (default hide labels column)
.PP
\fB\-\-sort\-by\fP=""
If non\-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
.PP
\fB\-\-template\fP=""
Template string or path to template file to use when \-o=go\-template, \-o=go\-template\-file. The template format is golang templates [
\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]].
.SH OPTIONS INHERITED FROM PARENT COMMANDS
.PP
\fB\-\-alsologtostderr\fP=false
log to standard error as well as files
.PP
\fB\-\-api\-version\fP=""
DEPRECATED: The API version to use when talking to the server
.PP
\fB\-\-as\fP=""
Username to impersonate for the operation.
.PP
\fB\-\-certificate\-authority\fP=""
Path to a cert. file for the certificate authority.
.PP
\fB\-\-client\-certificate\fP=""
Path to a client certificate file for TLS.
.PP
\fB\-\-client\-key\fP=""
Path to a client key file for TLS.
.PP
\fB\-\-cluster\fP=""
The name of the kubeconfig cluster to use
.PP
\fB\-\-context\fP=""
The name of the kubeconfig context to use
.PP
\fB\-\-insecure\-skip\-tls\-verify\fP=false
If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
.PP
\fB\-\-kubeconfig\fP=""
Path to the kubeconfig file to use for CLI requests.
.PP
\fB\-\-log\-backtrace\-at\fP=:0
when logging hits line file:N, emit a stack trace
.PP
\fB\-\-log\-dir\fP=""
If non\-empty, write log files in this directory
.PP
\fB\-\-log\-flush\-frequency\fP=5s
Maximum number of seconds between log flushes
.PP
\fB\-\-logtostderr\fP=true
log to standard error instead of files
.PP
\fB\-\-match\-server\-version\fP=false
Require server version to match client version
.PP
\fB\-\-namespace\fP=""
If present, the namespace scope for this CLI request.
.PP
\fB\-\-password\fP=""
Password for basic authentication to the API server.
.PP
\fB\-s\fP, \fB\-\-server\fP=""
The address and port of the Kubernetes API server
.PP
\fB\-\-stderrthreshold\fP=2
logs at or above this threshold go to stderr
.PP
\fB\-\-token\fP=""
Bearer token for authentication to the API server.
.PP
\fB\-\-user\fP=""
The name of the kubeconfig user to use
.PP
\fB\-\-username\fP=""
Username for basic authentication to the API server.
.PP
\fB\-\-v\fP=0
log level for V logs
.PP
\fB\-\-vmodule\fP=
comma\-separated list of pattern=N settings for file\-filtered logging
.SH EXAMPLE
.PP
.RS
.nf
# Set a deployment's nginx container image to 'nginx:1.9.1', and its busybox container image to 'busybox'.
kubectl set image deployment/nginx busybox=busybox nginx=nginx:1.9.1
# Update all deployments' and rc's nginx container's image to 'nginx:1.9.1'
kubectl set image deployments,rc nginx=nginx:1.9.1 \-\-all
# Update image of all containers of daemonset abc to 'nginx:1.9.1'
kubectl set image daemonset abc *=nginx:1.9.1
# Print result (in yaml format) of updating nginx container image from local file, without hitting the server
kubectl set image \-f path/to/file.yaml nginx=nginx:1.9.1 \-\-local \-o yaml
.fi
.RE
.SH SEE ALSO
.PP
\fBkubectl\-set(1)\fP,
.SH HISTORY
.PP
January 2015, Originally compiled by Eric Paris (eparis at redhat dot com) based on the kubernetes source material, but hopefully they have been automatically generated since!

View File

@ -119,7 +119,7 @@ These commands help you make changes to existing application resources.
.SH SEE ALSO
.PP
\fBkubectl(1)\fP,
\fBkubectl(1)\fP, \fBkubectl\-set\-image(1)\fP,
.SH HISTORY

View File

@ -62,7 +62,7 @@ kubectl autoscale rc foo --max=5 --cpu-percent=80
```
--cpu-percent=-1: The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, the server will apply a default value.
--dry-run[=false]: If true, only print the object that would be sent, without creating it.
--dry-run[=false]: If true, only print the object that would be sent, without sending it.
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to autoscale.
--generator="horizontalpodautoscaler/v1beta1": The name of the API generator to use. Currently there is only 1 generator.
--include-extended-apis[=true]: If true, include definitions of new APIs via calls to the API server. [default true]
@ -113,7 +113,7 @@ kubectl autoscale rc foo --max=5 --cpu-percent=80
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra on 30-Mar-2016
###### Auto generated by spf13/cobra on 13-May-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_autoscale.md?pixel)]()

View File

@ -85,7 +85,7 @@ kubectl expose deployment nginx --port=80 --target-port=8000
### Options
```
--dry-run[=false]: If true, only print the object that would be sent, without creating it.
--dry-run[=false]: If true, only print the object that would be sent, without sending it.
--external-ip="": Additional external IP address (not managed by Kubernetes) to accept for the service. If this IP is routed to a node, the service can be accessed by this IP in addition to its generated service IP.
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to expose a service
--generator="service/v2": The name of the API generator to use. There are 2 generators: 'service/v1' and 'service/v2'. The only difference between them is that service port in v1 is named 'default', while it is left unnamed in v2. Default is 'service/v2'.
@ -143,7 +143,7 @@ kubectl expose deployment nginx --port=80 --target-port=8000
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra on 11-May-2016
###### Auto generated by spf13/cobra on 13-May-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_expose.md?pixel)]()

View File

@ -75,7 +75,7 @@ kubectl rolling-update frontend-v1 frontend-v2 --rollback
```
--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
--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.
--dry-run[=false]: If true, only print the object that would be sent, without sending it.
-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-pull-policy="": Explicit policy for when to pull container images. Required when --image is same as existing image, ignored otherwise.
@ -127,7 +127,7 @@ kubectl rolling-update frontend-v1 frontend-v2 --rollback
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
###### Auto generated by spf13/cobra on 21-Apr-2016
###### Auto generated by spf13/cobra on 13-May-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_rolling-update.md?pixel)]()

View File

@ -73,6 +73,7 @@ kubectl set SUBCOMMAND
### SEE ALSO
* [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager
* [kubectl set image](kubectl_set_image.md) - Update image of a pod template
###### Auto generated by spf13/cobra on 10-May-2016

View File

@ -0,0 +1,116 @@
<!-- BEGIN MUNGE: UNVERSIONED_WARNING -->
<!-- BEGIN STRIP_FOR_RELEASE -->
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<img src="http://kubernetes.io/img/warning.png" alt="WARNING"
width="25" height="25">
<h2>PLEASE NOTE: This document applies to the HEAD of the source tree</h2>
If you are using a released version of Kubernetes, you should
refer to the docs that go with that version.
Documentation for other releases can be found at
[releases.k8s.io](http://releases.k8s.io).
</strong>
--
<!-- END STRIP_FOR_RELEASE -->
<!-- END MUNGE: UNVERSIONED_WARNING -->
## kubectl set image
Update image of a pod template
### Synopsis
Update existing container image(s) of resources.
Possible resources include (case insensitive):
pod (po), replicationcontroller (rc), deployment, daemonset (ds), job, replicaset (rs)
```
kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N
```
### Examples
```
# Set a deployment's nginx container image to 'nginx:1.9.1', and its busybox container image to 'busybox'.
kubectl set image deployment/nginx busybox=busybox nginx=nginx:1.9.1
# Update all deployments' and rc's nginx container's image to 'nginx:1.9.1'
kubectl set image deployments,rc nginx=nginx:1.9.1 --all
# Update image of all containers of daemonset abc to 'nginx:1.9.1'
kubectl set image daemonset abc *=nginx:1.9.1
# Print result (in yaml format) of updating nginx container image from local file, without hitting the server
kubectl set image -f path/to/file.yaml nginx=nginx:1.9.1 --local -o yaml
```
### Options
```
--all[=false]: select all resources in the namespace of the specified resource types
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to get from a server.
--local[=false]: If true, set image will NOT contact api-server but run locally.
--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].
--output-version="": Output the formatted object with the given group version (for ex: 'extensions/v1beta1').
--record[=false]: Record current kubectl command in the resource annotation.
-R, --recursive[=false]: If true, process directory recursively.
-l, --selector="": Selector (label query) to filter on
-a, --show-all[=false]: When printing, show all resources (default hide terminated pods.)
--show-labels[=false]: When printing, show all labels as the last column (default hide labels column)
--sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.
--template="": Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
```
### Options inherited from parent commands
```
--alsologtostderr[=false]: log to standard error as well as files
--as="": Username to impersonate for the operation.
--certificate-authority="": Path to a cert. file for the certificate authority.
--client-certificate="": Path to a client certificate file for TLS.
--client-key="": Path to a client key file for TLS.
--cluster="": The name of the kubeconfig cluster to use
--context="": The name of the kubeconfig context to use
--insecure-skip-tls-verify[=false]: If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.
--kubeconfig="": Path to the kubeconfig file to use for CLI requests.
--log-backtrace-at=:0: when logging hits line file:N, emit a stack trace
--log-dir="": If non-empty, write log files in this directory
--log-flush-frequency=5s: Maximum number of seconds between log flushes
--logtostderr[=true]: log to standard error instead of files
--match-server-version[=false]: Require server version to match client version
--namespace="": If present, the namespace scope for this CLI request.
--password="": Password for basic authentication to the API server.
-s, --server="": The address and port of the Kubernetes API server
--stderrthreshold=2: logs at or above this threshold go to stderr
--token="": Bearer token for authentication to the API server.
--user="": The name of the kubeconfig user to use
--username="": Username for basic authentication to the API server.
--v=0: log level for V logs
--vmodule=: comma-separated list of pattern=N settings for file-filtered logging
```
### SEE ALSO
* [kubectl set](kubectl_set.md) - Set specific features on objects
###### Auto generated by spf13/cobra on 17-May-2016
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_set_image.md?pixel)]()
<!-- END MUNGE: GENERATED_ANALYTICS -->

View File

@ -14,7 +14,7 @@ options:
- name: dry-run
default_value: "false"
usage: |
If true, only print the object that would be sent, without creating it.
If true, only print the object that would be sent, without sending it.
- name: filename
shorthand: f
default_value: '[]'

View File

@ -21,7 +21,7 @@ options:
- name: dry-run
default_value: "false"
usage: |
If true, only print the object that would be sent, without creating it.
If true, only print the object that would be sent, without sending it.
- name: external-ip
usage: |
Additional external IP address (not managed by Kubernetes) to accept for the service. If this IP is routed to a node, the service can be accessed by this IP in addition to its generated service IP.

View File

@ -17,7 +17,7 @@ options:
- name: dry-run
default_value: "false"
usage: |
If true, print out the changes that would be made, but don't actually make them.
If true, only print the object that would be sent, without sending it.
- name: filename
shorthand: f
default_value: '[]'

View File

@ -65,3 +65,4 @@ inherited_options:
comma-separated list of pattern=N settings for file-filtered logging
see_also:
- kubectl
- image

View File

@ -291,6 +291,7 @@ runTests() {
secret_data=".data"
secret_type=".type"
deployment_image_field="(index .spec.template.spec.containers 0).image"
deployment_second_image_field="(index .spec.template.spec.containers 1).image"
change_cause_annotation='.*kubernetes.io/change-cause.*'
# Passing no arguments to create is an error
@ -1620,6 +1621,11 @@ __EOF__
# Clean up
kubectl delete rc frontend "${kube_flags[@]}"
######################
# Deployments #
######################
### Auto scale deployment
# Pre-condition: no deployment exists
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" ''
@ -1670,6 +1676,39 @@ __EOF__
# Clean up
kubectl delete deployment nginx-deployment "${kube_flags[@]}"
### Set image of a deployment
# Pre-condition: no deployment exists
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" ''
# Create a deployment
kubectl create -f hack/testdata/deployment-multicontainer.yaml "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" 'nginx-deployment:'
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R1}:"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_second_image_field}}:{{end}}" "${IMAGE_PERL}:"
# Set the deployment's image
kubectl set image deployment nginx-deployment nginx="${IMAGE_DEPLOYMENT_R2}" "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R2}:"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_second_image_field}}:{{end}}" "${IMAGE_PERL}:"
# Set non-existing container should fail
! kubectl set image deployment nginx-deployment redis=redis "${kube_flags[@]}"
# Set image of deployments without specifying name
kubectl set image deployments --all nginx="${IMAGE_DEPLOYMENT_R1}" "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R1}:"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_second_image_field}}:{{end}}" "${IMAGE_PERL}:"
# Set image of a deployment specified by file
kubectl set image -f hack/testdata/deployment-multicontainer.yaml nginx="${IMAGE_DEPLOYMENT_R2}" "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R2}:"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_second_image_field}}:{{end}}" "${IMAGE_PERL}:"
# Set image of a local file without talking to the server
kubectl set image -f hack/testdata/deployment-multicontainer.yaml nginx="${IMAGE_DEPLOYMENT_R1}" "${kube_flags[@]}" --local -o yaml
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R2}:"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_second_image_field}}:{{end}}" "${IMAGE_PERL}:"
# Set image of all containers of the deployment
kubectl set image deployment nginx-deployment "*"="${IMAGE_DEPLOYMENT_R1}" "${kube_flags[@]}"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R1}:"
kube::test::get_object_assert deployment "{{range.items}}{{$deployment_second_image_field}}:{{end}}" "${IMAGE_DEPLOYMENT_R1}:"
# Clean up
kubectl delete deployment nginx-deployment "${kube_flags[@]}"
######################
# Replica Sets #

View File

@ -0,0 +1,23 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
labels:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
labels:
name: nginx
spec:
containers:
- name: nginx
image: gcr.io/google-containers/nginx:test-cmd
ports:
- containerPort: 80
- name: perl
image: gcr.io/google-containers/perl

View File

@ -148,22 +148,11 @@ func (o *AnnotateOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra
// retrieves resource and annotation args from args
// also checks args to verify that all resources are specified before annotations
annotationArgs := []string{}
metAnnotaionArg := false
for _, s := range args {
isAnnotation := strings.Contains(s, "=") || strings.HasSuffix(s, "-")
switch {
case !metAnnotaionArg && isAnnotation:
metAnnotaionArg = true
fallthrough
case metAnnotaionArg && isAnnotation:
annotationArgs = append(annotationArgs, s)
case !metAnnotaionArg && !isAnnotation:
o.resources = append(o.resources, s)
case metAnnotaionArg && !isAnnotation:
return fmt.Errorf("all resources must be specified before annotation changes: %s", s)
}
resources, annotationArgs, err := cmdutil.GetResourcesAndPairs(args, "annotation")
if err != nil {
return err
}
o.resources = resources
if len(o.resources) < 1 && len(o.filenames) == 0 {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
@ -277,34 +266,7 @@ func (o AnnotateOptions) RunAnnotate() error {
// parseAnnotations retrieves new and remove annotations from annotation args
func parseAnnotations(annotationArgs []string) (map[string]string, []string, error) {
var invalidBuf bytes.Buffer
newAnnotations := map[string]string{}
removeAnnotations := []string{}
for _, annotationArg := range annotationArgs {
if strings.Index(annotationArg, "=") != -1 {
parts := strings.SplitN(annotationArg, "=", 2)
if len(parts) != 2 || len(parts[1]) == 0 {
if invalidBuf.Len() > 0 {
invalidBuf.WriteString(", ")
}
invalidBuf.WriteString(fmt.Sprintf(annotationArg))
} else {
newAnnotations[parts[0]] = parts[1]
}
} else if strings.HasSuffix(annotationArg, "-") {
removeAnnotations = append(removeAnnotations, annotationArg[:len(annotationArg)-1])
} else {
if invalidBuf.Len() > 0 {
invalidBuf.WriteString(", ")
}
invalidBuf.WriteString(fmt.Sprintf(annotationArg))
}
}
if invalidBuf.Len() > 0 {
return newAnnotations, removeAnnotations, fmt.Errorf("invalid annotation format: %s", invalidBuf.String())
}
return newAnnotations, removeAnnotations, nil
return cmdutil.ParsePairs(annotationArgs, "annotation", true)
}
// validateAnnotations checks the format of annotation args and checks removed annotations aren't in the new annotations map

View File

@ -68,7 +68,7 @@ func NewCmdAutoscale(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.MarkFlagRequired("max")
cmd.Flags().Int("cpu-percent", -1, fmt.Sprintf("The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, the server will apply a default value."))
cmd.Flags().String("name", "", "The name for the newly created object. If not specified, the name of the input resource will be used.")
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.")
cmdutil.AddDryRunFlag(cmd)
usage := "Filename, directory, or URL to a file identifying the resource to autoscale."
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
@ -160,8 +160,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
}
object = hpa.Object
}
// TODO: extract this flag to a central location, when such a location exists.
if cmdutil.GetFlagBool(cmd, "dry-run") {
if cmdutil.GetDryRunFlag(cmd) {
return f.PrintObject(cmd, mapper, object, out)
}

View File

@ -90,7 +90,7 @@ func CreateConfigMap(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, a
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -71,7 +71,7 @@ func CreateNamespace(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Command, a
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -109,7 +109,7 @@ func CreateSecretGeneric(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Comman
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}
@ -188,7 +188,7 @@ func CreateSecretDockerRegistry(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -71,7 +71,7 @@ func CreateServiceAccount(f *cmdutil.Factory, cmdOut io.Writer, cmd *cobra.Comma
return RunCreateSubcommand(f, cmd, cmdOut, &CreateSubcommandOptions{
Name: name,
StructuredGenerator: generator,
DryRun: cmdutil.GetFlagBool(cmd, "dry-run"),
DryRun: cmdutil.GetDryRunFlag(cmd),
OutputFormat: cmdutil.GetFlagString(cmd, "output"),
})
}

View File

@ -110,7 +110,6 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().String("load-balancer-ip", "", "IP to assign to to the Load Balancer. If empty, an ephemeral IP will be created and used (cloud-provider specific).")
cmd.Flags().String("selector", "", "A label selector to use for this service. Only equality-based selector requirements are supported. If empty (the default) infer the selector from the replication controller or replica set.")
cmd.Flags().StringP("labels", "l", "", "Labels to apply to the service created by this call.")
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.")
cmd.Flags().String("container-port", "", "Synonym for --target-port")
cmd.Flags().MarkDeprecated("container-port", "--container-port will be removed in the future, please use --target-port instead")
cmd.Flags().String("target-port", "", "Name or number for the port on the container that the service should direct traffic to. Optional.")
@ -121,6 +120,7 @@ func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
usage := "Filename, directory, or URL to a file identifying the resource to expose a service"
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddRecordFlag(cmd)
@ -256,8 +256,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
}
}
info.Refresh(object, true)
// TODO: extract this flag to a central location, when such a location exists.
if cmdutil.GetFlagBool(cmd, "dry-run") {
if cmdutil.GetDryRunFlag(cmd) {
return f.PrintObject(cmd, mapper, object, out)
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {

View File

@ -101,7 +101,7 @@ func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command {
usage := "Filename, directory, or URL to a file identifying the resource to update the labels"
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
@ -176,21 +176,9 @@ func labelFunc(obj runtime.Object, overwrite bool, resourceVersion string, label
}
func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *LabelOptions) error {
resources, labelArgs := []string{}, []string{}
first := true
for _, s := range args {
isLabel := strings.Contains(s, "=") || strings.HasSuffix(s, "-")
switch {
case first && isLabel:
first = false
fallthrough
case !first && isLabel:
labelArgs = append(labelArgs, s)
case first && !isLabel:
resources = append(resources, s)
case !first && !isLabel:
return cmdutil.UsageError(cmd, "all resources must be specified before label changes: %s", s)
}
resources, labelArgs, err := cmdutil.GetResourcesAndPairs(args, "label")
if err != nil {
return err
}
if len(resources) < 1 && len(options.Filenames) == 0 {
return cmdutil.UsageError(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>")
@ -242,7 +230,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
var outputObj runtime.Object
dataChangeMsg := "not labeled"
if cmdutil.GetFlagBool(cmd, "dry-run") {
if cmdutil.GetDryRunFlag(cmd) {
err = labelFunc(info.Object, overwrite, resourceVersion, lbls, remove)
if err != nil {
return err

View File

@ -98,8 +98,8 @@ func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
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("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("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.AddDryRunFlag(cmd)
cmdutil.AddValidateFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
@ -157,7 +157,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
period := cmdutil.GetFlagDuration(cmd, "update-period")
interval := cmdutil.GetFlagDuration(cmd, "poll-interval")
timeout := cmdutil.GetFlagDuration(cmd, "timeout")
dryrun := cmdutil.GetFlagBool(cmd, "dry-run")
dryrun := cmdutil.GetDryRunFlag(cmd)
outputFormat := cmdutil.GetFlagString(cmd, "output")
container := cmdutil.GetFlagString(cmd, "container")

View File

@ -92,12 +92,12 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c
}
func addRunFlags(cmd *cobra.Command) {
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().String("generator", "", "The name of the API generator to use. Default is 'deployment/v1beta1' if --restart=Always, otherwise the default is 'job/v1'. This will happen only for cluster version at least 1.2, for olders we will fallback to 'run/v1' for --restart=Always, 'run-pod/v1' for others.")
cmd.Flags().String("image", "", "The image for the container to run.")
cmd.MarkFlagRequired("image")
cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.")
cmd.Flags().Bool("rm", false, "If true, delete resources created in this command for attached containers.")
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
cmd.Flags().String("overrides", "", "An inline JSON override for the generated object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field.")
cmd.Flags().StringSlice("env", []string{}, "Environment variables to set in the container")
cmd.Flags().Int("port", -1, "The port that this container exposes. If --expose is true, this is also the port used by the service that is created.")
@ -474,8 +474,7 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub
return nil, "", nil, nil, err
}
}
// TODO: extract this flag to a central location, when such a location exists.
if !cmdutil.GetFlagBool(cmd, "dry-run") {
if !cmdutil.GetDryRunFlag(cmd) {
resourceMapper := &resource.Mapper{
ObjectTyper: typer,
RESTMapper: mapper,

View File

@ -0,0 +1,151 @@
/*
Copyright 2016 The Kubernetes Authors 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 set
import (
"fmt"
"io"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/strategicpatch"
)
// selectContainers allows one or more containers to be matched against a string or wildcard
func selectContainers(containers []api.Container, spec string) ([]*api.Container, []*api.Container) {
out := []*api.Container{}
skipped := []*api.Container{}
for i, c := range containers {
if selectString(c.Name, spec) {
out = append(out, &containers[i])
} else {
skipped = append(skipped, &containers[i])
}
}
return out, skipped
}
// handlePodUpdateError prints a more useful error to the end user when mutating a pod.
func handlePodUpdateError(out io.Writer, err error, resource string) {
if statusError, ok := err.(*errors.StatusError); ok && errors.IsInvalid(err) {
errorDetails := statusError.Status().Details
if errorDetails.Kind == "Pod" {
all, match := true, false
for _, cause := range errorDetails.Causes {
if cause.Field == "spec" && strings.Contains(cause.Message, "may not update fields other than") {
fmt.Fprintf(out, "error: may not update %s in pod %q directly\n", resource, errorDetails.Name)
match = true
} else {
all = false
}
}
if all && match {
return
}
}
}
fmt.Fprintf(out, "error: %v\n", err)
}
// selectString returns true if the provided string matches spec, where spec is a string with
// a non-greedy '*' wildcard operator.
// TODO: turn into a regex and handle greedy matches and backtracking.
func selectString(s, spec string) bool {
if spec == "*" {
return true
}
if !strings.Contains(spec, "*") {
return s == spec
}
pos := 0
match := true
parts := strings.Split(spec, "*")
for i, part := range parts {
if len(part) == 0 {
continue
}
next := strings.Index(s[pos:], part)
switch {
// next part not in string
case next < pos:
fallthrough
// first part does not match start of string
case i == 0 && pos != 0:
fallthrough
// last part does not exactly match remaining part of string
case i == (len(parts)-1) && len(s) != (len(part)+next):
match = false
break
default:
pos = next
}
}
return match
}
// Patch represents the result of a mutation to an object.
type Patch struct {
Info *resource.Info
Err error
Before []byte
After []byte
Patch []byte
}
// CalculatePatches calls the mutation function on each provided info object, and generates a strategic merge patch for
// the changes in the object. Encoder must be able to encode the info into the appropriate destination type. If mutateFn
// returns false, the object is not included in the final list of patches.
func CalculatePatches(infos []*resource.Info, encoder runtime.Encoder, mutateFn func(*resource.Info) (bool, error)) []*Patch {
var patches []*Patch
for _, info := range infos {
patch := &Patch{Info: info}
patch.Before, patch.Err = runtime.Encode(encoder, info.Object)
ok, err := mutateFn(info)
if !ok {
continue
}
if err != nil {
patch.Err = err
}
patches = append(patches, patch)
if patch.Err != nil {
continue
}
patch.After, patch.Err = runtime.Encode(encoder, info.Object)
if patch.Err != nil {
continue
}
// TODO: should be via New
versioned, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion())
if err != nil {
patch.Err = err
continue
}
patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, versioned)
}
return patches
}

View File

@ -42,7 +42,8 @@ func NewCmdSet(f *cmdutil.Factory, out io.Writer) *cobra.Command {
},
}
// TODO: add subcommands
// add subcommands
cmd.AddCommand(NewCmdImage(f, out))
return cmd
}

View File

@ -0,0 +1,239 @@
/*
Copyright 2016 The Kubernetes Authors 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 set
import (
"fmt"
"io"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
)
// ImageOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags()
type ImageOptions struct {
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
Infos []*resource.Info
Encoder runtime.Encoder
Selector string
Out io.Writer
Err io.Writer
Filenames []string
Recursive bool
ShortOutput bool
All bool
Record bool
ChangeCause string
Local bool
Cmd *cobra.Command
PrintObject func(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
Resources []string
ContainerImages map[string]string
}
const (
image_resources = `
pod (po), replicationcontroller (rc), deployment, daemonset (ds), job, replicaset (rs)`
image_long = `Update existing container image(s) of resources.
Possible resources include (case insensitive):` + image_resources
image_example = `# Set a deployment's nginx container image to 'nginx:1.9.1', and its busybox container image to 'busybox'.
kubectl set image deployment/nginx busybox=busybox nginx=nginx:1.9.1
# Update all deployments' and rc's nginx container's image to 'nginx:1.9.1'
kubectl set image deployments,rc nginx=nginx:1.9.1 --all
# Update image of all containers of daemonset abc to 'nginx:1.9.1'
kubectl set image daemonset abc *=nginx:1.9.1
# Print result (in yaml format) of updating nginx container image from local file, without hitting the server
kubectl set image -f path/to/file.yaml nginx=nginx:1.9.1 --local -o yaml`
)
func NewCmdImage(f *cmdutil.Factory, out io.Writer) *cobra.Command {
options := &ImageOptions{
Out: out,
}
cmd := &cobra.Command{
Use: "image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N",
Short: "Update image of a pod template",
Long: image_long,
Example: image_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run())
},
}
cmdutil.AddPrinterFlags(cmd)
usage := "Filename, directory, or URL to a file identifying the resource to get from a server."
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
cmd.Flags().BoolVar(&options.All, "all", false, "select all resources in the namespace of the specified resource types")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on")
cmd.Flags().BoolVar(&options.Local, "local", false, "If true, set image will NOT contact api-server but run locally.")
cmdutil.AddRecordFlag(cmd)
cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
return cmd
}
func (o *ImageOptions) Complete(f *cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.Mapper, o.Typer = f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
o.Encoder = f.JSONEncoder()
o.ShortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
o.Record = cmdutil.GetRecordFlag(cmd)
o.ChangeCause = f.Command()
o.PrintObject = f.PrintObject
o.Cmd = cmd
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
o.Resources, o.ContainerImages, err = getResourcesAndImages(args)
if err != nil {
return err
}
builder := resource.NewBuilder(o.Mapper, o.Typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, o.Recursive, o.Filenames...).
Flatten()
if !o.Local {
builder = builder.
SelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.Resources...).
Latest()
}
o.Infos, err = builder.Do().Infos()
if err != nil {
return err
}
return nil
}
func (o *ImageOptions) Validate() error {
if len(o.Resources) < 1 && len(o.Filenames) == 0 {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
if len(o.ContainerImages) < 1 {
return fmt.Errorf("at least one image update is required")
} else if len(o.ContainerImages) > 1 && hasWildcardKey(o.ContainerImages) {
return fmt.Errorf("all containers are already specified by *, but saw more than one container_name=container_image pairs")
}
return nil
}
func (o *ImageOptions) Run() error {
allErrs := []error{}
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) (bool, error) {
transformed := false
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error {
for name, image := range o.ContainerImages {
containerFound := false
// Find the container to update, and update its image
for i, c := range spec.Containers {
if c.Name == name || name == "*" {
spec.Containers[i].Image = image
containerFound = true
// Perform updates
transformed = true
}
}
// Add a new container if not found
if !containerFound {
allErrs = append(allErrs, fmt.Errorf("error: unable to find container named %q", name))
}
}
return nil
})
return transformed, err
})
for _, patch := range patches {
info := patch.Info
if patch.Err != nil {
allErrs = append(allErrs, fmt.Errorf("error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err))
continue
}
// no changes
if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
continue
}
if o.Local {
fmt.Fprintln(o.Out, "running in local mode...")
return o.PrintObject(o.Cmd, o.Mapper, info.Object, o.Out)
}
// patch the change
obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, api.StrategicMergePatchType, patch.Patch)
if err != nil {
allErrs = append(allErrs, fmt.Errorf("failed to patch image update to pod template: %v\n", err))
continue
}
info.Refresh(obj, true)
// record this change (for rollout history)
if o.Record || cmdutil.ContainsChangeCause(info) {
if err := cmdutil.RecordChangeCause(obj, o.ChangeCause); err == nil {
if obj, err = resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, false, obj); err != nil {
allErrs = append(allErrs, fmt.Errorf("changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err))
}
}
}
info.Refresh(obj, true)
cmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, "image updated")
}
return utilerrors.NewAggregate(allErrs)
}
// getResourcesAndImages retrieves resources and container name:images pair from given args
func getResourcesAndImages(args []string) (resources []string, containerImages map[string]string, err error) {
pairType := "image"
resources, imageArgs, err := cmdutil.GetResourcesAndPairs(args, pairType)
if err != nil {
return
}
containerImages, _, err = cmdutil.ParsePairs(imageArgs, pairType, false)
return
}
func hasWildcardKey(containerImages map[string]string) bool {
_, ok := containerImages["*"]
return ok
}

View File

@ -139,6 +139,9 @@ type Factory struct {
CanBeAutoscaled func(kind unversioned.GroupKind) error
// AttachablePodForObject returns the pod to which to attach given an object.
AttachablePodForObject func(object runtime.Object) (*api.Pod, error)
// UpdatePodSpecForObject will call the provided function on the pod spec this object supports,
// return false if no pod spec is supported, or return an error.
UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error)
// EditorEnvs returns a group of environment variables that the edit command
// can range over in order to determine if the user has specified an editor
// of their choice.
@ -708,6 +711,31 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk)
}
},
// UpdatePodSpecForObject update the pod specification for the provided object
UpdatePodSpecForObject: func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) {
// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
switch t := obj.(type) {
case *api.Pod:
return true, fn(&t.Spec)
case *api.ReplicationController:
if t.Spec.Template == nil {
t.Spec.Template = &api.PodTemplateSpec{}
}
return true, fn(&t.Spec.Template.Spec)
case *extensions.Deployment:
return true, fn(&t.Spec.Template.Spec)
case *extensions.DaemonSet:
return true, fn(&t.Spec.Template.Spec)
case *extensions.ReplicaSet:
return true, fn(&t.Spec.Template.Spec)
case *apps.PetSet:
return true, fn(&t.Spec.Template.Spec)
case *batch.Job:
return true, fn(&t.Spec.Template.Spec)
default:
return false, fmt.Errorf("the object is not a pod or does not have a pod template")
}
},
EditorEnvs: func() []string {
return []string{"KUBE_EDITOR", "EDITOR"}
},

View File

@ -336,6 +336,11 @@ func AddRecursiveFlag(cmd *cobra.Command, value *bool) {
cmd.Flags().BoolVarP(value, "recursive", "R", *value, "If true, process directory recursively.")
}
// AddDryRunFlag adds dry-run flag to a command. Usually used by mutations.
func AddDryRunFlag(cmd *cobra.Command) {
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
}
func AddApplyAnnotationFlags(cmd *cobra.Command) {
cmd.Flags().Bool(ApplyAnnotationsFlag, false, "If true, the configuration of current object will be saved in its annotation. This is useful when you want to perform kubectl apply on this object in the future.")
}
@ -344,7 +349,7 @@ func AddApplyAnnotationFlags(cmd *cobra.Command) {
// TODO: need to take a pass at other generator commands to use this set of flags
func AddGeneratorFlags(cmd *cobra.Command, defaultGenerator string) {
cmd.Flags().String("generator", defaultGenerator, "The name of the API generator to use.")
cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.")
AddDryRunFlag(cmd)
}
func ReadConfigDataFromReader(reader io.Reader, source string) ([]byte, error) {
@ -433,6 +438,10 @@ func GetRecordFlag(cmd *cobra.Command) bool {
return GetFlagBool(cmd, "record")
}
func GetDryRunFlag(cmd *cobra.Command) bool {
return GetFlagBool(cmd, "dry-run")
}
// RecordChangeCause annotate change-cause to input runtime object.
func RecordChangeCause(obj runtime.Object, changeCause string) error {
accessor, err := meta.Accessor(obj)
@ -528,3 +537,60 @@ func GetIncludeThirdPartyAPIs(cmd *cobra.Command) bool {
func AddInclude3rdPartyFlags(cmd *cobra.Command) {
cmd.Flags().Bool("include-extended-apis", true, "If true, include definitions of new APIs via calls to the API server. [default true]")
}
// GetResourcesAndPairs retrieves resources and "KEY=VALUE or KEY-" pair args from given args
func GetResourcesAndPairs(args []string, pairType string) (resources []string, pairArgs []string, err error) {
foundPair := false
for _, s := range args {
nonResource := strings.Contains(s, "=") || strings.HasSuffix(s, "-")
switch {
case !foundPair && nonResource:
foundPair = true
fallthrough
case foundPair && nonResource:
pairArgs = append(pairArgs, s)
case !foundPair && !nonResource:
resources = append(resources, s)
case foundPair && !nonResource:
err = fmt.Errorf("all resources must be specified before %s changes: %s", pairType, s)
return
}
}
return
}
// ParsePairs retrieves new and remove pairs (if supportRemove is true) from "KEY=VALUE or KEY-" pair args
func ParsePairs(pairArgs []string, pairType string, supportRemove bool) (newPairs map[string]string, removePairs []string, err error) {
newPairs = map[string]string{}
if supportRemove {
removePairs = []string{}
}
var invalidBuf bytes.Buffer
for _, pairArg := range pairArgs {
if strings.Index(pairArg, "=") != -1 {
parts := strings.SplitN(pairArg, "=", 2)
if len(parts) != 2 || len(parts[1]) == 0 {
if invalidBuf.Len() > 0 {
invalidBuf.WriteString(", ")
}
invalidBuf.WriteString(fmt.Sprintf(pairArg))
} else {
newPairs[parts[0]] = parts[1]
}
} else if supportRemove && strings.HasSuffix(pairArg, "-") {
removePairs = append(removePairs, pairArg[:len(pairArg)-1])
} else {
if invalidBuf.Len() > 0 {
invalidBuf.WriteString(", ")
}
invalidBuf.WriteString(fmt.Sprintf(pairArg))
}
}
if invalidBuf.Len() > 0 {
err = fmt.Errorf("invalid %s format: %s", pairType, invalidBuf.String())
return
}
return
}