From 9f003f7db311ef2042ff93f5c94dae611f472c7e Mon Sep 17 00:00:00 2001 From: deads2k Date: Thu, 11 Feb 2016 15:52:42 -0500 Subject: [PATCH] allow patch to handle multiple types --- contrib/completions/bash/kubectl | 1 + docs/man/man1/kubectl-patch.1 | 7 +++++++ docs/user-guide/kubectl/kubectl_patch.md | 6 +++++- hack/test-cmd.sh | 8 ++++++++ pkg/kubectl/cmd/patch.go | 22 ++++++++++++++++++++-- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 4448f5c298a..eab1812c4ec 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -754,6 +754,7 @@ _kubectl_patch() flags+=("--patch=") two_word_flags+=("-p") flags+=("--record") + flags+=("--type=") flags+=("--alsologtostderr") flags+=("--api-version=") flags+=("--certificate-authority=") diff --git a/docs/man/man1/kubectl-patch.1 b/docs/man/man1/kubectl-patch.1 index ca1f1089924..bf506f95c2d 100644 --- a/docs/man/man1/kubectl-patch.1 +++ b/docs/man/man1/kubectl-patch.1 @@ -40,6 +40,10 @@ Please refer to the models in \fB\-\-record\fP=false Record current kubectl command in the resource annotation. +.PP +\fB\-\-type\fP="strategic" + The type of patch being provided; one of [json merge strategic] + .SH OPTIONS INHERITED FROM PARENT COMMANDS .PP @@ -150,6 +154,9 @@ kubectl patch \-f node.json \-p '{"spec":{"unschedulable":true}}' # Update a container's image; spec.containers[*].name is required because it's a merge key kubectl patch pod valid\-pod \-p '{"spec":{"containers":[{"name":"kubernetes\-serve\-hostname","image":"new image"}]}}' +# Update a container's image using a json patch with positional arrays +kubectl patch pod valid\-pod \-type='json' \-p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]' + .fi .RE diff --git a/docs/user-guide/kubectl/kubectl_patch.md b/docs/user-guide/kubectl/kubectl_patch.md index 6d67664c4aa..6f30dadcc84 100644 --- a/docs/user-guide/kubectl/kubectl_patch.md +++ b/docs/user-guide/kubectl/kubectl_patch.md @@ -61,6 +61,9 @@ kubectl patch -f node.json -p '{"spec":{"unschedulable":true}}' # Update a container's image; spec.containers[*].name is required because it's a merge key kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}' + +# Update a container's image using a json patch with positional arrays +kubectl patch pod valid-pod -type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]' ``` ### Options @@ -70,6 +73,7 @@ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve -o, --output="": Output mode. Use "-o name" for shorter output (resource/name). -p, --patch="": The patch to be applied to the resource JSON file. --record[=false]: Record current kubectl command in the resource annotation. + --type="strategic": The type of patch being provided; one of [json merge strategic] ``` ### Options inherited from parent commands @@ -104,7 +108,7 @@ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 22-Jan-2016 +###### Auto generated by spf13/cobra on 12-Feb-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_patch.md?pixel)]() diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 64acf9cf944..e4588fd507b 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -441,6 +441,14 @@ runTests() { kubectl patch "${kube_flags[@]}" pod valid-pod -p='{"spec":{"containers":[{"name": "kubernetes-serve-hostname", "image": "nginx"}]}}' # Post-condition: valid-pod POD has image nginx kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:' + # prove that patch can use different types + kubectl patch "${kube_flags[@]}" pod valid-pod --type="json" -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"nginx2"}]' + # Post-condition: valid-pod POD has image nginx + kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx2:' + # prove that patch can use different types + kubectl patch "${kube_flags[@]}" pod valid-pod --type="json" -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"nginx"}]' + # Post-condition: valid-pod POD has image nginx + kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'nginx:' # prove that yaml input works too YAML_PATCH=$'spec:\n containers:\n - name: kubernetes-serve-hostname\n image: changed-with-yaml\n' kubectl patch "${kube_flags[@]}" pod valid-pod -p="${YAML_PATCH}" diff --git a/pkg/kubectl/cmd/patch.go b/pkg/kubectl/cmd/patch.go index 9bce6f2088f..cd35b9d3038 100644 --- a/pkg/kubectl/cmd/patch.go +++ b/pkg/kubectl/cmd/patch.go @@ -19,6 +19,7 @@ package cmd import ( "fmt" "io" + "strings" "github.com/spf13/cobra" @@ -26,9 +27,12 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/util/yaml" ) +var patchTypes = map[string]api.PatchType{"json": api.JSONPatchType, "merge": api.MergePatchType, "strategic": api.StrategicMergePatchType} + // PatchOptions 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 PatchOptions struct { @@ -49,7 +53,10 @@ kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' kubectl patch -f node.json -p '{"spec":{"unschedulable":true}}' # Update a container's image; spec.containers[*].name is required because it's a merge key -kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'` +kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}' + +# Update a container's image using a json patch with positional arrays +kubectl patch pod valid-pod -type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'` ) func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command { @@ -69,6 +76,7 @@ func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command { } cmd.Flags().StringP("patch", "p", "", "The patch to be applied to the resource JSON file.") cmd.MarkFlagRequired("patch") + cmd.Flags().String("type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.StringKeySet(patchTypes).List())) cmdutil.AddOutputFlagsForMutation(cmd) cmdutil.AddRecordFlag(cmd) @@ -83,6 +91,16 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri return err } + patchType := api.StrategicMergePatchType + patchTypeString := strings.ToLower(cmdutil.GetFlagString(cmd, "type")) + if len(patchTypeString) != 0 { + ok := false + patchType, ok = patchTypes[patchTypeString] + if !ok { + return cmdutil.UsageError(cmd, fmt.Sprintf("--type must be one of %v, not %q", sets.StringKeySet(patchTypes).List(), patchTypeString)) + } + } + patch := cmdutil.GetFlagString(cmd, "patch") if len(patch) == 0 { return cmdutil.UsageError(cmd, "Must specify -p to patch") @@ -121,7 +139,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri } helper := resource.NewHelper(client, mapping) - _, err = helper.Patch(namespace, name, api.StrategicMergePatchType, patchBytes) + _, err = helper.Patch(namespace, name, patchType, patchBytes) if err != nil { return err }