mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-04 09:49:50 +00:00
Refactor printers to support rendering as a Table
Return tables from the server.
This commit is contained in:
parent
f203e42cb9
commit
7ce63eb608
@ -192,7 +192,7 @@ func Example_printReplicationControllerWithNamespace() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, ctrl, os.Stdout)
|
err := f.PrintObject(cmd, mapper, ctrl, printers.GetNewTabWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -247,7 +247,7 @@ func Example_printMultiContainersReplicationControllerWithWide() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, ctrl, os.Stdout)
|
err := f.PrintObject(cmd, mapper, ctrl, printers.GetNewTabWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -301,7 +301,7 @@ func Example_printReplicationController() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, ctrl, os.Stdout)
|
err := f.PrintObject(cmd, mapper, ctrl, printers.GetNewTabWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -344,7 +344,7 @@ func Example_printPodWithWideFormat() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, pod, os.Stdout)
|
err := f.PrintObject(cmd, mapper, pod, printers.GetNewTabWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -390,7 +390,7 @@ func Example_printPodWithShowLabels() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, pod, os.Stdout)
|
err := f.PrintObject(cmd, mapper, pod, printers.GetNewTabWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -514,7 +514,7 @@ func Example_printPodHideTerminated() {
|
|||||||
}
|
}
|
||||||
for _, pod := range filteredPodList {
|
for _, pod := range filteredPodList {
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, pod, os.Stdout)
|
err := f.PrintObject(cmd, mapper, pod, printers.GetNewTabWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -542,7 +542,7 @@ func Example_printPodShowAll() {
|
|||||||
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
|
||||||
podList := newAllPhasePodList()
|
podList := newAllPhasePodList()
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, podList, os.Stdout)
|
err := f.PrintObject(cmd, mapper, podList, printers.GetNewTabWriter(os.Stdout))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -616,9 +616,10 @@ func Example_printServiceWithNamespacesAndLabels() {
|
|||||||
}
|
}
|
||||||
ld := strings.NewLineDelimiter(os.Stdout, "|")
|
ld := strings.NewLineDelimiter(os.Stdout, "|")
|
||||||
defer ld.Flush()
|
defer ld.Flush()
|
||||||
|
out := printers.GetNewTabWriter(ld)
|
||||||
|
defer out.Flush()
|
||||||
mapper, _ := f.Object()
|
mapper, _ := f.Object()
|
||||||
err := f.PrintObject(cmd, mapper, svc, ld)
|
err := f.PrintObject(cmd, mapper, svc, out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Unexpected error: %v", err)
|
fmt.Printf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,14 @@ go_library(
|
|||||||
"//pkg/util/slice:go_default_library",
|
"//pkg/util/slice:go_default_library",
|
||||||
"//vendor/github.com/fatih/camelcase:go_default_library",
|
"//vendor/github.com/fatih/camelcase:go_default_library",
|
||||||
"//vendor/github.com/ghodss/yaml:go_default_library",
|
"//vendor/github.com/ghodss/yaml:go_default_library",
|
||||||
"//vendor/github.com/golang/glog:go_default_library",
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/util/jsonpath:go_default_library",
|
"//vendor/k8s.io/client-go/util/jsonpath:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -63,6 +65,7 @@ filegroup(
|
|||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
"//pkg/printers/internalversion:all-srcs",
|
"//pkg/printers/internalversion:all-srcs",
|
||||||
|
"//pkg/printers/storage:all-srcs",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
@ -26,20 +26,31 @@ import (
|
|||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/fatih/camelcase"
|
"github.com/fatih/camelcase"
|
||||||
"github.com/golang/glog"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/kubernetes/pkg/util/slice"
|
"k8s.io/kubernetes/pkg/util/slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type TablePrinter interface {
|
||||||
|
PrintTable(obj runtime.Object, options PrintOptions) (*metav1alpha1.Table, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrintHandler interface {
|
||||||
|
Handler(columns, columnsWithWide []string, printFunc interface{}) error
|
||||||
|
TableHandler(columns []metav1alpha1.TableColumnDefinition, printFunc interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too.
|
var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too.
|
||||||
|
|
||||||
type handlerEntry struct {
|
type handlerEntry struct {
|
||||||
columns []string
|
columnDefinitions []metav1alpha1.TableColumnDefinition
|
||||||
columnsWithWide []string
|
printRows bool
|
||||||
printFunc reflect.Value
|
printFunc reflect.Value
|
||||||
args []reflect.Value
|
args []reflect.Value
|
||||||
}
|
}
|
||||||
@ -57,6 +68,8 @@ type HumanReadablePrinter struct {
|
|||||||
decoder runtime.Decoder
|
decoder runtime.Decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ PrintHandler = &HumanReadablePrinter{}
|
||||||
|
|
||||||
// NewHumanReadablePrinter creates a HumanReadablePrinter.
|
// NewHumanReadablePrinter creates a HumanReadablePrinter.
|
||||||
// If encoder and decoder are provided, an attempt to convert unstructured types to internal types is made.
|
// If encoder and decoder are provided, an attempt to convert unstructured types to internal types is made.
|
||||||
func NewHumanReadablePrinter(encoder runtime.Encoder, decoder runtime.Decoder, options PrintOptions) *HumanReadablePrinter {
|
func NewHumanReadablePrinter(encoder runtime.Encoder, decoder runtime.Decoder, options PrintOptions) *HumanReadablePrinter {
|
||||||
@ -69,6 +82,20 @@ func NewHumanReadablePrinter(encoder runtime.Encoder, decoder runtime.Decoder, o
|
|||||||
return printer
|
return printer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTablePrinter creates a HumanReadablePrinter suitable for calling PrintTable().
|
||||||
|
func NewTablePrinter() *HumanReadablePrinter {
|
||||||
|
return &HumanReadablePrinter{
|
||||||
|
handlerMap: make(map[reflect.Type]*handlerEntry),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *HumanReadablePrinter) With(fns ...func(PrintHandler)) *HumanReadablePrinter {
|
||||||
|
for _, fn := range fns {
|
||||||
|
fn(a)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
// GetResourceKind returns the type currently set for a resource
|
// GetResourceKind returns the type currently set for a resource
|
||||||
func (h *HumanReadablePrinter) GetResourceKind() string {
|
func (h *HumanReadablePrinter) GetResourceKind() string {
|
||||||
return h.options.Kind
|
return h.options.Kind
|
||||||
@ -92,29 +119,100 @@ func (h *HumanReadablePrinter) EnsurePrintHeaders() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handler adds a print handler with a given set of columns to HumanReadablePrinter instance.
|
// Handler adds a print handler with a given set of columns to HumanReadablePrinter instance.
|
||||||
// See validatePrintHandlerFunc for required method signature.
|
// See ValidatePrintHandlerFunc for required method signature.
|
||||||
func (h *HumanReadablePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error {
|
func (h *HumanReadablePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error {
|
||||||
|
var columnDefinitions []metav1alpha1.TableColumnDefinition
|
||||||
|
for _, column := range columns {
|
||||||
|
columnDefinitions = append(columnDefinitions, metav1alpha1.TableColumnDefinition{
|
||||||
|
Name: column,
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, column := range columnsWithWide {
|
||||||
|
columnDefinitions = append(columnDefinitions, metav1alpha1.TableColumnDefinition{
|
||||||
|
Name: column,
|
||||||
|
Type: "string",
|
||||||
|
Priority: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
printFuncValue := reflect.ValueOf(printFunc)
|
printFuncValue := reflect.ValueOf(printFunc)
|
||||||
if err := h.validatePrintHandlerFunc(printFuncValue); err != nil {
|
if err := ValidatePrintHandlerFunc(printFuncValue); err != nil {
|
||||||
glog.Errorf("Unable to add print handler: %v", err)
|
utilruntime.HandleError(fmt.Errorf("unable to register print function: %v", err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
objType := printFuncValue.Type().In(0)
|
entry := &handlerEntry{
|
||||||
h.handlerMap[objType] = &handlerEntry{
|
columnDefinitions: columnDefinitions,
|
||||||
columns: columns,
|
|
||||||
columnsWithWide: columnsWithWide,
|
|
||||||
printFunc: printFuncValue,
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 []metav1alpha1.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,
|
||||||
|
printRows: true,
|
||||||
|
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) ([]metav1alpha1.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 coulmns 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((*[]metav1alpha1.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) ([]metav1alpha1.TableRow, error)", funcType.In(0))
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// validatePrintHandlerFunc validates print handler signature.
|
// ValidatePrintHandlerFunc validates print handler signature.
|
||||||
// printFunc is the function that will be called to print an object.
|
// printFunc is the function that will be called to print an object.
|
||||||
// It must be of the following type:
|
// It must be of the following type:
|
||||||
// func printFunc(object ObjectType, w io.Writer, options PrintOptions) error
|
// func printFunc(object ObjectType, w io.Writer, options PrintOptions) error
|
||||||
// where ObjectType is the type of the object that will be printed.
|
// where ObjectType is the type of the object that will be printed.
|
||||||
func (h *HumanReadablePrinter) validatePrintHandlerFunc(printFunc reflect.Value) error {
|
// DEPRECATED: will be replaced with ValidateRowPrintHandlerFunc
|
||||||
|
func ValidatePrintHandlerFunc(printFunc reflect.Value) error {
|
||||||
if printFunc.Kind() != reflect.Func {
|
if printFunc.Kind() != reflect.Func {
|
||||||
return fmt.Errorf("invalid print handler. %#v is not a function", printFunc)
|
return fmt.Errorf("invalid print handler. %#v is not a function", printFunc)
|
||||||
}
|
}
|
||||||
@ -167,12 +265,18 @@ func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) er
|
|||||||
// PrintObj prints the obj in a human-friendly format according to the type of the obj.
|
// 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 {
|
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {
|
||||||
// if output is a tabwriter (when it's called by kubectl get), we use it; create a new tabwriter otherwise
|
// if output is a tabwriter (when it's called by kubectl get), we use it; create a new tabwriter otherwise
|
||||||
w, found := output.(*tabwriter.Writer)
|
if w, found := output.(*tabwriter.Writer); found {
|
||||||
if !found {
|
|
||||||
w = GetNewTabWriter(output)
|
|
||||||
defer w.Flush()
|
defer w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display tables following the rules of options
|
||||||
|
if table, ok := obj.(*metav1alpha1.Table); ok {
|
||||||
|
if err := DecorateTable(table, h.options); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return PrintTable(table, output, h.options)
|
||||||
|
}
|
||||||
|
|
||||||
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand before
|
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand before
|
||||||
// trying to print, since the printers are keyed by type. This is extremely expensive.
|
// trying to print, since the printers are keyed by type. This is extremely expensive.
|
||||||
if h.encoder != nil && h.decoder != nil {
|
if h.encoder != nil && h.decoder != nil {
|
||||||
@ -182,9 +286,12 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
|||||||
t := reflect.TypeOf(obj)
|
t := reflect.TypeOf(obj)
|
||||||
if handler := h.handlerMap[t]; handler != nil {
|
if handler := h.handlerMap[t]; handler != nil {
|
||||||
if !h.options.NoHeaders && t != h.lastType {
|
if !h.options.NoHeaders && t != h.lastType {
|
||||||
headers := handler.columns
|
var headers []string
|
||||||
if h.options.Wide {
|
for _, column := range handler.columnDefinitions {
|
||||||
headers = append(headers, handler.columnsWithWide...)
|
if column.Priority != 0 && !h.options.Wide {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
headers = append(headers, strings.ToUpper(column.Name))
|
||||||
}
|
}
|
||||||
headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...)
|
headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...)
|
||||||
// LABELS is always the last column.
|
// LABELS is always the last column.
|
||||||
@ -192,10 +299,58 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
|||||||
if h.options.WithNamespace {
|
if h.options.WithNamespace {
|
||||||
headers = append(withNamespacePrefixColumns, headers...)
|
headers = append(withNamespacePrefixColumns, headers...)
|
||||||
}
|
}
|
||||||
h.printHeader(headers, w)
|
h.printHeader(headers, output)
|
||||||
h.lastType = t
|
h.lastType = t
|
||||||
}
|
}
|
||||||
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.options)}
|
|
||||||
|
if handler.printRows {
|
||||||
|
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(h.options)}
|
||||||
|
results := handler.printFunc.Call(args)
|
||||||
|
if results[1].IsNil() {
|
||||||
|
rows := results[0].Interface().([]metav1alpha1.TableRow)
|
||||||
|
for _, row := range rows {
|
||||||
|
|
||||||
|
if h.options.WithNamespace {
|
||||||
|
if obj := row.Object.Object; obj != nil {
|
||||||
|
if m, err := meta.Accessor(obj); err == nil {
|
||||||
|
fmt.Fprint(output, m.GetNamespace())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprint(output, "\t")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, cell := range row.Cells {
|
||||||
|
if i != 0 {
|
||||||
|
fmt.Fprint(output, "\t")
|
||||||
|
} else {
|
||||||
|
// TODO: remove this once we drop the legacy printers
|
||||||
|
if h.options.WithKind && len(h.options.Kind) > 0 {
|
||||||
|
fmt.Fprintf(output, "%s/%s", h.options.Kind, cell)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprint(output, cell)
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLabels := len(h.options.ColumnLabels) > 0
|
||||||
|
if obj := row.Object.Object; obj != nil && (hasLabels || h.options.ShowLabels) {
|
||||||
|
if m, err := meta.Accessor(obj); err == nil {
|
||||||
|
for _, value := range labelValues(m.GetLabels(), h.options) {
|
||||||
|
output.Write([]byte("\t"))
|
||||||
|
output.Write([]byte(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Write([]byte("\n"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return results[1].Interface().(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this code path is deprecated and will be removed when all handlers are row printers
|
||||||
|
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(output), reflect.ValueOf(h.options)}
|
||||||
resultValue := handler.printFunc.Call(args)[0]
|
resultValue := handler.printFunc.Call(args)[0]
|
||||||
if resultValue.IsNil() {
|
if resultValue.IsNil() {
|
||||||
return nil
|
return nil
|
||||||
@ -207,7 +362,7 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
|||||||
// we don't recognize this type, but we can still attempt to print some reasonable information about.
|
// we don't recognize this type, but we can still attempt to print some reasonable information about.
|
||||||
unstructured, ok := obj.(runtime.Unstructured)
|
unstructured, ok := obj.(runtime.Unstructured)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("error: unknown type %#v", obj)
|
return fmt.Errorf("error: unknown type %T, expected unstructured in %#v", obj, h.handlerMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
content := unstructured.UnstructuredContent()
|
content := unstructured.UnstructuredContent()
|
||||||
@ -255,12 +410,12 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
|||||||
if h.options.WithNamespace {
|
if h.options.WithNamespace {
|
||||||
headers = append(withNamespacePrefixColumns, headers...)
|
headers = append(withNamespacePrefixColumns, headers...)
|
||||||
}
|
}
|
||||||
h.printHeader(headers, w)
|
h.printHeader(headers, output)
|
||||||
h.lastType = t
|
h.lastType = t
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the error isn't nil, report the "I don't recognize this" error
|
// if the error isn't nil, report the "I don't recognize this" error
|
||||||
if err := printUnstructured(unstructured, w, discoveredFieldNames, h.options); err != nil {
|
if err := printUnstructured(unstructured, output, discoveredFieldNames, h.options); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -270,6 +425,250 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er
|
|||||||
return fmt.Errorf("error: unknown type %#v", obj)
|
return fmt.Errorf("error: unknown type %#v", obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasCondition(conditions []metav1alpha1.TableRowCondition, t metav1alpha1.RowConditionType) bool {
|
||||||
|
for _, condition := range conditions {
|
||||||
|
if condition.Type == t {
|
||||||
|
return condition.Status == metav1alpha1.ConditionTrue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintTable prints a table to the provided output respecting the filtering rules for options
|
||||||
|
// for wide columns and filetred rows. It filters out rows that are Completed. You should call
|
||||||
|
// DecorateTable if you receive a table from a remote server before calling PrintTable.
|
||||||
|
func PrintTable(table *metav1alpha1.Table, output io.Writer, options PrintOptions) error {
|
||||||
|
if !options.NoHeaders {
|
||||||
|
first := true
|
||||||
|
for _, column := range table.ColumnDefinitions {
|
||||||
|
if !options.Wide && column.Priority != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(output, "\t")
|
||||||
|
}
|
||||||
|
fmt.Fprint(output, strings.ToUpper(column.Name))
|
||||||
|
}
|
||||||
|
fmt.Fprintln(output)
|
||||||
|
}
|
||||||
|
for _, row := range table.Rows {
|
||||||
|
if !options.ShowAll && hasCondition(row.Conditions, metav1alpha1.RowCompleted) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
first := true
|
||||||
|
for i, cell := range row.Cells {
|
||||||
|
column := table.ColumnDefinitions[i]
|
||||||
|
if !options.Wide && column.Priority != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if first {
|
||||||
|
first = false
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(output, "\t")
|
||||||
|
}
|
||||||
|
if cell != nil {
|
||||||
|
fmt.Fprint(output, cell)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Fprintln(output)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecorateTable takes a table and attempts to add label columns and the
|
||||||
|
// namespace column. It will fill empty columns with nil (if the object
|
||||||
|
// does not expose metadata). It returns an error if the table cannot
|
||||||
|
// be decorated.
|
||||||
|
func DecorateTable(table *metav1alpha1.Table, options PrintOptions) error {
|
||||||
|
width := len(table.ColumnDefinitions) + len(options.ColumnLabels)
|
||||||
|
if options.WithNamespace {
|
||||||
|
width++
|
||||||
|
}
|
||||||
|
if options.ShowLabels {
|
||||||
|
width++
|
||||||
|
}
|
||||||
|
|
||||||
|
columns := table.ColumnDefinitions
|
||||||
|
|
||||||
|
nameColumn := -1
|
||||||
|
if options.WithKind && len(options.Kind) > 0 {
|
||||||
|
for i := range columns {
|
||||||
|
if columns[i].Format == "name" && columns[i].Type == "string" {
|
||||||
|
nameColumn = i
|
||||||
|
fmt.Printf("found name column: %d\n", i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if width != len(table.ColumnDefinitions) {
|
||||||
|
columns = make([]metav1alpha1.TableColumnDefinition, 0, width)
|
||||||
|
if options.WithNamespace {
|
||||||
|
columns = append(columns, metav1alpha1.TableColumnDefinition{
|
||||||
|
Name: "Namespace",
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
columns = append(columns, table.ColumnDefinitions...)
|
||||||
|
for _, label := range formatLabelHeaders(options.ColumnLabels) {
|
||||||
|
columns = append(columns, metav1alpha1.TableColumnDefinition{
|
||||||
|
Name: label,
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if options.ShowLabels {
|
||||||
|
columns = append(columns, metav1alpha1.TableColumnDefinition{
|
||||||
|
Name: "Labels",
|
||||||
|
Type: "string",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := table.Rows
|
||||||
|
|
||||||
|
includeLabels := len(options.ColumnLabels) > 0 || options.ShowLabels
|
||||||
|
if includeLabels || options.WithNamespace || nameColumn != -1 {
|
||||||
|
for i := range rows {
|
||||||
|
row := rows[i]
|
||||||
|
|
||||||
|
if nameColumn != -1 {
|
||||||
|
row.Cells[nameColumn] = fmt.Sprintf("%s/%s", options.Kind, row.Cells[nameColumn])
|
||||||
|
}
|
||||||
|
|
||||||
|
var m metav1.Object
|
||||||
|
if obj := row.Object.Object; obj != nil {
|
||||||
|
if acc, err := meta.Accessor(obj); err == nil {
|
||||||
|
m = acc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we can't get an accessor, fill out the appropriate columns with empty spaces
|
||||||
|
if m == nil {
|
||||||
|
if options.WithNamespace {
|
||||||
|
r := make([]interface{}, 1, width)
|
||||||
|
row.Cells = append(r, row.Cells...)
|
||||||
|
}
|
||||||
|
for j := 0; j < width-len(row.Cells); j++ {
|
||||||
|
row.Cells = append(row.Cells, nil)
|
||||||
|
}
|
||||||
|
rows[i] = row
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.WithNamespace {
|
||||||
|
r := make([]interface{}, 1, width)
|
||||||
|
r[0] = m.GetNamespace()
|
||||||
|
row.Cells = append(r, row.Cells...)
|
||||||
|
}
|
||||||
|
if includeLabels {
|
||||||
|
row.Cells = appendLabelCells(row.Cells, m.GetLabels(), options)
|
||||||
|
}
|
||||||
|
rows[i] = row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.ColumnDefinitions = columns
|
||||||
|
table.Rows = rows
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintTable 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) PrintTable(obj runtime.Object, options PrintOptions) (*metav1alpha1.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)
|
||||||
|
}
|
||||||
|
if !handler.printRows {
|
||||||
|
return h.legacyPrinterToTable(obj, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(options)}
|
||||||
|
results := handler.printFunc.Call(args)
|
||||||
|
if !results[1].IsNil() {
|
||||||
|
return nil, results[1].Interface().(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
columns := handler.columnDefinitions
|
||||||
|
if !options.Wide {
|
||||||
|
columns = make([]metav1alpha1.TableColumnDefinition, 0, len(handler.columnDefinitions))
|
||||||
|
for i := range handler.columnDefinitions {
|
||||||
|
if handler.columnDefinitions[i].Priority != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
columns = append(columns, handler.columnDefinitions[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
table := &metav1alpha1.Table{
|
||||||
|
ListMeta: metav1.ListMeta{
|
||||||
|
ResourceVersion: "",
|
||||||
|
},
|
||||||
|
ColumnDefinitions: columns,
|
||||||
|
Rows: results[0].Interface().([]metav1alpha1.TableRow),
|
||||||
|
}
|
||||||
|
if err := DecorateTable(table, options); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return table, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacyPrinterToTable uses the old printFunc with tabbed writer to generate a table.
|
||||||
|
// TODO: remove when all legacy printers are removed.
|
||||||
|
func (h *HumanReadablePrinter) legacyPrinterToTable(obj runtime.Object, handler *handlerEntry) (*metav1alpha1.Table, error) {
|
||||||
|
printFunc := handler.printFunc
|
||||||
|
table := &metav1alpha1.Table{
|
||||||
|
ColumnDefinitions: handler.columnDefinitions,
|
||||||
|
}
|
||||||
|
|
||||||
|
options := PrintOptions{
|
||||||
|
NoHeaders: true,
|
||||||
|
Wide: true,
|
||||||
|
}
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(buf), reflect.ValueOf(options)}
|
||||||
|
|
||||||
|
if meta.IsListType(obj) {
|
||||||
|
// TODO: this uses more memory than it has to, as we refactor printers we should remove the need
|
||||||
|
// for this.
|
||||||
|
args[0] = reflect.ValueOf(obj)
|
||||||
|
resultValue := printFunc.Call(args)[0]
|
||||||
|
if !resultValue.IsNil() {
|
||||||
|
return nil, resultValue.Interface().(error)
|
||||||
|
}
|
||||||
|
data := buf.Bytes()
|
||||||
|
i := 0
|
||||||
|
items, err := meta.ExtractList(obj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for len(data) > 0 {
|
||||||
|
cells, remainder := tabbedLineToCells(data, len(table.ColumnDefinitions))
|
||||||
|
table.Rows = append(table.Rows, metav1alpha1.TableRow{
|
||||||
|
Cells: cells,
|
||||||
|
Object: runtime.RawExtension{Object: items[i]},
|
||||||
|
})
|
||||||
|
data = remainder
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args[0] = reflect.ValueOf(obj)
|
||||||
|
resultValue := printFunc.Call(args)[0]
|
||||||
|
if !resultValue.IsNil() {
|
||||||
|
return nil, resultValue.Interface().(error)
|
||||||
|
}
|
||||||
|
data := buf.Bytes()
|
||||||
|
cells, _ := tabbedLineToCells(data, len(table.ColumnDefinitions))
|
||||||
|
table.Rows = append(table.Rows, metav1alpha1.TableRow{
|
||||||
|
Cells: cells,
|
||||||
|
Object: runtime.RawExtension{Object: obj},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return table, nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: this method assumes the meta/v1 server API, so should be refactored out of this package
|
// TODO: this method assumes the meta/v1 server API, so should be refactored out of this package
|
||||||
func printUnstructured(unstructured runtime.Unstructured, w io.Writer, additionalFields []string, options PrintOptions) error {
|
func printUnstructured(unstructured runtime.Unstructured, w io.Writer, additionalFields []string, options PrintOptions) error {
|
||||||
metadata, err := meta.Accessor(unstructured)
|
metadata, err := meta.Accessor(unstructured)
|
||||||
@ -349,6 +748,30 @@ func formatShowLabelsHeader(showLabels bool, t reflect.Type) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// labelValues returns a slice of value columns matching the requested print options.
|
||||||
|
func labelValues(itemLabels map[string]string, opts PrintOptions) []string {
|
||||||
|
var values []string
|
||||||
|
for _, key := range opts.ColumnLabels {
|
||||||
|
values = append(values, itemLabels[key])
|
||||||
|
}
|
||||||
|
if opts.ShowLabels {
|
||||||
|
values = append(values, labels.FormatLabels(itemLabels))
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendLabelCells returns a slice of value columns matching the requested print options.
|
||||||
|
// Intended for use with tables.
|
||||||
|
func appendLabelCells(values []interface{}, itemLabels map[string]string, opts PrintOptions) []interface{} {
|
||||||
|
for _, key := range opts.ColumnLabels {
|
||||||
|
values = append(values, itemLabels[key])
|
||||||
|
}
|
||||||
|
if opts.ShowLabels {
|
||||||
|
values = append(values, labels.FormatLabels(itemLabels))
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
// FormatResourceName receives a resource kind, name, and boolean specifying
|
// FormatResourceName receives a resource kind, name, and boolean specifying
|
||||||
// whether or not to update the current name to "kind/name"
|
// whether or not to update the current name to "kind/name"
|
||||||
func FormatResourceName(kind, name string, withKind bool) string {
|
func FormatResourceName(kind, name string, withKind bool) string {
|
||||||
@ -402,3 +825,27 @@ func decodeUnknownObject(obj runtime.Object, encoder runtime.Encoder, decoder ru
|
|||||||
|
|
||||||
return obj, err
|
return obj, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tabbedLineToCells(data []byte, expected int) ([]interface{}, []byte) {
|
||||||
|
var remainder []byte
|
||||||
|
max := bytes.Index(data, []byte("\n"))
|
||||||
|
if max != -1 {
|
||||||
|
remainder = data[max+1:]
|
||||||
|
data = data[:max]
|
||||||
|
}
|
||||||
|
cells := make([]interface{}, expected)
|
||||||
|
for i := 0; i < expected; i++ {
|
||||||
|
next := bytes.Index(data, []byte("\t"))
|
||||||
|
if next == -1 {
|
||||||
|
cells[i] = string(data)
|
||||||
|
// fill the remainder with empty strings, this indicates a printer bug
|
||||||
|
for j := i + 1; j < expected; j++ {
|
||||||
|
cells[j] = ""
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
cells[i] = string(data[:next])
|
||||||
|
data = data[next+1:]
|
||||||
|
}
|
||||||
|
return cells, remainder
|
||||||
|
}
|
||||||
|
@ -40,6 +40,7 @@ go_test(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/yaml:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/yaml:go_default_library",
|
||||||
@ -65,6 +66,7 @@ go_library(
|
|||||||
"//pkg/api/helper/qos:go_default_library",
|
"//pkg/api/helper/qos:go_default_library",
|
||||||
"//pkg/api/ref:go_default_library",
|
"//pkg/api/ref:go_default_library",
|
||||||
"//pkg/api/resource:go_default_library",
|
"//pkg/api/resource:go_default_library",
|
||||||
|
"//pkg/api/v1:go_default_library",
|
||||||
"//pkg/apis/apps:go_default_library",
|
"//pkg/apis/apps:go_default_library",
|
||||||
"//pkg/apis/autoscaling:go_default_library",
|
"//pkg/apis/autoscaling:go_default_library",
|
||||||
"//pkg/apis/batch:go_default_library",
|
"//pkg/apis/batch:go_default_library",
|
||||||
@ -94,8 +96,10 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||||
|
@ -27,12 +27,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/kubernetes/federation/apis/federation"
|
"k8s.io/kubernetes/federation/apis/federation"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/events"
|
"k8s.io/kubernetes/pkg/api/events"
|
||||||
"k8s.io/kubernetes/pkg/api/helper"
|
"k8s.io/kubernetes/pkg/api/helper"
|
||||||
|
apiv1 "k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||||
"k8s.io/kubernetes/pkg/apis/batch"
|
"k8s.io/kubernetes/pkg/apis/batch"
|
||||||
@ -53,8 +56,6 @@ const loadBalancerWidth = 16
|
|||||||
// NOTE: When adding a new resource type here, please update the list
|
// NOTE: When adding a new resource type here, please update the list
|
||||||
// pkg/kubectl/cmd/get.go to reflect the new resource type.
|
// pkg/kubectl/cmd/get.go to reflect the new resource type.
|
||||||
var (
|
var (
|
||||||
podColumns = []string{"NAME", "READY", "STATUS", "RESTARTS", "AGE"}
|
|
||||||
podWideColumns = []string{"IP", "NODE"}
|
|
||||||
podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"}
|
podTemplateColumns = []string{"TEMPLATE", "CONTAINER(S)", "IMAGE(S)", "PODLABELS"}
|
||||||
podDisruptionBudgetColumns = []string{"NAME", "MIN-AVAILABLE", "MAX-UNAVAILABLE", "ALLOWED-DISRUPTIONS", "AGE"}
|
podDisruptionBudgetColumns = []string{"NAME", "MIN-AVAILABLE", "MAX-UNAVAILABLE", "ALLOWED-DISRUPTIONS", "AGE"}
|
||||||
replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
|
replicationControllerColumns = []string{"NAME", "DESIRED", "CURRENT", "READY", "AGE"}
|
||||||
@ -105,27 +106,21 @@ var (
|
|||||||
podPresetColumns = []string{"NAME", "AGE"}
|
podPresetColumns = []string{"NAME", "AGE"}
|
||||||
)
|
)
|
||||||
|
|
||||||
func printPod(pod *api.Pod, w io.Writer, options printers.PrintOptions) error {
|
|
||||||
if err := printPodBase(pod, w, options); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func printPodList(podList *api.PodList, w io.Writer, options printers.PrintOptions) error {
|
|
||||||
for _, pod := range podList.Items {
|
|
||||||
if err := printPodBase(&pod, w, options); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHandlers adds print handlers for default Kubernetes types dealing with internal versions.
|
// AddHandlers adds print handlers for default Kubernetes types dealing with internal versions.
|
||||||
func AddHandlers(h *printers.HumanReadablePrinter) {
|
// TODO: handle errors from Handler
|
||||||
h.Handler(podColumns, podWideColumns, printPodList)
|
func AddHandlers(h printers.PrintHandler) {
|
||||||
h.Handler(podColumns, podWideColumns, printPod)
|
podColumnDefinitions := []metav1alpha1.TableColumnDefinition{
|
||||||
|
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||||
|
{Name: "Ready", Type: "string", Description: "The aggregate readiness state of this pod for accepting traffic."},
|
||||||
|
{Name: "Status", Type: "string", Description: "The aggregate status of the containers in this pod."},
|
||||||
|
{Name: "Restarts", Type: "integer", Description: "The number of times the containers in this pod have been restarted."},
|
||||||
|
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
|
||||||
|
{Name: "IP", Type: "string", Priority: 1, Description: apiv1.PodStatus{}.SwaggerDoc()["podIP"]},
|
||||||
|
{Name: "Node", Type: "string", Priority: 1, Description: apiv1.PodSpec{}.SwaggerDoc()["nodeName"]},
|
||||||
|
}
|
||||||
|
h.TableHandler(podColumnDefinitions, printPodList)
|
||||||
|
h.TableHandler(podColumnDefinitions, printPod)
|
||||||
|
|
||||||
h.Handler(podTemplateColumns, nil, printPodTemplate)
|
h.Handler(podTemplateColumns, nil, printPodTemplate)
|
||||||
h.Handler(podTemplateColumns, nil, printPodTemplateList)
|
h.Handler(podTemplateColumns, nil, printPodTemplateList)
|
||||||
h.Handler(podDisruptionBudgetColumns, nil, printPodDisruptionBudget)
|
h.Handler(podDisruptionBudgetColumns, nil, printPodDisruptionBudget)
|
||||||
@ -247,10 +242,24 @@ func translateTimestamp(timestamp metav1.Time) string {
|
|||||||
return printers.ShortHumanDuration(time.Now().Sub(timestamp.Time))
|
return printers.ShortHumanDuration(time.Now().Sub(timestamp.Time))
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPodBase(pod *api.Pod, w io.Writer, options printers.PrintOptions) error {
|
var (
|
||||||
name := printers.FormatResourceName(options.Kind, pod.Name, options.WithKind)
|
podSuccessConditions = []metav1alpha1.TableRowCondition{{Type: metav1alpha1.RowCompleted, Status: metav1alpha1.ConditionTrue, Reason: string(api.PodSucceeded), Message: "The pod has completed successfully."}}
|
||||||
namespace := pod.Namespace
|
podFailedConditions = []metav1alpha1.TableRowCondition{{Type: metav1alpha1.RowCompleted, Status: metav1alpha1.ConditionTrue, Reason: string(api.PodFailed), Message: "The pod failed."}}
|
||||||
|
)
|
||||||
|
|
||||||
|
func printPodList(podList *api.PodList, options printers.PrintOptions) ([]metav1alpha1.TableRow, error) {
|
||||||
|
rows := make([]metav1alpha1.TableRow, 0, len(podList.Items))
|
||||||
|
for i := range podList.Items {
|
||||||
|
r, err := printPod(&podList.Items[i], options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rows = append(rows, r...)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printPod(pod *api.Pod, options printers.PrintOptions) ([]metav1alpha1.TableRow, error) {
|
||||||
restarts := 0
|
restarts := 0
|
||||||
totalContainers := len(pod.Spec.Containers)
|
totalContainers := len(pod.Spec.Containers)
|
||||||
readyContainers := 0
|
readyContainers := 0
|
||||||
@ -260,6 +269,17 @@ func printPodBase(pod *api.Pod, w io.Writer, options printers.PrintOptions) erro
|
|||||||
reason = pod.Status.Reason
|
reason = pod.Status.Reason
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row := metav1alpha1.TableRow{
|
||||||
|
Object: runtime.RawExtension{Object: pod},
|
||||||
|
}
|
||||||
|
|
||||||
|
switch pod.Status.Phase {
|
||||||
|
case api.PodSucceeded:
|
||||||
|
row.Conditions = podSuccessConditions
|
||||||
|
case api.PodFailed:
|
||||||
|
row.Conditions = podFailedConditions
|
||||||
|
}
|
||||||
|
|
||||||
initializing := false
|
initializing := false
|
||||||
for i := range pod.Status.InitContainerStatuses {
|
for i := range pod.Status.InitContainerStatuses {
|
||||||
container := pod.Status.InitContainerStatuses[i]
|
container := pod.Status.InitContainerStatuses[i]
|
||||||
@ -316,21 +336,7 @@ func printPodBase(pod *api.Pod, w io.Writer, options printers.PrintOptions) erro
|
|||||||
reason = "Terminating"
|
reason = "Terminating"
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.WithNamespace {
|
row.Cells = append(row.Cells, pod.Name, fmt.Sprintf("%d/%d", readyContainers, totalContainers), reason, restarts, translateTimestamp(pod.CreationTimestamp))
|
||||||
if _, err := fmt.Fprintf(w, "%s\t", namespace); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprintf(w, "%s\t%d/%d\t%s\t%d\t%s",
|
|
||||||
name,
|
|
||||||
readyContainers,
|
|
||||||
totalContainers,
|
|
||||||
reason,
|
|
||||||
restarts,
|
|
||||||
translateTimestamp(pod.CreationTimestamp),
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Wide {
|
if options.Wide {
|
||||||
nodeName := pod.Spec.NodeName
|
nodeName := pod.Spec.NodeName
|
||||||
@ -341,22 +347,10 @@ func printPodBase(pod *api.Pod, w io.Writer, options printers.PrintOptions) erro
|
|||||||
if nodeName == "" {
|
if nodeName == "" {
|
||||||
nodeName = "<none>"
|
nodeName = "<none>"
|
||||||
}
|
}
|
||||||
if _, err := fmt.Fprintf(w, "\t%s\t%s",
|
row.Cells = append(row.Cells, podIP, nodeName)
|
||||||
podIP,
|
|
||||||
nodeName,
|
|
||||||
); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := fmt.Fprint(w, printers.AppendLabels(pod.Labels, options.ColumnLabels)); err != nil {
|
return []metav1alpha1.TableRow{row}, nil
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprint(w, printers.AppendAllLabels(options.ShowLabels, pod.Labels)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func printPodTemplate(pod *api.PodTemplate, w io.Writer, options printers.PrintOptions) error {
|
func printPodTemplate(pod *api.PodTemplate, w io.Writer, options printers.PrintOptions) error {
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -31,6 +32,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/resource"
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
||||||
@ -267,7 +269,7 @@ func TestCustomTypePrinting(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred printing the custom type: %#v", err)
|
t.Fatalf("An error occurred printing the custom type: %#v", err)
|
||||||
}
|
}
|
||||||
expectedOutput := "Data\ntest object"
|
expectedOutput := "DATA\ntest object"
|
||||||
if buffer.String() != expectedOutput {
|
if buffer.String() != expectedOutput {
|
||||||
t.Errorf("The data was not printed as expected. Expected:\n%s\nGot:\n%s", expectedOutput, buffer.String())
|
t.Errorf("The data was not printed as expected. Expected:\n%s\nGot:\n%s", expectedOutput, buffer.String())
|
||||||
}
|
}
|
||||||
@ -285,7 +287,7 @@ func TestCustomTypePrintingWithKind(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred printing the custom type: %#v", err)
|
t.Fatalf("An error occurred printing the custom type: %#v", err)
|
||||||
}
|
}
|
||||||
expectedOutput := "Data\ntest/test object"
|
expectedOutput := "DATA\ntest/test object"
|
||||||
if buffer.String() != expectedOutput {
|
if buffer.String() != expectedOutput {
|
||||||
t.Errorf("The data was not printed as expected. Expected:\n%s\nGot:\n%s", expectedOutput, buffer.String())
|
t.Errorf("The data was not printed as expected. Expected:\n%s\nGot:\n%s", expectedOutput, buffer.String())
|
||||||
}
|
}
|
||||||
@ -1252,7 +1254,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range table {
|
for i, test := range table {
|
||||||
if test.isNamespaced {
|
if test.isNamespaced {
|
||||||
// Expect output to include namespace when requested.
|
// Expect output to include namespace when requested.
|
||||||
printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
|
printer := printers.NewHumanReadablePrinter(nil, nil, printers.PrintOptions{
|
||||||
@ -1266,7 +1268,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
matched := contains(strings.Fields(buffer.String()), fmt.Sprintf("%s", namespaceName))
|
matched := contains(strings.Fields(buffer.String()), fmt.Sprintf("%s", namespaceName))
|
||||||
if !matched {
|
if !matched {
|
||||||
t.Errorf("Expect printing object to contain namespace: %#v", test.obj)
|
t.Errorf("%d: Expect printing object to contain namespace: %#v", i, test.obj)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Expect error when trying to get all namespaces for un-namespaced object.
|
// Expect error when trying to get all namespaces for un-namespaced object.
|
||||||
@ -1282,10 +1284,96 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrintPodTable(t *testing.T) {
|
||||||
|
runningPod := &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test1", Labels: map[string]string{"a": "1", "b": "2"}},
|
||||||
|
Spec: api.PodSpec{Containers: make([]api.Container, 2)},
|
||||||
|
Status: api.PodStatus{
|
||||||
|
Phase: "Running",
|
||||||
|
ContainerStatuses: []api.ContainerStatus{
|
||||||
|
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||||
|
{RestartCount: 3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
failedPod := &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test2", Labels: map[string]string{"b": "2"}},
|
||||||
|
Spec: api.PodSpec{Containers: make([]api.Container, 2)},
|
||||||
|
Status: api.PodStatus{
|
||||||
|
Phase: "Failed",
|
||||||
|
ContainerStatuses: []api.ContainerStatus{
|
||||||
|
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
|
||||||
|
{RestartCount: 3},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
obj runtime.Object
|
||||||
|
opts printers.PrintOptions
|
||||||
|
expect string
|
||||||
|
ignoreLegacy bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
obj: runningPod, opts: printers.PrintOptions{},
|
||||||
|
expect: "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\ntest1\t1/2\tRunning\t6\t<unknown>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: runningPod, opts: printers.PrintOptions{WithKind: true, Kind: "pods"},
|
||||||
|
expect: "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\npods/test1\t1/2\tRunning\t6\t<unknown>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: runningPod, opts: printers.PrintOptions{ShowLabels: true},
|
||||||
|
expect: "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\tLABELS\ntest1\t1/2\tRunning\t6\t<unknown>\ta=1,b=2\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: &api.PodList{Items: []api.Pod{*runningPod, *failedPod}}, opts: printers.PrintOptions{ShowAll: true, ColumnLabels: []string{"a"}},
|
||||||
|
expect: "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\tA\ntest1\t1/2\tRunning\t6\t<unknown>\t1\ntest2\t1/2\tFailed\t6\t<unknown>\t\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: runningPod, opts: printers.PrintOptions{NoHeaders: true},
|
||||||
|
expect: "test1\t1/2\tRunning\t6\t<unknown>\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: failedPod, opts: printers.PrintOptions{},
|
||||||
|
expect: "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\n",
|
||||||
|
ignoreLegacy: true, // filtering is not done by the printer in the legacy path
|
||||||
|
},
|
||||||
|
{
|
||||||
|
obj: failedPod, opts: printers.PrintOptions{ShowAll: true},
|
||||||
|
expect: "NAME\tREADY\tSTATUS\tRESTARTS\tAGE\ntest2\t1/2\tFailed\t6\t<unknown>\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
table, err := printers.NewTablePrinter().With(AddHandlers).PrintTable(test.obj, printers.PrintOptions{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
p := printers.NewHumanReadablePrinter(nil, nil, test.opts).With(AddHandlers)
|
||||||
|
if err := p.PrintObj(table, buf); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if test.expect != buf.String() {
|
||||||
|
t.Errorf("%d mismatch:\n%s\n%s", i, strconv.Quote(test.expect), strconv.Quote(buf.String()))
|
||||||
|
}
|
||||||
|
if test.ignoreLegacy {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
if err := p.PrintObj(test.obj, buf); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if test.expect != buf.String() {
|
||||||
|
t.Errorf("%d legacy mismatch:\n%s\n%s", i, strconv.Quote(test.expect), strconv.Quote(buf.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestPrintPod(t *testing.T) {
|
func TestPrintPod(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pod api.Pod
|
pod api.Pod
|
||||||
expect string
|
expect []metav1alpha1.TableRow
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// Test name, num of containers, restarts, container ready status
|
// Test name, num of containers, restarts, container ready status
|
||||||
@ -1300,7 +1388,7 @@ func TestPrintPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test1\t1/2\tpodPhase\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test container error overwrites pod phase
|
// Test container error overwrites pod phase
|
||||||
@ -1315,7 +1403,7 @@ func TestPrintPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test2\t1/2\tContainerWaitingReason\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test2", "1/2", "ContainerWaitingReason", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test the same as the above but with Terminated state and the first container overwrites the rest
|
// Test the same as the above but with Terminated state and the first container overwrites the rest
|
||||||
@ -1330,7 +1418,7 @@ func TestPrintPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test3\t0/2\tContainerWaitingReason\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test3", "0/2", "ContainerWaitingReason", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test ready is not enough for reporting running
|
// Test ready is not enough for reporting running
|
||||||
@ -1345,7 +1433,7 @@ func TestPrintPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test4\t1/2\tpodPhase\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test4", "1/2", "podPhase", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test ready is not enough for reporting running
|
// Test ready is not enough for reporting running
|
||||||
@ -1361,25 +1449,28 @@ func TestPrintPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test5\t1/2\tOutOfDisk\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test5", "1/2", "OutOfDisk", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
for i, test := range tests {
|
||||||
for _, test := range tests {
|
rows, err := printPod(&test.pod, printers.PrintOptions{ShowAll: true})
|
||||||
printPod(&test.pod, buf, printers.PrintOptions{ShowAll: true})
|
if err != nil {
|
||||||
// We ignore time
|
t.Fatal(err)
|
||||||
if !strings.HasPrefix(buf.String(), test.expect) {
|
}
|
||||||
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
|
for i := range rows {
|
||||||
|
rows[i].Object.Object = nil
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(test.expect, rows) {
|
||||||
|
t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
|
||||||
}
|
}
|
||||||
buf.Reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrintNonTerminatedPod(t *testing.T) {
|
func TestPrintNonTerminatedPod(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pod api.Pod
|
pod api.Pod
|
||||||
expect string
|
expect []metav1alpha1.TableRow
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// Test pod phase Running should be printed
|
// Test pod phase Running should be printed
|
||||||
@ -1394,7 +1485,7 @@ func TestPrintNonTerminatedPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test1\t1/2\tRunning\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "Running", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test pod phase Pending should be printed
|
// Test pod phase Pending should be printed
|
||||||
@ -1409,7 +1500,7 @@ func TestPrintNonTerminatedPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test2\t1/2\tPending\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test2", "1/2", "Pending", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test pod phase Unknown should be printed
|
// Test pod phase Unknown should be printed
|
||||||
@ -1424,7 +1515,7 @@ func TestPrintNonTerminatedPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test3\t1/2\tUnknown\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test3", "1/2", "Unknown", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test pod phase Succeeded shouldn't be printed
|
// Test pod phase Succeeded shouldn't be printed
|
||||||
@ -1439,7 +1530,7 @@ func TestPrintNonTerminatedPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test4", "1/2", "Succeeded", 6, "<unknown>"}, Conditions: podSuccessConditions}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test pod phase Failed shouldn't be printed
|
// Test pod phase Failed shouldn't be printed
|
||||||
@ -1454,18 +1545,22 @@ func TestPrintNonTerminatedPod(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test5", "1/2", "Failed", 6, "<unknown>"}, Conditions: podFailedConditions}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
for i, test := range tests {
|
||||||
for _, test := range tests {
|
table, err := printers.NewTablePrinter().With(AddHandlers).PrintTable(&test.pod, printers.PrintOptions{})
|
||||||
printPod(&test.pod, buf, printers.PrintOptions{})
|
if err != nil {
|
||||||
// We ignore time
|
t.Fatal(err)
|
||||||
if !strings.HasPrefix(buf.String(), test.expect) {
|
}
|
||||||
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
|
rows := table.Rows
|
||||||
|
for i := range rows {
|
||||||
|
rows[i].Object.Object = nil
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(test.expect, rows) {
|
||||||
|
t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
|
||||||
}
|
}
|
||||||
buf.Reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1473,8 +1568,7 @@ func TestPrintPodWithLabels(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
pod api.Pod
|
pod api.Pod
|
||||||
labelColumns []string
|
labelColumns []string
|
||||||
startsWith string
|
expect []metav1alpha1.TableRow
|
||||||
endsWith string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// Test name, num of containers, restarts, container ready status
|
// Test name, num of containers, restarts, container ready status
|
||||||
@ -1493,8 +1587,7 @@ func TestPrintPodWithLabels(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]string{"col1", "COL2"},
|
[]string{"col1", "COL2"},
|
||||||
"test1\t1/2\tpodPhase\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", 6, "<unknown>", "asd", "zxc"}}},
|
||||||
"\tasd\tzxc\n",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test name, num of containers, restarts, container ready status
|
// Test name, num of containers, restarts, container ready status
|
||||||
@ -1513,19 +1606,22 @@ func TestPrintPodWithLabels(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]string{},
|
[]string{},
|
||||||
"test1\t1/2\tpodPhase\t6\t",
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", 6, "<unknown>"}}},
|
||||||
"\n",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
for i, test := range tests {
|
||||||
for _, test := range tests {
|
table, err := printers.NewTablePrinter().With(AddHandlers).PrintTable(&test.pod, printers.PrintOptions{ColumnLabels: test.labelColumns})
|
||||||
printPod(&test.pod, buf, printers.PrintOptions{ColumnLabels: test.labelColumns})
|
if err != nil {
|
||||||
// We ignore time
|
t.Fatal(err)
|
||||||
if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) {
|
}
|
||||||
t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String())
|
rows := table.Rows
|
||||||
|
for i := range rows {
|
||||||
|
rows[i].Object.Object = nil
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(test.expect, rows) {
|
||||||
|
t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
|
||||||
}
|
}
|
||||||
buf.Reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2078,9 +2174,8 @@ func TestPrintHPA(t *testing.T) {
|
|||||||
func TestPrintPodShowLabels(t *testing.T) {
|
func TestPrintPodShowLabels(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pod api.Pod
|
pod api.Pod
|
||||||
startsWith string
|
|
||||||
endsWith string
|
|
||||||
showLabels bool
|
showLabels bool
|
||||||
|
expect []metav1alpha1.TableRow
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// Test name, num of containers, restarts, container ready status
|
// Test name, num of containers, restarts, container ready status
|
||||||
@ -2098,9 +2193,8 @@ func TestPrintPodShowLabels(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test1\t1/2\tpodPhase\t6\t",
|
|
||||||
"\tCOL2=zxc,col1=asd\n",
|
|
||||||
true,
|
true,
|
||||||
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", 6, "<unknown>", "COL2=zxc,col1=asd"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Test name, num of containers, restarts, container ready status
|
// Test name, num of containers, restarts, container ready status
|
||||||
@ -2118,20 +2212,23 @@ func TestPrintPodShowLabels(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"test1\t1/2\tpodPhase\t6\t",
|
|
||||||
"\n",
|
|
||||||
false,
|
false,
|
||||||
|
[]metav1alpha1.TableRow{{Cells: []interface{}{"test1", "1/2", "podPhase", 6, "<unknown>"}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer([]byte{})
|
for i, test := range tests {
|
||||||
for _, test := range tests {
|
table, err := printers.NewTablePrinter().With(AddHandlers).PrintTable(&test.pod, printers.PrintOptions{ShowLabels: test.showLabels})
|
||||||
printPod(&test.pod, buf, printers.PrintOptions{ShowLabels: test.showLabels})
|
if err != nil {
|
||||||
// We ignore time
|
t.Fatal(err)
|
||||||
if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) {
|
}
|
||||||
t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String())
|
rows := table.Rows
|
||||||
|
for i := range rows {
|
||||||
|
rows[i].Object.Object = nil
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(test.expect, rows) {
|
||||||
|
t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expect, rows))
|
||||||
}
|
}
|
||||||
buf.Reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
pkg/printers/storage/BUILD
Normal file
33
pkg/printers/storage/BUILD
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["storage.go"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/printers:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
)
|
32
pkg/printers/storage/storage.go
Normal file
32
pkg/printers/storage/storage.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TableConvertor struct {
|
||||||
|
printers.TablePrinter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c TableConvertor) ConvertToTable(ctx genericapirequest.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) {
|
||||||
|
return c.TablePrinter.PrintTable(obj, printers.PrintOptions{Wide: true})
|
||||||
|
}
|
@ -15,12 +15,14 @@ go_test(
|
|||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
|
"//pkg/api/v1:go_default_library",
|
||||||
"//pkg/registry/registrytest:go_default_library",
|
"//pkg/registry/registrytest:go_default_library",
|
||||||
"//pkg/securitycontext:go_default_library",
|
"//pkg/securitycontext:go_default_library",
|
||||||
"//vendor/golang.org/x/net/context:go_default_library",
|
"//vendor/golang.org/x/net/context:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
@ -49,6 +51,9 @@ go_library(
|
|||||||
"//pkg/client/clientset_generated/internalclientset/typed/policy/internalversion:go_default_library",
|
"//pkg/client/clientset_generated/internalclientset/typed/policy/internalversion:go_default_library",
|
||||||
"//pkg/client/retry:go_default_library",
|
"//pkg/client/retry:go_default_library",
|
||||||
"//pkg/kubelet/client:go_default_library",
|
"//pkg/kubelet/client:go_default_library",
|
||||||
|
"//pkg/printers:go_default_library",
|
||||||
|
"//pkg/printers/internalversion:go_default_library",
|
||||||
|
"//pkg/printers/storage:go_default_library",
|
||||||
"//pkg/registry/cachesize:go_default_library",
|
"//pkg/registry/cachesize:go_default_library",
|
||||||
"//pkg/registry/core/pod:go_default_library",
|
"//pkg/registry/core/pod:go_default_library",
|
||||||
"//pkg/registry/core/pod/rest:go_default_library",
|
"//pkg/registry/core/pod/rest:go_default_library",
|
||||||
|
@ -35,6 +35,9 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/validation"
|
"k8s.io/kubernetes/pkg/api/validation"
|
||||||
policyclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/policy/internalversion"
|
policyclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/policy/internalversion"
|
||||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
|
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||||
|
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||||
"k8s.io/kubernetes/pkg/registry/core/pod"
|
"k8s.io/kubernetes/pkg/registry/core/pod"
|
||||||
podrest "k8s.io/kubernetes/pkg/registry/core/pod/rest"
|
podrest "k8s.io/kubernetes/pkg/registry/core/pod/rest"
|
||||||
@ -61,6 +64,7 @@ type REST struct {
|
|||||||
|
|
||||||
// NewStorage returns a RESTStorage object that will work against pods.
|
// NewStorage returns a RESTStorage object that will work against pods.
|
||||||
func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGetter, proxyTransport http.RoundTripper, podDisruptionBudgetClient policyclient.PodDisruptionBudgetsGetter) PodStorage {
|
func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGetter, proxyTransport http.RoundTripper, podDisruptionBudgetClient policyclient.PodDisruptionBudgetsGetter) PodStorage {
|
||||||
|
|
||||||
store := &genericregistry.Store{
|
store := &genericregistry.Store{
|
||||||
Copier: api.Scheme,
|
Copier: api.Scheme,
|
||||||
NewFunc: func() runtime.Object { return &api.Pod{} },
|
NewFunc: func() runtime.Object { return &api.Pod{} },
|
||||||
@ -73,6 +77,8 @@ func NewStorage(optsGetter generic.RESTOptionsGetter, k client.ConnectionInfoGet
|
|||||||
UpdateStrategy: pod.Strategy,
|
UpdateStrategy: pod.Strategy,
|
||||||
DeleteStrategy: pod.Strategy,
|
DeleteStrategy: pod.Strategy,
|
||||||
ReturnDeletedObject: true,
|
ReturnDeletedObject: true,
|
||||||
|
|
||||||
|
TableConvertor: printerstorage.TableConvertor{TablePrinter: printers.NewTablePrinter().With(printersinternal.AddHandlers)},
|
||||||
}
|
}
|
||||||
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: pod.GetAttrs, TriggerFunc: pod.NodeNameTriggerFunc}
|
options := &generic.StoreOptions{RESTOptions: optsGetter, AttrFunc: pod.GetAttrs, TriggerFunc: pod.NodeNameTriggerFunc}
|
||||||
if err := store.CompleteWithOptions(options); err != nil {
|
if err := store.CompleteWithOptions(options); err != nil {
|
||||||
|
@ -19,11 +19,13 @@ package storage
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -35,6 +37,7 @@ import (
|
|||||||
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
||||||
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
|
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||||
"k8s.io/kubernetes/pkg/securitycontext"
|
"k8s.io/kubernetes/pkg/securitycontext"
|
||||||
)
|
)
|
||||||
@ -396,6 +399,88 @@ func TestWatch(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvertToTableList(t *testing.T) {
|
||||||
|
storage, _, _, server := newStorage(t)
|
||||||
|
defer server.Terminate(t)
|
||||||
|
defer storage.Store.DestroyFunc()
|
||||||
|
ctx := genericapirequest.NewDefaultContext()
|
||||||
|
|
||||||
|
columns := []metav1alpha1.TableColumnDefinition{
|
||||||
|
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
|
||||||
|
{Name: "Ready", Type: "string", Description: "The aggregate readiness state of this pod for accepting traffic."},
|
||||||
|
{Name: "Status", Type: "string", Description: "The aggregate status of the containers in this pod."},
|
||||||
|
{Name: "Restarts", Type: "integer", Description: "The number of times the containers in this pod have been restarted."},
|
||||||
|
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
|
||||||
|
{Name: "IP", Type: "string", Priority: 1, Description: v1.PodStatus{}.SwaggerDoc()["podIP"]},
|
||||||
|
{Name: "Node", Type: "string", Priority: 1, Description: v1.PodSpec{}.SwaggerDoc()["nodeName"]},
|
||||||
|
}
|
||||||
|
|
||||||
|
pod1 := &api.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "foo", CreationTimestamp: metav1.NewTime(time.Now().Add(-370 * 24 * time.Hour))},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{Name: "ctr1"},
|
||||||
|
{Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 9376}}},
|
||||||
|
},
|
||||||
|
NodeName: "test-node",
|
||||||
|
},
|
||||||
|
Status: api.PodStatus{
|
||||||
|
PodIP: "10.1.2.3",
|
||||||
|
Phase: api.PodPending,
|
||||||
|
ContainerStatuses: []api.ContainerStatus{
|
||||||
|
{Name: "ctr1", State: api.ContainerState{Running: &api.ContainerStateRunning{}}, RestartCount: 10, Ready: true},
|
||||||
|
{Name: "ctr2", State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}}, RestartCount: 0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
in runtime.Object
|
||||||
|
out *metav1alpha1.Table
|
||||||
|
err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
in: nil,
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: &api.Pod{},
|
||||||
|
out: &metav1alpha1.Table{
|
||||||
|
ColumnDefinitions: columns,
|
||||||
|
Rows: []metav1alpha1.TableRow{
|
||||||
|
{Cells: []interface{}{"", "0/0", "", 0, "<unknown>", "<none>", "<none>"}, Object: runtime.RawExtension{Object: &api.Pod{}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: pod1,
|
||||||
|
out: &metav1alpha1.Table{
|
||||||
|
ColumnDefinitions: columns,
|
||||||
|
Rows: []metav1alpha1.TableRow{
|
||||||
|
{Cells: []interface{}{"foo", "1/2", "Pending", 10, "1y", "10.1.2.3", "test-node"}, Object: runtime.RawExtension{Object: pod1}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
in: &api.PodList{},
|
||||||
|
out: &metav1alpha1.Table{ColumnDefinitions: columns},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, test := range testCases {
|
||||||
|
out, err := storage.ConvertToTable(ctx, test.in, nil)
|
||||||
|
if err != nil {
|
||||||
|
if test.err {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Errorf("%d: error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !apiequality.Semantic.DeepEqual(test.out, out) {
|
||||||
|
t.Errorf("%d: mismatch: %s", i, diff.ObjectReflectDiff(test.out, out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEtcdCreate(t *testing.T) {
|
func TestEtcdCreate(t *testing.T) {
|
||||||
storage, bindingStorage, _, server := newStorage(t)
|
storage, bindingStorage, _, server := newStorage(t)
|
||||||
defer server.Terminate(t)
|
defer server.Terminate(t)
|
||||||
|
@ -11,6 +11,7 @@ load(
|
|||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"meta_test.go",
|
||||||
"multirestmapper_test.go",
|
"multirestmapper_test.go",
|
||||||
"priority_test.go",
|
"priority_test.go",
|
||||||
"restmapper_test.go",
|
"restmapper_test.go",
|
||||||
@ -18,8 +19,12 @@ go_test(
|
|||||||
library = ":go_default_library",
|
library = ":go_default_library",
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//vendor/github.com/google/gofuzz:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ func AsPartialObjectMetadata(m metav1.Object) *metav1alpha1.PartialObjectMetadat
|
|||||||
OwnerReferences: m.GetOwnerReferences(),
|
OwnerReferences: m.GetOwnerReferences(),
|
||||||
Finalizers: m.GetFinalizers(),
|
Finalizers: m.GetFinalizers(),
|
||||||
ClusterName: m.GetClusterName(),
|
ClusterName: m.GetClusterName(),
|
||||||
|
Initializers: m.GetInitializers(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
51
staging/src/k8s.io/apimachinery/pkg/api/meta/meta_test.go
Normal file
51
staging/src/k8s.io/apimachinery/pkg/api/meta/meta_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package meta
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/diff"
|
||||||
|
|
||||||
|
fuzz "github.com/google/gofuzz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAsPartialObjectMetadata(t *testing.T) {
|
||||||
|
f := fuzz.New().NilChance(.5).NumElements(0, 1).RandSource(rand.NewSource(1))
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
m := &metav1.ObjectMeta{}
|
||||||
|
f.Fuzz(m)
|
||||||
|
partial := AsPartialObjectMetadata(m)
|
||||||
|
if !reflect.DeepEqual(&partial.ObjectMeta, m) {
|
||||||
|
t.Fatalf("incomplete partial object metadata: %s", diff.ObjectReflectDiff(&partial.ObjectMeta, m))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
m := &metav1alpha1.PartialObjectMetadata{}
|
||||||
|
f.Fuzz(&m.ObjectMeta)
|
||||||
|
partial := AsPartialObjectMetadata(m)
|
||||||
|
if !reflect.DeepEqual(&partial.ObjectMeta, &m.ObjectMeta) {
|
||||||
|
t.Fatalf("incomplete partial object metadata: %s", diff.ObjectReflectDiff(&partial.ObjectMeta, &m.ObjectMeta))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -149,6 +149,9 @@ func (meta *ObjectMeta) GetFinalizers() []string { return m
|
|||||||
func (meta *ObjectMeta) SetFinalizers(finalizers []string) { meta.Finalizers = finalizers }
|
func (meta *ObjectMeta) SetFinalizers(finalizers []string) { meta.Finalizers = finalizers }
|
||||||
|
|
||||||
func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference {
|
func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference {
|
||||||
|
if meta.OwnerReferences == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
ret := make([]OwnerReference, len(meta.OwnerReferences))
|
ret := make([]OwnerReference, len(meta.OwnerReferences))
|
||||||
for i := 0; i < len(meta.OwnerReferences); i++ {
|
for i := 0; i < len(meta.OwnerReferences); i++ {
|
||||||
ret[i].Kind = meta.OwnerReferences[i].Kind
|
ret[i].Kind = meta.OwnerReferences[i].Kind
|
||||||
@ -168,6 +171,10 @@ func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
|
func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
|
||||||
|
if references == nil {
|
||||||
|
meta.OwnerReferences = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
newReferences := make([]OwnerReference, len(references))
|
newReferences := make([]OwnerReference, len(references))
|
||||||
for i := 0; i < len(references); i++ {
|
for i := 0; i < len(references); i++ {
|
||||||
newReferences[i].Kind = references[i].Kind
|
newReferences[i].Kind = references[i].Kind
|
||||||
|
@ -34,7 +34,7 @@ import (
|
|||||||
// transformResponseObject takes an object loaded from storage and performs any necessary transformations.
|
// transformResponseObject takes an object loaded from storage and performs any necessary transformations.
|
||||||
// Will write the complete response object.
|
// Will write the complete response object.
|
||||||
func transformResponseObject(ctx request.Context, scope RequestScope, req *http.Request, w http.ResponseWriter, statusCode int, result runtime.Object) {
|
func transformResponseObject(ctx request.Context, scope RequestScope, req *http.Request, w http.ResponseWriter, statusCode int, result runtime.Object) {
|
||||||
// TODO: use returned serializer
|
// TODO: fetch the media type much earlier in request processing and pass it into this method.
|
||||||
mediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope)
|
mediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, &scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status := responsewriters.ErrorToAPIStatus(err)
|
status := responsewriters.ErrorToAPIStatus(err)
|
||||||
@ -169,7 +169,7 @@ func transformResponseObject(ctx request.Context, scope RequestScope, req *http.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responsewriters.WriteObject(statusCode, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
responsewriters.WriteObject(ctx, statusCode, scope.Kind.GroupVersion(), scope.Serializer, result, w, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// errNotAcceptable indicates Accept negotiation has failed
|
// errNotAcceptable indicates Accept negotiation has failed
|
||||||
|
@ -62,6 +62,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/api/validation/path:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/validation/path:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/api/validation/path"
|
"k8s.io/apimachinery/pkg/api/validation/path"
|
||||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
@ -155,6 +156,9 @@ type Store struct {
|
|||||||
// ExportStrategy implements resource-specific behavior during export,
|
// ExportStrategy implements resource-specific behavior during export,
|
||||||
// optional. Exported objects are not decorated.
|
// optional. Exported objects are not decorated.
|
||||||
ExportStrategy rest.RESTExportStrategy
|
ExportStrategy rest.RESTExportStrategy
|
||||||
|
// TableConvertor is an optional interface for transforming items or lists
|
||||||
|
// of items into tabular output. If unset, the default will be used.
|
||||||
|
TableConvertor rest.TableConvertor
|
||||||
|
|
||||||
// Storage is the interface for the underlying storage for the resource.
|
// Storage is the interface for the underlying storage for the resource.
|
||||||
Storage storage.Interface
|
Storage storage.Interface
|
||||||
@ -169,6 +173,7 @@ type Store struct {
|
|||||||
// Note: the rest.StandardStorage interface aggregates the common REST verbs
|
// Note: the rest.StandardStorage interface aggregates the common REST verbs
|
||||||
var _ rest.StandardStorage = &Store{}
|
var _ rest.StandardStorage = &Store{}
|
||||||
var _ rest.Exporter = &Store{}
|
var _ rest.Exporter = &Store{}
|
||||||
|
var _ rest.TableConvertor = &Store{}
|
||||||
|
|
||||||
const OptimisticLockErrorMsg = "the object has been modified; please apply your changes to the latest version and try again"
|
const OptimisticLockErrorMsg = "the object has been modified; please apply your changes to the latest version and try again"
|
||||||
|
|
||||||
@ -1230,3 +1235,10 @@ func (e *Store) CompleteWithOptions(options *generic.StoreOptions) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Store) ConvertToTable(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) {
|
||||||
|
if e.TableConvertor != nil {
|
||||||
|
return e.TableConvertor.ConvertToTable(ctx, object, tableOptions)
|
||||||
|
}
|
||||||
|
return rest.DefaultTableConvertor.ConvertToTable(ctx, object, tableOptions)
|
||||||
|
}
|
||||||
|
@ -31,6 +31,7 @@ go_library(
|
|||||||
"export.go",
|
"export.go",
|
||||||
"meta.go",
|
"meta.go",
|
||||||
"rest.go",
|
"rest.go",
|
||||||
|
"table.go",
|
||||||
"update.go",
|
"update.go",
|
||||||
],
|
],
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
@ -42,6 +43,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
|
||||||
|
@ -118,7 +118,7 @@ type GetterWithOptions interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TableConvertor interface {
|
type TableConvertor interface {
|
||||||
ConvertToTableList(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.TableList, error)
|
ConvertToTable(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deleter is an object that can delete a named RESTful resource.
|
// Deleter is an object that can delete a named RESTful resource.
|
||||||
|
@ -32,15 +32,15 @@ type defaultTableConvertor struct{}
|
|||||||
|
|
||||||
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
|
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
|
||||||
|
|
||||||
func (defaultTableConvertor) ConvertToTableList(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.TableList, error) {
|
func (defaultTableConvertor) ConvertToTable(ctx genericapirequest.Context, object runtime.Object, tableOptions runtime.Object) (*metav1alpha1.Table, error) {
|
||||||
var table metav1alpha1.TableList
|
var table metav1alpha1.Table
|
||||||
fn := func(obj runtime.Object) error {
|
fn := func(obj runtime.Object) error {
|
||||||
m, err := meta.Accessor(obj)
|
m, err := meta.Accessor(obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: skip objects we don't recognize
|
// TODO: skip objects we don't recognize
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
table.Items = append(table.Items, metav1alpha1.TableListItem{
|
table.Rows = append(table.Rows, metav1alpha1.TableRow{
|
||||||
Cells: []interface{}{m.GetClusterName(), m.GetNamespace(), m.GetName(), m.GetCreationTimestamp().Time.UTC().Format(time.RFC3339)},
|
Cells: []interface{}{m.GetClusterName(), m.GetNamespace(), m.GetName(), m.GetCreationTimestamp().Time.UTC().Format(time.RFC3339)},
|
||||||
Object: runtime.RawExtension{Object: obj},
|
Object: runtime.RawExtension{Object: obj},
|
||||||
})
|
})
|
||||||
@ -56,7 +56,7 @@ func (defaultTableConvertor) ConvertToTableList(ctx genericapirequest.Context, o
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
table.Headers = []metav1alpha1.TableListHeader{
|
table.ColumnDefinitions = []metav1alpha1.TableColumnDefinition{
|
||||||
{Name: "Cluster Name", Type: "string", Description: swaggerMetadataDescriptions["clusterName"]},
|
{Name: "Cluster Name", Type: "string", Description: swaggerMetadataDescriptions["clusterName"]},
|
||||||
{Name: "Namespace", Type: "string", Description: swaggerMetadataDescriptions["namespace"]},
|
{Name: "Namespace", Type: "string", Description: swaggerMetadataDescriptions["namespace"]},
|
||||||
{Name: "Name", Type: "string", Description: swaggerMetadataDescriptions["name"]},
|
{Name: "Name", Type: "string", Description: swaggerMetadataDescriptions["name"]},
|
||||||
@ -71,8 +71,8 @@ func (defaultTableConvertor) ConvertToTableList(ctx genericapirequest.Context, o
|
|||||||
return &table, nil
|
return &table, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimColumn(column int, table *metav1alpha1.TableList) bool {
|
func trimColumn(column int, table *metav1alpha1.Table) bool {
|
||||||
for _, item := range table.Items {
|
for _, item := range table.Rows {
|
||||||
switch t := item.Cells[column].(type) {
|
switch t := item.Cells[column].(type) {
|
||||||
case string:
|
case string:
|
||||||
if len(t) > 0 {
|
if len(t) > 0 {
|
||||||
@ -85,22 +85,22 @@ func trimColumn(column int, table *metav1alpha1.TableList) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if column == 0 {
|
if column == 0 {
|
||||||
table.Headers = table.Headers[1:]
|
table.ColumnDefinitions = table.ColumnDefinitions[1:]
|
||||||
} else {
|
} else {
|
||||||
for j := column; j < len(table.Headers); j++ {
|
for j := column; j < len(table.ColumnDefinitions); j++ {
|
||||||
table.Headers[j] = table.Headers[j+1]
|
table.ColumnDefinitions[j] = table.ColumnDefinitions[j+1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range table.Items {
|
for i := range table.Rows {
|
||||||
cells := table.Items[i].Cells
|
cells := table.Rows[i].Cells
|
||||||
if column == 0 {
|
if column == 0 {
|
||||||
table.Items[i].Cells = cells[1:]
|
table.Rows[i].Cells = cells[1:]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for j := column; j < len(cells); j++ {
|
for j := column; j < len(cells); j++ {
|
||||||
cells[j] = cells[j+1]
|
cells[j] = cells[j+1]
|
||||||
}
|
}
|
||||||
table.Items[i].Cells = cells[:len(cells)-1]
|
table.Rows[i].Cells = cells[:len(cells)-1]
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
4
staging/src/k8s.io/client-go/Godeps/Godeps.json
generated
4
staging/src/k8s.io/client-go/Godeps/Godeps.json
generated
@ -330,6 +330,10 @@
|
|||||||
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
||||||
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "k8s.io/apimachinery/pkg/apis/meta/v1alpha1",
|
||||||
|
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
|
"ImportPath": "k8s.io/apimachinery/pkg/conversion",
|
||||||
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
Loading…
Reference in New Issue
Block a user