From 2ca200f087362c240ee958e9304baaf6bebc9c6a Mon Sep 17 00:00:00 2001 From: feihjiang Date: Tue, 7 Jul 2015 15:31:08 +0800 Subject: [PATCH] Kubectl describe command accepts a filename param --- contrib/completions/bash/kubectl | 6 +++ docs/man/man1/kubectl-describe.1 | 7 +++ .../kubectl/kubectl_api-versions.md | 2 +- docs/user-guide/kubectl/kubectl_describe.md | 8 ++- pkg/kubectl/cmd/describe.go | 49 ++++++++++++------- pkg/kubectl/cmd/describe_test.go | 33 +++++++++++++ 6 files changed, 85 insertions(+), 20 deletions(-) diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index bfb00cfa5c8..b577387b694 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -275,6 +275,12 @@ _kubectl_describe() 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+=("--selector=") diff --git a/docs/man/man1/kubectl-describe.1 b/docs/man/man1/kubectl-describe.1 index 02d9e4c2fff..3dea0f6b49d 100644 --- a/docs/man/man1/kubectl-describe.1 +++ b/docs/man/man1/kubectl-describe.1 @@ -34,6 +34,10 @@ namespaces (ns) or secrets. .SH OPTIONS +.PP +\fB\-f\fP, \fB\-\-filename\fP=[] + Filename, directory, or URL to a file containing the resource to describe + .PP \fB\-h\fP, \fB\-\-help\fP=false help for describe @@ -152,6 +156,9 @@ $ 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. +$ kubectl describe \-f pod.json + // Describe pods by label name=myLabel $ kubectl describe po \-l name=myLabel diff --git a/docs/user-guide/kubectl/kubectl_api-versions.md b/docs/user-guide/kubectl/kubectl_api-versions.md index cb702b805e0..63612ffc1c8 100644 --- a/docs/user-guide/kubectl/kubectl_api-versions.md +++ b/docs/user-guide/kubectl/kubectl_api-versions.md @@ -83,7 +83,7 @@ kubectl api-versions * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 23:27:50.890645232 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 07:32:08.138043968 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_api-versions.md?pixel)]() diff --git a/docs/user-guide/kubectl/kubectl_describe.md b/docs/user-guide/kubectl/kubectl_describe.md index c80b791bdbc..18833a1b2c0 100644 --- a/docs/user-guide/kubectl/kubectl_describe.md +++ b/docs/user-guide/kubectl/kubectl_describe.md @@ -54,7 +54,7 @@ persistentvolumes (pv), persistentvolumeclaims (pvc), resourcequotas (quota), namespaces (ns) or secrets. ``` -kubectl describe (TYPE [(NAME_PREFIX | -l label] | TYPE/NAME) +kubectl describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME) ``` ### Examples @@ -66,6 +66,9 @@ $ 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. +$ kubectl describe -f pod.json + // Describe pods by label name=myLabel $ kubectl describe po -l name=myLabel @@ -77,6 +80,7 @@ $ kubectl describe pods frontend ### Options ``` + -f, --filename=[]: Filename, directory, or URL to a file containing the resource to describe -h, --help[=false]: help for describe -l, --selector="": Selector (label query) to filter on ``` @@ -114,7 +118,7 @@ $ kubectl describe pods frontend * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-08-05 23:27:50.885301316 +0000 UTC +###### Auto generated by spf13/cobra at 2015-08-07 07:32:08.128980687 +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/describe.go b/pkg/kubectl/cmd/describe.go index 61e1ff914fd..cf50ca887cf 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -29,6 +29,7 @@ import ( cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/errors" ) const ( @@ -52,6 +53,9 @@ $ 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. +$ kubectl describe -f pod.json + // Describe pods by label name=myLabel $ kubectl describe po -l name=myLabel @@ -62,7 +66,7 @@ $ kubectl describe pods frontend` func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd := &cobra.Command{ - Use: "describe (TYPE [(NAME_PREFIX | -l label] | TYPE/NAME)", + Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)", Short: "Show details of a specific resource or group of resources", Long: describe_long, Example: describe_example, @@ -72,18 +76,20 @@ func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { }, ValidArgs: kubectl.DescribableResources(), } + usage := "Filename, directory, or URL to a file containing the resource to describe" + kubectl.AddJsonFilenameFlag(cmd, usage) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") return cmd } func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { selector := cmdutil.GetFlagString(cmd, "selector") - cmdNamespace, _, err := f.DefaultNamespace() + cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } - - if len(args) == 0 { + filenames := cmdutil.GetFlagStringSlice(cmd, "filename") + if len(args) == 0 && len(filenames) == 0 { fmt.Fprint(out, "You must specify the type of resource to describe. ", valid_resources) return cmdutil.UsageError(cmd, "Required resource not specified.") } @@ -92,6 +98,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). + FilenameParam(enforceNamespace, filenames...). SelectorParam(selector). ResourceTypeOrNameArgs(false, args...). Flatten(). @@ -100,41 +107,49 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s if err != nil { return err } - mapping, err := r.ResourceMapping() - if err != nil { - return err - } - describer, err := f.Describer(mapping) - if err != nil { - return err - } + allErrs := []error{} infos, err := r.Infos() if err != nil { if apierrors.IsNotFound(err) && len(args) == 2 { - return DescribeMatchingResources(mapper, typer, describer, f, cmdNamespace, args[0], args[1], out, err) + return DescribeMatchingResources(mapper, typer, f, cmdNamespace, args[0], args[1], out, err) } - return err + allErrs = append(allErrs, err) } for _, info := range infos { + mapping := info.ResourceMapping() + describer, err := f.Describer(mapping) + if err != nil { + allErrs = append(allErrs, err) + continue + } s, err := describer.Describe(info.Namespace, info.Name) if err != nil { - return err + allErrs = append(allErrs, err) + continue } fmt.Fprintf(out, "%s\n\n", s) } - return nil + return errors.NewAggregate(allErrs) } -func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, describer kubectl.Describer, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error { +func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error { r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). NamespaceParam(namespace).DefaultNamespace(). ResourceTypeOrNameArgs(true, rsrc). SingleResourceType(). Flatten(). Do() + mapping, err := r.ResourceMapping() + if err != nil { + return err + } + describer, err := f.Describer(mapping) + if err != nil { + return err + } infos, err := r.Infos() if err != nil { return err diff --git a/pkg/kubectl/cmd/describe_test.go b/pkg/kubectl/cmd/describe_test.go index 2e21d4c38db..cfeccb803bc 100644 --- a/pkg/kubectl/cmd/describe_test.go +++ b/pkg/kubectl/cmd/describe_test.go @@ -48,3 +48,36 @@ func TestDescribeUnknownSchemaObject(t *testing.T) { t.Errorf("unexpected output: %s", buf.String()) } } + +func TestDescribeObject(t *testing.T) { + _, _, rc := testData() + f, tf, codec := NewAPIFactory() + d := &testDescriber{Output: "test output"} + tf.Describer = d + 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/replicationcontrollers/redis-master" && m == "GET": + return &http.Response{StatusCode: 200, Body: objBody(codec, &rc.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 := NewCmdDescribe(f, buf) + cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.yaml") + cmd.Run(cmd, []string{}) + + if d.Name != "redis-master" || d.Namespace != "test" { + t.Errorf("unexpected describer: %#v", d) + } + + if buf.String() != fmt.Sprintf("%s\n\n", d.Output) { + t.Errorf("unexpected output: %s", buf.String()) + } +}