diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 46084270960..97022f9e00b 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -832,6 +832,12 @@ _kubectl_annotate() 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+=("--help") flags+=("-h") flags+=("--no-headers") diff --git a/docs/man/man1/kubectl-annotate.1 b/docs/man/man1/kubectl-annotate.1 index c218eb0cea1..f6b9bf9b5f9 100644 --- a/docs/man/man1/kubectl-annotate.1 +++ b/docs/man/man1/kubectl-annotate.1 @@ -33,6 +33,10 @@ resourcequotas (quota) or secrets. \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 update the annotation + .PP \fB\-h\fP, \fB\-\-help\fP=false help for annotate @@ -179,6 +183,9 @@ resourcequotas (quota) or secrets. # If the same annotation is set multiple times, only the last value will be applied $ kubectl annotate pods foo description='my frontend' +# Update a pod identified by type and name in "pod.json" +$ kubectl annotate \-f pod.json description='my frontend' + # Update pod 'foo' with the annotation 'description' and the value 'my frontend running nginx', overwriting any existing value. $ kubectl annotate \-\-overwrite pods foo description='my frontend running nginx' diff --git a/docs/man/man1/kubectl-describe.1 b/docs/man/man1/kubectl-describe.1 index 5d025316fa3..61dba11d7bd 100644 --- a/docs/man/man1/kubectl-describe.1 +++ b/docs/man/man1/kubectl-describe.1 @@ -156,7 +156,7 @@ $ kubectl describe nodes kubernetes\-minion\-emt8.c.myproject.internal # Describe a pod $ kubectl describe pods/nginx -# Describe a pod using the data in pod.json. +# Describe a pod identified by type and name in "pod.json" $ kubectl describe \-f pod.json # Describe all pods diff --git a/docs/user-guide/kubectl/kubectl_annotate.md b/docs/user-guide/kubectl/kubectl_annotate.md index 0a5d64ada1c..ee0c1a54875 100644 --- a/docs/user-guide/kubectl/kubectl_annotate.md +++ b/docs/user-guide/kubectl/kubectl_annotate.md @@ -51,7 +51,7 @@ limitranges (limits), persistentvolumes (pv), persistentvolumeclaims (pvc), resourcequotas (quota) or secrets. ``` -kubectl annotate [--overwrite] RESOURCE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version] +kubectl annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version] ``` ### Examples @@ -61,6 +61,9 @@ kubectl annotate [--overwrite] RESOURCE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--reso # If the same annotation is set multiple times, only the last value will be applied $ kubectl annotate pods foo description='my frontend' +# Update a pod identified by type and name in "pod.json" +$ kubectl annotate -f pod.json description='my frontend' + # Update pod 'foo' with the annotation 'description' and the value 'my frontend running nginx', overwriting any existing value. $ kubectl annotate --overwrite pods foo description='my frontend running nginx' @@ -79,6 +82,7 @@ $ kubectl annotate pods foo description- ``` --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 update the annotation -h, --help[=false]: help for annotate --no-headers[=false]: When using the default output, don't print headers. -o, --output="": Output format. One of: json|yaml|template|templatefile|wide|jsonpath. @@ -123,7 +127,7 @@ $ kubectl annotate pods foo description- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-20 22:01:12.4790376 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-21 07:07:55.977091863 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_annotate.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_describe.md b/docs/user-guide/kubectl/kubectl_describe.md index b7441b7f494..074aa0bda2f 100644 --- a/docs/user-guide/kubectl/kubectl_describe.md +++ b/docs/user-guide/kubectl/kubectl_describe.md @@ -66,7 +66,7 @@ $ kubectl describe nodes kubernetes-minion-emt8.c.myproject.internal # Describe a pod $ kubectl describe pods/nginx -# Describe a pod using the data in pod.json. +# Describe a pod identified by type and name in "pod.json" $ kubectl describe -f pod.json # Describe all pods @@ -121,7 +121,7 @@ $ kubectl describe pods frontend * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-20 22:01:12.475314072 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-21 07:07:55.972896481 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_describe.md?pixel)]() diff --git a/pkg/kubectl/cmd/annotation.go b/pkg/kubectl/cmd/annotation.go index 89a1ae56f99..8c6ad7d3b34 100644 --- a/pkg/kubectl/cmd/annotation.go +++ b/pkg/kubectl/cmd/annotation.go @@ -24,6 +24,7 @@ import ( "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" "k8s.io/kubernetes/pkg/runtime" @@ -36,6 +37,7 @@ type AnnotateOptions struct { newAnnotations map[string]string removeAnnotations []string builder *resource.Builder + filenames []string overwrite bool all bool @@ -58,6 +60,9 @@ resourcequotas (quota) or secrets.` # If the same annotation is set multiple times, only the last value will be applied $ kubectl annotate pods foo description='my frontend' +# Update a pod identified by type and name in "pod.json" +$ kubectl annotate -f pod.json description='my frontend' + # Update pod 'foo' with the annotation 'description' and the value 'my frontend running nginx', overwriting any existing value. $ kubectl annotate --overwrite pods foo description='my frontend running nginx' @@ -76,11 +81,12 @@ func NewCmdAnnotate(f *cmdutil.Factory, out io.Writer) *cobra.Command { options := &AnnotateOptions{} cmd := &cobra.Command{ - Use: "annotate [--overwrite] RESOURCE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", + Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", Short: "Update the annotations on a resource", Long: annotate_long, Example: annotate_example, Run: func(cmd *cobra.Command, args []string) { + options.filenames = cmdutil.GetFlagStringSlice(cmd, "filename") if err := options.Complete(f, args, out); err != nil { cmdutil.CheckErr(err) } @@ -96,12 +102,14 @@ func NewCmdAnnotate(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().BoolVar(&options.overwrite, "overwrite", false, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.") cmd.Flags().BoolVar(&options.all, "all", false, "select all resources in the namespace of the specified resource types") cmd.Flags().StringVar(&options.resourceVersion, "resource-version", "", "If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.") + usage := "Filename, directory, or URL to a file identifying the resource to update the annotation" + kubectl.AddJsonFilenameFlag(cmd, usage) return cmd } // Complete adapts from the command line args and factory to the data required. func (o *AnnotateOptions) Complete(f *cmdutil.Factory, args []string, out io.Writer) (err error) { - namespace, _, err := f.DefaultNamespace() + namespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } @@ -124,7 +132,7 @@ func (o *AnnotateOptions) Complete(f *cmdutil.Factory, args []string, out io.Wri return fmt.Errorf("all resources must be specified before annotation changes: %s", s) } } - if len(o.resources) < 1 { + if len(o.resources) < 1 && len(o.filenames) == 0 { return fmt.Errorf("one or more resources must be specified as or /") } if len(annotationArgs) < 1 { @@ -139,6 +147,7 @@ func (o *AnnotateOptions) Complete(f *cmdutil.Factory, args []string, out io.Wri o.builder = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). + FilenameParam(enforceNamespace, o.filenames...). ResourceTypeOrNameArgs(o.all, o.resources...). Flatten(). Latest() diff --git a/pkg/kubectl/cmd/annotation_test.go b/pkg/kubectl/cmd/annotation_test.go index 3998f69a3ae..b5f9b693989 100644 --- a/pkg/kubectl/cmd/annotation_test.go +++ b/pkg/kubectl/cmd/annotation_test.go @@ -463,6 +463,55 @@ func TestAnnotateObject(t *testing.T) { } } +func TestAnnotateObjectFromFile(t *testing.T) { + pods, _, _ := 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 req.Method { + case "GET": + switch req.URL.Path { + case "/namespaces/test/pods/cassandra": + return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + case "PUT": + switch req.URL.Path { + case "/namespaces/test/pods/cassandra": + return &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, nil + default: + t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) + return nil, nil + } + default: + t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) + return nil, nil + } + }), + } + tf.Namespace = "test" + tf.ClientConfig = &client.Config{Version: testapi.Version()} + buf := bytes.NewBuffer([]byte{}) + + options := &AnnotateOptions{} + options.filenames = []string{"../../../examples/cassandra/cassandra.yaml"} + args := []string{"a=b", "c-"} + if err := options.Complete(f, args, buf); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if err := options.Validate(args); err != nil { + t.Fatalf("unexpected error: %v", err) + } + if err := options.RunAnnotate(); err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + func TestAnnotateMultipleObjects(t *testing.T) { pods, _, _ := testData() diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index cc4ec90e868..5ed7b2ab780 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -53,7 +53,7 @@ $ kubectl describe nodes kubernetes-minion-emt8.c.myproject.internal # Describe a pod $ kubectl describe pods/nginx -# Describe a pod using the data in pod.json. +# Describe a pod identified by type and name in "pod.json" $ kubectl describe -f pod.json # Describe all pods