diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index ef0a924cbac..eec1023483e 100644 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -1160,6 +1160,19 @@ run_kubectl_get_tests() { output_message=$(! kubectl get pod valid-pod --allow-missing-template-keys=false -o go-template='{{.missing}}' "${kube_flags[@]}") kube::test::if_has_string "${output_message}" 'map has no entry for key "missing"' + ### Test kubectl get watch + output_message=$(kubectl get pods -w --request-timeout=1 "${kube_flags[@]}") + kube::test::if_has_string "${output_message}" 'STATUS' # headers + kube::test::if_has_string "${output_message}" 'valid-pod' # pod details + output_message=$(kubectl get pods/valid-pod -o name -w --request-timeout=1 "${kube_flags[@]}") + kube::test::if_has_not_string "${output_message}" 'STATUS' # no headers + kube::test::if_has_string "${output_message}" 'pods/valid-pod' # resource name + output_message=$(kubectl get pods/valid-pod -o yaml -w --request-timeout=1 "${kube_flags[@]}") + kube::test::if_has_not_string "${output_message}" 'STATUS' # no headers + kube::test::if_has_string "${output_message}" 'name: valid-pod' # yaml + output_message=$(! kubectl get pods/invalid-pod -w --request-timeout=1 "${kube_flags[@]}" 2>&1) + kube::test::if_has_string "${output_message}" '"invalid-pod" not found' + # cleanup kubectl delete pods valid-pod "${kube_flags[@]}" diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 9901f2b752d..19514f2fa1f 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -147,7 +147,7 @@ func stringBody(body string) io.ReadCloser { func Example_printReplicationControllerWithNamespace() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ WithNamespace: true, ColumnLabels: []string{}, }) @@ -200,7 +200,7 @@ func Example_printReplicationControllerWithNamespace() { func Example_printMultiContainersReplicationControllerWithWide() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ Wide: true, ColumnLabels: []string{}, }) @@ -255,7 +255,7 @@ func Example_printMultiContainersReplicationControllerWithWide() { func Example_printReplicationController() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ ColumnLabels: []string{}, }) printersinternal.AddHandlers(p) @@ -309,7 +309,7 @@ func Example_printReplicationController() { func Example_printPodWithWideFormat() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ Wide: true, ColumnLabels: []string{}, }) @@ -352,7 +352,7 @@ func Example_printPodWithWideFormat() { func Example_printPodWithShowLabels() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ ShowLabels: true, ColumnLabels: []string{}, }) @@ -490,7 +490,7 @@ func newAllPhasePodList() *api.PodList { func Example_printPodHideTerminated() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ ColumnLabels: []string{}, }) printersinternal.AddHandlers(p) @@ -525,7 +525,7 @@ func Example_printPodHideTerminated() { func Example_printPodShowAll() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ ShowAll: true, ColumnLabels: []string{}, }) @@ -554,7 +554,7 @@ func Example_printPodShowAll() { func Example_printServiceWithNamespacesAndLabels() { f, tf, _, ns := cmdtesting.NewAPIFactory() - p := printers.NewHumanReadablePrinter(printers.PrintOptions{ + p := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ WithNamespace: true, ColumnLabels: []string{"l1"}, }) diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index bde9402a396..c8afaf1ab0a 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -251,8 +251,16 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ // print the current object filteredResourceCount := 0 if !isWatchOnly { - if err := printer.PrintObj(obj, out); err != nil { - return fmt.Errorf("unable to output the provided object: %v", err) + var objsToPrint []runtime.Object + if isList { + objsToPrint, _ = meta.ExtractList(obj) + } else { + objsToPrint = append(objsToPrint, obj) + } + for _, objToPrint := range objsToPrint { + if err := printer.PrintObj(objToPrint, out); err != nil { + return fmt.Errorf("unable to output the provided object: %v", err) + } } filteredResourceCount++ cmdutil.PrintFilterCount(filteredResourceCount, mapping.Resource, filterOpts) diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 3b15124ae83..7cc4fdaeef8 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -848,7 +848,7 @@ func TestWatchSelector(t *testing.T) { cmd.Flags().Set("selector", "a=b") cmd.Run(cmd, []string{"pods"}) - expected := []runtime.Object{podList, events[2].Object, events[3].Object} + expected := []runtime.Object{&pods[0], &pods[1], events[2].Object, events[3].Object} verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects) if len(buf.String()) == 0 { diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index 5d8cf3c461f..6338868927c 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -416,7 +416,7 @@ func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource { } func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) { - p := printers.NewHumanReadablePrinter(options) + p := printers.NewHumanReadablePrinter(f.JSONEncoder(), f.Decoder(true), options) printersinternal.AddHandlers(p) return p, nil } diff --git a/pkg/printers/humanreadable.go b/pkg/printers/humanreadable.go index 12f579aa703..69aa34fa051 100644 --- a/pkg/printers/humanreadable.go +++ b/pkg/printers/humanreadable.go @@ -50,13 +50,18 @@ type HumanReadablePrinter struct { options PrintOptions lastType reflect.Type hiddenObjNum int + encoder runtime.Encoder + decoder runtime.Decoder } // NewHumanReadablePrinter creates a HumanReadablePrinter. -func NewHumanReadablePrinter(options PrintOptions) *HumanReadablePrinter { +// If encoder and decoder are provided, an attempt to convert unstructured types to internal types is made. +func NewHumanReadablePrinter(encoder runtime.Encoder, decoder runtime.Decoder, options PrintOptions) *HumanReadablePrinter { printer := &HumanReadablePrinter{ handlerMap: make(map[reflect.Type]*handlerEntry), options: options, + encoder: encoder, + decoder: decoder, } return printer } @@ -167,7 +172,9 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er // check if the object is unstructured. If so, let's attempt to convert it to a type we can understand before // trying to print, since the printers are keyed by type. This is extremely expensive. - //obj, _ = DecodeUnknownObject(obj) + if h.encoder != nil && h.decoder != nil { + obj, _ = decodeUnknownObject(obj, h.encoder, h.decoder) + } t := reflect.TypeOf(obj) if handler := h.handlerMap[t]; handler != nil { @@ -327,3 +334,18 @@ func appendAllLabels(showLabels bool, itemLabels map[string]string) string { return buffer.String() } + +// check if the object is unstructured. If so, attempt to convert it to a type we can understand. +func decodeUnknownObject(obj runtime.Object, encoder runtime.Encoder, decoder runtime.Decoder) (runtime.Object, error) { + var err error + switch obj.(type) { + case runtime.Unstructured, *runtime.Unknown: + if objBytes, err := runtime.Encode(encoder, obj); err == nil { + if decodedObj, err := runtime.Decode(decoder, objBytes); err == nil { + obj = decodedObj + } + } + } + + return obj, err +} diff --git a/pkg/printers/internalversion/printers_test.go b/pkg/printers/internalversion/printers_test.go index 55e5a98158f..28a3ca2678d 100644 --- a/pkg/printers/internalversion/printers_test.go +++ b/pkg/printers/internalversion/printers_test.go @@ -258,7 +258,7 @@ func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options printers.PrintOp func TestCustomTypePrinting(t *testing.T) { columns := []string{"Data"} - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{}) printer.Handler(columns, nil, PrintCustomType) obj := TestPrintType{"test object"} @@ -275,7 +275,7 @@ func TestCustomTypePrinting(t *testing.T) { func TestCustomTypePrintingWithKind(t *testing.T) { columns := []string{"Data"} - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{}) printer.Handler(columns, nil, PrintCustomType) printer.EnsurePrintWithKind("test") @@ -293,7 +293,7 @@ func TestCustomTypePrintingWithKind(t *testing.T) { func TestPrintHandlerError(t *testing.T) { columns := []string{"Data"} - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{}) printer.Handler(columns, nil, ErrorPrintHandler) obj := TestPrintType{"test object"} buffer := &bytes.Buffer{} @@ -304,7 +304,7 @@ func TestPrintHandlerError(t *testing.T) { } func TestUnknownTypePrinting(t *testing.T) { - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{}) buffer := &bytes.Buffer{} err := printer.PrintObj(&TestUnknownType{}, buffer) if err == nil { @@ -522,10 +522,10 @@ func TestPrinters(t *testing.T) { jsonpathPrinter = printers.NewVersionedPrinter(jsonpathPrinter, api.Scheme, v1.SchemeGroupVersion) allPrinters := map[string]printers.ResourcePrinter{ - "humanReadable": printers.NewHumanReadablePrinter(printers.PrintOptions{ + "humanReadable": printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ NoHeaders: true, }), - "humanReadableHeaders": printers.NewHumanReadablePrinter(printers.PrintOptions{}), + "humanReadableHeaders": printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{}), "json": &printers.JSONPrinter{}, "yaml": &printers.YAMLPrinter{}, "template": templatePrinter, @@ -571,7 +571,7 @@ func TestPrinters(t *testing.T) { func TestPrintEventsResultSorted(t *testing.T) { // Arrange - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{}) AddHandlers(printer) obj := api.EventList{ @@ -616,7 +616,7 @@ func TestPrintEventsResultSorted(t *testing.T) { } func TestPrintNodeStatus(t *testing.T) { - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{}) AddHandlers(printer) table := []struct { node api.Node @@ -736,7 +736,7 @@ func TestPrintNodeStatus(t *testing.T) { } func TestPrintNodeOSImage(t *testing.T) { - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ ColumnLabels: []string{}, Wide: true, }) @@ -781,7 +781,7 @@ func TestPrintNodeOSImage(t *testing.T) { } func TestPrintNodeKernelVersion(t *testing.T) { - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ ColumnLabels: []string{}, Wide: true, }) @@ -826,7 +826,7 @@ func TestPrintNodeKernelVersion(t *testing.T) { } func TestPrintNodeExternalIP(t *testing.T) { - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ Wide: true, }) AddHandlers(printer) @@ -1219,7 +1219,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { for _, test := range table { if test.isNamespaced { // Expect output to include namespace when requested. - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ WithNamespace: true, }) AddHandlers(printer) @@ -1234,7 +1234,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { } } else { // Expect error when trying to get all namespaces for un-namespaced object. - printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ + printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{ WithNamespace: true, }) buffer := &bytes.Buffer{}