Skip to content

Commit 34e9d80

Browse files
committed
Refactor kubectl table printing for watch
Warn if sorting and watching are requested together
1 parent 135d2f1 commit 34e9d80

File tree

3 files changed

+119
-73
lines changed

3 files changed

+119
-73
lines changed

pkg/kubectl/cmd/get/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ go_library(
2323
"get_flags.go",
2424
"humanreadable_flags.go",
2525
"sorter.go",
26+
"table_printer.go",
2627
],
2728
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/get",
2829
visibility = ["//visibility:public"],

pkg/kubectl/cmd/get/get.go

Lines changed: 41 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,11 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
204204
o.ExplicitNamespace = false
205205
}
206206

207-
isSorting, err := cmd.Flags().GetString("sort-by")
207+
sortBy, err := cmd.Flags().GetString("sort-by")
208208
if err != nil {
209209
return err
210210
}
211-
o.Sort = len(isSorting) > 0
211+
o.Sort = len(sortBy) > 0
212212

213213
o.NoHeaders = cmdutil.GetFlagBool(cmd, "no-headers")
214214

@@ -253,12 +253,20 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
253253
return nil, err
254254
}
255255

256-
printer = maybeWrapSortingPrinter(printer, isSorting)
256+
if o.Sort {
257+
printer = &SortingPrinter{Delegate: printer, SortField: sortBy}
258+
}
259+
if o.ServerPrint {
260+
printer = &TablePrinter{Delegate: printer}
261+
}
257262
return printer.PrintObj, nil
258263
}
259264

260265
switch {
261266
case o.Watch || o.WatchOnly:
267+
if o.Sort {
268+
fmt.Fprintf(o.IOStreams.ErrOut, "warning: --watch or --watch-only requested, --sort-by will be ignored\n")
269+
}
262270
default:
263271
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames, o.Kustomize) {
264272
fmt.Fprintf(o.ErrOut, "You must specify the type of resource to get. %s\n\n", cmdutil.SuggestAPIResources(o.CmdParent))
@@ -271,6 +279,12 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
271279
return cmdutil.UsageErrorf(cmd, usageString)
272280
}
273281
}
282+
283+
// openapi printing is mutually exclusive with server side printing
284+
if o.PrintWithOpenAPICols && o.ServerPrint {
285+
fmt.Fprintf(o.IOStreams.ErrOut, "warning: --%s requested, --%s will be ignored\n", useOpenAPIPrintColumnFlagLabel, useServerPrintColumns)
286+
}
287+
274288
return nil
275289
}
276290

@@ -398,6 +412,27 @@ func NewRuntimeSorter(objects []runtime.Object, sortBy string) *RuntimeSorter {
398412
}
399413
}
400414

415+
func (o *GetOptions) transformRequests(req *rest.Request) {
416+
// We need full objects if printing with openapi columns
417+
if o.PrintWithOpenAPICols {
418+
return
419+
}
420+
if !o.ServerPrint || !o.IsHumanReadablePrinter {
421+
return
422+
}
423+
424+
group := metav1beta1.GroupName
425+
version := metav1beta1.SchemeGroupVersion.Version
426+
427+
tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
428+
req.SetHeader("Accept", tableParam)
429+
430+
// if sorting, ensure we receive the full object in order to introspect its fields via jsonpath
431+
if o.Sort {
432+
req.Param("includeObject", "Object")
433+
}
434+
}
435+
401436
// Run performs the get operation.
402437
// TODO: remove the need to pass these arguments, like other commands.
403438
func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
@@ -408,11 +443,6 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
408443
return o.watch(f, cmd, args)
409444
}
410445

