diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 89e3f27c9cb..509c45190ff 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -123,6 +123,15 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string return cmdutil.UsageError(cmd, "Required resource not specified.") } + // always show resources when getting by name or filename + argsHasNames, err := resource.HasNames(args) + if err != nil { + return err + } + if len(options.Filenames) > 0 || argsHasNames { + cmd.Flag("show-all").Value.Set("true") + } + // handle watch separately since we cannot watch multiple resource types isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only") if isWatch || isWatchOnly { diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 3fef6448504..7cedaf51786 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -589,6 +589,29 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) { t.Errorf("unexpected empty output") } } + +func TestGetByNameForcesFlag(t *testing.T) { + pods, _, _ := testData() + + f, tf, codec := NewAPIFactory() + tf.Printer = &testPrinter{} + tf.Client = &fake.RESTClient{ + Codec: codec, + Resp: &http.Response{StatusCode: 200, Body: objBody(codec, &pods.Items[0])}, + } + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + + cmd := NewCmdGet(f, buf) + cmd.SetOutput(buf) + cmd.Run(cmd, []string{"pods", "foo"}) + + showAllFlag, _ := cmd.Flags().GetBool("show-all") + if !showAllFlag { + t.Errorf("expected showAll to be true when getting resource by name") + } +} + func watchTestData() ([]api.Pod, []watch.Event) { pods := []api.Pod{ { diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index a2ca5dcd53f..6091360b8a2 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -282,23 +282,7 @@ func (b *Builder) SelectAllParam(selectAll bool) *Builder { // When two or more arguments are received, they must be a single type and resource name(s). // The allowEmptySelector permits to select all the resources (via Everything func). func (b *Builder) ResourceTypeOrNameArgs(allowEmptySelector bool, args ...string) *Builder { - // convert multiple resources to resource tuples, a,b,c d as a transform to a/d b/d c/d - if len(args) >= 2 { - resources := []string{} - resources = append(resources, SplitResourceArgument(args[0])...) - if len(resources) > 1 { - names := []string{} - names = append(names, args[1:]...) - newArgs := []string{} - for _, resource := range resources { - for _, name := range names { - newArgs = append(newArgs, strings.Join([]string{resource, name}, "/")) - } - } - args = newArgs - } - } - + args = normalizeMultipleResourcesArgs(args) if ok, err := hasCombinedTypeArgs(args); ok { if err != nil { b.errs = append(b.errs, err) @@ -369,6 +353,27 @@ func hasCombinedTypeArgs(args []string) (bool, error) { } } +// Normalize args convert multiple resources to resource tuples, a,b,c d +// as a transform to a/d b/d c/d +func normalizeMultipleResourcesArgs(args []string) []string { + if len(args) >= 2 { + resources := []string{} + resources = append(resources, SplitResourceArgument(args[0])...) + if len(resources) > 1 { + names := []string{} + names = append(names, args[1:]...) + newArgs := []string{} + for _, resource := range resources { + for _, name := range names { + newArgs = append(newArgs, strings.Join([]string{resource, name}, "/")) + } + } + return newArgs + } + } + return args +} + // splitResourceTypeName handles type/name resource formats and returns a resource tuple // (empty or not), whether it successfully found one, and an error func splitResourceTypeName(s string) (resourceTuple, bool, error) { @@ -697,3 +702,13 @@ func SplitResourceArgument(arg string) []string { } return out } + +// HasNames returns true if the provided args contain resource names +func HasNames(args []string) (bool, error) { + args = normalizeMultipleResourcesArgs(args) + hasCombinedTypes, err := hasCombinedTypeArgs(args) + if err != nil { + return false, err + } + return hasCombinedTypes || len(args) > 1, nil +} diff --git a/pkg/kubectl/resource/builder_test.go b/pkg/kubectl/resource/builder_test.go index daed6445ad5..420e3283e6c 100644 --- a/pkg/kubectl/resource/builder_test.go +++ b/pkg/kubectl/resource/builder_test.go @@ -1003,3 +1003,61 @@ func TestReplaceAliases(t *testing.T) { } } } + +func TestHasNames(t *testing.T) { + tests := []struct { + args []string + expectedHasName bool + expectedError error + }{ + { + args: []string{""}, + expectedHasName: false, + expectedError: nil, + }, + { + args: []string{"rc"}, + expectedHasName: false, + expectedError: nil, + }, + { + args: []string{"rc,pod,svc"}, + expectedHasName: false, + expectedError: nil, + }, + { + args: []string{"rc/foo"}, + expectedHasName: true, + expectedError: nil, + }, + { + args: []string{"rc", "foo"}, + expectedHasName: true, + expectedError: nil, + }, + { + args: []string{"rc,pod,svc", "foo"}, + expectedHasName: true, + expectedError: nil, + }, + { + args: []string{"rc/foo", "rc/bar", "rc/zee"}, + expectedHasName: true, + expectedError: nil, + }, + { + args: []string{"rc/foo", "bar"}, + expectedHasName: false, + expectedError: fmt.Errorf("when passing arguments in resource/name form, all arguments must include the resource"), + }, + } + for _, test := range tests { + hasNames, err := HasNames(test.args) + if !reflect.DeepEqual(test.expectedError, err) { + t.Errorf("expected HasName to error %v, got %s", test.expectedError, err) + } + if hasNames != test.expectedHasName { + t.Errorf("expected HasName to return %v for %s", test.expectedHasName, test.args) + } + } +} diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index 55c8b345827..d9e818e6b24 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -550,10 +550,10 @@ func translateTimestamp(timestamp unversioned.Time) string { } func printPod(pod *api.Pod, w io.Writer, options printOptions) error { - return printPodBase(pod, w, true, options) + return printPodBase(pod, w, options) } -func printPodBase(pod *api.Pod, w io.Writer, showIfTerminating bool, options printOptions) error { +func printPodBase(pod *api.Pod, w io.Writer, options printOptions) error { name := pod.Name namespace := pod.Namespace @@ -563,7 +563,7 @@ func printPodBase(pod *api.Pod, w io.Writer, showIfTerminating bool, options pri reason := string(pod.Status.Phase) // if not printing all pods, skip terminated pods (default) - if !showIfTerminating && !options.showAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed)) { + if !options.showAll && (reason == string(api.PodSucceeded) || reason == string(api.PodFailed)) { return nil } if pod.Status.Reason != "" { @@ -623,7 +623,7 @@ func printPodBase(pod *api.Pod, w io.Writer, showIfTerminating bool, options pri func printPodList(podList *api.PodList, w io.Writer, options printOptions) error { for _, pod := range podList.Items { - if err := printPodBase(&pod, w, false, options); err != nil { + if err := printPodBase(&pod, w, options); err != nil { return err } }