mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Fix --watch-only of a single item with table output
This commit is contained in:
parent
2b65c74b94
commit
5d508760e3
@ -22,6 +22,7 @@ go_library(
|
||||
"get.go",
|
||||
"get_flags.go",
|
||||
"humanreadable_flags.go",
|
||||
"skip_printer.go",
|
||||
"sorter.go",
|
||||
"table_printer.go",
|
||||
],
|
||||
@ -60,6 +61,7 @@ go_library(
|
||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/k8s.io/utils/integer:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
"//vendor/vbom.ml/util/sortorder:go_default_library",
|
||||
],
|
||||
)
|
||||
@ -97,7 +99,6 @@ go_test(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
|
@ -49,12 +49,13 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
// GetOptions contains the input to the get command.
|
||||
type GetOptions struct {
|
||||
PrintFlags *PrintFlags
|
||||
ToPrinter func(*meta.RESTMapping, bool, bool) (printers.ResourcePrinterFunc, error)
|
||||
ToPrinter func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error)
|
||||
IsHumanReadablePrinter bool
|
||||
PrintWithOpenAPICols bool
|
||||
|
||||
@ -230,7 +231,7 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
||||
o.IsHumanReadablePrinter = true
|
||||
}
|
||||
|
||||
o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
|
||||
o.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
|
||||
// make a new copy of current flags / opts before mutating
|
||||
printFlags := o.PrintFlags.Copy()
|
||||
|
||||
@ -257,6 +258,9 @@ func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
|
||||
if o.Sort {
|
||||
printer = &SortingPrinter{Delegate: printer, SortField: sortBy}
|
||||
}
|
||||
if outputObjects != nil {
|
||||
printer = &skipPrinter{delegate: printer, output: outputObjects}
|
||||
}
|
||||
if o.ServerPrint {
|
||||
printer = &TablePrinter{Delegate: printer}
|
||||
}
|
||||
@ -539,7 +543,7 @@ func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) e
|
||||
fmt.Fprintln(o.ErrOut)
|
||||
}
|
||||
|
||||
printer, err = o.ToPrinter(mapping, printWithNamespace, printWithKind)
|
||||
printer, err = o.ToPrinter(mapping, nil, printWithNamespace, printWithKind)
|
||||
if err != nil {
|
||||
if !errs.Has(err.Error()) {
|
||||
errs.Insert(err.Error())
|
||||
@ -635,7 +639,8 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
|
||||
info := infos[0]
|
||||
mapping := info.ResourceMapping()
|
||||
printer, err := o.ToPrinter(mapping, o.AllNamespaces, false)
|
||||
outputObjects := utilpointer.BoolPtr(!o.WatchOnly)
|
||||
printer, err := o.ToPrinter(mapping, outputObjects, o.AllNamespaces, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -664,25 +669,29 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
tableGK := metainternal.SchemeGroupVersion.WithKind("Table").GroupKind()
|
||||
|
||||
// print the current object
|
||||
if !o.WatchOnly {
|
||||
var objsToPrint []runtime.Object
|
||||
|
||||
if isList {
|
||||
objsToPrint, _ = meta.ExtractList(obj)
|
||||
} else {
|
||||
objsToPrint = append(objsToPrint, obj)
|
||||
var objsToPrint []runtime.Object
|
||||
if isList {
|
||||
objsToPrint, _ = meta.ExtractList(obj)
|
||||
} else {
|
||||
objsToPrint = append(objsToPrint, obj)
|
||||
}
|
||||
for _, objToPrint := range objsToPrint {
|
||||
if o.IsHumanReadablePrinter && objToPrint.GetObjectKind().GroupVersionKind().GroupKind() != tableGK {
|
||||
// printing anything other than tables always takes the internal version, but the watch event uses externals
|
||||
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
|
||||
objToPrint = attemptToConvertToInternal(objToPrint, legacyscheme.Scheme, internalGV)
|
||||
}
|
||||
for _, objToPrint := range objsToPrint {
|
||||
if o.IsHumanReadablePrinter && objToPrint.GetObjectKind().GroupVersionKind().GroupKind() != tableGK {
|
||||
// printing anything other than tables always takes the internal version, but the watch event uses externals
|
||||
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
|
||||
objToPrint = attemptToConvertToInternal(objToPrint, legacyscheme.Scheme, internalGV)
|
||||
}
|
||||
if err := printer.PrintObj(objToPrint, writer); err != nil {
|
||||
return fmt.Errorf("unable to output the provided object: %v", err)
|
||||
}
|
||||
if err := printer.PrintObj(objToPrint, writer); err != nil {
|
||||
return fmt.Errorf("unable to output the provided object: %v", err)
|
||||
}
|
||||
writer.Flush()
|
||||
}
|
||||
writer.Flush()
|
||||
if isList {
|
||||
// we can start outputting objects now, watches started from lists don't emit synthetic added events
|
||||
*outputObjects = true
|
||||
} else {
|
||||
// suppress output, since watches started for individual items emit a synthetic ADDED event first
|
||||
*outputObjects = false
|
||||
}
|
||||
|
||||
// print watched changes
|
||||
@ -691,18 +700,11 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
return err
|
||||
}
|
||||
|
||||
first := true
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
intr := interrupt.New(nil, cancel)
|
||||
intr.Run(func() error {
|
||||
_, err := watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) {
|
||||
if !isList && first {
|
||||
// drop the initial watch event in the single resource case
|
||||
first = false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// printing always takes the internal version, but the watch event uses externals
|
||||
// TODO fix printing to use server-side or be version agnostic
|
||||
objToPrint := e.Object
|
||||
@ -714,6 +716,8 @@ func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string)
|
||||
return false, err
|
||||
}
|
||||
writer.Flush()
|
||||
// after processing at least one event, start outputting objects
|
||||
*outputObjects = true
|
||||
return false, nil
|
||||
})
|
||||
return err
|
||||
@ -750,7 +754,7 @@ func (o *GetOptions) printGeneric(r *resource.Result) error {
|
||||
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
|
||||
}
|
||||
|
||||
printer, err := o.ToPrinter(nil, false, false)
|
||||
printer, err := o.ToPrinter(nil, nil, false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
48
pkg/kubectl/cmd/get/skip_printer.go
Normal file
48
pkg/kubectl/cmd/get/skip_printer.go
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package get
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/cli-runtime/pkg/printers"
|
||||
)
|
||||
|
||||
// skipPrinter allows conditionally suppressing object output via the output field.
|
||||
// table objects are suppressed by setting their Rows to nil (allowing column definitions to propagate to the delegate).
|
||||
// non-table objects are suppressed by not calling the delegate at all.
|
||||
type skipPrinter struct {
|
||||
delegate printers.ResourcePrinter
|
||||
output *bool
|
||||
}
|
||||
|
||||
func (p *skipPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
|
||||
if *p.output {
|
||||
return p.delegate.PrintObj(obj, writer)
|
||||
}
|
||||
|
||||
table, isTable := obj.(*metav1beta1.Table)
|
||||
if !isTable {
|
||||
return nil
|
||||
}
|
||||
|
||||
table = table.DeepCopy()
|
||||
table.Rows = nil
|
||||
return p.delegate.PrintObj(table, writer)
|
||||
}
|
@ -48,10 +48,11 @@ type handlerEntry struct {
|
||||
// will only be printed if the object type changes. This makes it useful for printing items
|
||||
// received from watches.
|
||||
type HumanReadablePrinter struct {
|
||||
handlerMap map[reflect.Type]*handlerEntry
|
||||
options PrintOptions
|
||||
lastType interface{}
|
||||
lastColumns []metav1beta1.TableColumnDefinition
|
||||
handlerMap map[reflect.Type]*handlerEntry
|
||||
options PrintOptions
|
||||
lastType interface{}
|
||||
lastColumns []metav1beta1.TableColumnDefinition
|
||||
printedHeaders bool
|
||||
}
|
||||
|
||||
var _ TableGenerator = &HumanReadablePrinter{}
|
||||
|
@ -80,18 +80,22 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
||||
if table, ok := obj.(*metav1beta1.Table); ok {
|
||||
// Do not print headers if this table has no column definitions, or they are the same as the last ones we printed
|
||||
localOptions := h.options
|
||||
if len(table.ColumnDefinitions) == 0 || reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns) {
|
||||
if h.printedHeaders && (len(table.ColumnDefinitions) == 0 || reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns)) {
|
||||
localOptions.NoHeaders = true
|
||||
}
|
||||
|
||||
if len(table.ColumnDefinitions) == 0 {
|
||||
// If this table has no column definitions, use the columns from the last table we printed for decoration and layout.
|
||||
// This is done when receiving tables in watch events to save bandwidth.
|
||||
localOptions.NoHeaders = true
|
||||
table.ColumnDefinitions = h.lastColumns
|
||||
} else {
|
||||
} else if !reflect.DeepEqual(table.ColumnDefinitions, h.lastColumns) {
|
||||
// If this table has column definitions, remember them for future use.
|
||||
h.lastColumns = table.ColumnDefinitions
|
||||
h.printedHeaders = false
|
||||
}
|
||||
|
||||
if len(table.Rows) > 0 {
|
||||
h.printedHeaders = true
|
||||
}
|
||||
|
||||
if err := decorateTable(table, localOptions); err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user