tablegenerator.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package printers
  14. import (
  15. "fmt"
  16. "reflect"
  17. "k8s.io/apimachinery/pkg/api/meta"
  18. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  19. metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  22. )
  23. // TableGenerator - an interface for generating metav1beta1.Table provided a runtime.Object
  24. type TableGenerator interface {
  25. GenerateTable(obj runtime.Object, options PrintOptions) (*metav1beta1.Table, error)
  26. }
  27. // PrintHandler - interface to handle printing provided an array of metav1beta1.TableColumnDefinition
  28. type PrintHandler interface {
  29. TableHandler(columns []metav1beta1.TableColumnDefinition, printFunc interface{}) error
  30. }
  31. type handlerEntry struct {
  32. columnDefinitions []metav1beta1.TableColumnDefinition
  33. printFunc reflect.Value
  34. args []reflect.Value
  35. }
  36. // HumanReadablePrinter is an implementation of ResourcePrinter which attempts to provide
  37. // more elegant output. It is not threadsafe, but you may call PrintObj repeatedly; headers
  38. // will only be printed if the object type changes. This makes it useful for printing items
  39. // received from watches.
  40. type HumanReadablePrinter struct {
  41. handlerMap map[reflect.Type]*handlerEntry
  42. options PrintOptions
  43. lastType interface{}
  44. lastColumns []metav1beta1.TableColumnDefinition
  45. }
  46. var _ TableGenerator = &HumanReadablePrinter{}
  47. var _ PrintHandler = &HumanReadablePrinter{}
  48. // NewTableGenerator creates a HumanReadablePrinter suitable for calling GenerateTable().
  49. func NewTableGenerator() *HumanReadablePrinter {
  50. return &HumanReadablePrinter{
  51. handlerMap: make(map[reflect.Type]*handlerEntry),
  52. }
  53. }
  54. // With method - accepts a list of builder functions that modify HumanReadablePrinter
  55. func (h *HumanReadablePrinter) With(fns ...func(PrintHandler)) *HumanReadablePrinter {
  56. for _, fn := range fns {
  57. fn(h)
  58. }
  59. return h
  60. }
  61. // GenerateTable returns a table for the provided object, using the printer registered for that type. It returns
  62. // a table that includes all of the information requested by options, but will not remove rows or columns. The
  63. // caller is responsible for applying rules related to filtering rows or columns.
  64. func (h *HumanReadablePrinter) GenerateTable(obj runtime.Object, options PrintOptions) (*metav1beta1.Table, error) {
  65. t := reflect.TypeOf(obj)
  66. handler, ok := h.handlerMap[t]
  67. if !ok {
  68. return nil, fmt.Errorf("no table handler registered for this type %v", t)
  69. }
  70. args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(options)}
  71. results := handler.printFunc.Call(args)
  72. if !results[1].IsNil() {
  73. return nil, results[1].Interface().(error)
  74. }
  75. var columns []metav1beta1.TableColumnDefinition
  76. if !options.NoHeaders {
  77. columns = handler.columnDefinitions
  78. if !options.Wide {
  79. columns = make([]metav1beta1.TableColumnDefinition, 0, len(handler.columnDefinitions))
  80. for i := range handler.columnDefinitions {
  81. if handler.columnDefinitions[i].Priority != 0 {
  82. continue
  83. }
  84. columns = append(columns, handler.columnDefinitions[i])
  85. }
  86. }
  87. }
  88. table := &metav1beta1.Table{
  89. ListMeta: metav1.ListMeta{
  90. ResourceVersion: "",
  91. },
  92. ColumnDefinitions: columns,
  93. Rows: results[0].Interface().([]metav1beta1.TableRow),
  94. }
  95. if m, err := meta.ListAccessor(obj); err == nil {
  96. table.ResourceVersion = m.GetResourceVersion()
  97. table.SelfLink = m.GetSelfLink()
  98. table.Continue = m.GetContinue()
  99. table.RemainingItemCount = m.GetRemainingItemCount()
  100. } else {
  101. if m, err := meta.CommonAccessor(obj); err == nil {
  102. table.ResourceVersion = m.GetResourceVersion()
  103. table.SelfLink = m.GetSelfLink()
  104. }
  105. }
  106. if err := decorateTable(table, options); err != nil {
  107. return nil, err
  108. }
  109. return table, nil
  110. }
  111. // TableHandler adds a print handler with a given set of columns to HumanReadablePrinter instance.
  112. // See ValidateRowPrintHandlerFunc for required method signature.
  113. func (h *HumanReadablePrinter) TableHandler(columnDefinitions []metav1beta1.TableColumnDefinition, printFunc interface{}) error {
  114. printFuncValue := reflect.ValueOf(printFunc)
  115. if err := ValidateRowPrintHandlerFunc(printFuncValue); err != nil {
  116. utilruntime.HandleError(fmt.Errorf("unable to register print function: %v", err))
  117. return err
  118. }
  119. entry := &handlerEntry{
  120. columnDefinitions: columnDefinitions,
  121. printFunc: printFuncValue,
  122. }
  123. objType := printFuncValue.Type().In(0)
  124. if _, ok := h.handlerMap[objType]; ok {
  125. err := fmt.Errorf("registered duplicate printer for %v", objType)
  126. utilruntime.HandleError(err)
  127. return err
  128. }
  129. h.handlerMap[objType] = entry
  130. return nil
  131. }
  132. // ValidateRowPrintHandlerFunc validates print handler signature.
  133. // printFunc is the function that will be called to print an object.
  134. // It must be of the following type:
  135. // func printFunc(object ObjectType, options PrintOptions) ([]metav1beta1.TableRow, error)
  136. // where ObjectType is the type of the object that will be printed, and the first
  137. // return value is an array of rows, with each row containing a number of cells that
  138. // match the number of columns defined for that printer function.
  139. func ValidateRowPrintHandlerFunc(printFunc reflect.Value) error {
  140. if printFunc.Kind() != reflect.Func {
  141. return fmt.Errorf("invalid print handler. %#v is not a function", printFunc)
  142. }
  143. funcType := printFunc.Type()
  144. if funcType.NumIn() != 2 || funcType.NumOut() != 2 {
  145. return fmt.Errorf("invalid print handler." +
  146. "Must accept 2 parameters and return 2 value.")
  147. }
  148. if funcType.In(1) != reflect.TypeOf((*PrintOptions)(nil)).Elem() ||
  149. funcType.Out(0) != reflect.TypeOf((*[]metav1beta1.TableRow)(nil)).Elem() ||
  150. funcType.Out(1) != reflect.TypeOf((*error)(nil)).Elem() {
  151. return fmt.Errorf("invalid print handler. The expected signature is: "+
  152. "func handler(obj %v, options PrintOptions) ([]metav1beta1.TableRow, error)", funcType.In(0))
  153. }
  154. return nil
  155. }