411-
// openapi printing is mutually exclusive with server side printing
412-
if o.PrintWithOpenAPICols && o.ServerPrint {
413-
fmt.Fprintf(o.IOStreams.ErrOut, "warning: --%s requested, --%s will be ignored\n", useOpenAPIPrintColumnFlagLabel, useServerPrintColumns)
414-
}
415-
416446
chunkSize := o.ChunkSize
417447
if o.Sort {
418448
// TODO(juanvallejo): in the future, we could have the client use chunking
@@ -432,26 +462,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
432462
ContinueOnError().
433463
Latest().
434464
Flatten().
435-
TransformRequests(func(req *rest.Request) {
436-
// We need full objects if printing with openapi columns
437-
if o.PrintWithOpenAPICols {
438-
return
439-
}
440-
if !o.ServerPrint || !o.IsHumanReadablePrinter {
441-
return
442-
}
443-
444-
group := metav1beta1.GroupName
445-
version := metav1beta1.SchemeGroupVersion.Version
446-
447-
tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
448-
req.SetHeader("Accept", tableParam)
449-
450-
// if sorting, ensure we receive the full object in order to introspect its fields via jsonpath
451-
if o.Sort {
452-
req.Param("includeObject", "Object")
453-
}
454-
}).
465+
TransformRequests(o.transformRequests).
455466
Do()
456467

457468
if o.IgnoreNotFound {
@@ -475,17 +486,13 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
475486

476487
objs := make([]runtime.Object, len(infos))
477488
for ix := range infos {
489+
// TODO: remove this and just pass the table objects to the printer opaquely once `info.Object.(*metav1beta1.Table)` checking is removed below
478490
if o.ServerPrint {
479-
table, err := o.decodeIntoTable(infos[ix].Object)
491+
table, err := decodeIntoTable(infos[ix].Object)
480492
if err == nil {
481493
infos[ix].Object = table
482-
} else {
483-
// if we are unable to decode server response into a v1beta1.Table,
484-
// fallback to client-side printing with whatever info the server returned.
485-
klog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
486494
}
487495
}
488-
489496
objs[ix] = infos[ix].Object
490497
}
491498

@@ -723,35 +730,6 @@ func attemptToConvertToInternal(obj runtime.Object, converter runtime.ObjectConv
723730
return internalObject
724731
}
725732

726-
func (o *GetOptions) decodeIntoTable(obj runtime.Object) (runtime.Object, error) {
727-
if obj.GetObjectKind().GroupVersionKind().Kind != "Table" {
728-
return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table")
729-
}
730-
731-
unstr, ok := obj.(*unstructured.Unstructured)
732-
if !ok {
733-
return nil, fmt.Errorf("attempt to decode non-Unstructured object")
734-
}
735-
table := &metav1beta1.Table{}
736-
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.Object, table); err != nil {
737-
return nil, err
738-
}
739-
740-
for i := range table.Rows {
741-
row := &table.Rows[i]
742-
if row.Object.Raw == nil || row.Object.Object != nil {
743-
continue
744-
}
745-
converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, row.Object.Raw)
746-
if err != nil {
747-
return nil, err
748-
}
749-
row.Object.Object = converted
750-
}
751-
752-
return table, nil
753-
}
754-
755733
func (o *GetOptions) printGeneric(r *resource.Result) error {
756734
// we flattened the data from the builder, so we have individual items, but now we'd like to either:
757735
// 1. if there is more than one item, combine them all into a single list
@@ -863,16 +841,6 @@ func cmdSpecifiesOutputFmt(cmd *cobra.Command) bool {
863841
return cmdutil.GetFlagString(cmd, "output") != ""
864842
}
865843

866-
func maybeWrapSortingPrinter(printer printers.ResourcePrinter, sortBy string) printers.ResourcePrinter {
867-
if len(sortBy) != 0 {
868-
return &SortingPrinter{
869-
Delegate: printer,
870-
SortField: fmt.Sprintf("%s", sortBy),
871-
}
872-
}
873-
return printer
874-
}
875-
876844
func multipleGVKsRequested(infos []*resource.Info) bool {
877845
if len(infos) < 2 {
878846
return false
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package get
18+
19+
import (
20+
"fmt"
21+
"io"
22+
23+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
24+
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
25+
"k8s.io/apimachinery/pkg/runtime"
26+
"k8s.io/cli-runtime/pkg/printers"
27+
"k8s.io/klog"
28+
)
29+
30+
// TablePrinter decodes table objects into typed objects before delegating to another printer.
31+
// Non-table types are simply passed through
32+
type TablePrinter struct {
33+
Delegate printers.ResourcePrinter
34+
}
35+
36+
func (t *TablePrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
37+
table, err := decodeIntoTable(obj)
38+
if err == nil {
39+
return t.Delegate.PrintObj(table, writer)
40+
}
41+
// if we are unable to decode server response into a v1beta1.Table,
42+
// fallback to client-side printing with whatever info the server returned.
43+
klog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
44+
return t.Delegate.PrintObj(obj, writer)
45+
}
46+
47+
func decodeIntoTable(obj runtime.Object) (runtime.Object, error) {
48+
if obj.GetObjectKind().GroupVersionKind().Group != metav1beta1.GroupName {
49+
return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table")
50+
}
51+
if obj.GetObjectKind().GroupVersionKind().Kind != "Table" {
52+
return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table")
53+
}
54+
55+
unstr, ok := obj.(*unstructured.Unstructured)
56+
if !ok {
57+
return nil, fmt.Errorf("attempt to decode non-Unstructured object")
58+
}
59+
table := &metav1beta1.Table{}
60+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstr.Object, table); err != nil {
61+
return nil, err
62+
}
63+
64+
for i := range table.Rows {
65+
row := &table.Rows[i]
66+
if row.Object.Raw == nil || row.Object.Object != nil {
67+
continue
68+
}
69+
converted, err := runtime.Decode(unstructured.UnstructuredJSONScheme, row.Object.Raw)
70+
if err != nil {
71+
return nil, err
72+
}
73+
row.Object.Object = converted
74+
}
75+
76+
return table, nil
77+
}

0 commit comments

Comments
 (0)