diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 33c0c4d911e..85d20b236a0 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -747,6 +747,12 @@ _kubectl_label() 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-label.1 b/docs/man/man1/kubectl-label.1 index 7445c7f1deb..b321f408162 100644 --- a/docs/man/man1/kubectl-label.1 +++ b/docs/man/man1/kubectl-label.1 @@ -26,6 +26,10 @@ If \-\-resource\-version is specified, then updates will use this resource versi \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 labels + .PP \fB\-h\fP, \fB\-\-help\fP=false help for label @@ -176,6 +180,9 @@ $ kubectl label \-\-overwrite pods foo status=unhealthy # Update all pods in the namespace $ kubectl label pods \-\-all status=unhealthy +# Update a pod identified by the type and name in "pod.json" +$ kubectl label \-f pod.json status=unhealthy + # Update pod 'foo' only if the resource is unchanged from version 1. $ kubectl label pods foo status=unhealthy \-\-resource\-version=1 diff --git a/docs/user-guide/kubectl/kubectl_label.md b/docs/user-guide/kubectl/kubectl_label.md index 71b4d7e2133..ed588928175 100644 --- a/docs/user-guide/kubectl/kubectl_label.md +++ b/docs/user-guide/kubectl/kubectl_label.md @@ -45,7 +45,7 @@ If --overwrite is true, then existing labels can be overwritten, otherwise attem If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used. ``` -kubectl label [--overwrite] TYPE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version] +kubectl label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version] ``` ### Examples @@ -60,6 +60,9 @@ $ kubectl label --overwrite pods foo status=unhealthy # Update all pods in the namespace $ kubectl label pods --all status=unhealthy +# Update a pod identified by the type and name in "pod.json" +$ kubectl label -f pod.json status=unhealthy + # Update pod 'foo' only if the resource is unchanged from version 1. $ kubectl label pods foo status=unhealthy --resource-version=1 @@ -72,6 +75,7 @@ $ kubectl label pods foo bar- ``` --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 labels -h, --help[=false]: help for label --no-headers[=false]: When using the default output, don't print headers. -o, --output="": Output format. One of: json|yaml|template|templatefile|wide. @@ -116,7 +120,7 @@ $ kubectl label pods foo bar- * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-12 23:41:01.309176995 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-13 02:21:16.349210188 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_label.md?pixel)]() diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index ce6bed24c57..50862008680 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -23,6 +23,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" @@ -44,6 +45,9 @@ $ kubectl label --overwrite pods foo status=unhealthy # Update all pods in the namespace $ kubectl label pods --all status=unhealthy +# Update a pod identified by the type and name in "pod.json" +$ kubectl label -f pod.json status=unhealthy + # Update pod 'foo' only if the resource is unchanged from version 1. $ kubectl label pods foo status=unhealthy --resource-version=1 @@ -54,7 +58,7 @@ $ kubectl label pods foo bar-` func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "label [--overwrite] TYPE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", + Use: "label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", Short: "Update the labels on a resource", Long: fmt.Sprintf(label_long, util.LabelValueMaxLength), Example: label_example, @@ -68,6 +72,8 @@ func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().Bool("all", false, "select all resources in the namespace of the specified resource types") cmd.Flags().String("resource-version", "", "If non-empty, the labels 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 labels" + kubectl.AddJsonFilenameFlag(cmd, usage) return cmd } @@ -149,7 +155,8 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri return cmdutil.UsageError(cmd, "all resources must be specified before label changes: %s", s) } } - if len(resources) < 1 { + filenames := cmdutil.GetFlagStringSlice(cmd, "filename") + if len(resources) < 1 && len(filenames) == 0 { return cmdutil.UsageError(cmd, "one or more resources must be specified as or /") } if len(labelArgs) < 1 { @@ -161,7 +168,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri overwrite := cmdutil.GetFlagBool(cmd, "overwrite") resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") - cmdNamespace, _, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } @@ -170,11 +177,11 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri if err != nil { return cmdutil.UsageError(cmd, err.Error()) } - mapper, typer := f.Object() b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). + FilenameParam(enforceNamespace, filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(all, resources...). Flatten(). @@ -185,6 +192,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri if err := r.Err(); err != nil { return err } + // only apply resource version locking on a single resource if !one && len(resourceVersion) > 0 { return cmdutil.UsageError(cmd, "--resource-version may only be used with a single resource") diff --git a/pkg/kubectl/cmd/label_test.go b/pkg/kubectl/cmd/label_test.go index f354526a039..df0da3c2dcb 100644 --- a/pkg/kubectl/cmd/label_test.go +++ b/pkg/kubectl/cmd/label_test.go @@ -323,6 +323,60 @@ func TestLabelErrors(t *testing.T) { } } +func TestLabelForResourceFromFile(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{}) + cmd := NewCmdLabel(f, buf) + cmd.Flags().Set("filename", "../../../examples/cassandra/cassandra.yaml") + + cmd.SetOutput(buf) + err := RunLabel(f, buf, cmd, []string{"a=b"}) + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if tf.Printer.(*testPrinter).Objects == nil { + t.Errorf("unexpected print to default printer") + } + if !reflect.DeepEqual(tf.Printer.(*testPrinter).Objects[0].(*api.Pod).Labels, map[string]string{"a": "b"}) { + t.Errorf("did not set labels: %#v", string(buf.Bytes())) + } +} + func TestLabelMultipleObjects(t *testing.T) { pods, _, _ := testData() diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index 35c8cbad4ed..c28146cd21d 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -584,6 +584,10 @@ func (b *Builder) visitorResult() *Result { if b.flatten { visitors = NewFlattenListVisitor(visitors, b.mapper) } + // must set namespace prior to fetching + if b.defaultNamespace { + visitors = NewDecoratedVisitor(visitors, SetNamespace(b.namespace)) + } visitors = NewDecoratedVisitor(visitors, RetrieveLatest) } return &Result{singular: singular, visitor: visitors, sources: b.paths}