...

Source file src/k8s.io/kubernetes/pkg/printers/tablegenerator.go

Documentation: k8s.io/kubernetes/pkg/printers

     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  	"k8s.io/apimachinery/pkg/runtime"
    26  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    27  )
    28  
    29  // GenerateOptions encapsulates attributes for table generation.
    30  type GenerateOptions struct {
    31  	NoHeaders bool
    32  	Wide      bool
    33  }
    34  
    35  // TableGenerator - an interface for generating metav1.Table provided a runtime.Object
    36  type TableGenerator interface {
    37  	GenerateTable(obj runtime.Object, options GenerateOptions) (*metav1.Table, error)
    38  }
    39  
    40  // PrintHandler - interface to handle printing provided an array of metav1.TableColumnDefinition
    41  type PrintHandler interface {
    42  	TableHandler(columns []metav1.TableColumnDefinition, printFunc interface{}) error
    43  }
    44  
    45  type handlerEntry struct {
    46  	columnDefinitions []metav1.TableColumnDefinition
    47  	printFunc         reflect.Value
    48  }
    49  
    50  // HumanReadableGenerator is an implementation of TableGenerator used to generate
    51  // a table for a specific resource. The table is printed with a TablePrinter using
    52  // PrintObj().
    53  type HumanReadableGenerator struct {
    54  	handlerMap map[reflect.Type]*handlerEntry
    55  }
    56  
    57  var _ TableGenerator = &HumanReadableGenerator{}
    58  var _ PrintHandler = &HumanReadableGenerator{}
    59  
    60  // NewTableGenerator creates a HumanReadableGenerator suitable for calling GenerateTable().
    61  func NewTableGenerator() *HumanReadableGenerator {
    62  	return &HumanReadableGenerator{
    63  		handlerMap: make(map[reflect.Type]*handlerEntry),
    64  	}
    65  }
    66  
    67  // With method - accepts a list of builder functions that modify HumanReadableGenerator
    68  func (h *HumanReadableGenerator) With(fns ...func(PrintHandler)) *HumanReadableGenerator {
    69  	for _, fn := range fns {
    70  		fn(h)
    71  	}
    72  	return h
    73  }
    74  
    75  // GenerateTable returns a table for the provided object, using the printer registered for that type. It returns
    76  // a table that includes all of the information requested by options, but will not remove rows or columns. The
    77  // caller is responsible for applying rules related to filtering rows or columns.
    78  func (h *HumanReadableGenerator) GenerateTable(obj runtime.Object, options GenerateOptions) (*metav1.Table, error) {
    79  	t := reflect.TypeOf(obj)
    80  	handler, ok := h.handlerMap[t]
    81  	if !ok {
    82  		return nil, fmt.Errorf("no table handler registered for this type %v", t)
    83  	}
    84  
    85  	args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(options)}
    86  	results := handler.printFunc.Call(args)
    87  	if !results[1].IsNil() {
    88  		return nil, results[1].Interface().(error)
    89  	}
    90  
    91  	var columns []metav1.TableColumnDefinition
    92  	if !options.NoHeaders {
    93  		columns = handler.columnDefinitions
    94  		if !options.Wide {
    95  			columns = make([]metav1.TableColumnDefinition, 0, len(handler.columnDefinitions))
    96  			for i := range handler.columnDefinitions {
    97  				if handler.columnDefinitions[i].Priority != 0 {
    98  					continue
    99  				}
   100  				columns = append(columns, handler.columnDefinitions[i])
   101  			}
   102  		}
   103  	}
   104  	table := &metav1.Table{
   105  		ListMeta: metav1.ListMeta{
   106  			ResourceVersion: "",
   107  		},
   108  		ColumnDefinitions: columns,
   109  		Rows:              results[0].Interface().([]metav1.TableRow),
   110  	}
   111  	if m, err := meta.ListAccessor(obj); err == nil {
   112  		table.ResourceVersion = m.GetResourceVersion()
   113  		table.Continue = m.GetContinue()
   114  		table.RemainingItemCount = m.GetRemainingItemCount()
   115  	} else {
   116  		if m, err := meta.CommonAccessor(obj); err == nil {
   117  			table.ResourceVersion = m.GetResourceVersion()
   118  		}
   119  	}
   120  	return table, nil
   121  }
   122  
   123  // TableHandler adds a print handler with a given set of columns to HumanReadableGenerator instance.
   124  // See ValidateRowPrintHandlerFunc for required method signature.
   125  func (h *HumanReadableGenerator) TableHandler(columnDefinitions []metav1.TableColumnDefinition, printFunc interface{}) error {
   126  	printFuncValue := reflect.ValueOf(printFunc)
   127  	if err := ValidateRowPrintHandlerFunc(printFuncValue); err != nil {
   128  		utilruntime.HandleError(fmt.Errorf("unable to register print function: %v", err))
   129  		return err
   130  	}
   131  	entry := &handlerEntry{
   132  		columnDefinitions: columnDefinitions,
   133  		printFunc:         printFuncValue,
   134  	}
   135  
   136  	objType := printFuncValue.Type().In(0)
   137  	if _, ok := h.handlerMap[objType]; ok {
   138  		err := fmt.Errorf("registered duplicate printer for %v", objType)
   139  		utilruntime.HandleError(err)
   140  		return err
   141  	}
   142  	h.handlerMap[objType] = entry
   143  	return nil
   144  }
   145  
   146  // ValidateRowPrintHandlerFunc validates print handler signature.
   147  // printFunc is the function that will be called to print an object.
   148  // It must be of the following type:
   149  //
   150  //	func printFunc(object ObjectType, options GenerateOptions) ([]metav1.TableRow, error)
   151  //
   152  // where ObjectType is the type of the object that will be printed, and the first
   153  // return value is an array of rows, with each row containing a number of cells that
   154  // match the number of columns defined for that printer function.
   155  func ValidateRowPrintHandlerFunc(printFunc reflect.Value) error {
   156  	if printFunc.Kind() != reflect.Func {
   157  		return fmt.Errorf("invalid print handler. %#v is not a function", printFunc)
   158  	}
   159  	funcType := printFunc.Type()
   160  	if funcType.NumIn() != 2 || funcType.NumOut() != 2 {
   161  		return fmt.Errorf("invalid print handler." +
   162  			"Must accept 2 parameters and return 2 value")
   163  	}
   164  	if funcType.In(1) != reflect.TypeOf((*GenerateOptions)(nil)).Elem() ||
   165  		funcType.Out(0) != reflect.TypeOf((*[]metav1.TableRow)(nil)).Elem() ||
   166  		funcType.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
   167  		return fmt.Errorf("invalid print handler. The expected signature is: "+
   168  			"func handler(obj %v, options GenerateOptions) ([]metav1.TableRow, error)", funcType.In(0))
   169  	}
   170  	return nil
   171  }
   172  

View as plain text