diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index ba60f1c73a..639b947469 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -361,6 +361,7 @@ _kubectl_create() flags_completion+=("__handle_filename_extension_flag json|yaml|yml") flags+=("--output=") two_word_flags+=("-o") + flags+=("--save-config") flags+=("--schema-cache-dir=") flags+=("--validate") @@ -391,6 +392,7 @@ _kubectl_replace() flags+=("--grace-period=") flags+=("--output=") two_word_flags+=("-o") + flags+=("--save-config") flags+=("--schema-cache-dir=") flags+=("--timeout=") flags+=("--validate") @@ -498,6 +500,7 @@ _kubectl_edit() flags+=("--output=") two_word_flags+=("-o") flags+=("--output-version=") + flags+=("--save-config") flags+=("--windows-line-endings") must_have_one_flag=() @@ -766,6 +769,7 @@ _kubectl_run() two_word_flags+=("-r") flags+=("--requests=") flags+=("--restart=") + flags+=("--save-config") flags+=("--service-generator=") flags+=("--service-overrides=") flags+=("--show-all") @@ -843,6 +847,7 @@ _kubectl_expose() flags+=("--overrides=") flags+=("--port=") flags+=("--protocol=") + flags+=("--save-config") flags+=("--selector=") flags+=("--session-affinity=") flags+=("--show-all") @@ -883,6 +888,7 @@ _kubectl_autoscale() flags+=("--output=") two_word_flags+=("-o") flags+=("--output-version=") + flags+=("--save-config") flags+=("--show-all") flags+=("-a") flags+=("--sort-by=") diff --git a/docs/man/man1/kubectl-autoscale.1 b/docs/man/man1/kubectl-autoscale.1 index a4fd1d865e..0dda920eb7 100644 --- a/docs/man/man1/kubectl-autoscale.1 +++ b/docs/man/man1/kubectl-autoscale.1 @@ -63,6 +63,10 @@ An autoscaler can automatically increase or decrease number of pods deployed wit \fB\-\-output\-version\fP="" Output the formatted object with the given version (default api\-version). +.PP +\fB\-\-save\-config\fP=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. + .PP \fB\-a\fP, \fB\-\-show\-all\fP=false When printing, show all resources (default hide terminated pods.) diff --git a/docs/man/man1/kubectl-create.1 b/docs/man/man1/kubectl-create.1 index 653554aaa4..ded0717d01 100644 --- a/docs/man/man1/kubectl-create.1 +++ b/docs/man/man1/kubectl-create.1 @@ -28,6 +28,10 @@ JSON and YAML formats are accepted. \fB\-o\fP, \fB\-\-output\fP="" Output mode. Use "\-o name" for shorter output (resource/name). +.PP +\fB\-\-save\-config\fP=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. + .PP \fB\-\-schema\-cache\-dir\fP="\~/.kube/schema" If non\-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' diff --git a/docs/man/man1/kubectl-edit.1 b/docs/man/man1/kubectl-edit.1 index 50ac32251a..ae445238f1 100644 --- a/docs/man/man1/kubectl-edit.1 +++ b/docs/man/man1/kubectl-edit.1 @@ -50,6 +50,10 @@ saved copy to include the latest resource version. \fB\-\-output\-version\fP="" Output the formatted object with the given version (default api\-version). +.PP +\fB\-\-save\-config\fP=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. + .PP \fB\-\-windows\-line\-endings\fP=false Use Windows line\-endings (default Unix line\-endings) diff --git a/docs/man/man1/kubectl-expose.1 b/docs/man/man1/kubectl-expose.1 index fc147f1e0b..d889655b60 100644 --- a/docs/man/man1/kubectl-expose.1 +++ b/docs/man/man1/kubectl-expose.1 @@ -85,6 +85,10 @@ the new service will re\-use the labels from the resource it exposes. \fB\-\-protocol\fP="TCP" The network protocol for the service to be created. Default is 'tcp'. +.PP +\fB\-\-save\-config\fP=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. + .PP \fB\-\-selector\fP="" A label selector to use for this service. If empty (the default) infer the selector from the replication controller. diff --git a/docs/man/man1/kubectl-replace.1 b/docs/man/man1/kubectl-replace.1 index 4da4d9f706..1085a53612 100644 --- a/docs/man/man1/kubectl-replace.1 +++ b/docs/man/man1/kubectl-replace.1 @@ -46,6 +46,10 @@ Please refer to the models in \fB\-o\fP, \fB\-\-output\fP="" Output mode. Use "\-o name" for shorter output (resource/name). +.PP +\fB\-\-save\-config\fP=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. + .PP \fB\-\-schema\-cache\-dir\fP="\~/.kube/schema" If non\-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' diff --git a/docs/man/man1/kubectl-run.1 b/docs/man/man1/kubectl-run.1 index a40d494ac8..b10d35c0bd 100644 --- a/docs/man/man1/kubectl-run.1 +++ b/docs/man/man1/kubectl-run.1 @@ -96,6 +96,10 @@ Creates a replication controller to manage the created container(s). \fB\-\-restart\fP="Always" The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and \-\-replicas must be 1. Default 'Always' +.PP +\fB\-\-save\-config\fP=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. + .PP \fB\-\-service\-generator\fP="service/v2" The name of the generator to use for creating a service. Only used if \-\-expose is true diff --git a/docs/user-guide/kubectl/kubectl_autoscale.md b/docs/user-guide/kubectl/kubectl_autoscale.md index a535f2a684..0bd81032c2 100644 --- a/docs/user-guide/kubectl/kubectl_autoscale.md +++ b/docs/user-guide/kubectl/kubectl_autoscale.md @@ -70,6 +70,7 @@ $ kubectl autoscale rc foo --max=5 --cpu-percent=80 --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 version (default api-version). + --save-config[=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. -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) --sort-by="": If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. 'ObjectMeta.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]. @@ -107,7 +108,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 16-Oct-2015 +###### Auto generated by spf13/cobra on 6-Nov-2015 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_autoscale.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_create.md b/docs/user-guide/kubectl/kubectl_create.md index 505a96260f..8e53cf554f 100644 --- a/docs/user-guide/kubectl/kubectl_create.md +++ b/docs/user-guide/kubectl/kubectl_create.md @@ -61,6 +61,7 @@ $ cat pod.json | kubectl create -f - ``` -f, --filename=[]: Filename, directory, or URL to file to use to create the resource -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). + --save-config[=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. --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' --validate[=true]: If true, use a schema to validate the input before sending it ``` @@ -97,7 +98,7 @@ $ cat pod.json | kubectl create -f - * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 9-Oct-2015 +###### Auto generated by spf13/cobra on 6-Nov-2015 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_create.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_edit.md b/docs/user-guide/kubectl/kubectl_edit.md index dcfebe37ad..5a3a3b3fd4 100644 --- a/docs/user-guide/kubectl/kubectl_edit.md +++ b/docs/user-guide/kubectl/kubectl_edit.md @@ -81,6 +81,7 @@ kubectl edit (RESOURCE/NAME | -f FILENAME) -f, --filename=[]: Filename, directory, or URL to file to use to edit the resource -o, --output="yaml": Output format. One of: yaml|json. --output-version="": Output the formatted object with the given version (default api-version). + --save-config[=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. --windows-line-endings[=false]: Use Windows line-endings (default Unix line-endings) ``` @@ -116,7 +117,7 @@ kubectl edit (RESOURCE/NAME | -f FILENAME) * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 23-Oct-2015 +###### Auto generated by spf13/cobra on 6-Nov-2015 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_edit.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_expose.md b/docs/user-guide/kubectl/kubectl_expose.md index 3d75379f04..d56a941481 100644 --- a/docs/user-guide/kubectl/kubectl_expose.md +++ b/docs/user-guide/kubectl/kubectl_expose.md @@ -85,6 +85,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream --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. --port="": The port that the service should serve on. Copied from the resource being exposed, if unspecified --protocol="TCP": The network protocol for the service to be created. Default is 'tcp'. + --save-config[=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. --selector="": A label selector to use for this service. If empty (the default) infer the selector from the replication controller. --session-affinity="": If non-empty, set the session affinity for the service to this; legal values: 'None', 'ClientIP' -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) @@ -126,7 +127,7 @@ $ kubectl expose rc streamer --port=4100 --protocol=udp --name=video-stream * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-10-14 10:34:09.969832007 +0000 UTC +###### Auto generated by spf13/cobra on 6-Nov-2015 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_expose.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_replace.md b/docs/user-guide/kubectl/kubectl_replace.md index 1229967eae..29ca4bd5dc 100644 --- a/docs/user-guide/kubectl/kubectl_replace.md +++ b/docs/user-guide/kubectl/kubectl_replace.md @@ -74,6 +74,7 @@ kubectl replace --force -f ./pod.json --force[=false]: Delete and re-create the specified resource --grace-period=-1: Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative. -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). + --save-config[=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. --schema-cache-dir="~/.kube/schema": If non-empty, load/store cached API schemas in this directory, default is '$HOME/.kube/schema' --timeout=0: Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object --validate[=true]: If true, use a schema to validate the input before sending it @@ -111,7 +112,7 @@ kubectl replace --force -f ./pod.json * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 9-Oct-2015 +###### Auto generated by spf13/cobra on 6-Nov-2015 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_replace.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_run.md b/docs/user-guide/kubectl/kubectl_run.md index 22f1771482..e2e5af3f7d 100644 --- a/docs/user-guide/kubectl/kubectl_run.md +++ b/docs/user-guide/kubectl/kubectl_run.md @@ -98,6 +98,7 @@ $ kubectl run nginx --image=nginx --command -- ... -r, --replicas=1: Number of replicas to create for this container. Default is 1. --requests="": The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi' --restart="Always": The restart policy for this Pod. Legal values [Always, OnFailure, Never]. If set to 'Always' a replication controller is created for this pod, if set to OnFailure or Never, only the Pod is created and --replicas must be 1. Default 'Always' + --save-config[=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. --service-generator="service/v2": The name of the generator to use for creating a service. Only used if --expose is true --service-overrides="": An inline JSON override for the generated service object. If this is non-empty, it is used to override the generated object. Requires that the object supply a valid apiVersion field. Only used if --expose is true. -a, --show-all[=false]: When printing, show all resources (default hide terminated pods.) @@ -139,7 +140,7 @@ $ kubectl run nginx --image=nginx --command -- ... * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 30-Oct-2015 +###### Auto generated by spf13/cobra on 6-Nov-2015 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_run.md?pixel)]() diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 3777095773..29c02b02c0 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -570,6 +570,70 @@ runTests() { ! [[ $(diff -q "${KUBE_TEMP}"/annotation-configuration "${KUBE_TEMP}"/annotation-configuration-replaced > /dev/null) ]] # Clean up rm "${KUBE_TEMP}"/test-pod-replace.yaml "${KUBE_TEMP}"/annotation-configuration "${KUBE_TEMP}"/annotation-configuration-replaced + kubectl delete pods test-pod "${kube_flags[@]}" + + ## Configuration annotations should be set when --save-config is enabled + ## 1. kubectl create --save-config should generate configuration annotation + # Pre-Condition: no POD is running + kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" '' + # Command: create a pod "test-pod" + kubectl create -f hack/testdata/pod.yaml --save-config "${kube_flags[@]}" + # Post-Condition: pod "test-pod" has configuration annotation + [[ "$(kubectl get pods test-pod -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Clean up + kubectl delete -f hack/testdata/pod.yaml "${kube_flags[@]}" + ## 2. kubectl edit --save-config should generate configuration annotation + # Pre-Condition: no POD is running, then create pod "test-pod", which shouldn't have configuration annotation + kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" '' + kubectl create -f hack/testdata/pod.yaml "${kube_flags[@]}" + ! [[ "$(kubectl get pods test-pod -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Command: edit the pod "test-pod" + temp_editor="${KUBE_TEMP}/tmp-editor.sh" + echo -e '#!/bin/bash\nsed -i "s/test-pod-label/test-pod-label-edited/g" $@' > "${temp_editor}" + chmod +x "${temp_editor}" + EDITOR=${temp_editor} kubectl edit pod test-pod --save-config "${kube_flags[@]}" + # Post-Condition: pod "test-pod" has configuration annotation + [[ "$(kubectl get pods test-pod -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Clean up + kubectl delete -f hack/testdata/pod.yaml "${kube_flags[@]}" + ## 3. kubectl replace --save-config should generate configuration annotation + # Pre-Condition: no POD is running, then create pod "test-pod", which shouldn't have configuration annotation + kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" '' + kubectl create -f hack/testdata/pod.yaml "${kube_flags[@]}" + ! [[ "$(kubectl get pods test-pod -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Command: replace the pod "test-pod" + kubectl replace -f hack/testdata/pod.yaml --save-config "${kube_flags[@]}" + # Post-Condition: pod "test-pod" has configuration annotation + [[ "$(kubectl get pods test-pod -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Clean up + kubectl delete -f hack/testdata/pod.yaml "${kube_flags[@]}" + ## 4. kubectl run --save-config should generate configuration annotation + # Pre-Condition: no RC is running + kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" '' + # Command: create the rc "nginx" with image nginx + kubectl run nginx --image=nginx --save-config "${kube_flags[@]}" + # Post-Condition: rc "nginx" has configuration annotation + [[ "$(kubectl get rc nginx -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + ## 5. kubectl expose --save-config should generate configuration annotation + # Pre-Condition: no service is running + kube::test::get_object_assert svc "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:' + # Command: expose the rc "nginx" + kubectl expose rc nginx --save-config --port=80 --target-port=8000 "${kube_flags[@]}" + # Post-Condition: service "nginx" has configuration annotation + [[ "$(kubectl get svc nginx -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Clean up + kubectl delete rc,svc nginx + ## 6. kubectl autoscale --save-config should generate configuration annotation + # Pre-Condition: no RC is running, then create the rc "frontend", which shouldn't have configuration annotation + kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" '' + kubectl create -f examples/guestbook/frontend-controller.yaml "${kube_flags[@]}" + ! [[ "$(kubectl get rc frontend -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Command: autoscale rc "frontend" + kubectl autoscale -f examples/guestbook/frontend-controller.yaml --save-config "${kube_flags[@]}" --max=2 + # Post-Condition: hpa "frontend" has configuration annotation + [[ "$(kubectl get hpa frontend -o yaml "${kube_flags[@]}" | grep kubectl.kubernetes.io/last-applied-configuration)" ]] + # Clean up + kubectl delete rc,hpa frontend ############## # Namespaces # diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index 8f9172dfa6..b43e16f5a2 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -266,6 +266,7 @@ root-ca-file root-dir run-proxy runtime-config +save-config scheduler-config schema-cache-dir secure-port diff --git a/pkg/kubectl/apply.go b/pkg/kubectl/apply.go index 35600fb043..f8e89046a7 100644 --- a/pkg/kubectl/apply.go +++ b/pkg/kubectl/apply.go @@ -163,26 +163,30 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error return modified, nil } -// If the last applied configuration annotation is already present, then -// UpdateApplyAnnotation gets the modified configuration of the object, -// without embedding it again, and then sets it on the object as the annotation. -// Otherwise, it does nothing. +// UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied +// configuration annotation is already present. Otherwise, it does nothing. func UpdateApplyAnnotation(info *resource.Info) error { - original, err := GetOriginalConfiguration(info) + if original, err := GetOriginalConfiguration(info); err != nil || len(original) <= 0 { + return err + } + return CreateApplyAnnotation(info) +} + +// CreateApplyAnnotation gets the modified configuration of the object, +// without embedding it again, and then sets it on the object as the annotation. +func CreateApplyAnnotation(info *resource.Info) error { + modified, err := GetModifiedConfiguration(info, false) if err != nil { return err } - - if len(original) > 0 { - modified, err := GetModifiedConfiguration(info, false) - if err != nil { - return err - } - - if err := SetOriginalConfiguration(info, modified); err != nil { - return err - } - } - - return nil + return SetOriginalConfiguration(info, modified) +} + +// Create the annotation used by kubectl apply only when createAnnotation is true +// Otherwise, only update the annotation when it already exists +func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info) error { + if createAnnotation { + return CreateApplyAnnotation(info) + } + return UpdateApplyAnnotation(info) } diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index 5faef92897..92cce454c9 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -64,6 +64,7 @@ func NewCmdAutoscale(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without creating it.") usage := "Filename, directory, or URL to a file identifying the resource to get from a server." kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) + cmdutil.AddApplyAnnotationFlags(cmd) return cmd } @@ -134,8 +135,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return f.PrintObject(cmd, object, out) } - // Serialize the configuration into an annotation. - if err := kubectl.UpdateApplyAnnotation(hpa); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa); err != nil { return err } diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index b4edb3281e..fb4677fb36 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -67,6 +67,7 @@ func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.MarkFlagRequired("filename") cmdutil.AddValidateFlags(cmd) cmdutil.AddOutputFlagsForMutation(cmd) + cmdutil.AddApplyAnnotationFlags(cmd) return cmd } @@ -106,9 +107,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C if err != nil { return err } - - // Update the annotation used by kubectl apply - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { return cmdutil.AddSourceToErr("creating", info.Source, err) } diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index f5a9467be2..840e9d4c22 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -95,6 +95,7 @@ func NewCmdEdit(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.") cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).") cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") + cmdutil.AddApplyAnnotationFlags(cmd) return cmd } @@ -220,8 +221,12 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin return fmt.Errorf("The edited file had a syntax error: %v", err) } - // annotate the edited object for kubectl apply - if err := kubectl.UpdateApplyAnnotation(updates); err != nil { + // put configuration annotation in "updates" + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates); err != nil { + return preservedFile(err, file, out) + } + // encode updates back to "edited" since we'll only generate patch from "edited" + if edited, err = updates.Mapping.Codec.Encode(updates.Object); err != nil { return preservedFile(err, file, out) } diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index 52af5b00e8..6bbc6f23de 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -93,6 +93,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.AddApplyAnnotationFlags(cmd) return cmd } @@ -201,8 +202,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str if cmdutil.GetFlagBool(cmd, "dry-run") { return f.PrintObject(cmd, object, out) } - // Serialize the configuration into an annotation. - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { return err } diff --git a/pkg/kubectl/cmd/replace.go b/pkg/kubectl/cmd/replace.go index 4dd7caae94..de50f8de91 100644 --- a/pkg/kubectl/cmd/replace.go +++ b/pkg/kubectl/cmd/replace.go @@ -83,6 +83,7 @@ func NewCmdReplace(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object") cmdutil.AddValidateFlags(cmd) cmdutil.AddOutputFlagsForMutation(cmd) + cmdutil.AddApplyAnnotationFlags(cmd) return cmd } @@ -128,9 +129,8 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st return err } - // Serialize the configuration into an annotation. - if err := kubectl.UpdateApplyAnnotation(info); err != nil { - return err + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { + return cmdutil.AddSourceToErr("replacing", info.Source, err) } // Serialize the object with the annotation applied. @@ -216,8 +216,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args [] return err } - // Serialize the configuration into an annotation. - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { return err } diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index 3b71f23128..0b88a8c26e 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -78,6 +78,7 @@ func NewCmdRun(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *c } cmdutil.AddPrinterFlags(cmd) addRunFlags(cmd) + cmdutil.AddApplyAnnotationFlags(cmd) return cmd } @@ -371,8 +372,7 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub return nil, "", nil, nil, err } - // Serialize the configuration into an annotation. - if err := kubectl.UpdateApplyAnnotation(info); err != nil { + if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { return nil, "", nil, nil, err } diff --git a/pkg/kubectl/cmd/run_test.go b/pkg/kubectl/cmd/run_test.go index 422da9eb70..0428166393 100644 --- a/pkg/kubectl/cmd/run_test.go +++ b/pkg/kubectl/cmd/run_test.go @@ -235,6 +235,7 @@ func TestGenerateService(t *testing.T) { } cmd := &cobra.Command{} cmd.Flags().String("output", "", "") + cmd.Flags().Bool(cmdutil.ApplyAnnotationsFlag, false, "") addRunFlags(cmd) if !test.expectPOST { diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 9f191d1629..8159310c43 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -28,7 +28,6 @@ import ( "strings" "time" - "github.com/evanphx/json-patch" "k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/unversioned" @@ -39,11 +38,16 @@ import ( "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" + "github.com/evanphx/json-patch" "github.com/golang/glog" "github.com/spf13/cobra" "github.com/spf13/pflag" ) +const ( + ApplyAnnotationsFlag = "save-config" +) + type debugError interface { DebugError() (msg string, args []interface{}) } @@ -283,6 +287,10 @@ func AddValidateFlags(cmd *cobra.Command) { cmd.Flags().String("schema-cache-dir", fmt.Sprintf("~/%s/%s", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName), fmt.Sprintf("If non-empty, load/store cached API schemas in this directory, default is '$HOME/%s/%s'", clientcmd.RecommendedHomeDir, clientcmd.RecommendedSchemaName)) } +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.") +} + func ReadConfigDataFromReader(reader io.Reader, source string) ([]byte, error) { data, err := ioutil.ReadAll(reader) if err != nil {