diff --git a/pkg/printers/interface.go b/pkg/printers/interface.go new file mode 100644 index 000000000..70aa97b4d --- /dev/null +++ b/pkg/printers/interface.go @@ -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 +} diff --git a/pkg/printers/tablegenerator.go b/pkg/printers/tablegenerator.go new file mode 100644 index 000000000..0c91b9de7 --- /dev/null +++ b/pkg/printers/tablegenerator.go @@ -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 +} diff --git a/pkg/printers/tableprinter.go b/pkg/printers/tableprinter.go new file mode 100644 index 000000000..f2a4fd6a2 --- /dev/null +++ b/pkg/printers/tableprinter.go @@ -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")) + } +} diff --git a/pkg/printers/tabwriter.go b/pkg/printers/tabwriter.go new file mode 100644 index 000000000..717e5e6e1 --- /dev/null +++ b/pkg/printers/tabwriter.go @@ -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) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 53ceccbcf..e67f99a12 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -196,10 +196,13 @@ k8s.io/api/storage/v1beta1 k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/apis/meta/v1 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/validation k8s.io/apimachinery/pkg/apis/meta/v1/unstructured -k8s.io/apimachinery/pkg/runtime k8s.io/apimachinery/pkg/util/intstr k8s.io/apimachinery/pkg/util/sets k8s.io/apimachinery/pkg/util/validation @@ -210,16 +213,14 @@ k8s.io/apimachinery/pkg/conversion k8s.io/apimachinery/pkg/fields k8s.io/apimachinery/pkg/labels 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/net k8s.io/apimachinery/pkg/util/yaml 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/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/protobuf 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/runtime/serializer/streaming 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/version k8s.io/apimachinery/pkg/util/strategicpatch