Skip to content

Commit 7790fbe

Browse files
authored
Merge pull request kubernetes#76746 from seans3/table-printer-split
Split humanreadble.go into tablegenerator.go and tableprinter.go
2 parents 47c0340 + 0501fec commit 7790fbe

File tree

4 files changed

+221
-185
lines changed

4 files changed

+221
-185
lines changed

pkg/printers/BUILD

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ load(
99
go_library(
1010
name = "go_default_library",
1111
srcs = [
12-
"humanreadable.go",
1312
"interface.go",
13+
"tablegenerator.go",
14+
"tableprinter.go",
1415
"tabwriter.go",
1516
],
1617
importpath = "k8s.io/kubernetes/pkg/printers",
@@ -45,10 +46,10 @@ filegroup(
4546

4647
go_test(
4748
name = "go_default_test",
48-
srcs = ["humanreadable_test.go"],
49+
srcs = ["tableprinter_test.go"],
4950
embed = [":go_default_library"],
5051
deps = [
51-
"//pkg/apis/core:go_default_library",
52+
"//staging/src/k8s.io/api/core/v1:go_default_library",
5253
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
5354
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1beta1:go_default_library",
5455
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",

pkg/printers/tablegenerator.go

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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 printers
18+
19+
import (
20+
"fmt"
21+
"reflect"
22+
23+
"k8s.io/apimachinery/pkg/api/meta"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
26+
"k8s.io/apimachinery/pkg/runtime"
27+
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
28+
)
29+
30+
type TableGenerator interface {
31+
GenerateTable(obj runtime.Object, options PrintOptions) (*metav1beta1.Table, error)
32+
}
33+
34+
type PrintHandler interface {
35+
TableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error
36+
DefaultTableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error
37+
}
38+
39+
type handlerEntry struct {
40+
columnDefinitions []metav1beta1.TableColumnDefinition
41+
printFunc reflect.Value
42+
args []reflect.Value
43+
}
44+
45+
// HumanReadablePrinter is an implementation of ResourcePrinter which attempts to provide
46+
// more elegant output. It is not threadsafe, but you may call PrintObj repeatedly; headers
47+
// will only be printed if the object type changes. This makes it useful for printing items
48+
// received from watches.
49+
type HumanReadablePrinter struct {
50+
handlerMap map[reflect.Type]*handlerEntry
51+
defaultHandler *handlerEntry
52+
options PrintOptions
53+
lastType interface{}
54+
lastColumns []metav1beta1.TableColumnDefinition
55+
}
56+
57+
var _ TableGenerator = &HumanReadablePrinter{}
58+
var _ PrintHandler = &HumanReadablePrinter{}
59+
60+
// NewTableGenerator creates a HumanReadablePrinter suitable for calling GenerateTable().
61+
func NewTableGenerator() *HumanReadablePrinter {
62+
return &HumanReadablePrinter{
63+
handlerMap: make(map[reflect.Type]*handlerEntry),
64+
}
65+
}
66+
67+
func (a *HumanReadablePrinter) With(fns ...func(PrintHandler)) *HumanReadablePrinter {
68+
for _, fn := range fns {
69+
fn(a)
70+
}
71+
return a
72+
}
73+
74+
// GenerateTable returns a table for the provided object, using the printer registered for that type. It returns
75+
// a table that includes all of the information requested by options, but will not remove rows or columns. The
76+
// caller is responsible for applying rules related to filtering rows or columns.
77+
func (h *HumanReadablePrinter) GenerateTable(obj runtime.Object, options PrintOptions) (*metav1beta1.Table, error) {
78+
t := reflect.TypeOf(obj)
79+
handler, ok := h.handlerMap[t]
80+
if !ok {
81+
return nil, fmt.Errorf("no table handler registered for this type %v", t)
82+
}
83+
84+
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(options)}
85+
results := handler.printFunc.Call(args)
86+
if !results[1].IsNil() {
87+
return nil, results[1].Interface().(error)
88+
}
89+
90+
var columns []metav1beta1.TableColumnDefinition
91+
if !options.NoHeaders {
92+
columns = handler.columnDefinitions
93+
if !options.Wide {
94+
columns = make([]metav1beta1.TableColumnDefinition, 0, len(handler.columnDefinitions))
95+
for i := range handler.columnDefinitions {
96+
if handler.columnDefinitions[i].Priority != 0 {
97+
continue
98+
}
99+
columns = append(columns, handler.columnDefinitions[i])
100+
}
101+
}
102+
}
103+
table := &metav1beta1.Table{
104+
ListMeta: metav1.ListMeta{
105+
ResourceVersion: "",
106+
},
107+
ColumnDefinitions: columns,
108+
Rows: results[0].Interface().([]metav1beta1.TableRow),
109+
}
110+
if m, err := meta.ListAccessor(obj); err == nil {
111+
table.ResourceVersion = m.GetResourceVersion()
112+
table.SelfLink = m.GetSelfLink()
113+
table.Continue = m.GetContinue()
114+
} else {
115+
if m, err := meta.CommonAccessor(obj); err == nil {
116+
table.ResourceVersion = m.GetResourceVersion()
117+
table.SelfLink = m.GetSelfLink()
118+
}
119+
}
120+
if err := decorateTable(table, options); err != nil {
121+
return nil, err
122+
}
123+
return table, nil
124+
}
125+
126+
// TableHandler adds a print handler with a given set of columns to HumanReadablePrinter instance.
127+
// See ValidateRowPrintHandlerFunc for required method signature.
128+
func (h *HumanReadablePrinter) TableHandler(columnDefinitions []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
129+
printFuncValue := reflect.ValueOf(printFunc)
130+
if err := ValidateRowPrintHandlerFunc(printFuncValue); err != nil {
131+
utilruntime.HandleError(fmt.Errorf("unable to register print function: %v", err))
132+
return err
133+
}
134+
entry := &handlerEntry{
135+
columnDefinitions: columnDefinitions,
136+
printFunc: printFuncValue,
137+
}
138+
139+
objType := printFuncValue.Type().In(0)
140+
if _, ok := h.handlerMap[objType]; ok {
141+
err := fmt.Errorf("registered duplicate printer for %v", objType)
142+
utilruntime.HandleError(err)
143+
return err
144+
}
145+
h.handlerMap[objType] = entry
146+
return nil
147+
}
148+
149+
// DefaultTableHandler registers a set of columns and a print func that is given a chance to process
150+
// any object without an explicit handler. Only the most recently set print handler is used.
151+
// See ValidateRowPrintHandlerFunc for required method signature.
152+
func (h *HumanReadablePrinter) DefaultTableHandler(columnDefinitions []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
153+
printFuncValue := reflect.ValueOf(printFunc)
154+
if err := ValidateRowPrintHandlerFunc(printFuncValue); err != nil {
155+
utilruntime.HandleError(fmt.Errorf("unable to register print function: %v", err))
156+
return err
157+
}
158+
entry := &handlerEntry{
159+
columnDefinitions: columnDefinitions,
160+
printFunc: printFuncValue,
161+
}
162+
163+
h.defaultHandler = entry
164+
return nil
165+
}
166+
167+
// ValidateRowPrintHandlerFunc validates print handler signature.
168+
// printFunc is the function that will be called to print an object.
169+
// It must be of the following type:
170+
// func printFunc(object ObjectType, options PrintOptions) ([]metav1beta1.TableRow, error)
171+
// where ObjectType is the type of the object that will be printed, and the first
172+
// return value is an array of rows, with each row containing a number of cells that
173+
// match the number of columns defined for that printer function.
174+
func ValidateRowPrintHandlerFunc(printFunc reflect.Value) error {
175+
if printFunc.Kind() != reflect.Func {
176+
return fmt.Errorf("invalid print handler. %#v is not a function", printFunc)
177+
}
178+
funcType := printFunc.Type()
179+
if funcType.NumIn() != 2 || funcType.NumOut() != 2 {
180+
return fmt.Errorf("invalid print handler." +
181+
"Must accept 2 parameters and return 2 value.")
182+
}
183+
if funcType.In(1) != reflect.TypeOf((*PrintOptions)(nil)).Elem() ||
184+
funcType.Out(0) != reflect.TypeOf((*[]metav1beta1.TableRow)(nil)).Elem() ||
185+
funcType.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
186+
return fmt.Errorf("invalid print handler. The expected signature is: "+
187+
"func handler(obj %v, options PrintOptions) ([]metav1beta1.TableRow, error)", funcType.In(0))
188+
}
189+
return nil
190+
}

0 commit comments

Comments
 (0)