Skip to content

Commit 9021692

Browse files
juanvallejojingxu97
authored andcommitted
handle Table response in client
This patch adds support for the "server-side GET operation" introduced by pull/40848 and proposed by kubernetes/community#363.
1 parent 30d1485 commit 9021692

File tree

3 files changed

+94
-9
lines changed

3 files changed

+94
-9
lines changed

pkg/kubectl/cmd/resource/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ go_library(
2121
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
2222
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
2323
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
24+
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
2425
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
2526
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
2627
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
2728
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
2829
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
30+
"//vendor/k8s.io/client-go/rest:go_default_library",
2931
],
3032
)
3133

pkg/kubectl/cmd/resource/get.go

Lines changed: 87 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ import (
3131
"k8s.io/apimachinery/pkg/api/meta"
3232
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3333
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
34+
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
3435
"k8s.io/apimachinery/pkg/runtime"
3536
"k8s.io/apimachinery/pkg/runtime/schema"
3637
utilerrors "k8s.io/apimachinery/pkg/util/errors"
3738
"k8s.io/apimachinery/pkg/util/sets"
3839
"k8s.io/apimachinery/pkg/watch"
40+
"k8s.io/client-go/rest"
3941
api "k8s.io/kubernetes/pkg/apis/core"
4042
"k8s.io/kubernetes/pkg/kubectl"
4143
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@@ -64,6 +66,9 @@ type GetOptions struct {
6466
Namespace string
6567
ExplicitNamespace bool
6668

69+
ServerPrint bool
70+
71+
Sort bool
6772
IgnoreNotFound bool
6873
ShowKind bool
6974
LabelColumns []string
@@ -120,6 +125,7 @@ var (
120125

121126
const (
122127
useOpenAPIPrintColumnFlagLabel = "use-openapi-print-columns"
128+
useServerPrintColumns = "experimental-server-print"
123129
)
124130

125131
// NewCmdGet creates a command object for the generic "get" action, which
@@ -158,6 +164,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
158164
cmdutil.AddIncludeUninitializedFlag(cmd)
159165
cmdutil.AddPrinterFlags(cmd)
160166
addOpenAPIPrintColumnFlags(cmd)
167+
addServerPrintColumnFlags(cmd)
161168
cmd.Flags().BoolVar(&options.ShowKind, "show-kind", options.ShowKind, "If present, list the resource type for the requested object(s).")
162169
cmd.Flags().StringSliceVarP(&options.LabelColumns, "label-columns", "L", options.LabelColumns, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...")
163170
cmd.Flags().BoolVar(&options.Export, "export", options.Export, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
@@ -175,6 +182,8 @@ func (options *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
175182
return nil
176183
}
177184

185+
options.ServerPrint = cmdutil.GetFlagBool(cmd, useServerPrintColumns)
186+
178187
var err error
179188
options.Namespace, options.ExplicitNamespace, err = f.DefaultNamespace()
180189
if err != nil {
@@ -184,6 +193,12 @@ func (options *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
184193
options.ExplicitNamespace = false
185194
}
186195

196+
isSorting, err := cmd.Flags().GetString("sort-by")
197+
if err != nil {
198+
return err
199+
}
200+
options.Sort = len(isSorting) > 0
201+
187202
options.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
188203

189204
switch {
@@ -238,6 +253,12 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
238253
return options.watch(f, cmd, args)
239254
}
240255

256+
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
257+
printer, err := cmdutil.PrinterForOptions(printOpts)
258+
if err != nil {
259+
return err
260+
}
261+
241262
r := f.NewBuilder().
242263
Unstructured().
243264
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
@@ -251,6 +272,15 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
251272
ContinueOnError().
252273
Latest().
253274
Flatten().
275+
TransformRequests(func(req *rest.Request) {
276+
if options.ServerPrint && !printer.IsGeneric() && !options.Sort {
277+
group := metav1beta1.GroupName
278+
version := metav1beta1.SchemeGroupVersion.Version
279+
280+
tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
281+
req.SetHeader("Accept", tableParam)
282+
}
283+
}).
254284
Do()
255285

256286
if options.IgnoreNotFound {
@@ -260,12 +290,6 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
260290
return err
261291
}
262292

263-
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
264-
printer, err := cmdutil.PrinterForOptions(printOpts)
265-
if err != nil {
266-
return err
267-
}
268-
269293
filterOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
270294
filterFuncs := f.DefaultResourceFilterFunc()
271295
if r.TargetsSingleItems() {
@@ -285,6 +309,17 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
285309

286310
objs := make([]runtime.Object, len(infos))
287311
for ix := range infos {
312+
if options.ServerPrint {
313+
table, err := options.decodeIntoTable(cmdutil.InternalVersionJSONEncoder(), infos[ix].Object)
314+
if err == nil {
315+
infos[ix].Object = table
316+
} else {
317+
// if we are unable to decode server response into a v1beta1.Table,
318+
// fallback to client-side printing with whatever info the server returned.
319+
glog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
320+
}
321+
}
322+
288323
objs[ix] = infos[ix].Object
289324
}
290325

@@ -293,7 +328,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
293328
return err
294329
}
295330
var sorter *kubectl.RuntimeSort
296-
if len(sorting) > 0 && len(objs) > 1 {
331+
if options.Sort && len(objs) > 1 {
297332
// TODO: questionable
298333
if sorter, err = kubectl.SortObjects(cmdutil.InternalVersionDecoder(), objs, sorting); err != nil {
299334
return err
@@ -324,6 +359,15 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
324359
mapping = info.Mapping
325360
original = info.Object
326361
}
362+
363+
// if dealing with a table that has no rows, skip remaining steps
364+
// and avoid printing an unnecessary newline
365+
if table, isTable := info.Object.(*metav1beta1.Table); isTable {
366+
if len(table.Rows) == 0 {
367+
continue
368+
}
369+
}
370+
327371
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
328372
if printer != nil {
329373
w.Flush()
@@ -416,7 +460,19 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
416460
}
417461
}
418462
w.Flush()
419-
cmdutil.PrintFilterCount(options.ErrOut, len(objs), filteredResourceCount, len(allErrs), filterOpts, options.IgnoreNotFound)
463+
nonEmptyObjCount := 0
464+
for _, obj := range objs {
465+
if table, ok := obj.(*metav1beta1.Table); ok {
466+
// exclude any Table objects with empty rows from our total object count
467+
if len(table.Rows) == 0 {
468+
continue
469+
}
470+
}
471+
472+
nonEmptyObjCount++
473+
}
474+
475+
cmdutil.PrintFilterCount(options.ErrOut, nonEmptyObjCount, filteredResourceCount, len(allErrs), filterOpts, options.IgnoreNotFound)
420476
return utilerrors.NewAggregate(allErrs)
421477
}
422478

@@ -592,6 +648,25 @@ func attemptToConvertToInternal(obj runtime.Object, converter runtime.ObjectConv
592648
return internalObject
593649
}
594650

651+
func (options *GetOptions) decodeIntoTable(encoder runtime.Encoder, obj runtime.Object) (runtime.Object, error) {
652+
if obj.GetObjectKind().GroupVersionKind().Kind != "Table" {
653+
return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table")
654+
}
655+
656+
b, err := runtime.Encode(encoder, obj)
657+
if err != nil {
658+
return nil, err
659+
}
660+
661+
table := &metav1beta1.Table{}
662+
err = json.Unmarshal(b, table)
663+
if err != nil {
664+
return nil, err
665+
}
666+
667+
return table, nil
668+
}
669+
595670
func (options *GetOptions) printGeneric(printer printers.ResourcePrinter, r *resource.Result, filterFuncs kubectl.Filters, filterOpts *printers.PrintOptions) error {
596671
// we flattened the data from the builder, so we have individual items, but now we'd like to either:
597672
// 1. if there is more than one item, combine them all into a single list
@@ -689,6 +764,10 @@ func addOpenAPIPrintColumnFlags(cmd *cobra.Command) {
689764
cmd.Flags().Bool(useOpenAPIPrintColumnFlagLabel, true, "If true, use x-kubernetes-print-column metadata (if present) from the OpenAPI schema for displaying a resource.")
690765
}
691766

767+
func addServerPrintColumnFlags(cmd *cobra.Command) {
768+
cmd.Flags().Bool(useServerPrintColumns, false, "If true, have the server return the appropriate table output. Supports extension APIs and CRD. Experimental.")
769+
}
770+
692771
func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {
693772
return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
694773
}

pkg/printers/humanreadable.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ func hasCondition(conditions []metav1beta1.TableRowCondition, t metav1beta1.RowC
355355
// DecorateTable if you receive a table from a remote server before calling PrintTable.
356356
func PrintTable(table *metav1beta1.Table, output io.Writer, options PrintOptions) error {
357357
if !options.NoHeaders {
358+
// avoid printing headers if we have no rows to display
359+
if len(table.Rows) == 0 {
360+
return nil
361+
}
362+
358363
first := true
359364
for _, column := range table.ColumnDefinitions {
360365
if !options.Wide && column.Priority != 0 {
@@ -413,7 +418,6 @@ func DecorateTable(table *metav1beta1.Table, options PrintOptions) error {
413418
for i := range columns {
414419
if columns[i].Format == "name" && columns[i].Type == "string" {
415420
nameColumn = i
416-
fmt.Printf("found name column: %d\n", i)
417421
break
418422
}
419423
}

0 commit comments

Comments
 (0)