Skip to content

Commit

Permalink
Kubectl patch command accepts a filename param
Browse files Browse the repository at this point in the history
  • Loading branch information
feihujiang committed Aug 13, 2015
1 parent a6148e7 commit 0110f31
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 12 deletions.
6 changes: 6 additions & 0 deletions contrib/completions/bash/kubectl
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,12 @@ _kubectl_patch()
flags_with_completion=()
flags_completion=()

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+=("--help")
flags+=("-h")
flags+=("--output=")
Expand Down
7 changes: 7 additions & 0 deletions docs/man/man1/kubectl-patch.1
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Please refer to the models in


.SH OPTIONS
.PP
\fB\-f\fP, \fB\-\-filename\fP=[]
Filename, directory, or URL to a file identifying the resource to update

.PP
\fB\-h\fP, \fB\-\-help\fP=false
help for patch
Expand Down Expand Up @@ -144,6 +148,9 @@ Please refer to the models in
# Partially update a node using strategic merge patch
kubectl patch node k8s\-node\-1 \-p '{"spec":{"unschedulable":true}}'

# Partially update a node identified by the type and name specified in "node.json" using strategic merge patch
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"}]}}'

Expand Down
8 changes: 6 additions & 2 deletions docs/user-guide/kubectl/kubectl_patch.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ JSON and YAML formats are accepted.
Please refer to the models in https://htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/HEAD/docs/api-reference/definitions.html to find if a field is mutable.

```
kubectl patch TYPE NAME -p PATCH
kubectl patch (-f FILENAME | TYPE NAME) -p PATCH
```

### Examples
Expand All @@ -55,13 +55,17 @@ kubectl patch TYPE NAME -p PATCH
# Partially update a node using strategic merge patch
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'
# Partially update a node identified by the type and name specified in "node.json" using strategic merge patch
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"}]}}'
```

### Options

```
-f, --filename=[]: Filename, directory, or URL to a file identifying the resource to update
-h, --help[=false]: help for patch
-o, --output="": Output mode. Use "-o name" for shorter output (resource/name).
-p, --patch="": The patch to be applied to the resource JSON file.
Expand Down Expand Up @@ -100,7 +104,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 at 2015-08-12 21:51:38.836855054 +0000 UTC
###### Auto generated by spf13/cobra at 2015-08-13 02:12:21.577994505 +0000 UTC

<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_patch.md?pixel)]()
Expand Down
5 changes: 5 additions & 0 deletions hack/test-cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,11 @@ 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:'
## Patch pod from JSON can change image
# Command
kubectl patch "${kube_flags[@]}" -f docs/admin/limitrange/valid-pod.yaml -p='{"spec":{"containers":[{"name": "kubernetes-serve-hostname", "image": "kubernetes/pause"}]}}'
# Post-condition: valid-pod POD has image kubernetes/pause
kube::test::get_object_assert pods "{{range.items}}{{$image_field}}:{{end}}" 'kubernetes/pause:'

## --force replace pod can change other field, e.g., spec.container.name
# Command
Expand Down
29 changes: 19 additions & 10 deletions pkg/kubectl/cmd/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ limitations under the License.
package cmd

import (
"fmt"
"io"

"github.com/spf13/cobra"

"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
Expand All @@ -36,13 +37,16 @@ Please refer to the models in https://htmlpreview.github.io/?https://github.com/
# Partially update a node using strategic merge patch
kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'
# Partially update a node identified by the type and name specified in "node.json" using strategic merge patch
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"}]}}'`
)

func NewCmdPatch(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "patch TYPE NAME -p PATCH",
Use: "patch (-f FILENAME | TYPE NAME) -p PATCH",
Short: "Update field(s) of a resource by stdin.",
Long: patch_long,
Example: patch_example,
Expand All @@ -56,11 +60,14 @@ 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")
cmdutil.AddOutputFlagsForMutation(cmd)

usage := "Filename, directory, or URL to a file identifying the resource to update"
kubectl.AddJsonFilenameFlag(cmd, usage)
return cmd
}

func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool) error {
cmdNamespace, _, err := f.DefaultNamespace()
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
Expand All @@ -74,27 +81,29 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, cmdutil.GetFlagStringSlice(cmd, "filename")...).
ResourceTypeOrNameArgs(false, args...).
Flatten().
Do()
err = r.Err()
if err != nil {
return err
}
mapping, err := r.ResourceMapping()

infos, err := r.Infos()
if err != nil {
return err
}
client, err := f.RESTClient(mapping)
if err != nil {
return err
if len(infos) > 1 {
return fmt.Errorf("multiple resources provided")
}

infos, err := r.Infos()
info := infos[0]
name, namespace := info.Name, info.Namespace
mapping := info.ResourceMapping()
client, err := f.RESTClient(mapping)
if err != nil {
return err
}
name, namespace := infos[0].Name, infos[0].Namespace

helper := resource.NewHelper(client, mapping)
_, err = helper.Patch(namespace, name, api.StrategicMergePatchType, []byte(patch))
Expand Down
33 changes: 33 additions & 0 deletions pkg/kubectl/cmd/patch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,36 @@ func TestPatchObject(t *testing.T) {
t.Errorf("unexpected output: %s", buf.String())
}
}

func TestPatchObjectFromFile(t *testing.T) {
_, svc, _ := testData()

f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{}
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 == "/namespaces/test/services/frontend" && (m == "PATCH" || m == "GET"):
return &http.Response{StatusCode: 200, Body: objBody(codec, &svc.Items[0])}, nil
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})

cmd := NewCmdPatch(f, buf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
cmd.Flags().Set("output", "name")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml")
cmd.Run(cmd, []string{})

// uses the name from the file, not the response
if buf.String() != "frontend\n" {
t.Errorf("unexpected output: %s", buf.String())
}
}

0 comments on commit 0110f31

Please sign in to comment.