show kind for multiple resource types
review: simplify and trim codes refactor ToPrinter and pass show kind flag remove tests together with removed function
This commit is contained in:
		@@ -77,7 +77,12 @@ go_test(
 | 
			
		||||
        "//pkg/kubectl/genericclioptions:go_default_library",
 | 
			
		||||
        "//pkg/kubectl/genericclioptions/resource:go_default_library",
 | 
			
		||||
        "//pkg/kubectl/scheme:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/apps/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/batch/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/batch/v1beta1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/core/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/api/extensions/v1beta1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
 | 
			
		||||
        "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
 | 
			
		||||
 
 | 
			
		||||
@@ -51,7 +51,7 @@ import (
 | 
			
		||||
// GetOptions contains the input to the get command.
 | 
			
		||||
type GetOptions struct {
 | 
			
		||||
	PrintFlags             *PrintFlags
 | 
			
		||||
	ToPrinter              func(*meta.RESTMapping, bool) (printers.ResourcePrinterFunc, error)
 | 
			
		||||
	ToPrinter              func(*meta.RESTMapping, bool, bool) (printers.ResourcePrinterFunc, error)
 | 
			
		||||
	IsHumanReadablePrinter bool
 | 
			
		||||
	PrintWithOpenAPICols   bool
 | 
			
		||||
 | 
			
		||||
@@ -224,10 +224,7 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
 | 
			
		||||
 | 
			
		||||
	o.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
 | 
			
		||||
 | 
			
		||||
	if resource.MultipleTypesRequested(args) {
 | 
			
		||||
		o.PrintFlags.EnsureWithKind()
 | 
			
		||||
	}
 | 
			
		||||
	o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinterFunc, error) {
 | 
			
		||||
	o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
 | 
			
		||||
		// make a new copy of current flags / opts before mutating
 | 
			
		||||
		printFlags := o.PrintFlags.Copy()
 | 
			
		||||
 | 
			
		||||
@@ -242,6 +239,9 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
 | 
			
		||||
		if withNamespace {
 | 
			
		||||
			printFlags.EnsureWithNamespace()
 | 
			
		||||
		}
 | 
			
		||||
		if withKind {
 | 
			
		||||
			printFlags.EnsureWithKind()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		printer, err := printFlags.ToPrinter()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -345,6 +345,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		allErrs = append(allErrs, err)
 | 
			
		||||
	}
 | 
			
		||||
	printWithKind := multipleGVKsRequested(infos)
 | 
			
		||||
 | 
			
		||||
	objs := make([]runtime.Object, len(infos))
 | 
			
		||||
	for ix := range infos {
 | 
			
		||||
@@ -400,6 +401,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
 | 
			
		||||
		nonEmptyObjCount++
 | 
			
		||||
 | 
			
		||||
		printWithNamespace := o.AllNamespaces
 | 
			
		||||
 | 
			
		||||
		if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
 | 
			
		||||
			printWithNamespace = false
 | 
			
		||||
		}
 | 
			
		||||
@@ -414,7 +416,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
 | 
			
		||||
				fmt.Fprintln(o.ErrOut)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			printer, err = o.ToPrinter(mapping, printWithNamespace)
 | 
			
		||||
			printer, err = o.ToPrinter(mapping, printWithNamespace, printWithKind)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if !errs.Has(err.Error()) {
 | 
			
		||||
					errs.Insert(err.Error())
 | 
			
		||||
@@ -493,30 +495,13 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if len(infos) > 1 {
 | 
			
		||||
		gvk := infos[0].Mapping.GroupVersionKind
 | 
			
		||||
		uniqueGVKs := 1
 | 
			
		||||
 | 
			
		||||
		// If requesting a resource count greater than a request's --chunk-size,
 | 
			
		||||
		// we will end up making multiple requests to the server, with each
 | 
			
		||||
		// request producing its own "Info" object. Although overall we are
 | 
			
		||||
		// dealing with a single resource type, we will end up with multiple
 | 
			
		||||
		// infos returned by the builder. To handle this case, only fail if we
 | 
			
		||||
		// have at least one info with a different GVK than the others.
 | 
			
		||||
		for _, info := range infos {
 | 
			
		||||
			if info.Mapping.GroupVersionKind != gvk {
 | 
			
		||||
				uniqueGVKs++
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if uniqueGVKs > 1 {
 | 
			
		||||
			return i18n.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", uniqueGVKs)
 | 
			
		||||
		}
 | 
			
		||||
	if multipleGVKsRequested(infos) {
 | 
			
		||||
		return i18n.Errorf("watch is only supported on individual resources and resource collections - more than 1 resources were found")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	info := infos[0]
 | 
			
		||||
	mapping := info.ResourceMapping()
 | 
			
		||||
	printer, err := o.ToPrinter(mapping, o.AllNamespaces)
 | 
			
		||||
	printer, err := o.ToPrinter(mapping, o.AllNamespaces, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -650,7 +635,7 @@ func (o *GetOptions) printGeneric(r *resource.Result) error {
 | 
			
		||||
		return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printer, err := o.ToPrinter(nil, false)
 | 
			
		||||
	printer, err := o.ToPrinter(nil, false, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -750,3 +735,16 @@ func maybeWrapSortingPrinter(printer printers.ResourcePrinter, sortBy string) pr
 | 
			
		||||
	}
 | 
			
		||||
	return printer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func multipleGVKsRequested(infos []*resource.Info) bool {
 | 
			
		||||
	if len(infos) < 2 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	gvk := infos[0].Mapping.GroupVersionKind
 | 
			
		||||
	for _, info := range infos {
 | 
			
		||||
		if info.Mapping.GroupVersionKind != gvk {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,12 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	apiapps "k8s.io/api/apps/v1"
 | 
			
		||||
	apiautoscaling "k8s.io/api/autoscaling/v1"
 | 
			
		||||
	apibatchv1 "k8s.io/api/batch/v1"
 | 
			
		||||
	apibatchv1beta1 "k8s.io/api/batch/v1beta1"
 | 
			
		||||
	api "k8s.io/api/core/v1"
 | 
			
		||||
	apiextensionsv1beta1 "k8s.io/api/extensions/v1beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
@@ -351,6 +356,65 @@ pod/foo   0/0                 0          <unknown>
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetMultipleResourceTypesShowKinds(t *testing.T) {
 | 
			
		||||
	pods, svcs, _ := testData()
 | 
			
		||||
 | 
			
		||||
	tf := cmdtesting.NewTestFactory().WithNamespace("test")
 | 
			
		||||
	defer tf.Cleanup()
 | 
			
		||||
	codec := legacyscheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
 | 
			
		||||
 | 
			
		||||
	tf.UnstructuredClient = &fake.RESTClient{
 | 
			
		||||
		NegotiatedSerializer: unstructuredSerializer,
 | 
			
		||||
		Resp:                 &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])},
 | 
			
		||||
	}
 | 
			
		||||
	tf.UnstructuredClient = &fake.RESTClient{
 | 
			
		||||
		NegotiatedSerializer: unstructuredSerializer,
 | 
			
		||||
		Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
 | 
			
		||||
			switch p, m := req.URL.Path, req.Method; {
 | 
			
		||||
			case p == "/namespaces/test/pods" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
 | 
			
		||||
			case p == "/namespaces/test/replicationcontrollers" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.ReplicationControllerList{})}, nil
 | 
			
		||||
			case p == "/namespaces/test/services" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svcs)}, nil
 | 
			
		||||
			case p == "/namespaces/test/statefulsets" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &apiapps.StatefulSetList{})}, nil
 | 
			
		||||
			case p == "/namespaces/test/horizontalpodautoscalers" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &apiautoscaling.HorizontalPodAutoscalerList{})}, nil
 | 
			
		||||
			case p == "/namespaces/test/jobs" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &apibatchv1.JobList{})}, nil
 | 
			
		||||
			case p == "/namespaces/test/cronjobs" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &apibatchv1beta1.CronJobList{})}, nil
 | 
			
		||||
			case p == "/namespaces/test/daemonsets" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &apiapps.DaemonSetList{})}, nil
 | 
			
		||||
			case p == "/namespaces/test/deployments" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &apiextensionsv1beta1.DeploymentList{})}, nil
 | 
			
		||||
			case p == "/namespaces/test/replicasets" && m == "GET":
 | 
			
		||||
				return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &apiextensionsv1beta1.ReplicaSetList{})}, nil
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				t.Fatalf("request url: %#v,and request: %#v", req.URL, req)
 | 
			
		||||
				return nil, nil
 | 
			
		||||
			}
 | 
			
		||||
		}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	streams, _, buf, _ := genericclioptions.NewTestIOStreams()
 | 
			
		||||
	cmd := NewCmdGet("kubectl", tf, streams)
 | 
			
		||||
	cmd.SetOutput(buf)
 | 
			
		||||
	cmd.Run(cmd, []string{"all"})
 | 
			
		||||
 | 
			
		||||
	expected := `NAME      READY     STATUS    RESTARTS   AGE
 | 
			
		||||
pod/foo   0/0                 0          <unknown>
 | 
			
		||||
pod/bar   0/0                 0          <unknown>
 | 
			
		||||
NAME          TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
 | 
			
		||||
service/baz   ClusterIP   <none>       <none>        <none>    <unknown>
 | 
			
		||||
`
 | 
			
		||||
	if e, a := expected, buf.String(); e != a {
 | 
			
		||||
		t.Errorf("expected %v, got %v", e, a)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetObjectsShowLabels(t *testing.T) {
 | 
			
		||||
	pods, _, _ := testData()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -501,6 +501,24 @@ func testDynamicResources() []*restmapper.APIGroupResources {
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Group: metav1.APIGroup{
 | 
			
		||||
				Name: "batch",
 | 
			
		||||
				Versions: []metav1.GroupVersionForDiscovery{
 | 
			
		||||
					{Version: "v1beta1"},
 | 
			
		||||
					{Version: "v1"},
 | 
			
		||||
				},
 | 
			
		||||
				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
 | 
			
		||||
			},
 | 
			
		||||
			VersionedResources: map[string][]metav1.APIResource{
 | 
			
		||||
				"v1beta1": {
 | 
			
		||||
					{Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
 | 
			
		||||
				},
 | 
			
		||||
				"v1": {
 | 
			
		||||
					{Name: "jobs", Namespaced: true, Kind: "Job"},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Group: metav1.APIGroup{
 | 
			
		||||
				Name: "autoscaling",
 | 
			
		||||
 
 | 
			
		||||
@@ -156,6 +156,8 @@ func newBuilder(clientConfigFn ClientConfigFunc, restMapper meta.RESTMapper, cat
 | 
			
		||||
		restMapper:       restMapper,
 | 
			
		||||
		categoryExpander: categoryExpander,
 | 
			
		||||
		requireObject:    true,
 | 
			
		||||
		labelSelector:    nil,
 | 
			
		||||
		fieldSelector:    nil,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1107,37 +1109,3 @@ func HasNames(args []string) (bool, error) {
 | 
			
		||||
	}
 | 
			
		||||
	return hasCombinedTypes || len(args) > 1, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MultipleTypesRequested returns true if the provided args contain multiple resource kinds
 | 
			
		||||
func MultipleTypesRequested(args []string) bool {
 | 
			
		||||
	if len(args) == 1 && args[0] == "all" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args = normalizeMultipleResourcesArgs(args)
 | 
			
		||||
	rKinds := sets.NewString()
 | 
			
		||||
	for _, arg := range args {
 | 
			
		||||
		rTuple, found, err := splitResourceTypeName(arg)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// if tuple not found, assume arg is of the form "type1,type2,...".
 | 
			
		||||
		// Since SplitResourceArgument returns a unique list of kinds,
 | 
			
		||||
		// return true here if len(uniqueList) > 1
 | 
			
		||||
		if !found {
 | 
			
		||||
			if strings.Contains(arg, ",") {
 | 
			
		||||
				splitArgs := SplitResourceArgument(arg)
 | 
			
		||||
				if len(splitArgs) > 1 {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if rKinds.Has(rTuple.Resource) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		rKinds.Insert(rTuple.Resource)
 | 
			
		||||
	}
 | 
			
		||||
	return rKinds.Len() > 1
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1396,80 +1396,3 @@ func TestHasNames(t *testing.T) {
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMultipleTypesRequested(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name                  string
 | 
			
		||||
		args                  []string
 | 
			
		||||
		expectedMultipleTypes bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "test1",
 | 
			
		||||
			args: []string{""},
 | 
			
		||||
			expectedMultipleTypes: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test2",
 | 
			
		||||
			args: []string{"all"},
 | 
			
		||||
			expectedMultipleTypes: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test3",
 | 
			
		||||
			args: []string{"rc"},
 | 
			
		||||
			expectedMultipleTypes: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test4",
 | 
			
		||||
			args: []string{"pod,all"},
 | 
			
		||||
			expectedMultipleTypes: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test5",
 | 
			
		||||
			args: []string{"all,rc,pod"},
 | 
			
		||||
			expectedMultipleTypes: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test6",
 | 
			
		||||
			args: []string{"rc,pod,svc"},
 | 
			
		||||
			expectedMultipleTypes: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test7",
 | 
			
		||||
			args: []string{"rc/foo"},
 | 
			
		||||
			expectedMultipleTypes: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test8",
 | 
			
		||||
			args: []string{"rc/foo", "rc/bar"},
 | 
			
		||||
			expectedMultipleTypes: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test9",
 | 
			
		||||
			args: []string{"rc", "foo"},
 | 
			
		||||
			expectedMultipleTypes: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test10",
 | 
			
		||||
			args: []string{"rc,pod,svc", "foo"},
 | 
			
		||||
			expectedMultipleTypes: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test11",
 | 
			
		||||
			args: []string{"rc,secrets"},
 | 
			
		||||
			expectedMultipleTypes: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "test12",
 | 
			
		||||
			args: []string{"rc/foo", "rc/bar", "svc/svc"},
 | 
			
		||||
			expectedMultipleTypes: true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			hasMultipleTypes := MultipleTypesRequested(tt.args)
 | 
			
		||||
			if hasMultipleTypes != tt.expectedMultipleTypes {
 | 
			
		||||
				t.Errorf("expected MultipleTypesRequested to return %v for %s", tt.expectedMultipleTypes, tt.args)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user