mirror of https://github.com/k3s-io/k3s
Merge pull request #55637 from juanvallejo/jvallejo/kubectl-get-table-response-proto-v2
Automatic merge from submit-queue (batch tested with PRs 55637, 57461, 60268, 60290, 60210). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Update get.go to use server-side printing Addresses part of https://github.com/kubernetes/kubernetes/issues/58536 Adds support for server-side changes implemented in https://github.com/kubernetes/kubernetes/pull/40848 and updated in https://github.com/kubernetes/kubernetes/pull/59059 @deads2k per our discussion, opening this as a separate PR. This wires through a per-request use of `as=Table;...` header parameters using the resource builder from the `kube get` command. #### Items to consider going forward: - [ ] Figure out how to handle sorting when dealing with multiple Table objects from the server - [ ] Figure out sorting when dealing with a mixed response from the server consisting of Tables and normal resources (`--sort-by` is handled in this PR by falling back to old behavior) - [ ] Filtering: How should we filter Table objects? Separate filter for rows? Filter on jsonpath? We have access to partial object metadata for each table row - not enough to know how to filter pods, for example but we could request that the original object be included along with each Table.Row by adding an `includeObject` param in the client request. #### Resources that do not yet support Table output - [ ] Namespaces - [ ] Services - [ ] Service catalog resources: https://github.com/kubernetes-incubator/service-catalog/blob/master/pkg/apis/servicecatalog/v1beta1/types.go **Release note**: ```release-note NONE ```pull/6/head
commit
9116cb8310
|
@ -21,11 +21,13 @@ go_library(
|
|||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -31,11 +31,13 @@ import (
|
|||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/client-go/rest"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||
|
@ -64,6 +66,9 @@ type GetOptions struct {
|
|||
Namespace string
|
||||
ExplicitNamespace bool
|
||||
|
||||
ServerPrint bool
|
||||
|
||||
Sort bool
|
||||
IgnoreNotFound bool
|
||||
ShowKind bool
|
||||
LabelColumns []string
|
||||
|
@ -120,6 +125,7 @@ var (
|
|||
|
||||
const (
|
||||
useOpenAPIPrintColumnFlagLabel = "use-openapi-print-columns"
|
||||
useServerPrintColumns = "experimental-server-print"
|
||||
)
|
||||
|
||||
// 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
|
|||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
addOpenAPIPrintColumnFlags(cmd)
|
||||
addServerPrintColumnFlags(cmd)
|
||||
cmd.Flags().BoolVar(&options.ShowKind, "show-kind", options.ShowKind, "If present, list the resource type for the requested object(s).")
|
||||
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...")
|
||||
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
|
|||
return nil
|
||||
}
|
||||
|
||||
options.ServerPrint = cmdutil.GetFlagBool(cmd, useServerPrintColumns)
|
||||
|
||||
var err error
|
||||
options.Namespace, options.ExplicitNamespace, err = f.DefaultNamespace()
|
||||
if err != nil {
|
||||
|
@ -184,6 +193,12 @@ func (options *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
|
|||
options.ExplicitNamespace = false
|
||||
}
|
||||
|
||||
isSorting, err := cmd.Flags().GetString("sort-by")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Sort = len(isSorting) > 0
|
||||
|
||||
options.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
|
||||
|
||||
switch {
|
||||
|
@ -238,6 +253,12 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
|
|||
return options.watch(f, cmd, args)
|
||||
}
|
||||
|
||||
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
|
||||
printer, err := cmdutil.PrinterForOptions(printOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := f.NewBuilder().
|
||||
Unstructured().
|
||||
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
|
||||
|
@ -251,6 +272,15 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
|
|||
ContinueOnError().
|
||||
Latest().
|
||||
Flatten().
|
||||
TransformRequests(func(req *rest.Request) {
|
||||
if options.ServerPrint && !printer.IsGeneric() && !options.Sort {
|
||||
group := metav1beta1.GroupName
|
||||
version := metav1beta1.SchemeGroupVersion.Version
|
||||
|
||||
tableParam := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
|
||||
req.SetHeader("Accept", tableParam)
|
||||
}
|
||||
}).
|
||||
Do()
|
||||
|
||||
if options.IgnoreNotFound {
|
||||
|
@ -260,12 +290,6 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
|
|||
return err
|
||||
}
|
||||
|
||||
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
|
||||
printer, err := cmdutil.PrinterForOptions(printOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filterOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
|
||||
filterFuncs := f.DefaultResourceFilterFunc()
|
||||
if r.TargetsSingleItems() {
|
||||
|
@ -285,6 +309,17 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
|
|||
|
||||
objs := make([]runtime.Object, len(infos))
|
||||
for ix := range infos {
|
||||
if options.ServerPrint {
|
||||
table, err := options.decodeIntoTable(cmdutil.InternalVersionJSONEncoder(), infos[ix].Object)
|
||||
if err == nil {
|
||||
infos[ix].Object = table
|
||||
} else {
|
||||
// if we are unable to decode server response into a v1beta1.Table,
|
||||
// fallback to client-side printing with whatever info the server returned.
|
||||
glog.V(2).Infof("Unable to decode server response into a Table. Falling back to hardcoded types: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
objs[ix] = infos[ix].Object
|
||||
}
|
||||
|
||||
|
@ -293,7 +328,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
|
|||
return err
|
||||
}
|
||||
var sorter *kubectl.RuntimeSort
|
||||
if len(sorting) > 0 && len(objs) > 1 {
|
||||
if options.Sort && len(objs) > 1 {
|
||||
// TODO: questionable
|
||||
if sorter, err = kubectl.SortObjects(cmdutil.InternalVersionDecoder(), objs, sorting); err != nil {
|
||||
return err
|
||||
|
@ -324,6 +359,15 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
|
|||
mapping = info.Mapping
|
||||
original = info.Object
|
||||
}
|
||||
|
||||
// if dealing with a table that has no rows, skip remaining steps
|
||||
// and avoid printing an unnecessary newline
|
||||
if table, isTable := info.Object.(*metav1beta1.Table); isTable {
|
||||
if len(table.Rows) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
|
||||
if printer != nil {
|
||||
w.Flush()
|
||||
|
@ -416,7 +460,19 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
|
|||
}
|
||||
}
|
||||
w.Flush()
|
||||
cmdutil.PrintFilterCount(options.ErrOut, len(objs), filteredResourceCount, len(allErrs), filterOpts, options.IgnoreNotFound)
|
||||
nonEmptyObjCount := 0
|
||||
for _, obj := range objs {
|
||||
if table, ok := obj.(*metav1beta1.Table); ok {
|
||||
// exclude any Table objects with empty rows from our total object count
|
||||
if len(table.Rows) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
nonEmptyObjCount++
|
||||
}
|
||||
|
||||
cmdutil.PrintFilterCount(options.ErrOut, nonEmptyObjCount, filteredResourceCount, len(allErrs), filterOpts, options.IgnoreNotFound)
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
|
@ -592,6 +648,25 @@ func attemptToConvertToInternal(obj runtime.Object, converter runtime.ObjectConv
|
|||
return internalObject
|
||||
}
|
||||
|
||||
func (options *GetOptions) decodeIntoTable(encoder runtime.Encoder, obj runtime.Object) (runtime.Object, error) {
|
||||
if obj.GetObjectKind().GroupVersionKind().Kind != "Table" {
|
||||
return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table")
|
||||
}
|
||||
|
||||
b, err := runtime.Encode(encoder, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
table := &metav1beta1.Table{}
|
||||
err = json.Unmarshal(b, table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return table, nil
|
||||
}
|
||||
|
||||
func (options *GetOptions) printGeneric(printer printers.ResourcePrinter, r *resource.Result, filterFuncs kubectl.Filters, filterOpts *printers.PrintOptions) error {
|
||||
// we flattened the data from the builder, so we have individual items, but now we'd like to either:
|
||||
// 1. if there is more than one item, combine them all into a single list
|
||||
|
@ -689,6 +764,10 @@ func addOpenAPIPrintColumnFlags(cmd *cobra.Command) {
|
|||
cmd.Flags().Bool(useOpenAPIPrintColumnFlagLabel, true, "If true, use x-kubernetes-print-column metadata (if present) from the OpenAPI schema for displaying a resource.")
|
||||
}
|
||||
|
||||
func addServerPrintColumnFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool(useServerPrintColumns, false, "If true, have the server return the appropriate table output. Supports extension APIs and CRD. Experimental.")
|
||||
}
|
||||
|
||||
func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {
|
||||
return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
|
||||
}
|
||||
|
|
|
@ -355,6 +355,11 @@ func hasCondition(conditions []metav1beta1.TableRowCondition, t metav1beta1.RowC
|
|||
// DecorateTable if you receive a table from a remote server before calling PrintTable.
|
||||
func PrintTable(table *metav1beta1.Table, output io.Writer, options PrintOptions) error {
|
||||
if !options.NoHeaders {
|
||||
// avoid printing headers if we have no rows to display
|
||||
if len(table.Rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
first := true
|
||||
for _, column := range table.ColumnDefinitions {
|
||||
if !options.Wide && column.Priority != 0 {
|
||||
|
@ -413,7 +418,6 @@ func DecorateTable(table *metav1beta1.Table, options PrintOptions) error {
|
|||
for i := range columns {
|
||||
if columns[i].Format == "name" && columns[i].Type == "string" {
|
||||
nameColumn = i
|
||||
fmt.Printf("found name column: %d\n", i)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue