Adds human-friendly table printing for resource listing (#85)

* Adds human-friendly printer

 All the credits goes to authors of https://github.com/kubernetes/kubernetes/tree/master/pkg/printers

* Updates the vendor/modules.txt

 As its updated after running hack/verify-codegen.sh

* Removes WithKind and WithNamespace printing options

 Lets start with bare minimum printer

* Adds licenses

 and references the original implementation file link.

 Removes pkg/printers/printers.go

* Removes unused imports
This commit is contained in:
Navid Shaikh 2019-05-10 04:57:38 +05:30 committed by Knative Prow Robot
parent ba59ad8b9a
commit b6d1bb47e7
5 changed files with 342 additions and 6 deletions

42
pkg/printers/interface.go Normal file
View File

@ -0,0 +1,42 @@
/*
Copyright 2017 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.
*/
// The following is a subset of original implementation
// at https://github.com/kubernetes/kubernetes/blob/v1.15.0-alpha.2/pkg/printers/interface.go
package printers
import (
"io"
"k8s.io/apimachinery/pkg/runtime"
)
// ResourcePrinter is an interface that knows how to print runtime objects.
type ResourcePrinter interface {
// Print receives a runtime object, formats it and prints it to a writer.
PrintObj(runtime.Object, io.Writer) error
}
// ResourcePrinterFunc is a function that can print objects
type ResourcePrinterFunc func(runtime.Object, io.Writer) error
// PrintObj implements ResourcePrinter
func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error {
return fn(obj, w)
}
// PrintOptions for different table printing options
type PrintOptions struct {
//TODO: Add options for eg: with-kind, server-printing, wide etc
}

View File

@ -0,0 +1,150 @@
/*
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.
*/
// The following is a subset of original implementation
// at https://github.com/kubernetes/kubernetes/blob/v1.15.0-alpha.2/pkg/printers/tablegenerator.go
package printers
import (
"fmt"
"reflect"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)
type TableGenerator interface {
GenerateTable(obj runtime.Object, options PrintOptions) (*metav1beta1.Table, error)
}
type PrintHandler interface {
TableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error
}
type handlerEntry struct {
columnDefinitions []metav1beta1.TableColumnDefinition
printFunc reflect.Value
args []reflect.Value
}
// HumanReadablePrinter is an implementation of ResourcePrinter which attempts to provide
// more elegant output.
type HumanReadablePrinter struct {
handlerMap map[reflect.Type]*handlerEntry
options PrintOptions
}
var _ TableGenerator = &HumanReadablePrinter{}
var _ PrintHandler = &HumanReadablePrinter{}
// NewTableGenerator creates a HumanReadablePrinter suitable for calling GenerateTable().
func NewTableGenerator() *HumanReadablePrinter {
return &HumanReadablePrinter{
handlerMap: make(map[reflect.Type]*handlerEntry),
}
}
// GenerateTable returns a table for the provided object, using the printer registered for that type. It returns
// a table that includes all of the information requested by options, but will not remove rows or columns. The
// caller is responsible for applying rules related to filtering rows or columns.
func (h *HumanReadablePrinter) GenerateTable(obj runtime.Object, options PrintOptions) (*metav1beta1.Table, error) {
t := reflect.TypeOf(obj)
handler, ok := h.handlerMap[t]
if !ok {
return nil, fmt.Errorf("no table handler registered for this type %v", t)
}
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(options)}
results := handler.printFunc.Call(args)
if !results[1].IsNil() {
return nil, results[1].Interface().(error)
}
var columns []metav1beta1.TableColumnDefinition
columns = make([]metav1beta1.TableColumnDefinition, 0, len(handler.columnDefinitions))
for i := range handler.columnDefinitions {
columns = append(columns, handler.columnDefinitions[i])
}
table := &metav1beta1.Table{
ListMeta: metav1.ListMeta{
ResourceVersion: "",
},
ColumnDefinitions: columns,
Rows: results[0].Interface().([]metav1beta1.TableRow),
}
if m, err := meta.ListAccessor(obj); err == nil {
table.ResourceVersion = m.GetResourceVersion()
table.SelfLink = m.GetSelfLink()
table.Continue = m.GetContinue()
} else {
if m, err := meta.CommonAccessor(obj); err == nil {
table.ResourceVersion = m.GetResourceVersion()
table.SelfLink = m.GetSelfLink()
}
}
return table, nil
}
// TableHandler adds a print handler with a given set of columns to HumanReadablePrinter instance.
// See ValidateRowPrintHandlerFunc for required method signature.
func (h *HumanReadablePrinter) TableHandler(columnDefinitions []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
printFuncValue := reflect.ValueOf(printFunc)
if err := ValidateRowPrintHandlerFunc(printFuncValue); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to register print function: %v", err))
return err
}
entry := &handlerEntry{
columnDefinitions: columnDefinitions,
printFunc: printFuncValue,
}
objType := printFuncValue.Type().In(0)
if _, ok := h.handlerMap[objType]; ok {
err := fmt.Errorf("registered duplicate printer for %v", objType)
utilruntime.HandleError(err)
return err
}
h.handlerMap[objType] = entry
return nil
}
// ValidateRowPrintHandlerFunc validates print handler signature.
// printFunc is the function that will be called to print an object.
// It must be of the following type:
// func printFunc(object ObjectType, options PrintOptions) ([]metav1beta1.TableRow, error)
// where ObjectType is the type of the object that will be printed, and the first
// return value is an array of rows, with each row containing a number of cells that
// match the number of columns defined for that printer function.
func ValidateRowPrintHandlerFunc(printFunc reflect.Value) error {
if printFunc.Kind() != reflect.Func {
return fmt.Errorf("invalid print handler. %#v is not a function", printFunc)
}
funcType := printFunc.Type()
if funcType.NumIn() != 2 || funcType.NumOut() != 2 {
return fmt.Errorf("invalid print handler." +
"Must accept 2 parameters and return 2 value.")
}
if funcType.In(1) != reflect.TypeOf((*PrintOptions)(nil)).Elem() ||
funcType.Out(0) != reflect.TypeOf((*[]metav1beta1.TableRow)(nil)).Elem() ||
funcType.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
return fmt.Errorf("invalid print handler. The expected signature is: "+
"func handler(obj %v, options PrintOptions) ([]metav1beta1.TableRow, error)", funcType.In(0))
}
return nil
}

View File

@ -0,0 +1,108 @@
/*
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.
*/
// The following is a subset of original implementation
// at https://github.com/kubernetes/kubernetes/blob/v1.15.0-alpha.2/pkg/printers/tableprinter.go
package printers
import (
"fmt"
"io"
"reflect"
"strings"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"text/tabwriter"
)
var _ ResourcePrinter = &HumanReadablePrinter{}
// NewTablePrinter creates a printer suitable for calling PrintObj().
func NewTablePrinter(options PrintOptions) *HumanReadablePrinter {
printer := &HumanReadablePrinter{
handlerMap: make(map[reflect.Type]*handlerEntry),
options: options,
}
return printer
}
// PrintObj prints the obj in a human-friendly format according to the type of the obj.
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {
w, found := output.(*tabwriter.Writer)
if !found {
w = GetNewTabWriter(output)
output = w
defer w.Flush()
}
// Search for a handler registered handler to print it
t := reflect.TypeOf(obj)
if handler := h.handlerMap[t]; handler != nil {
if err := printRowsForHandlerEntry(output, handler, obj, h.options); err != nil {
return err
}
return nil
}
// we failed all reasonable printing efforts, report failure
return fmt.Errorf("error: unknown type %#v", obj)
}
// printRowsForHandlerEntry prints the incremental table output
// including all the rows in the object. It returns the current type
// or an error, if any.
func printRowsForHandlerEntry(output io.Writer, handler *handlerEntry, obj runtime.Object, options PrintOptions) error {
var results []reflect.Value
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(options)}
results = handler.printFunc.Call(args)
if !results[1].IsNil() {
return results[1].Interface().(error)
}
var headers []string
for _, column := range handler.columnDefinitions {
headers = append(headers, strings.ToUpper(column.Name))
}
printHeader(headers, output)
if results[1].IsNil() {
rows := results[0].Interface().([]metav1beta1.TableRow)
printRows(output, rows, options)
return nil
}
return results[1].Interface().(error)
}
func printHeader(columnNames []string, w io.Writer) error {
if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil {
return err
}
return nil
}
// printRows writes the provided rows to output.
func printRows(output io.Writer, rows []metav1beta1.TableRow, options PrintOptions) {
for _, row := range rows {
for i, cell := range row.Cells {
if i != 0 {
fmt.Fprint(output, "\t")
}
fmt.Fprint(output, cell)
}
output.Write([]byte("\n"))
}
}

36
pkg/printers/tabwriter.go Normal file
View File

@ -0,0 +1,36 @@
/*
Copyright 2017 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.
*/
// The following is a subset of original implementation
// at https://github.com/kubernetes/kubernetes/blob/v1.15.0-alpha.2/pkg/printers/tabwriter.go
package printers
import (
"io"
"text/tabwriter"
)
const (
tabwriterMinWidth = 6
tabwriterWidth = 4
tabwriterPadding = 3
tabwriterPadChar = ' '
tabwriterFlags = tabwriter.TabIndent
)
// GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text.
func GetNewTabWriter(output io.Writer) *tabwriter.Writer {
return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags)
}

12
vendor/modules.txt vendored
View File

@ -196,10 +196,13 @@ k8s.io/api/storage/v1beta1
k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/api/resource
k8s.io/apimachinery/pkg/apis/meta/v1 k8s.io/apimachinery/pkg/apis/meta/v1
k8s.io/apimachinery/pkg/runtime/schema k8s.io/apimachinery/pkg/runtime/schema
k8s.io/apimachinery/pkg/api/meta
k8s.io/apimachinery/pkg/apis/meta/v1beta1
k8s.io/apimachinery/pkg/runtime
k8s.io/apimachinery/pkg/util/runtime
k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/equality
k8s.io/apimachinery/pkg/api/validation k8s.io/apimachinery/pkg/api/validation
k8s.io/apimachinery/pkg/apis/meta/v1/unstructured k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
k8s.io/apimachinery/pkg/runtime
k8s.io/apimachinery/pkg/util/intstr k8s.io/apimachinery/pkg/util/intstr
k8s.io/apimachinery/pkg/util/sets k8s.io/apimachinery/pkg/util/sets
k8s.io/apimachinery/pkg/util/validation k8s.io/apimachinery/pkg/util/validation
@ -210,16 +213,14 @@ k8s.io/apimachinery/pkg/conversion
k8s.io/apimachinery/pkg/fields k8s.io/apimachinery/pkg/fields
k8s.io/apimachinery/pkg/labels k8s.io/apimachinery/pkg/labels
k8s.io/apimachinery/pkg/selection k8s.io/apimachinery/pkg/selection
k8s.io/apimachinery/pkg/util/runtime
k8s.io/apimachinery/pkg/api/meta
k8s.io/apimachinery/pkg/util/json k8s.io/apimachinery/pkg/util/json
k8s.io/apimachinery/pkg/util/net k8s.io/apimachinery/pkg/util/net
k8s.io/apimachinery/pkg/util/yaml k8s.io/apimachinery/pkg/util/yaml
k8s.io/apimachinery/pkg/util/errors k8s.io/apimachinery/pkg/util/errors
k8s.io/apimachinery/pkg/apis/meta/v1/validation
k8s.io/apimachinery/pkg/util/validation/field
k8s.io/apimachinery/pkg/conversion/queryparams k8s.io/apimachinery/pkg/conversion/queryparams
k8s.io/apimachinery/pkg/util/naming k8s.io/apimachinery/pkg/util/naming
k8s.io/apimachinery/pkg/apis/meta/v1/validation
k8s.io/apimachinery/pkg/util/validation/field
k8s.io/apimachinery/pkg/runtime/serializer/json k8s.io/apimachinery/pkg/runtime/serializer/json
k8s.io/apimachinery/pkg/runtime/serializer/protobuf k8s.io/apimachinery/pkg/runtime/serializer/protobuf
k8s.io/apimachinery/pkg/runtime/serializer/recognizer k8s.io/apimachinery/pkg/runtime/serializer/recognizer
@ -227,7 +228,6 @@ k8s.io/apimachinery/pkg/runtime/serializer/versioning
k8s.io/apimachinery/pkg/api/errors k8s.io/apimachinery/pkg/api/errors
k8s.io/apimachinery/pkg/runtime/serializer/streaming k8s.io/apimachinery/pkg/runtime/serializer/streaming
k8s.io/apimachinery/third_party/forked/golang/reflect k8s.io/apimachinery/third_party/forked/golang/reflect
k8s.io/apimachinery/pkg/apis/meta/v1beta1
k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructuredscheme
k8s.io/apimachinery/pkg/version k8s.io/apimachinery/pkg/version
k8s.io/apimachinery/pkg/util/strategicpatch k8s.io/apimachinery/pkg/util/strategicpatch