From 6d1c7308a57121392e0835164ee74bfa94aa0eeb Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sat, 18 Feb 2017 22:40:38 -0500 Subject: [PATCH 1/8] Move, but do not refactor, printers to their own package --- pkg/kubectl/sorted_resource_name_list.go | 86 --- .../customcolumn.go} | 0 .../customcolumn_test.go} | 0 pkg/printers/humanreadable.go | 159 +++++ pkg/printers/interface.go | 62 ++ .../internalversion}/describe.go | 0 .../internalversion}/describe_test.go | 2 +- .../internalversion/internalversion.go} | 588 +----------------- .../internalversion/internalversion_test.go} | 2 +- .../sorted_resource_name_list_test.go | 0 pkg/printers/json.go | 101 +++ pkg/printers/jsonpath.go | 157 +++++ pkg/printers/name.go | 89 +++ pkg/printers/printers.go | 116 ++++ pkg/printers/tabwriter.go | 36 ++ pkg/printers/template.go | 110 ++++ pkg/printers/versioned.go | 63 ++ 17 files changed, 899 insertions(+), 672 deletions(-) delete mode 100644 pkg/kubectl/sorted_resource_name_list.go rename pkg/{kubectl/custom_column_printer.go => printers/customcolumn.go} (100%) rename pkg/{kubectl/custom_column_printer_test.go => printers/customcolumn_test.go} (100%) create mode 100644 pkg/printers/humanreadable.go create mode 100644 pkg/printers/interface.go rename pkg/{kubectl => printers/internalversion}/describe.go (100%) rename pkg/{kubectl => printers/internalversion}/describe_test.go (99%) rename pkg/{kubectl/resource_printer.go => printers/internalversion/internalversion.go} (79%) rename pkg/{kubectl/resource_printer_test.go => printers/internalversion/internalversion_test.go} (99%) rename pkg/{kubectl => printers/internalversion}/sorted_resource_name_list_test.go (100%) create mode 100644 pkg/printers/json.go create mode 100644 pkg/printers/jsonpath.go create mode 100644 pkg/printers/name.go create mode 100644 pkg/printers/printers.go create mode 100644 pkg/printers/tabwriter.go create mode 100644 pkg/printers/template.go create mode 100644 pkg/printers/versioned.go diff --git a/pkg/kubectl/sorted_resource_name_list.go b/pkg/kubectl/sorted_resource_name_list.go deleted file mode 100644 index f5f44c86bd4..00000000000 --- a/pkg/kubectl/sorted_resource_name_list.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright 2014 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 kubectl - -import ( - "sort" - - "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubelet/qos" -) - -type SortableResourceNames []api.ResourceName - -func (list SortableResourceNames) Len() int { - return len(list) -} - -func (list SortableResourceNames) Swap(i, j int) { - list[i], list[j] = list[j], list[i] -} - -func (list SortableResourceNames) Less(i, j int) bool { - return list[i] < list[j] -} - -// SortedResourceNames returns the sorted resource names of a resource list. -func SortedResourceNames(list api.ResourceList) []api.ResourceName { - resources := make([]api.ResourceName, 0, len(list)) - for res := range list { - resources = append(resources, res) - } - sort.Sort(SortableResourceNames(resources)) - return resources -} - -type SortableResourceQuotas []api.ResourceQuota - -func (list SortableResourceQuotas) Len() int { - return len(list) -} - -func (list SortableResourceQuotas) Swap(i, j int) { - list[i], list[j] = list[j], list[i] -} - -func (list SortableResourceQuotas) Less(i, j int) bool { - return list[i].Name < list[j].Name -} - -type SortableVolumeMounts []api.VolumeMount - -func (list SortableVolumeMounts) Len() int { - return len(list) -} - -func (list SortableVolumeMounts) Swap(i, j int) { - list[i], list[j] = list[j], list[i] -} - -func (list SortableVolumeMounts) Less(i, j int) bool { - return list[i].MountPath < list[j].MountPath -} - -// SortedQoSResourceNames returns the sorted resource names of a QoS list. -func SortedQoSResourceNames(list qos.QOSList) []api.ResourceName { - resources := make([]api.ResourceName, 0, len(list)) - for res := range list { - resources = append(resources, api.ResourceName(res)) - } - sort.Sort(SortableResourceNames(resources)) - return resources -} diff --git a/pkg/kubectl/custom_column_printer.go b/pkg/printers/customcolumn.go similarity index 100% rename from pkg/kubectl/custom_column_printer.go rename to pkg/printers/customcolumn.go diff --git a/pkg/kubectl/custom_column_printer_test.go b/pkg/printers/customcolumn_test.go similarity index 100% rename from pkg/kubectl/custom_column_printer_test.go rename to pkg/printers/customcolumn_test.go diff --git a/pkg/printers/humanreadable.go b/pkg/printers/humanreadable.go new file mode 100644 index 00000000000..86dd62beb9e --- /dev/null +++ b/pkg/printers/humanreadable.go @@ -0,0 +1,159 @@ +/* +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 printers + +import ( + "fmt" + "io" + "reflect" + "strings" + + "github.com/golang/glog" +) + +type handlerEntry struct { + columns []string + columnsWithWide []string + printFunc reflect.Value + args []reflect.Value +} + +// HumanReadablePrinter is an implementation of ResourcePrinter which attempts to provide +// more elegant output. It is not threadsafe, but you may call PrintObj repeatedly; headers +// will only be printed if the object type changes. This makes it useful for printing items +// received from watches. +type HumanReadablePrinter struct { + handlerMap map[reflect.Type]*handlerEntry + options PrintOptions + lastType reflect.Type + hiddenObjNum int +} + +// NewHumanReadablePrinter creates a HumanReadablePrinter. +func NewHumanReadablePrinter(options PrintOptions) *HumanReadablePrinter { + printer := &HumanReadablePrinter{ + handlerMap: make(map[reflect.Type]*handlerEntry), + options: options, + } + printer.addDefaultHandlers() + return printer +} + +// formatResourceName receives a resource kind, name, and boolean specifying +// whether or not to update the current name to "kind/name" +func formatResourceName(kind, name string, withKind bool) string { + if !withKind || kind == "" { + return name + } + + return kind + "/" + name +} + +// GetResourceKind returns the type currently set for a resource +func (h *HumanReadablePrinter) GetResourceKind() string { + return h.options.Kind +} + +// EnsurePrintWithKind sets HumanReadablePrinter options "WithKind" to true +// and "Kind" to the string arg it receives, pre-pending this string +// to the "NAME" column in an output of resources. +func (h *HumanReadablePrinter) EnsurePrintWithKind(kind string) { + h.options.WithKind = true + h.options.Kind = kind +} + +// EnsurePrintHeaders sets the HumanReadablePrinter option "NoHeaders" to false +// and removes the .lastType that was printed, which forces headers to be +// printed in cases where multiple lists of the same resource are printed +// consecutively, but are separated by non-printer related information. +func (h *HumanReadablePrinter) EnsurePrintHeaders() { + h.options.NoHeaders = false + h.lastType = nil +} + +// Handler adds a print handler with a given set of columns to HumanReadablePrinter instance. +// See validatePrintHandlerFunc for required method signature. +func (h *HumanReadablePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error { + printFuncValue := reflect.ValueOf(printFunc) + if err := h.validatePrintHandlerFunc(printFuncValue); err != nil { + glog.Errorf("Unable to add print handler: %v", err) + return err + } + + objType := printFuncValue.Type().In(0) + h.handlerMap[objType] = &handlerEntry{ + columns: columns, + columnsWithWide: columnsWithWide, + printFunc: printFuncValue, + } + return nil +} + +// validatePrintHandlerFunc 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, w io.Writer, options PrintOptions) error +// where ObjectType is the type of the object that will be printed. +func (h *HumanReadablePrinter) validatePrintHandlerFunc(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() != 3 || funcType.NumOut() != 1 { + return fmt.Errorf("invalid print handler." + + "Must accept 3 parameters and return 1 value.") + } + if funcType.In(1) != reflect.TypeOf((*io.Writer)(nil)).Elem() || + funcType.In(2) != reflect.TypeOf((*PrintOptions)(nil)).Elem() || + funcType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() { + return fmt.Errorf("invalid print handler. The expected signature is: "+ + "func handler(obj %v, w io.Writer, options PrintOptions) error", funcType.In(0)) + } + return nil +} + +func (h *HumanReadablePrinter) HandledResources() []string { + keys := make([]string, 0) + + for k := range h.handlerMap { + // k.String looks like "*api.PodList" and we want just "pod" + api := strings.Split(k.String(), ".") + resource := api[len(api)-1] + if strings.HasSuffix(resource, "List") { + continue + } + resource = strings.ToLower(resource) + keys = append(keys, resource) + } + return keys +} + +func (h *HumanReadablePrinter) AfterPrint(output io.Writer, res string) error { + return nil +} + +func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error { + _, err := fmt.Fprintf(w, "Unknown object: %s", string(data)) + return err +} + +func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) error { + if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil { + return err + } + return nil +} diff --git a/pkg/printers/interface.go b/pkg/printers/interface.go new file mode 100644 index 00000000000..0b301565422 --- /dev/null +++ b/pkg/printers/interface.go @@ -0,0 +1,62 @@ +/* +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 printers + +import ( + "io" + + "k8s.io/apimachinery/pkg/runtime" +) + +// ResourcePrinter is an interface that knows how to print runtime objects. +type ResourcePrinter interface { + // Print receives a runtime object, formats it and prints it to a writer. + PrintObj(runtime.Object, io.Writer) error + HandledResources() []string + //Can be used to print out warning/clarifications if needed + //after all objects were printed + AfterPrint(io.Writer, string) error +} + +// ResourcePrinterFunc is a function that can print objects +type ResourcePrinterFunc func(runtime.Object, io.Writer) error + +// PrintObj implements ResourcePrinter +func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error { + return fn(obj, w) +} + +// TODO: implement HandledResources() +func (fn ResourcePrinterFunc) HandledResources() []string { + return []string{} +} + +func (fn ResourcePrinterFunc) AfterPrint(io.Writer, string) error { + return nil +} + +type PrintOptions struct { + NoHeaders bool + WithNamespace bool + WithKind bool + Wide bool + ShowAll bool + ShowLabels bool + AbsoluteTimestamps bool + Kind string + ColumnLabels []string +} diff --git a/pkg/kubectl/describe.go b/pkg/printers/internalversion/describe.go similarity index 100% rename from pkg/kubectl/describe.go rename to pkg/printers/internalversion/describe.go diff --git a/pkg/kubectl/describe_test.go b/pkg/printers/internalversion/describe_test.go similarity index 99% rename from pkg/kubectl/describe_test.go rename to pkg/printers/internalversion/describe_test.go index 8fa18dbe874..9de552368cf 100644 --- a/pkg/kubectl/describe_test.go +++ b/pkg/printers/internalversion/describe_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package internalversion import ( "bytes" diff --git a/pkg/kubectl/resource_printer.go b/pkg/printers/internalversion/internalversion.go similarity index 79% rename from pkg/kubectl/resource_printer.go rename to pkg/printers/internalversion/internalversion.go index d7985961cd8..aff5133972c 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/printers/internalversion/internalversion.go @@ -1,5 +1,5 @@ /* -Copyright 2014 The Kubernetes Authors. +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. @@ -14,11 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package internalversion import ( "bytes" - "encoding/json" "fmt" "io" "io/ioutil" @@ -27,18 +26,14 @@ import ( "sort" "strings" "text/tabwriter" - "text/template" "time" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/client-go/util/jsonpath" "k8s.io/kubernetes/federation/apis/federation" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/events" @@ -52,18 +47,6 @@ import ( "k8s.io/kubernetes/pkg/apis/storage" storageutil "k8s.io/kubernetes/pkg/apis/storage/util" "k8s.io/kubernetes/pkg/util/node" - - "github.com/ghodss/yaml" - "github.com/golang/glog" -) - -const ( - tabwriterMinWidth = 10 - tabwriterWidth = 4 - tabwriterPadding = 3 - tabwriterPadChar = ' ' - tabwriterFlags = 0 - loadBalancerWidth = 16 ) // GetPrinter takes a format type, an optional format argument. It will return true @@ -156,339 +139,6 @@ func GetPrinter(format, formatArgument string, noHeaders, allowMissingTemplateKe return printer, true, nil } -// ResourcePrinter is an interface that knows how to print runtime objects. -type ResourcePrinter interface { - // Print receives a runtime object, formats it and prints it to a writer. - PrintObj(runtime.Object, io.Writer) error - HandledResources() []string - //Can be used to print out warning/clarifications if needed - //after all objects were printed - AfterPrint(io.Writer, string) error -} - -// ResourcePrinterFunc is a function that can print objects -type ResourcePrinterFunc func(runtime.Object, io.Writer) error - -// PrintObj implements ResourcePrinter -func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error { - return fn(obj, w) -} - -// TODO: implement HandledResources() -func (fn ResourcePrinterFunc) HandledResources() []string { - return []string{} -} - -func (fn ResourcePrinterFunc) AfterPrint(io.Writer, string) error { - return nil -} - -// VersionedPrinter takes runtime objects and ensures they are converted to a given API version -// prior to being passed to a nested printer. -type VersionedPrinter struct { - printer ResourcePrinter - converter runtime.ObjectConvertor - versions []schema.GroupVersion -} - -// NewVersionedPrinter wraps a printer to convert objects to a known API version prior to printing. -func NewVersionedPrinter(printer ResourcePrinter, converter runtime.ObjectConvertor, versions ...schema.GroupVersion) ResourcePrinter { - return &VersionedPrinter{ - printer: printer, - converter: converter, - versions: versions, - } -} - -func (p *VersionedPrinter) AfterPrint(w io.Writer, res string) error { - return nil -} - -// PrintObj implements ResourcePrinter -func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error { - if len(p.versions) == 0 { - return fmt.Errorf("no version specified, object cannot be converted") - } - converted, err := p.converter.ConvertToVersion(obj, schema.GroupVersions(p.versions)) - if err != nil { - return err - } - return p.printer.PrintObj(converted, w) -} - -// TODO: implement HandledResources() -func (p *VersionedPrinter) HandledResources() []string { - return []string{} -} - -// NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object. -type NamePrinter struct { - Decoder runtime.Decoder - Typer runtime.ObjectTyper -} - -func (p *NamePrinter) AfterPrint(w io.Writer, res string) error { - return nil -} - -// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object -// and print "resource/name" pair. If the object is a List, print all items in it. -func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { - if meta.IsListType(obj) { - items, err := meta.ExtractList(obj) - if err != nil { - return err - } - if errs := runtime.DecodeList(items, p.Decoder, unstructured.UnstructuredJSONScheme); len(errs) > 0 { - return utilerrors.NewAggregate(errs) - } - for _, obj := range items { - if err := p.PrintObj(obj, w); err != nil { - return err - } - } - return nil - } - - name := "" - if acc, err := meta.Accessor(obj); err == nil { - if n := acc.GetName(); len(n) > 0 { - name = n - } - } - - if kind := obj.GetObjectKind().GroupVersionKind(); len(kind.Kind) == 0 { - // this is the old code. It's unnecessary on decoded external objects, but on internal objects - // you may have to do it. Tests are definitely calling it with internals and I'm not sure who else - // is - if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil { - // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper - _, resource := meta.KindToResource(gvks[0]) - fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) - } else { - fmt.Fprintf(w, "/%s\n", name) - } - - } else { - // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper - _, resource := meta.KindToResource(kind) - fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) - } - - return nil -} - -// TODO: implement HandledResources() -func (p *NamePrinter) HandledResources() []string { - return []string{} -} - -// JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON. -type JSONPrinter struct { -} - -func (p *JSONPrinter) AfterPrint(w io.Writer, res string) error { - return nil -} - -// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer. -func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error { - switch obj := obj.(type) { - case *runtime.Unknown: - var buf bytes.Buffer - err := json.Indent(&buf, obj.Raw, "", " ") - if err != nil { - return err - } - buf.WriteRune('\n') - _, err = buf.WriteTo(w) - return err - } - - data, err := json.MarshalIndent(obj, "", " ") - if err != nil { - return err - } - data = append(data, '\n') - _, err = w.Write(data) - return err -} - -// TODO: implement HandledResources() -func (p *JSONPrinter) HandledResources() []string { - return []string{} -} - -// YAMLPrinter is an implementation of ResourcePrinter which outputs an object as YAML. -// The input object is assumed to be in the internal version of an API and is converted -// to the given version first. -type YAMLPrinter struct { - version string - converter runtime.ObjectConvertor -} - -func (p *YAMLPrinter) AfterPrint(w io.Writer, res string) error { - return nil -} - -// PrintObj prints the data as YAML. -func (p *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error { - switch obj := obj.(type) { - case *runtime.Unknown: - data, err := yaml.JSONToYAML(obj.Raw) - if err != nil { - return err - } - _, err = w.Write(data) - return err - } - - output, err := yaml.Marshal(obj) - if err != nil { - return err - } - _, err = fmt.Fprint(w, string(output)) - return err -} - -// TODO: implement HandledResources() -func (p *YAMLPrinter) HandledResources() []string { - return []string{} -} - -type handlerEntry struct { - columns []string - columnsWithWide []string - printFunc reflect.Value - args []reflect.Value -} - -type PrintOptions struct { - NoHeaders bool - WithNamespace bool - WithKind bool - Wide bool - ShowAll bool - ShowLabels bool - AbsoluteTimestamps bool - Kind string - ColumnLabels []string -} - -// HumanReadablePrinter is an implementation of ResourcePrinter which attempts to provide -// more elegant output. It is not threadsafe, but you may call PrintObj repeatedly; headers -// will only be printed if the object type changes. This makes it useful for printing items -// received from watches. -type HumanReadablePrinter struct { - handlerMap map[reflect.Type]*handlerEntry - options PrintOptions - lastType reflect.Type - hiddenObjNum int -} - -// NewHumanReadablePrinter creates a HumanReadablePrinter. -func NewHumanReadablePrinter(options PrintOptions) *HumanReadablePrinter { - printer := &HumanReadablePrinter{ - handlerMap: make(map[reflect.Type]*handlerEntry), - options: options, - } - printer.addDefaultHandlers() - return printer -} - -// formatResourceName receives a resource kind, name, and boolean specifying -// whether or not to update the current name to "kind/name" -func formatResourceName(kind, name string, withKind bool) string { - if !withKind || kind == "" { - return name - } - - return kind + "/" + name -} - -// GetResourceKind returns the type currently set for a resource -func (h *HumanReadablePrinter) GetResourceKind() string { - return h.options.Kind -} - -// EnsurePrintWithKind sets HumanReadablePrinter options "WithKind" to true -// and "Kind" to the string arg it receives, pre-pending this string -// to the "NAME" column in an output of resources. -func (h *HumanReadablePrinter) EnsurePrintWithKind(kind string) { - h.options.WithKind = true - h.options.Kind = kind -} - -// EnsurePrintHeaders sets the HumanReadablePrinter option "NoHeaders" to false -// and removes the .lastType that was printed, which forces headers to be -// printed in cases where multiple lists of the same resource are printed -// consecutively, but are separated by non-printer related information. -func (h *HumanReadablePrinter) EnsurePrintHeaders() { - h.options.NoHeaders = false - h.lastType = nil -} - -// Handler adds a print handler with a given set of columns to HumanReadablePrinter instance. -// See validatePrintHandlerFunc for required method signature. -func (h *HumanReadablePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error { - printFuncValue := reflect.ValueOf(printFunc) - if err := h.validatePrintHandlerFunc(printFuncValue); err != nil { - glog.Errorf("Unable to add print handler: %v", err) - return err - } - - objType := printFuncValue.Type().In(0) - h.handlerMap[objType] = &handlerEntry{ - columns: columns, - columnsWithWide: columnsWithWide, - printFunc: printFuncValue, - } - return nil -} - -// validatePrintHandlerFunc 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, w io.Writer, options PrintOptions) error -// where ObjectType is the type of the object that will be printed. -func (h *HumanReadablePrinter) validatePrintHandlerFunc(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() != 3 || funcType.NumOut() != 1 { - return fmt.Errorf("invalid print handler." + - "Must accept 3 parameters and return 1 value.") - } - if funcType.In(1) != reflect.TypeOf((*io.Writer)(nil)).Elem() || - funcType.In(2) != reflect.TypeOf((*PrintOptions)(nil)).Elem() || - funcType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() { - return fmt.Errorf("invalid print handler. The expected signature is: "+ - "func handler(obj %v, w io.Writer, options PrintOptions) error", funcType.In(0)) - } - return nil -} - -func (h *HumanReadablePrinter) HandledResources() []string { - keys := make([]string, 0) - - for k := range h.handlerMap { - // k.String looks like "*api.PodList" and we want just "pod" - api := strings.Split(k.String(), ".") - resource := api[len(api)-1] - if strings.HasSuffix(resource, "List") { - continue - } - resource = strings.ToLower(resource) - keys = append(keys, resource) - } - return keys -} - -func (h *HumanReadablePrinter) AfterPrint(output io.Writer, res string) error { - return nil -} - // NOTE: When adding a new resource type here, please update the list // pkg/kubectl/cmd/get.go to reflect the new resource type. var ( @@ -544,7 +194,7 @@ var ( certificateSigningRequestColumns = []string{"NAME", "AGE", "REQUESTOR", "CONDITION"} ) -func (h *HumanReadablePrinter) printPod(pod *api.Pod, w io.Writer, options PrintOptions) error { +func printPod(pod *api.Pod, w io.Writer, options PrintOptions) error { if err := printPodBase(pod, w, options); err != nil { return err } @@ -552,7 +202,7 @@ func (h *HumanReadablePrinter) printPod(pod *api.Pod, w io.Writer, options Print return nil } -func (h *HumanReadablePrinter) printPodList(podList *api.PodList, w io.Writer, options PrintOptions) error { +func printPodList(podList *api.PodList, w io.Writer, options PrintOptions) error { for _, pod := range podList.Items { if err := printPodBase(&pod, w, options); err != nil { return err @@ -638,18 +288,6 @@ func (h *HumanReadablePrinter) addDefaultHandlers() { h.Handler(statusColumns, nil, printStatus) } -func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error { - _, err := fmt.Fprintf(w, "Unknown object: %s", string(data)) - return err -} - -func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) error { - if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil { - return err - } - return nil -} - // Pass ports=nil for all ports. func formatEndpoints(endpoints *api.Endpoints, ports sets.String) string { if len(endpoints.Subsets) == 0 { @@ -2449,11 +2087,6 @@ func formatShowLabelsHeader(showLabels bool, t reflect.Type) []string { return nil } -// GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text. -func GetNewTabWriter(output io.Writer) *tabwriter.Writer { - return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags) -} - // 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 { // if output is a tabwriter (when it's called by kubectl get), we use it; create a new tabwriter otherwise @@ -2563,90 +2196,6 @@ func printUnstructured(unstructured runtime.Unstructured, w io.Writer, options P return nil } -// TemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template. -type TemplatePrinter struct { - rawTemplate string - template *template.Template -} - -func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) { - t, err := template.New("output"). - Funcs(template.FuncMap{"exists": exists}). - Parse(string(tmpl)) - if err != nil { - return nil, err - } - return &TemplatePrinter{ - rawTemplate: string(tmpl), - template: t, - }, nil -} - -// AllowMissingKeys tells the template engine if missing keys are allowed. -func (p *TemplatePrinter) AllowMissingKeys(allow bool) { - if allow { - p.template.Option("missingkey=default") - } else { - p.template.Option("missingkey=error") - } -} - -func (p *TemplatePrinter) AfterPrint(w io.Writer, res string) error { - return nil -} - -// PrintObj formats the obj with the Go Template. -func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error { - var data []byte - var err error - data, err = json.Marshal(obj) - if err != nil { - return err - } - - out := map[string]interface{}{} - if err := json.Unmarshal(data, &out); err != nil { - return err - } - if err = p.safeExecute(w, out); err != nil { - // It is way easier to debug this stuff when it shows up in - // stdout instead of just stdin. So in addition to returning - // a nice error, also print useful stuff with the writer. - fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err) - fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", p.rawTemplate) - fmt.Fprintf(w, "\traw data was:\n\t\t%v\n", string(data)) - fmt.Fprintf(w, "\tobject given to template engine was:\n\t\t%+v\n\n", out) - return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err) - } - return nil -} - -// TODO: implement HandledResources() -func (p *TemplatePrinter) HandledResources() []string { - return []string{} -} - -// safeExecute tries to execute the template, but catches panics and returns an error -// should the template engine panic. -func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error { - var panicErr error - // Sorry for the double anonymous function. There's probably a clever way - // to do this that has the defer'd func setting the value to be returned, but - // that would be even less obvious. - retErr := func() error { - defer func() { - if x := recover(); x != nil { - panicErr = fmt.Errorf("caught panic: %+v", x) - } - }() - return p.template.Execute(w, obj) - }() - if panicErr != nil { - return panicErr - } - return retErr -} - func tabbedString(f func(io.Writer) error) (string, error) { out := new(tabwriter.Writer) buf := &bytes.Buffer{} @@ -2662,135 +2211,6 @@ func tabbedString(f func(io.Writer) error) (string, error) { return str, nil } -// exists returns true if it would be possible to call the index function -// with these arguments. -// -// TODO: how to document this for users? -// -// index returns the result of indexing its first argument by the following -// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each -// indexed item must be a map, slice, or array. -func exists(item interface{}, indices ...interface{}) bool { - v := reflect.ValueOf(item) - for _, i := range indices { - index := reflect.ValueOf(i) - var isNil bool - if v, isNil = indirect(v); isNil { - return false - } - switch v.Kind() { - case reflect.Array, reflect.Slice, reflect.String: - var x int64 - switch index.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - x = index.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - x = int64(index.Uint()) - default: - return false - } - if x < 0 || x >= int64(v.Len()) { - return false - } - v = v.Index(int(x)) - case reflect.Map: - if !index.IsValid() { - index = reflect.Zero(v.Type().Key()) - } - if !index.Type().AssignableTo(v.Type().Key()) { - return false - } - if x := v.MapIndex(index); x.IsValid() { - v = x - } else { - v = reflect.Zero(v.Type().Elem()) - } - default: - return false - } - } - if _, isNil := indirect(v); isNil { - return false - } - return true -} - -// stolen from text/template -// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. -// We indirect through pointers and empty interfaces (only) because -// non-empty interfaces have methods we might need. -func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { - for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { - if v.IsNil() { - return v, true - } - if v.Kind() == reflect.Interface && v.NumMethod() > 0 { - break - } - } - return v, false -} - -// JSONPathPrinter is an implementation of ResourcePrinter which formats data with jsonpath expression. -type JSONPathPrinter struct { - rawTemplate string - *jsonpath.JSONPath -} - -func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) { - j := jsonpath.New("out") - if err := j.Parse(tmpl); err != nil { - return nil, err - } - return &JSONPathPrinter{tmpl, j}, nil -} - -func (j *JSONPathPrinter) AfterPrint(w io.Writer, res string) error { - return nil -} - -// PrintObj formats the obj with the JSONPath Template. -func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error { - var queryObj interface{} = obj - if meta.IsListType(obj) { - data, err := json.Marshal(obj) - if err != nil { - return err - } - queryObj = map[string]interface{}{} - if err := json.Unmarshal(data, &queryObj); err != nil { - return err - } - } - - if unknown, ok := obj.(*runtime.Unknown); ok { - data, err := json.Marshal(unknown) - if err != nil { - return err - } - queryObj = map[string]interface{}{} - if err := json.Unmarshal(data, &queryObj); err != nil { - return err - } - } - if unstructured, ok := obj.(*unstructured.Unstructured); ok { - queryObj = unstructured.Object - } - - if err := j.JSONPath.Execute(w, queryObj); err != nil { - fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err) - fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", j.rawTemplate) - fmt.Fprintf(w, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n", queryObj) - return fmt.Errorf("error executing jsonpath %q: %v\n", j.rawTemplate, err) - } - return nil -} - -// TODO: implement HandledResources() -func (p *JSONPathPrinter) HandledResources() []string { - return []string{} -} - // formatEventSource formats EventSource as a comma separated string excluding Host when empty func formatEventSource(es api.EventSource) string { EventSourceString := []string{es.Component} diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/printers/internalversion/internalversion_test.go similarity index 99% rename from pkg/kubectl/resource_printer_test.go rename to pkg/printers/internalversion/internalversion_test.go index 984bcbd96a6..2c6d8fe318e 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/printers/internalversion/internalversion_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package internalversion import ( "bytes" diff --git a/pkg/kubectl/sorted_resource_name_list_test.go b/pkg/printers/internalversion/sorted_resource_name_list_test.go similarity index 100% rename from pkg/kubectl/sorted_resource_name_list_test.go rename to pkg/printers/internalversion/sorted_resource_name_list_test.go diff --git a/pkg/printers/json.go b/pkg/printers/json.go new file mode 100644 index 00000000000..19c734e5bf7 --- /dev/null +++ b/pkg/printers/json.go @@ -0,0 +1,101 @@ +/* +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 printers + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + + "k8s.io/apimachinery/pkg/runtime" + + "github.com/ghodss/yaml" +) + +// JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON. +type JSONPrinter struct { +} + +func (p *JSONPrinter) AfterPrint(w io.Writer, res string) error { + return nil +} + +// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer. +func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error { + switch obj := obj.(type) { + case *runtime.Unknown: + var buf bytes.Buffer + err := json.Indent(&buf, obj.Raw, "", " ") + if err != nil { + return err + } + buf.WriteRune('\n') + _, err = buf.WriteTo(w) + return err + } + + data, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + data = append(data, '\n') + _, err = w.Write(data) + return err +} + +// TODO: implement HandledResources() +func (p *JSONPrinter) HandledResources() []string { + return []string{} +} + +// YAMLPrinter is an implementation of ResourcePrinter which outputs an object as YAML. +// The input object is assumed to be in the internal version of an API and is converted +// to the given version first. +type YAMLPrinter struct { + version string + converter runtime.ObjectConvertor +} + +func (p *YAMLPrinter) AfterPrint(w io.Writer, res string) error { + return nil +} + +// PrintObj prints the data as YAML. +func (p *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error { + switch obj := obj.(type) { + case *runtime.Unknown: + data, err := yaml.JSONToYAML(obj.Raw) + if err != nil { + return err + } + _, err = w.Write(data) + return err + } + + output, err := yaml.Marshal(obj) + if err != nil { + return err + } + _, err = fmt.Fprint(w, string(output)) + return err +} + +// TODO: implement HandledResources() +func (p *YAMLPrinter) HandledResources() []string { + return []string{} +} diff --git a/pkg/printers/jsonpath.go b/pkg/printers/jsonpath.go new file mode 100644 index 00000000000..404648a7757 --- /dev/null +++ b/pkg/printers/jsonpath.go @@ -0,0 +1,157 @@ +/* +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 printers + +import ( + "encoding/json" + "fmt" + "io" + "reflect" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/util/jsonpath" +) + +// exists returns true if it would be possible to call the index function +// with these arguments. +// +// TODO: how to document this for users? +// +// index returns the result of indexing its first argument by the following +// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each +// indexed item must be a map, slice, or array. +func exists(item interface{}, indices ...interface{}) bool { + v := reflect.ValueOf(item) + for _, i := range indices { + index := reflect.ValueOf(i) + var isNil bool + if v, isNil = indirect(v); isNil { + return false + } + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.String: + var x int64 + switch index.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x = index.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + x = int64(index.Uint()) + default: + return false + } + if x < 0 || x >= int64(v.Len()) { + return false + } + v = v.Index(int(x)) + case reflect.Map: + if !index.IsValid() { + index = reflect.Zero(v.Type().Key()) + } + if !index.Type().AssignableTo(v.Type().Key()) { + return false + } + if x := v.MapIndex(index); x.IsValid() { + v = x + } else { + v = reflect.Zero(v.Type().Elem()) + } + default: + return false + } + } + if _, isNil := indirect(v); isNil { + return false + } + return true +} + +// stolen from text/template +// indirect returns the item at the end of indirection, and a bool to indicate if it's nil. +// We indirect through pointers and empty interfaces (only) because +// non-empty interfaces have methods we might need. +func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { + for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() { + if v.IsNil() { + return v, true + } + if v.Kind() == reflect.Interface && v.NumMethod() > 0 { + break + } + } + return v, false +} + +// JSONPathPrinter is an implementation of ResourcePrinter which formats data with jsonpath expression. +type JSONPathPrinter struct { + rawTemplate string + *jsonpath.JSONPath +} + +func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) { + j := jsonpath.New("out") + if err := j.Parse(tmpl); err != nil { + return nil, err + } + return &JSONPathPrinter{tmpl, j}, nil +} + +func (j *JSONPathPrinter) AfterPrint(w io.Writer, res string) error { + return nil +} + +// PrintObj formats the obj with the JSONPath Template. +func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error { + var queryObj interface{} = obj + if meta.IsListType(obj) { + data, err := json.Marshal(obj) + if err != nil { + return err + } + queryObj = map[string]interface{}{} + if err := json.Unmarshal(data, &queryObj); err != nil { + return err + } + } + + if unknown, ok := obj.(*runtime.Unknown); ok { + data, err := json.Marshal(unknown) + if err != nil { + return err + } + queryObj = map[string]interface{}{} + if err := json.Unmarshal(data, &queryObj); err != nil { + return err + } + } + if unstructured, ok := obj.(runtime.Unstructured); ok { + queryObj = unstructured.UnstructuredContent() + } + + if err := j.JSONPath.Execute(w, queryObj); err != nil { + fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err) + fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", j.rawTemplate) + fmt.Fprintf(w, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n", queryObj) + return fmt.Errorf("error executing jsonpath %q: %v\n", j.rawTemplate, err) + } + return nil +} + +// TODO: implement HandledResources() +func (p *JSONPathPrinter) HandledResources() []string { + return []string{} +} diff --git a/pkg/printers/name.go b/pkg/printers/name.go new file mode 100644 index 00000000000..192eed4d941 --- /dev/null +++ b/pkg/printers/name.go @@ -0,0 +1,89 @@ +/* +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 printers + +import ( + "fmt" + "io" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + utilerrors "k8s.io/apimachinery/pkg/util/errors" +) + +// NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object. +type NamePrinter struct { + Decoder runtime.Decoder + Typer runtime.ObjectTyper +} + +func (p *NamePrinter) AfterPrint(w io.Writer, res string) error { + return nil +} + +// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object +// and print "resource/name" pair. If the object is a List, print all items in it. +func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { + if meta.IsListType(obj) { + items, err := meta.ExtractList(obj) + if err != nil { + return err + } + if errs := runtime.DecodeList(items, p.Decoder, unstructured.UnstructuredJSONScheme); len(errs) > 0 { + return utilerrors.NewAggregate(errs) + } + for _, obj := range items { + if err := p.PrintObj(obj, w); err != nil { + return err + } + } + return nil + } + + name := "" + if acc, err := meta.Accessor(obj); err == nil { + if n := acc.GetName(); len(n) > 0 { + name = n + } + } + + if kind := obj.GetObjectKind().GroupVersionKind(); len(kind.Kind) == 0 { + // this is the old code. It's unnecessary on decoded external objects, but on internal objects + // you may have to do it. Tests are definitely calling it with internals and I'm not sure who else + // is + if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil { + // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper + _, resource := meta.KindToResource(gvks[0]) + fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) + } else { + fmt.Fprintf(w, "/%s\n", name) + } + + } else { + // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper + _, resource := meta.KindToResource(kind) + fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) + } + + return nil +} + +// TODO: implement HandledResources() +func (p *NamePrinter) HandledResources() []string { + return []string{} +} diff --git a/pkg/printers/printers.go b/pkg/printers/printers.go new file mode 100644 index 00000000000..6272ebc4574 --- /dev/null +++ b/pkg/printers/printers.go @@ -0,0 +1,116 @@ +/* +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 printers + +import ( + "fmt" + "io/ioutil" + "os" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" +) + +// GetStandardPrinter takes a format type, an optional format argument. It will return true +// if the format is generic (untyped), otherwise it will return false. The printer +// is agnostic to schema versions, so you must send arguments to PrintObj in the +// version you wish them to be shown using a VersionedPrinter (typically when +// generic is true). +func GetStandardPrinter(format, formatArgument string, noHeaders, allowMissingTemplateKeys bool, mapper meta.RESTMapper, typer runtime.ObjectTyper, decoders []runtime.Decoder) (ResourcePrinter, bool, error) { + var printer ResourcePrinter + switch format { + case "json": + printer = &JSONPrinter{} + case "yaml": + printer = &YAMLPrinter{} + case "name": + printer = &NamePrinter{ + Typer: typer, + Decoders: decoders, + Mapper: mapper, + } + case "template", "go-template": + if len(formatArgument) == 0 { + return nil, false, fmt.Errorf("template format specified but no template given") + } + templatePrinter, err := NewTemplatePrinter([]byte(formatArgument)) + if err != nil { + return nil, false, fmt.Errorf("error parsing template %s, %v\n", formatArgument, err) + } + templatePrinter.AllowMissingKeys(allowMissingTemplateKeys) + printer = templatePrinter + case "templatefile", "go-template-file": + if len(formatArgument) == 0 { + return nil, false, fmt.Errorf("templatefile format specified but no template file given") + } + data, err := ioutil.ReadFile(formatArgument) + if err != nil { + return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err) + } + templatePrinter, err := NewTemplatePrinter(data) + if err != nil { + return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err) + } + templatePrinter.AllowMissingKeys(allowMissingTemplateKeys) + printer = templatePrinter + case "jsonpath": + if len(formatArgument) == 0 { + return nil, false, fmt.Errorf("jsonpath template format specified but no template given") + } + jsonpathPrinter, err := NewJSONPathPrinter(formatArgument) + if err != nil { + return nil, false, fmt.Errorf("error parsing jsonpath %s, %v\n", formatArgument, err) + } + jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys) + printer = jsonpathPrinter + case "jsonpath-file": + if len(formatArgument) == 0 { + return nil, false, fmt.Errorf("jsonpath file format specified but no template file file given") + } + data, err := ioutil.ReadFile(formatArgument) + if err != nil { + return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err) + } + jsonpathPrinter, err := NewJSONPathPrinter(string(data)) + if err != nil { + return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err) + } + jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys) + printer = jsonpathPrinter + case "custom-columns": + var err error + if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, decoders[0], noHeaders); err != nil { + return nil, false, err + } + case "custom-columns-file": + file, err := os.Open(formatArgument) + if err != nil { + return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err) + } + defer file.Close() + if printer, err = NewCustomColumnsPrinterFromTemplate(file, decoders[0]); err != nil { + return nil, false, err + } + case "wide": + fallthrough + case "": + return nil, false, nil + default: + return nil, false, fmt.Errorf("output format %q not recognized", format) + } + return printer, true, nil +} diff --git a/pkg/printers/tabwriter.go b/pkg/printers/tabwriter.go new file mode 100644 index 00000000000..eb272f5038c --- /dev/null +++ b/pkg/printers/tabwriter.go @@ -0,0 +1,36 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package printers + +import ( + "io" + "text/tabwriter" +) + +const ( + tabwriterMinWidth = 10 + tabwriterWidth = 4 + tabwriterPadding = 3 + tabwriterPadChar = ' ' + tabwriterFlags = 0 + loadBalancerWidth = 16 +) + +// GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text. +func GetNewTabWriter(output io.Writer) *tabwriter.Writer { + return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags) +} diff --git a/pkg/printers/template.go b/pkg/printers/template.go new file mode 100644 index 00000000000..9dbc33ce8fc --- /dev/null +++ b/pkg/printers/template.go @@ -0,0 +1,110 @@ +/* +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 printers + +import ( + "encoding/json" + "fmt" + "io" + "text/template" + + "k8s.io/apimachinery/pkg/runtime" +) + +// TemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template. +type TemplatePrinter struct { + rawTemplate string + template *template.Template +} + +func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) { + t, err := template.New("output"). + Funcs(template.FuncMap{"exists": exists}). + Parse(string(tmpl)) + if err != nil { + return nil, err + } + return &TemplatePrinter{ + rawTemplate: string(tmpl), + template: t, + }, nil +} + +// AllowMissingKeys tells the template engine if missing keys are allowed. +func (p *TemplatePrinter) AllowMissingKeys(allow bool) { + if allow { + p.template.Option("missingkey=default") + } else { + p.template.Option("missingkey=error") + } +} + +func (p *TemplatePrinter) AfterPrint(w io.Writer, res string) error { + return nil +} + +// PrintObj formats the obj with the Go Template. +func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error { + var data []byte + var err error + data, err = json.Marshal(obj) + if err != nil { + return err + } + + out := map[string]interface{}{} + if err := json.Unmarshal(data, &out); err != nil { + return err + } + if err = p.safeExecute(w, out); err != nil { + // It is way easier to debug this stuff when it shows up in + // stdout instead of just stdin. So in addition to returning + // a nice error, also print useful stuff with the writer. + fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err) + fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", p.rawTemplate) + fmt.Fprintf(w, "\traw data was:\n\t\t%v\n", string(data)) + fmt.Fprintf(w, "\tobject given to template engine was:\n\t\t%+v\n\n", out) + return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err) + } + return nil +} + +// TODO: implement HandledResources() +func (p *TemplatePrinter) HandledResources() []string { + return []string{} +} + +// safeExecute tries to execute the template, but catches panics and returns an error +// should the template engine panic. +func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error { + var panicErr error + // Sorry for the double anonymous function. There's probably a clever way + // to do this that has the defer'd func setting the value to be returned, but + // that would be even less obvious. + retErr := func() error { + defer func() { + if x := recover(); x != nil { + panicErr = fmt.Errorf("caught panic: %+v", x) + } + }() + return p.template.Execute(w, obj) + }() + if panicErr != nil { + return panicErr + } + return retErr +} diff --git a/pkg/printers/versioned.go b/pkg/printers/versioned.go new file mode 100644 index 00000000000..e8330992c30 --- /dev/null +++ b/pkg/printers/versioned.go @@ -0,0 +1,63 @@ +/* +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 printers + +import ( + "fmt" + "io" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// VersionedPrinter takes runtime objects and ensures they are converted to a given API version +// prior to being passed to a nested printer. +type VersionedPrinter struct { + printer ResourcePrinter + converter runtime.ObjectConvertor + versions []schema.GroupVersion +} + +// NewVersionedPrinter wraps a printer to convert objects to a known API version prior to printing. +func NewVersionedPrinter(printer ResourcePrinter, converter runtime.ObjectConvertor, versions ...schema.GroupVersion) ResourcePrinter { + return &VersionedPrinter{ + printer: printer, + converter: converter, + versions: versions, + } +} + +func (p *VersionedPrinter) AfterPrint(w io.Writer, res string) error { + return nil +} + +// PrintObj implements ResourcePrinter +func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error { + if len(p.versions) == 0 { + return fmt.Errorf("no version specified, object cannot be converted") + } + converted, err := p.converter.ConvertToVersion(obj, schema.GroupVersions(p.versions)) + if err != nil { + return err + } + return p.printer.PrintObj(converted, w) +} + +// TODO: implement HandledResources() +func (p *VersionedPrinter) HandledResources() []string { + return []string{} +} From 3704ceffd2f568673e6dbadf18e184eeebeea90c Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 19 Feb 2017 17:37:24 -0500 Subject: [PATCH 2/8] Refactor printers to separate typed printers from generic code --- pkg/api/testing/compat/BUILD | 2 +- .../testing/compat/compatibility_tester.go | 4 +- pkg/api/v1/BUILD | 1 + pkg/api/v1/backward_compatibility_test.go | 2 + pkg/printers/common.go | 41 ++ pkg/printers/customcolumn.go | 12 +- pkg/printers/customcolumn_test.go | 38 +- pkg/printers/humanreadable.go | 192 +++++++- pkg/printers/interface.go | 34 ++ pkg/printers/internalversion/BUILD | 113 +++++ pkg/printers/internalversion/describe.go | 218 ++++++--- pkg/printers/internalversion/describe_test.go | 31 +- .../{internalversion.go => printers.go} | 431 ++++-------------- ...ternalversion_test.go => printers_test.go} | 136 +++--- .../sorted_resource_name_list_test.go | 2 +- pkg/printers/tabwriter.go | 1 - 16 files changed, 729 insertions(+), 529 deletions(-) create mode 100644 pkg/printers/common.go create mode 100644 pkg/printers/internalversion/BUILD rename pkg/printers/internalversion/{internalversion.go => printers.go} (81%) rename pkg/printers/internalversion/{internalversion_test.go => printers_test.go} (92%) diff --git a/pkg/api/testing/compat/BUILD b/pkg/api/testing/compat/BUILD index 63031bfb902..ad03b1d53a0 100644 --- a/pkg/api/testing/compat/BUILD +++ b/pkg/api/testing/compat/BUILD @@ -13,7 +13,7 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", - "//pkg/kubectl:go_default_library", + "//pkg/printers:go_default_library", "//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime/schema", "//vendor:k8s.io/apimachinery/pkg/util/validation/field", diff --git a/pkg/api/testing/compat/compatibility_tester.go b/pkg/api/testing/compat/compatibility_tester.go index 9cc9a7b24b3..953eda803e4 100644 --- a/pkg/api/testing/compat/compatibility_tester.go +++ b/pkg/api/testing/compat/compatibility_tester.go @@ -30,7 +30,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/printers" ) // Based on: https://github.com/openshift/origin/blob/master/pkg/api/compatibility_test.go @@ -94,7 +94,7 @@ func TestCompatibility( } if hasError { - printer := new(kubectl.JSONPrinter) + printer := &printers.JSONPrinter{} printer.PrintObj(obj, os.Stdout) t.Logf("2: Encoded value: %#v", string(output)) } diff --git a/pkg/api/v1/BUILD b/pkg/api/v1/BUILD index 29061a45b81..69f5da9f4f1 100644 --- a/pkg/api/v1/BUILD +++ b/pkg/api/v1/BUILD @@ -63,6 +63,7 @@ go_test( tags = ["automanaged"], deps = [ "//pkg/api:go_default_library", + "//pkg/api/install:go_default_library", "//pkg/api/testing/compat:go_default_library", "//pkg/api/v1:go_default_library", "//pkg/api/validation:go_default_library", diff --git a/pkg/api/v1/backward_compatibility_test.go b/pkg/api/v1/backward_compatibility_test.go index b55f4f4959b..b04611d1ab1 100644 --- a/pkg/api/v1/backward_compatibility_test.go +++ b/pkg/api/v1/backward_compatibility_test.go @@ -25,6 +25,8 @@ import ( "k8s.io/kubernetes/pkg/api/testing/compat" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/validation" + + _ "k8s.io/kubernetes/pkg/api/install" ) func TestCompatibility_v1_PodSecurityContext(t *testing.T) { diff --git a/pkg/printers/common.go b/pkg/printers/common.go new file mode 100644 index 00000000000..cacd8de78c3 --- /dev/null +++ b/pkg/printers/common.go @@ -0,0 +1,41 @@ +/* +Copyright 2014 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 printers + +import ( + "fmt" + "time" +) + +func ShortHumanDuration(d time.Duration) string { + // Allow deviation no more than 2 seconds(excluded) to tolerate machine time + // inconsistence, it can be considered as almost now. + if seconds := int(d.Seconds()); seconds < -1 { + return fmt.Sprintf("") + } else if seconds < 0 { + return fmt.Sprintf("0s") + } else if seconds < 60 { + return fmt.Sprintf("%ds", seconds) + } else if minutes := int(d.Minutes()); minutes < 60 { + return fmt.Sprintf("%dm", minutes) + } else if hours := int(d.Hours()); hours < 24 { + return fmt.Sprintf("%dh", hours) + } else if hours < 24*364 { + return fmt.Sprintf("%dd", hours/24) + } + return fmt.Sprintf("%dy", int(d.Hours()/24/365)) +} diff --git a/pkg/printers/customcolumn.go b/pkg/printers/customcolumn.go index 114a375f565..ac533b2cdd9 100644 --- a/pkg/printers/customcolumn.go +++ b/pkg/printers/customcolumn.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package printers import ( "bufio" @@ -41,14 +41,14 @@ const ( var jsonRegexp = regexp.MustCompile("^\\{\\.?([^{}]+)\\}$|^\\.?([^{}]+)$") -// MassageJSONPath attempts to be flexible with JSONPath expressions, it accepts: +// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts: // * metadata.name (no leading '.' or curly brances '{...}' // * {metadata.name} (no leading '.') // * .metadata.name (no curly braces '{...}') // * {.metadata.name} (complete expression) -// And transforms them all into a valid jsonpat expression: +// And transforms them all into a valid jsonpath expression: // {.metadata.name} -func massageJSONPath(pathExpression string) (string, error) { +func RelaxedJSONPathExpression(pathExpression string) (string, error) { if len(pathExpression) == 0 { return pathExpression, nil } @@ -84,7 +84,7 @@ func NewCustomColumnsPrinterFromSpec(spec string, decoder runtime.Decoder, noHea if len(colSpec) != 2 { return nil, fmt.Errorf("unexpected custom-columns spec: %s, expected
:", parts[ix]) } - spec, err := massageJSONPath(colSpec[1]) + spec, err := RelaxedJSONPathExpression(colSpec[1]) if err != nil { return nil, err } @@ -126,7 +126,7 @@ func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runti columns := make([]Column, len(headers)) for ix := range headers { - spec, err := massageJSONPath(specs[ix]) + spec, err := RelaxedJSONPathExpression(specs[ix]) if err != nil { return nil, err } diff --git a/pkg/printers/customcolumn_test.go b/pkg/printers/customcolumn_test.go index 991a5c42f19..dc1e72101c4 100644 --- a/pkg/printers/customcolumn_test.go +++ b/pkg/printers/customcolumn_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package printers_test import ( "bytes" @@ -26,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/printers" ) func TestMassageJSONPath(t *testing.T) { @@ -45,7 +46,7 @@ func TestMassageJSONPath(t *testing.T) { {input: "{{foo.bar}", expectErr: true}, } for _, test := range tests { - output, err := massageJSONPath(test.input) + output, err := printers.RelaxedJSONPathExpression(test.input) if err != nil && !test.expectErr { t.Errorf("unexpected error: %v", err) continue @@ -65,7 +66,7 @@ func TestMassageJSONPath(t *testing.T) { func TestNewColumnPrinterFromSpec(t *testing.T) { tests := []struct { spec string - expectedColumns []Column + expectedColumns []printers.Column expectErr bool name string noHeaders bool @@ -93,7 +94,7 @@ func TestNewColumnPrinterFromSpec(t *testing.T) { { spec: "NAME:metadata.name,API_VERSION:apiVersion", name: "ok", - expectedColumns: []Column{ + expectedColumns: []printers.Column{ { Header: "NAME", FieldSpec: "{.metadata.name}", @@ -111,7 +112,7 @@ func TestNewColumnPrinterFromSpec(t *testing.T) { }, } for _, test := range tests { - printer, err := NewCustomColumnsPrinterFromSpec(test.spec, api.Codecs.UniversalDecoder(), test.noHeaders) + printer, err := printers.NewCustomColumnsPrinterFromSpec(test.spec, api.Codecs.UniversalDecoder(), test.noHeaders) if test.expectErr { if err == nil { t.Errorf("[%s] unexpected non-error", test.name) @@ -141,6 +142,15 @@ func TestNewColumnPrinterFromSpec(t *testing.T) { } } +func contains(arr []string, s string) bool { + for i := range arr { + if arr[i] == s { + return true + } + } + return false +} + const exampleTemplateOne = `NAME API_VERSION {metadata.name} {apiVersion}` @@ -150,7 +160,7 @@ const exampleTemplateTwo = `NAME API_VERSION func TestNewColumnPrinterFromTemplate(t *testing.T) { tests := []struct { spec string - expectedColumns []Column + expectedColumns []printers.Column expectErr bool name string }{ @@ -177,7 +187,7 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) { { spec: exampleTemplateOne, name: "ok", - expectedColumns: []Column{ + expectedColumns: []printers.Column{ { Header: "NAME", FieldSpec: "{.metadata.name}", @@ -191,7 +201,7 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) { { spec: exampleTemplateTwo, name: "ok-2", - expectedColumns: []Column{ + expectedColumns: []printers.Column{ { Header: "NAME", FieldSpec: "{.metadata.name}", @@ -205,7 +215,7 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) { } for _, test := range tests { reader := bytes.NewBufferString(test.spec) - printer, err := NewCustomColumnsPrinterFromTemplate(reader, api.Codecs.UniversalDecoder()) + printer, err := printers.NewCustomColumnsPrinterFromTemplate(reader, api.Codecs.UniversalDecoder()) if test.expectErr { if err == nil { t.Errorf("[%s] unexpected non-error", test.name) @@ -226,12 +236,12 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) { func TestColumnPrint(t *testing.T) { tests := []struct { - columns []Column + columns []printers.Column obj runtime.Object expectedOutput string }{ { - columns: []Column{ + columns: []printers.Column{ { Header: "NAME", FieldSpec: "{.metadata.name}", @@ -243,7 +253,7 @@ foo `, }, { - columns: []Column{ + columns: []printers.Column{ { Header: "NAME", FieldSpec: "{.metadata.name}", @@ -261,7 +271,7 @@ bar `, }, { - columns: []Column{ + columns: []printers.Column{ { Header: "NAME", FieldSpec: "{.metadata.name}", @@ -279,7 +289,7 @@ foo baz } for _, test := range tests { - printer := &CustomColumnsPrinter{ + printer := &printers.CustomColumnsPrinter{ Columns: test.columns, Decoder: api.Codecs.UniversalDecoder(), } diff --git a/pkg/printers/humanreadable.go b/pkg/printers/humanreadable.go index 86dd62beb9e..12f579aa703 100644 --- a/pkg/printers/humanreadable.go +++ b/pkg/printers/humanreadable.go @@ -17,14 +17,23 @@ limitations under the License. package printers import ( + "bytes" "fmt" "io" "reflect" "strings" + "text/tabwriter" "github.com/golang/glog" + + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ) +var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too. + type handlerEntry struct { columns []string columnsWithWide []string @@ -49,20 +58,9 @@ func NewHumanReadablePrinter(options PrintOptions) *HumanReadablePrinter { handlerMap: make(map[reflect.Type]*handlerEntry), options: options, } - printer.addDefaultHandlers() return printer } -// formatResourceName receives a resource kind, name, and boolean specifying -// whether or not to update the current name to "kind/name" -func formatResourceName(kind, name string, withKind bool) string { - if !withKind || kind == "" { - return name - } - - return kind + "/" + name -} - // GetResourceKind returns the type currently set for a resource func (h *HumanReadablePrinter) GetResourceKind() string { return h.options.Kind @@ -157,3 +155,175 @@ func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) er } return nil } + +// 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 { + // 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 !found { + w = GetNewTabWriter(output) + defer w.Flush() + } + + // 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. + //obj, _ = DecodeUnknownObject(obj) + + t := reflect.TypeOf(obj) + if handler := h.handlerMap[t]; handler != nil { + if !h.options.NoHeaders && t != h.lastType { + headers := handler.columns + if h.options.Wide { + headers = append(headers, handler.columnsWithWide...) + } + headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...) + // LABELS is always the last column. + headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...) + if h.options.WithNamespace { + headers = append(withNamespacePrefixColumns, headers...) + } + h.printHeader(headers, w) + h.lastType = t + } + args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.options)} + resultValue := handler.printFunc.Call(args)[0] + if resultValue.IsNil() { + return nil + } + return resultValue.Interface().(error) + } + + if _, err := meta.Accessor(obj); err == nil { + if !h.options.NoHeaders && t != h.lastType { + headers := []string{"NAME", "KIND"} + headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...) + // LABELS is always the last column. + headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...) + if h.options.WithNamespace { + headers = append(withNamespacePrefixColumns, headers...) + } + h.printHeader(headers, w) + h.lastType = t + } + + // we don't recognize this type, but we can still attempt to print some reasonable information about. + unstructured, ok := obj.(runtime.Unstructured) + if !ok { + return fmt.Errorf("error: unknown type %#v", obj) + } + // if the error isn't nil, report the "I don't recognize this" error + if err := printUnstructured(unstructured, w, h.options); err != nil { + return err + } + return nil + } + + // we failed all reasonable printing efforts, report failure + return fmt.Errorf("error: unknown type %#v", obj) +} + +// 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, options PrintOptions) error { + metadata, err := meta.Accessor(unstructured) + if err != nil { + return err + } + + if options.WithNamespace { + if _, err := fmt.Fprintf(w, "%s\t", metadata.GetNamespace()); err != nil { + return err + } + } + + content := unstructured.UnstructuredContent() + kind := "" + if objKind, ok := content["kind"]; ok { + if str, ok := objKind.(string); ok { + kind = str + } + } + if objAPIVersion, ok := content["apiVersion"]; ok { + if str, ok := objAPIVersion.(string); ok { + version, err := schema.ParseGroupVersion(str) + if err != nil { + return err + } + kind = kind + "." + version.Version + "." + version.Group + } + } + name := formatResourceName(options.Kind, metadata.GetName(), options.WithKind) + + if _, err := fmt.Fprintf(w, "%s\t%s", name, kind); err != nil { + return err + } + if _, err := fmt.Fprint(w, appendLabels(metadata.GetLabels(), options.ColumnLabels)); err != nil { + return err + } + if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, metadata.GetLabels())); err != nil { + return err + } + + return nil +} + +func formatLabelHeaders(columnLabels []string) []string { + formHead := make([]string, len(columnLabels)) + for i, l := range columnLabels { + p := strings.Split(l, "/") + formHead[i] = strings.ToUpper((p[len(p)-1])) + } + return formHead +} + +// headers for --show-labels=true +func formatShowLabelsHeader(showLabels bool, t reflect.Type) []string { + if showLabels { + // TODO: this is all sorts of hack, fix + if t.String() != "*api.ThirdPartyResource" && t.String() != "*api.ThirdPartyResourceList" { + return []string{"LABELS"} + } + } + return nil +} + +// formatResourceName receives a resource kind, name, and boolean specifying +// whether or not to update the current name to "kind/name" +// TODO: dedup this with printers/internalversions +func formatResourceName(kind, name string, withKind bool) string { + if !withKind || kind == "" { + return name + } + + return kind + "/" + name +} + +// TODO: dedup this with printers/internalversions +func appendLabels(itemLabels map[string]string, columnLabels []string) string { + var buffer bytes.Buffer + + for _, cl := range columnLabels { + buffer.WriteString(fmt.Sprint("\t")) + if il, ok := itemLabels[cl]; ok { + buffer.WriteString(fmt.Sprint(il)) + } else { + buffer.WriteString("") + } + } + + return buffer.String() +} + +// Append all labels to a single column. We need this even when show-labels flag* is +// false, since this adds newline delimiter to the end of each row. +// TODO: dedup this with printers/internalversions +func appendAllLabels(showLabels bool, itemLabels map[string]string) string { + var buffer bytes.Buffer + + if showLabels { + buffer.WriteString(fmt.Sprint("\t")) + buffer.WriteString(labels.FormatLabels(itemLabels)) + } + buffer.WriteString("\n") + + return buffer.String() +} diff --git a/pkg/printers/interface.go b/pkg/printers/interface.go index 0b301565422..aaadecc62b7 100644 --- a/pkg/printers/interface.go +++ b/pkg/printers/interface.go @@ -17,6 +17,7 @@ limitations under the License. package printers import ( + "fmt" "io" "k8s.io/apimachinery/pkg/runtime" @@ -60,3 +61,36 @@ type PrintOptions struct { Kind string ColumnLabels []string } + +// Describer generates output for the named resource or an error +// if the output could not be generated. Implementers typically +// abstract the retrieval of the named object from a remote server. +type Describer interface { + Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error) +} + +// DescriberSettings holds display configuration for each object +// describer to control what is printed. +type DescriberSettings struct { + ShowEvents bool +} + +// ObjectDescriber is an interface for displaying arbitrary objects with extra +// information. Use when an object is in hand (on disk, or already retrieved). +// Implementers may ignore the additional information passed on extra, or use it +// by default. ObjectDescribers may return ErrNoDescriber if no suitable describer +// is found. +type ObjectDescriber interface { + DescribeObject(object interface{}, extra ...interface{}) (output string, err error) +} + +// ErrNoDescriber is a structured error indicating the provided object or objects +// cannot be described. +type ErrNoDescriber struct { + Types []string +} + +// Error implements the error interface. +func (e ErrNoDescriber) Error() string { + return fmt.Sprintf("no describer has been defined for %v", e.Types) +} diff --git a/pkg/printers/internalversion/BUILD b/pkg/printers/internalversion/BUILD new file mode 100644 index 00000000000..d8c73ed6845 --- /dev/null +++ b/pkg/printers/internalversion/BUILD @@ -0,0 +1,113 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = [ + "describe_test.go", + "printers_test.go", + "sorted_resource_name_list_test.go", + ], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//federation/apis/federation:go_default_library", + "//federation/client/clientset_generated/federation_internalclientset/fake:go_default_library", + "//pkg/api:go_default_library", + "//pkg/api/testapi:go_default_library", + "//pkg/api/v1:go_default_library", + "//pkg/apis/autoscaling:go_default_library", + "//pkg/apis/batch:go_default_library", + "//pkg/apis/extensions:go_default_library", + "//pkg/apis/extensions/v1beta1:go_default_library", + "//pkg/apis/policy:go_default_library", + "//pkg/apis/storage:go_default_library", + "//pkg/client/clientset_generated/clientset/fake:go_default_library", + "//pkg/client/clientset_generated/internalclientset:go_default_library", + "//pkg/client/clientset_generated/internalclientset/fake:go_default_library", + "//pkg/kubectl/testing:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/util:go_default_library", + "//vendor:github.com/ghodss/yaml", + "//vendor:k8s.io/apimachinery/pkg/api/equality", + "//vendor:k8s.io/apimachinery/pkg/api/resource", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", + "//vendor:k8s.io/apimachinery/pkg/runtime", + "//vendor:k8s.io/apimachinery/pkg/runtime/schema", + "//vendor:k8s.io/apimachinery/pkg/runtime/serializer/yaml", + "//vendor:k8s.io/apimachinery/pkg/util/diff", + "//vendor:k8s.io/apimachinery/pkg/util/intstr", + "//vendor:k8s.io/apimachinery/pkg/util/sets", + ], +) + +go_library( + name = "go_default_library", + srcs = [ + "describe.go", + "printers.go", + ], + tags = ["automanaged"], + deps = [ + "//federation/apis/federation:go_default_library", + "//federation/client/clientset_generated/federation_internalclientset:go_default_library", + "//pkg/api:go_default_library", + "//pkg/api/annotations:go_default_library", + "//pkg/api/events:go_default_library", + "//pkg/api/v1:go_default_library", + "//pkg/apis/apps:go_default_library", + "//pkg/apis/autoscaling:go_default_library", + "//pkg/apis/batch:go_default_library", + "//pkg/apis/certificates:go_default_library", + "//pkg/apis/extensions:go_default_library", + "//pkg/apis/extensions/v1beta1:go_default_library", + "//pkg/apis/policy:go_default_library", + "//pkg/apis/rbac:go_default_library", + "//pkg/apis/storage:go_default_library", + "//pkg/apis/storage/util:go_default_library", + "//pkg/client/clientset_generated/clientset:go_default_library", + "//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library", + "//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library", + "//pkg/client/clientset_generated/internalclientset:go_default_library", + "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", + "//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library", + "//pkg/controller/deployment/util:go_default_library", + "//pkg/fieldpath:go_default_library", + "//pkg/kubelet/qos:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/util/node:go_default_library", + "//vendor:github.com/golang/glog", + "//vendor:k8s.io/apimachinery/pkg/api/errors", + "//vendor:k8s.io/apimachinery/pkg/api/meta", + "//vendor:k8s.io/apimachinery/pkg/api/resource", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + "//vendor:k8s.io/apimachinery/pkg/fields", + "//vendor:k8s.io/apimachinery/pkg/labels", + "//vendor:k8s.io/apimachinery/pkg/runtime/schema", + "//vendor:k8s.io/apimachinery/pkg/types", + "//vendor:k8s.io/apimachinery/pkg/util/intstr", + "//vendor:k8s.io/apimachinery/pkg/util/sets", + "//vendor:k8s.io/client-go/dynamic", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/printers/internalversion/describe.go b/pkg/printers/internalversion/describe.go index c6167a5aeae..f00b9f1add2 100644 --- a/pkg/printers/internalversion/describe.go +++ b/pkg/printers/internalversion/describe.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package internalversion import ( "bytes" @@ -26,8 +26,11 @@ import ( "reflect" "sort" "strings" + "text/tabwriter" "time" + "github.com/golang/glog" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/resource" @@ -42,7 +45,6 @@ import ( "k8s.io/kubernetes/federation/apis/federation" fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/annotations" "k8s.io/kubernetes/pkg/api/events" "k8s.io/kubernetes/pkg/apis/apps" @@ -55,48 +57,17 @@ import ( "k8s.io/kubernetes/pkg/apis/storage" storageutil "k8s.io/kubernetes/pkg/apis/storage/util" versionedclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" + coreclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1" + extensionsclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" "k8s.io/kubernetes/pkg/fieldpath" - - "github.com/golang/glog" + "k8s.io/kubernetes/pkg/kubelet/qos" + "k8s.io/kubernetes/pkg/printers" ) -// Describer generates output for the named resource or an error -// if the output could not be generated. Implementers typically -// abstract the retrieval of the named object from a remote server. -type Describer interface { - Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error) -} - -// DescriberSettings holds display configuration for each object -// describer to control what is printed. -type DescriberSettings struct { - ShowEvents bool -} - -// ObjectDescriber is an interface for displaying arbitrary objects with extra -// information. Use when an object is in hand (on disk, or already retrieved). -// Implementers may ignore the additional information passed on extra, or use it -// by default. ObjectDescribers may return ErrNoDescriber if no suitable describer -// is found. -type ObjectDescriber interface { - DescribeObject(object interface{}, extra ...interface{}) (output string, err error) -} - -// ErrNoDescriber is a structured error indicating the provided object or objects -// cannot be described. -type ErrNoDescriber struct { - Types []string -} - -// Error implements the error interface. -func (e ErrNoDescriber) Error() string { - return fmt.Sprintf("no describer has been defined for %v", e.Types) -} - // Each level has 2 spaces for PrefixWriter const ( LEVEL_0 = iota @@ -122,8 +93,8 @@ func (pw *PrefixWriter) WriteLine(a ...interface{}) { fmt.Fprintln(pw.out, a...) } -func describerMap(c clientset.Interface) map[schema.GroupKind]Describer { - m := map[schema.GroupKind]Describer{ +func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer { + m := map[schema.GroupKind]printers.Describer{ api.Kind("Pod"): &PodDescriber{c}, api.Kind("ReplicationController"): &ReplicationControllerDescriber{c}, api.Kind("Secret"): &SecretDescriber{c}, @@ -155,7 +126,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]Describer { return m } -// List of all resource types we can describe +// DescribableResources lists all resource types we can describe. func DescribableResources() []string { keys := make([]string, 0) @@ -166,16 +137,16 @@ func DescribableResources() []string { return keys } -// Describer returns the default describe functions for each of the standard +// DescriberFor returns the default describe functions for each of the standard // Kubernetes types. -func DescriberFor(kind schema.GroupKind, c clientset.Interface) (Describer, bool) { +func DescriberFor(kind schema.GroupKind, c clientset.Interface) (printers.Describer, bool) { f, ok := describerMap(c)[kind] return f, ok } // GenericDescriberFor returns a generic describer for the specified mapping // that uses only information available from runtime.Unstructured -func GenericDescriberFor(mapping *meta.RESTMapping, dynamic *dynamic.Client, events coreclient.EventsGetter) Describer { +func GenericDescriberFor(mapping *meta.RESTMapping, dynamic *dynamic.Client, events coreclient.EventsGetter) printers.Describer { return &genericDescriber{mapping, dynamic, events} } @@ -185,7 +156,7 @@ type genericDescriber struct { events coreclient.EventsGetter } -func (g *genericDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error) { +func (g *genericDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) { apiResource := &metav1.APIResource{ Name: g.mapping.Resource, Namespaced: g.mapping.Scope.Name() == meta.RESTScopeNameNamespace, @@ -214,7 +185,7 @@ func (g *genericDescriber) Describe(namespace, name string, describerSettings De } // DefaultObjectDescriber can describe the default Kubernetes objects. -var DefaultObjectDescriber ObjectDescriber +var DefaultObjectDescriber printers.ObjectDescriber func init() { d := &Describers{} @@ -239,7 +210,7 @@ type NamespaceDescriber struct { clientset.Interface } -func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { ns, err := d.Core().Namespaces().Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -411,7 +382,7 @@ type LimitRangeDescriber struct { clientset.Interface } -func (d *LimitRangeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *LimitRangeDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { lr := d.Core().LimitRanges(namespace) limitRange, err := lr.Get(name, metav1.GetOptions{}) @@ -438,7 +409,7 @@ type ResourceQuotaDescriber struct { clientset.Interface } -func (d *ResourceQuotaDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *ResourceQuotaDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { rq := d.Core().ResourceQuotas(namespace) resourceQuota, err := rq.Get(name, metav1.GetOptions{}) @@ -508,7 +479,7 @@ type PodDescriber struct { clientset.Interface } -func (d *PodDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *PodDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { pod, err := d.Core().Pods(namespace).Get(name, metav1.GetOptions{}) if err != nil { if describerSettings.ShowEvents { @@ -810,7 +781,7 @@ type PersistentVolumeDescriber struct { clientset.Interface } -func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Core().PersistentVolumes() pv, err := c.Get(name, metav1.GetOptions{}) @@ -881,7 +852,7 @@ type PersistentVolumeClaimDescriber struct { clientset.Interface } -func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Core().PersistentVolumeClaims(namespace) pvc, err := c.Get(name, metav1.GetOptions{}) @@ -1214,7 +1185,7 @@ type ReplicationControllerDescriber struct { clientset.Interface } -func (d *ReplicationControllerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *ReplicationControllerDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { rc := d.Core().ReplicationControllers(namespace) pc := d.Core().Pods(namespace) @@ -1286,7 +1257,7 @@ type ReplicaSetDescriber struct { clientset.Interface } -func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { rsc := d.Extensions().ReplicaSets(namespace) pc := d.Core().Pods(namespace) @@ -1338,7 +1309,7 @@ type JobDescriber struct { clientset.Interface } -func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *JobDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { job, err := d.Batch().Jobs(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -1387,7 +1358,7 @@ type CronJobDescriber struct { clientset.Interface } -func (d *CronJobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *CronJobDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { scheduledJob, err := d.Batch().CronJobs(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -1474,7 +1445,7 @@ type DaemonSetDescriber struct { clientset.Interface } -func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { dc := d.Extensions().DaemonSets(namespace) pc := d.Core().Pods(namespace) @@ -1529,7 +1500,7 @@ type SecretDescriber struct { clientset.Interface } -func (d *SecretDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *SecretDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Core().Secrets(namespace) secret, err := c.Get(name, metav1.GetOptions{}) @@ -1569,7 +1540,7 @@ type IngressDescriber struct { clientset.Interface } -func (i *IngressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (i *IngressDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := i.Extensions().Ingresses(namespace) ing, err := c.Get(name, metav1.GetOptions{}) if err != nil { @@ -1598,7 +1569,7 @@ func (i *IngressDescriber) describeBackend(ns string, backend *extensions.Ingres return formatEndpoints(endpoints, sets.NewString(spName)) } -func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSettings DescriberSettings) (string, error) { +func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSettings printers.DescriberSettings) (string, error) { return tabbedString(func(out io.Writer) error { w := &PrefixWriter{out} w.Write(LEVEL_0, "Name:\t%v\n", ing.Name) @@ -1682,7 +1653,7 @@ type ServiceDescriber struct { clientset.Interface } -func (d *ServiceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *ServiceDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Core().Services(namespace) service, err := c.Get(name, metav1.GetOptions{}) @@ -1762,7 +1733,7 @@ type EndpointsDescriber struct { clientset.Interface } -func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Core().Endpoints(namespace) ep, err := c.Get(name, metav1.GetOptions{}) @@ -1836,7 +1807,7 @@ type ServiceAccountDescriber struct { clientset.Interface } -func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Core().ServiceAccounts(namespace) serviceAccount, err := c.Get(name, metav1.GetOptions{}) @@ -1944,7 +1915,7 @@ type NodeDescriber struct { clientset.Interface } -func (d *NodeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *NodeDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { mc := d.Core().Nodes() node, err := mc.Get(name, metav1.GetOptions{}) if err != nil { @@ -2065,7 +2036,7 @@ type StatefulSetDescriber struct { client clientset.Interface } -func (p *StatefulSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (p *StatefulSetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { ps, err := p.client.Apps().StatefulSets(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -2108,7 +2079,7 @@ type CertificateSigningRequestDescriber struct { client clientset.Interface } -func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { csr, err := p.client.Certificates().CertificateSigningRequests().Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -2178,7 +2149,7 @@ type HorizontalPodAutoscalerDescriber struct { client clientset.Interface } -func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { hpa, err := d.client.Autoscaling().HorizontalPodAutoscalers(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -2369,7 +2340,7 @@ type DeploymentDescriber struct { versionedClient versionedclientset.Interface } -func (dd *DeploymentDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (dd *DeploymentDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { d, err := dd.versionedClient.Extensions().Deployments(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -2504,7 +2475,7 @@ type ConfigMapDescriber struct { clientset.Interface } -func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Core().ConfigMaps(namespace) configMap, err := c.Get(name, metav1.GetOptions{}) @@ -2537,7 +2508,7 @@ type ClusterDescriber struct { fedclientset.Interface } -func (d *ClusterDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *ClusterDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { cluster, err := d.Federation().Clusters().Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -2579,7 +2550,7 @@ type NetworkPolicyDescriber struct { clientset.Interface } -func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { c := d.Extensions().NetworkPolicies(namespace) networkPolicy, err := c.Get(name, metav1.GetOptions{}) @@ -2606,7 +2577,7 @@ type StorageClassDescriber struct { clientset.Interface } -func (s *StorageClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (s *StorageClassDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { sc, err := s.Storage().StorageClasses().Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -2635,7 +2606,7 @@ type PodDisruptionBudgetDescriber struct { clientset.Interface } -func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { +func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) { pdb, err := p.Policy().PodDisruptionBudgets(namespace).Get(name, metav1.GetOptions{}) if err != nil { return "", err @@ -2673,7 +2644,7 @@ func newErrNoDescriber(types ...reflect.Type) error { for _, t := range types { names = append(names, t.String()) } - return ErrNoDescriber{Types: names} + return printers.ErrNoDescriber{Types: names} } // Describers implements ObjectDescriber against functions registered via Add. Those functions can @@ -2720,7 +2691,7 @@ func (d *Describers) DescribeObject(exact interface{}, extra ...interface{}) (st return "", newErrNoDescriber(append([]reflect.Type{exactType}, types...)...) } -// Add adds one or more describer functions to the Describer. The passed function must +// Add adds one or more describer functions to the printers.Describer. The passed function must // match the signature: // // func(...) (string, error) @@ -2928,3 +2899,102 @@ func printTolerationsMultilineWithIndent(w *PrefixWriter, initialIndent, title, } } } + +func tabbedString(f func(io.Writer) error) (string, error) { + out := new(tabwriter.Writer) + buf := &bytes.Buffer{} + out.Init(buf, 0, 8, 1, '\t', 0) + + err := f(out) + if err != nil { + return "", err + } + + out.Flush() + str := string(buf.String()) + return str, nil +} + +type SortableResourceNames []api.ResourceName + +func (list SortableResourceNames) Len() int { + return len(list) +} + +func (list SortableResourceNames) Swap(i, j int) { + list[i], list[j] = list[j], list[i] +} + +func (list SortableResourceNames) Less(i, j int) bool { + return list[i] < list[j] +} + +// SortedResourceNames returns the sorted resource names of a resource list. +func SortedResourceNames(list api.ResourceList) []api.ResourceName { + resources := make([]api.ResourceName, 0, len(list)) + for res := range list { + resources = append(resources, res) + } + sort.Sort(SortableResourceNames(resources)) + return resources +} + +type SortableResourceQuotas []api.ResourceQuota + +func (list SortableResourceQuotas) Len() int { + return len(list) +} + +func (list SortableResourceQuotas) Swap(i, j int) { + list[i], list[j] = list[j], list[i] +} + +func (list SortableResourceQuotas) Less(i, j int) bool { + return list[i].Name < list[j].Name +} + +type SortableVolumeMounts []api.VolumeMount + +func (list SortableVolumeMounts) Len() int { + return len(list) +} + +func (list SortableVolumeMounts) Swap(i, j int) { + list[i], list[j] = list[j], list[i] +} + +func (list SortableVolumeMounts) Less(i, j int) bool { + return list[i].MountPath < list[j].MountPath +} + +// SortedQoSResourceNames returns the sorted resource names of a QoS list. +func SortedQoSResourceNames(list qos.QOSList) []api.ResourceName { + resources := make([]api.ResourceName, 0, len(list)) + for res := range list { + resources = append(resources, api.ResourceName(res)) + } + sort.Sort(SortableResourceNames(resources)) + return resources +} + +func listOfImages(spec *api.PodSpec) []string { + images := make([]string, 0, len(spec.Containers)) + for _, container := range spec.Containers { + images = append(images, container.Image) + } + return images +} + +func makeImageList(spec *api.PodSpec) string { + return strings.Join(listOfImages(spec), ",") +} + +func versionedClientsetForDeployment(internalClient clientset.Interface) versionedclientset.Interface { + if internalClient == nil { + return &versionedclientset.Clientset{} + } + return &versionedclientset.Clientset{ + CoreV1Client: coreclientset.New(internalClient.Core().RESTClient()), + ExtensionsV1beta1Client: extensionsclientset.New(internalClient.Extensions().RESTClient()), + } +} diff --git a/pkg/printers/internalversion/describe_test.go b/pkg/printers/internalversion/describe_test.go index 9de552368cf..218ad7a9351 100644 --- a/pkg/printers/internalversion/describe_test.go +++ b/pkg/printers/internalversion/describe_test.go @@ -40,6 +40,7 @@ import ( versionedfake "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util" ) @@ -59,7 +60,7 @@ func TestDescribePod(t *testing.T) { }) c := &describeClient{T: t, Namespace: "foo", Interface: fake} d := PodDescriber{c} - out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -83,7 +84,7 @@ func TestDescribePodTolerations(t *testing.T) { }) c := &describeClient{T: t, Namespace: "foo", Interface: fake} d := PodDescriber{c} - out, err := d.Describe("foo", "bar", DescriberSettings{}) + out, err := d.Describe("foo", "bar", printers.DescriberSettings{}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -100,7 +101,7 @@ func TestDescribeNamespace(t *testing.T) { }) c := &describeClient{T: t, Namespace: "", Interface: fake} d := NamespaceDescriber{c} - out, err := d.Describe("", "myns", DescriberSettings{ShowEvents: true}) + out, err := d.Describe("", "myns", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -118,7 +119,7 @@ func TestDescribeService(t *testing.T) { }) c := &describeClient{T: t, Namespace: "foo", Interface: fake} d := ServiceDescriber{c} - out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -167,7 +168,7 @@ func TestPodDescribeResultsSorted(t *testing.T) { d := PodDescriber{c} // Act - out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true}) // Assert if err != nil { @@ -436,7 +437,7 @@ func TestDescribers(t *testing.T) { if out, err := d.DescribeObject(first, second, third); out != "" || err == nil { t.Errorf("unexpected result: %s %v", out, err) } else { - if noDescriber, ok := err.(ErrNoDescriber); ok { + if noDescriber, ok := err.(printers.ErrNoDescriber); ok { if !reflect.DeepEqual(noDescriber.Types, []string{"*api.Event", "*api.Pod", "*api.Pod"}) { t.Errorf("unexpected describer: %v", err) } @@ -649,7 +650,7 @@ func TestPersistentVolumeDescriber(t *testing.T) { for name, pv := range tests { fake := fake.NewSimpleClientset(pv) c := PersistentVolumeDescriber{fake} - str, err := c.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + str, err := c.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("Unexpected error for test %s: %v", name, err) } @@ -673,7 +674,7 @@ func TestDescribeDeployment(t *testing.T) { }, }) d := DeploymentDescriber{fake, versionedFake} - out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -707,7 +708,7 @@ func TestDescribeCluster(t *testing.T) { } fake := fedfake.NewSimpleClientset(&cluster) d := ClusterDescriber{Interface: fake} - out, err := d.Describe("any", "foo", DescriberSettings{ShowEvents: true}) + out, err := d.Describe("any", "foo", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -732,7 +733,7 @@ func TestDescribeStorageClass(t *testing.T) { }, }) s := StorageClassDescriber{f} - out, err := s.Describe("", "foo", DescriberSettings{ShowEvents: true}) + out, err := s.Describe("", "foo", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -756,7 +757,7 @@ func TestDescribePodDisruptionBudget(t *testing.T) { }, }) s := PodDisruptionBudgetDescriber{f} - out, err := s.Describe("ns1", "pdb1", DescriberSettings{ShowEvents: true}) + out, err := s.Describe("ns1", "pdb1", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -1112,7 +1113,7 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) { } fake := fake.NewSimpleClientset(&test.hpa) desc := HorizontalPodAutoscalerDescriber{fake} - str, err := desc.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + str, err := desc.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("Unexpected error for test %s: %v", test.name, err) } @@ -1141,7 +1142,7 @@ func TestDescribeEvents(t *testing.T) { }, } - m := map[string]Describer{ + m := map[string]printers.Describer{ "DaemonSetDescriber": &DaemonSetDescriber{ fake.NewSimpleClientset(&extensions.DaemonSet{ ObjectMeta: metav1.ObjectMeta{ @@ -1241,7 +1242,7 @@ func TestDescribeEvents(t *testing.T) { } for name, d := range m { - out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error for %q: %v", name, err) } @@ -1252,7 +1253,7 @@ func TestDescribeEvents(t *testing.T) { t.Errorf("events not found for %q when ShowEvents=true: %s", name, out) } - out, err = d.Describe("foo", "bar", DescriberSettings{ShowEvents: false}) + out, err = d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: false}) if err != nil { t.Errorf("unexpected error for %q: %s", name, err) } diff --git a/pkg/printers/internalversion/internalversion.go b/pkg/printers/internalversion/printers.go similarity index 81% rename from pkg/printers/internalversion/internalversion.go rename to pkg/printers/internalversion/printers.go index aff5133972c..a4c88f4a3e8 100644 --- a/pkg/printers/internalversion/internalversion.go +++ b/pkg/printers/internalversion/printers.go @@ -20,19 +20,12 @@ import ( "bytes" "fmt" "io" - "io/ioutil" - "os" - "reflect" "sort" "strings" - "text/tabwriter" "time" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/kubernetes/federation/apis/federation" "k8s.io/kubernetes/pkg/api" @@ -46,98 +39,11 @@ import ( "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/storage" storageutil "k8s.io/kubernetes/pkg/apis/storage/util" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/node" ) -// GetPrinter takes a format type, an optional format argument. It will return true -// if the format is generic (untyped), otherwise it will return false. The printer -// is agnostic to schema versions, so you must send arguments to PrintObj in the -// version you wish them to be shown using a VersionedPrinter (typically when -// generic is true). -func GetPrinter(format, formatArgument string, noHeaders, allowMissingTemplateKeys bool) (ResourcePrinter, bool, error) { - var printer ResourcePrinter - switch format { - case "json": - printer = &JSONPrinter{} - case "yaml": - printer = &YAMLPrinter{} - case "name": - printer = &NamePrinter{ - // TODO: this is wrong, these should be provided as an argument to GetPrinter - Typer: api.Scheme, - Decoder: api.Codecs.UniversalDecoder(), - } - case "template", "go-template": - if len(formatArgument) == 0 { - return nil, false, fmt.Errorf("template format specified but no template given") - } - templatePrinter, err := NewTemplatePrinter([]byte(formatArgument)) - if err != nil { - return nil, false, fmt.Errorf("error parsing template %s, %v\n", formatArgument, err) - } - templatePrinter.AllowMissingKeys(allowMissingTemplateKeys) - printer = templatePrinter - case "templatefile", "go-template-file": - if len(formatArgument) == 0 { - return nil, false, fmt.Errorf("templatefile format specified but no template file given") - } - data, err := ioutil.ReadFile(formatArgument) - if err != nil { - return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err) - } - templatePrinter, err := NewTemplatePrinter(data) - if err != nil { - return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err) - } - templatePrinter.AllowMissingKeys(allowMissingTemplateKeys) - printer = templatePrinter - case "jsonpath": - if len(formatArgument) == 0 { - return nil, false, fmt.Errorf("jsonpath template format specified but no template given") - } - jsonpathPrinter, err := NewJSONPathPrinter(formatArgument) - if err != nil { - return nil, false, fmt.Errorf("error parsing jsonpath %s, %v\n", formatArgument, err) - } - jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys) - printer = jsonpathPrinter - case "jsonpath-file": - if len(formatArgument) == 0 { - return nil, false, fmt.Errorf("jsonpath file format specified but no template file file given") - } - data, err := ioutil.ReadFile(formatArgument) - if err != nil { - return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err) - } - jsonpathPrinter, err := NewJSONPathPrinter(string(data)) - if err != nil { - return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err) - } - jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys) - printer = jsonpathPrinter - case "custom-columns": - var err error - if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, api.Codecs.UniversalDecoder(), noHeaders); err != nil { - return nil, false, err - } - case "custom-columns-file": - file, err := os.Open(formatArgument) - if err != nil { - return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err) - } - defer file.Close() - if printer, err = NewCustomColumnsPrinterFromTemplate(file, api.Codecs.UniversalDecoder()); err != nil { - return nil, false, err - } - case "wide": - fallthrough - case "": - return nil, false, nil - default: - return nil, false, fmt.Errorf("output format %q not recognized", format) - } - return printer, true, nil -} +const loadBalancerWidth = 16 // NOTE: When adding a new resource type here, please update the list // pkg/kubectl/cmd/get.go to reflect the new resource type. @@ -184,7 +90,6 @@ var ( // TODO: consider having 'KIND' for third party resource data thirdPartyResourceDataColumns = []string{"NAME", "LABELS", "DATA"} horizontalPodAutoscalerColumns = []string{"NAME", "REFERENCE", "TARGETS", "MINPODS", "MAXPODS", "REPLICAS", "AGE"} - withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too. deploymentColumns = []string{"NAME", "DESIRED", "CURRENT", "UP-TO-DATE", "AVAILABLE", "AGE"} deploymentWideColumns = []string{"CONTAINER(S)", "IMAGE(S)", "SELECTOR"} configMapColumns = []string{"NAME", "DATA", "AGE"} @@ -194,7 +99,7 @@ var ( certificateSigningRequestColumns = []string{"NAME", "AGE", "REQUESTOR", "CONDITION"} ) -func printPod(pod *api.Pod, w io.Writer, options PrintOptions) error { +func printPod(pod *api.Pod, w io.Writer, options printers.PrintOptions) error { if err := printPodBase(pod, w, options); err != nil { return err } @@ -202,7 +107,7 @@ func printPod(pod *api.Pod, w io.Writer, options PrintOptions) error { return nil } -func printPodList(podList *api.PodList, w io.Writer, options PrintOptions) error { +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 @@ -211,10 +116,10 @@ func printPodList(podList *api.PodList, w io.Writer, options PrintOptions) error return nil } -// addDefaultHandlers adds print handlers for default Kubernetes types. -func (h *HumanReadablePrinter) addDefaultHandlers() { - h.Handler(podColumns, podWideColumns, h.printPodList) - h.Handler(podColumns, podWideColumns, h.printPod) +// AddHandlers adds print handlers for default Kubernetes types dealing with internal versions. +func AddHandlers(h *printers.HumanReadablePrinter) { + h.Handler(podColumns, podWideColumns, printPodList) + h.Handler(podColumns, podWideColumns, printPod) h.Handler(podTemplateColumns, nil, printPodTemplate) h.Handler(podTemplateColumns, nil, printPodTemplateList) h.Handler(podDisruptionBudgetColumns, nil, printPodDisruptionBudget) @@ -288,6 +193,16 @@ func (h *HumanReadablePrinter) addDefaultHandlers() { h.Handler(statusColumns, nil, printStatus) } +// formatResourceName receives a resource kind, name, and boolean specifying +// whether or not to update the current name to "kind/name" +func formatResourceName(kind, name string, withKind bool) string { + if !withKind || kind == "" { + return name + } + + return kind + "/" + name +} + // Pass ports=nil for all ports. func formatEndpoints(endpoints *api.Endpoints, ports sets.String) string { if len(endpoints.Subsets) == 0 { @@ -322,35 +237,16 @@ func formatEndpoints(endpoints *api.Endpoints, ports sets.String) string { return ret } -func ShortHumanDuration(d time.Duration) string { - // Allow deviation no more than 2 seconds(excluded) to tolerate machine time - // inconsistence, it can be considered as almost now. - if seconds := int(d.Seconds()); seconds < -1 { - return fmt.Sprintf("") - } else if seconds < 0 { - return fmt.Sprintf("0s") - } else if seconds < 60 { - return fmt.Sprintf("%ds", seconds) - } else if minutes := int(d.Minutes()); minutes < 60 { - return fmt.Sprintf("%dm", minutes) - } else if hours := int(d.Hours()); hours < 24 { - return fmt.Sprintf("%dh", hours) - } else if hours < 24*364 { - return fmt.Sprintf("%dd", hours/24) - } - return fmt.Sprintf("%dy", int(d.Hours()/24/365)) -} - // translateTimestamp returns the elapsed time since timestamp in // human-readable approximation. func translateTimestamp(timestamp metav1.Time) string { if timestamp.IsZero() { return "" } - return ShortHumanDuration(time.Now().Sub(timestamp.Time)) + return printers.ShortHumanDuration(time.Now().Sub(timestamp.Time)) } -func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error { +func printPodBase(pod *api.Pod, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, pod.Name, options.WithKind) namespace := pod.Namespace @@ -459,7 +355,7 @@ func printPodBase(pod *api.Pod, w io.Writer, options PrintOptions) error { return nil } -func printPodTemplate(pod *api.PodTemplate, w io.Writer, options PrintOptions) error { +func printPodTemplate(pod *api.PodTemplate, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, pod.Name, options.WithKind) namespace := pod.Namespace @@ -490,7 +386,7 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer, options PrintOptions) e return nil } -func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, options PrintOptions) error { +func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, options printers.PrintOptions) error { for _, pod := range podList.Items { if err := printPodTemplate(&pod, w, options); err != nil { return err @@ -499,7 +395,7 @@ func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, options Pri return nil } -func printPodDisruptionBudget(pdb *policy.PodDisruptionBudget, w io.Writer, options PrintOptions) error { +func printPodDisruptionBudget(pdb *policy.PodDisruptionBudget, w io.Writer, options printers.PrintOptions) error { // name, minavailable, selector name := formatResourceName(options.Kind, pdb.Name, options.WithKind) namespace := pdb.Namespace @@ -521,7 +417,7 @@ func printPodDisruptionBudget(pdb *policy.PodDisruptionBudget, w io.Writer, opti return nil } -func printPodDisruptionBudgetList(pdbList *policy.PodDisruptionBudgetList, w io.Writer, options PrintOptions) error { +func printPodDisruptionBudgetList(pdbList *policy.PodDisruptionBudgetList, w io.Writer, options printers.PrintOptions) error { for _, pdb := range pdbList.Items { if err := printPodDisruptionBudget(&pdb, w, options); err != nil { return err @@ -531,7 +427,7 @@ func printPodDisruptionBudgetList(pdbList *policy.PodDisruptionBudgetList, w io. } // TODO(AdoHe): try to put wide output in a single method -func printReplicationController(controller *api.ReplicationController, w io.Writer, options PrintOptions) error { +func printReplicationController(controller *api.ReplicationController, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, controller.Name, options.WithKind) namespace := controller.Namespace @@ -574,7 +470,7 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ return nil } -func printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer, options PrintOptions) error { +func printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer, options printers.PrintOptions) error { for _, controller := range list.Items { if err := printReplicationController(&controller, w, options); err != nil { return err @@ -583,7 +479,7 @@ func printReplicationControllerList(list *api.ReplicationControllerList, w io.Wr return nil } -func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOptions) error { +func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, rs.Name, options.WithKind) namespace := rs.Namespace @@ -625,7 +521,7 @@ func printReplicaSet(rs *extensions.ReplicaSet, w io.Writer, options PrintOption return nil } -func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options PrintOptions) error { +func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options printers.PrintOptions) error { for _, rs := range list.Items { if err := printReplicaSet(&rs, w, options); err != nil { return err @@ -634,7 +530,7 @@ func printReplicaSetList(list *extensions.ReplicaSetList, w io.Writer, options P return nil } -func printCluster(c *federation.Cluster, w io.Writer, options PrintOptions) error { +func printCluster(c *federation.Cluster, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, c.Name, options.WithKind) var statuses []string @@ -658,7 +554,7 @@ func printCluster(c *federation.Cluster, w io.Writer, options PrintOptions) erro } return nil } -func printClusterList(list *federation.ClusterList, w io.Writer, options PrintOptions) error { +func printClusterList(list *federation.ClusterList, w io.Writer, options printers.PrintOptions) error { for _, rs := range list.Items { if err := printCluster(&rs, w, options); err != nil { return err @@ -667,7 +563,7 @@ func printClusterList(list *federation.ClusterList, w io.Writer, options PrintOp return nil } -func printJob(job *batch.Job, w io.Writer, options PrintOptions) error { +func printJob(job *batch.Job, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, job.Name, options.WithKind) namespace := job.Namespace @@ -721,7 +617,7 @@ func printJob(job *batch.Job, w io.Writer, options PrintOptions) error { return nil } -func printJobList(list *batch.JobList, w io.Writer, options PrintOptions) error { +func printJobList(list *batch.JobList, w io.Writer, options printers.PrintOptions) error { for _, job := range list.Items { if err := printJob(&job, w, options); err != nil { return err @@ -730,7 +626,7 @@ func printJobList(list *batch.JobList, w io.Writer, options PrintOptions) error return nil } -func printCronJob(cronJob *batch.CronJob, w io.Writer, options PrintOptions) error { +func printCronJob(cronJob *batch.CronJob, w io.Writer, options printers.PrintOptions) error { name := cronJob.Name namespace := cronJob.Namespace @@ -757,7 +653,7 @@ func printCronJob(cronJob *batch.CronJob, w io.Writer, options PrintOptions) err return nil } -func printCronJobList(list *batch.CronJobList, w io.Writer, options PrintOptions) error { +func printCronJobList(list *batch.CronJobList, w io.Writer, options printers.PrintOptions) error { for _, cronJob := range list.Items { if err := printCronJob(&cronJob, w, options); err != nil { return err @@ -825,7 +721,7 @@ func makePortString(ports []api.ServicePort) string { return strings.Join(pieces, ",") } -func printService(svc *api.Service, w io.Writer, options PrintOptions) error { +func printService(svc *api.Service, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, svc.Name, options.WithKind) namespace := svc.Namespace @@ -859,7 +755,7 @@ func printService(svc *api.Service, w io.Writer, options PrintOptions) error { return err } -func printServiceList(list *api.ServiceList, w io.Writer, options PrintOptions) error { +func printServiceList(list *api.ServiceList, w io.Writer, options printers.PrintOptions) error { for _, svc := range list.Items { if err := printService(&svc, w, options); err != nil { return err @@ -905,7 +801,7 @@ func formatPorts(tls []extensions.IngressTLS) string { return "80" } -func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions) error { +func printIngress(ingress *extensions.Ingress, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, ingress.Name, options.WithKind) namespace := ingress.Namespace @@ -935,7 +831,7 @@ func printIngress(ingress *extensions.Ingress, w io.Writer, options PrintOptions return nil } -func printIngressList(ingressList *extensions.IngressList, w io.Writer, options PrintOptions) error { +func printIngressList(ingressList *extensions.IngressList, w io.Writer, options printers.PrintOptions) error { for _, ingress := range ingressList.Items { if err := printIngress(&ingress, w, options); err != nil { return err @@ -944,7 +840,7 @@ func printIngressList(ingressList *extensions.IngressList, w io.Writer, options return nil } -func printStatefulSet(ps *apps.StatefulSet, w io.Writer, options PrintOptions) error { +func printStatefulSet(ps *apps.StatefulSet, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, ps.Name, options.WithKind) namespace := ps.Namespace @@ -983,7 +879,7 @@ func printStatefulSet(ps *apps.StatefulSet, w io.Writer, options PrintOptions) e return nil } -func printStatefulSetList(statefulSetList *apps.StatefulSetList, w io.Writer, options PrintOptions) error { +func printStatefulSetList(statefulSetList *apps.StatefulSetList, w io.Writer, options printers.PrintOptions) error { for _, ps := range statefulSetList.Items { if err := printStatefulSet(&ps, w, options); err != nil { return err @@ -992,7 +888,7 @@ func printStatefulSetList(statefulSetList *apps.StatefulSetList, w io.Writer, op return nil } -func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions) error { +func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, ds.Name, options.WithKind) namespace := ds.Namespace @@ -1041,7 +937,7 @@ func printDaemonSet(ds *extensions.DaemonSet, w io.Writer, options PrintOptions) return nil } -func printDaemonSetList(list *extensions.DaemonSetList, w io.Writer, options PrintOptions) error { +func printDaemonSetList(list *extensions.DaemonSetList, w io.Writer, options printers.PrintOptions) error { for _, ds := range list.Items { if err := printDaemonSet(&ds, w, options); err != nil { return err @@ -1050,7 +946,7 @@ func printDaemonSetList(list *extensions.DaemonSetList, w io.Writer, options Pri return nil } -func printEndpoints(endpoints *api.Endpoints, w io.Writer, options PrintOptions) error { +func printEndpoints(endpoints *api.Endpoints, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, endpoints.Name, options.WithKind) namespace := endpoints.Namespace @@ -1070,7 +966,7 @@ func printEndpoints(endpoints *api.Endpoints, w io.Writer, options PrintOptions) return err } -func printEndpointsList(list *api.EndpointsList, w io.Writer, options PrintOptions) error { +func printEndpointsList(list *api.EndpointsList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printEndpoints(&item, w, options); err != nil { return err @@ -1079,7 +975,7 @@ func printEndpointsList(list *api.EndpointsList, w io.Writer, options PrintOptio return nil } -func printNamespace(item *api.Namespace, w io.Writer, options PrintOptions) error { +func printNamespace(item *api.Namespace, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, item.Name, options.WithKind) if options.WithNamespace { @@ -1096,7 +992,7 @@ func printNamespace(item *api.Namespace, w io.Writer, options PrintOptions) erro return err } -func printNamespaceList(list *api.NamespaceList, w io.Writer, options PrintOptions) error { +func printNamespaceList(list *api.NamespaceList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printNamespace(&item, w, options); err != nil { return err @@ -1105,7 +1001,7 @@ func printNamespaceList(list *api.NamespaceList, w io.Writer, options PrintOptio return nil } -func printSecret(item *api.Secret, w io.Writer, options PrintOptions) error { +func printSecret(item *api.Secret, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, item.Name, options.WithKind) namespace := item.Namespace @@ -1125,7 +1021,7 @@ func printSecret(item *api.Secret, w io.Writer, options PrintOptions) error { return err } -func printSecretList(list *api.SecretList, w io.Writer, options PrintOptions) error { +func printSecretList(list *api.SecretList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printSecret(&item, w, options); err != nil { return err @@ -1135,7 +1031,7 @@ func printSecretList(list *api.SecretList, w io.Writer, options PrintOptions) er return nil } -func printServiceAccount(item *api.ServiceAccount, w io.Writer, options PrintOptions) error { +func printServiceAccount(item *api.ServiceAccount, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, item.Name, options.WithKind) namespace := item.Namespace @@ -1155,7 +1051,7 @@ func printServiceAccount(item *api.ServiceAccount, w io.Writer, options PrintOpt return err } -func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, options PrintOptions) error { +func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printServiceAccount(&item, w, options); err != nil { return err @@ -1165,7 +1061,7 @@ func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, options return nil } -func printNode(node *api.Node, w io.Writer, options PrintOptions) error { +func printNode(node *api.Node, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, node.Name, options.WithKind) if options.WithNamespace { @@ -1249,7 +1145,7 @@ func findNodeRole(node *api.Node) string { return "" } -func printNodeList(list *api.NodeList, w io.Writer, options PrintOptions) error { +func printNodeList(list *api.NodeList, w io.Writer, options printers.PrintOptions) error { for _, node := range list.Items { if err := printNode(&node, w, options); err != nil { return err @@ -1258,7 +1154,7 @@ func printNodeList(list *api.NodeList, w io.Writer, options PrintOptions) error return nil } -func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, options PrintOptions) error { +func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, pv.Name, options.WithKind) if options.WithNamespace { @@ -1296,7 +1192,7 @@ func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, options PrintO return err } -func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer, options PrintOptions) error { +func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer, options printers.PrintOptions) error { for _, pv := range list.Items { if err := printPersistentVolume(&pv, w, options); err != nil { return err @@ -1305,7 +1201,7 @@ func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer, opti return nil } -func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, options PrintOptions) error { +func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, pvc.Name, options.WithKind) namespace := pvc.Namespace @@ -1336,7 +1232,7 @@ func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, opt return err } -func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Writer, options PrintOptions) error { +func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Writer, options printers.PrintOptions) error { for _, psd := range list.Items { if err := printPersistentVolumeClaim(&psd, w, options); err != nil { return err @@ -1345,7 +1241,7 @@ func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Wr return nil } -func printEvent(event *api.Event, w io.Writer, options PrintOptions) error { +func printEvent(event *api.Event, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, event.InvolvedObject.Name, options.WithKind) namespace := event.Namespace @@ -1388,7 +1284,7 @@ func printEvent(event *api.Event, w io.Writer, options PrintOptions) error { } // Sorts and prints the EventList in a human-friendly format. -func printEventList(list *api.EventList, w io.Writer, options PrintOptions) error { +func printEventList(list *api.EventList, w io.Writer, options printers.PrintOptions) error { sort.Sort(events.SortableEvents(list.Items)) for i := range list.Items { if err := printEvent(&list.Items[i], w, options); err != nil { @@ -1398,12 +1294,12 @@ func printEventList(list *api.EventList, w io.Writer, options PrintOptions) erro return nil } -func printLimitRange(limitRange *api.LimitRange, w io.Writer, options PrintOptions) error { +func printLimitRange(limitRange *api.LimitRange, w io.Writer, options printers.PrintOptions) error { return printObjectMeta(limitRange.ObjectMeta, w, options, true) } // Prints the LimitRangeList in a human-friendly format. -func printLimitRangeList(list *api.LimitRangeList, w io.Writer, options PrintOptions) error { +func printLimitRangeList(list *api.LimitRangeList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printLimitRange(&list.Items[i], w, options); err != nil { return err @@ -1413,7 +1309,7 @@ func printLimitRangeList(list *api.LimitRangeList, w io.Writer, options PrintOpt } // printObjectMeta prints the object metadata of a given resource. -func printObjectMeta(meta metav1.ObjectMeta, w io.Writer, options PrintOptions, namespaced bool) error { +func printObjectMeta(meta metav1.ObjectMeta, w io.Writer, options printers.PrintOptions, namespaced bool) error { name := formatResourceName(options.Kind, meta.Name, options.WithKind) if namespaced && options.WithNamespace { @@ -1436,12 +1332,12 @@ func printObjectMeta(meta metav1.ObjectMeta, w io.Writer, options PrintOptions, return err } -func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, options PrintOptions) error { +func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, options printers.PrintOptions) error { return printObjectMeta(resourceQuota.ObjectMeta, w, options, true) } // Prints the ResourceQuotaList in a human-friendly format. -func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, options PrintOptions) error { +func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printResourceQuota(&list.Items[i], w, options); err != nil { return err @@ -1450,12 +1346,12 @@ func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, options Pr return nil } -func printRole(role *rbac.Role, w io.Writer, options PrintOptions) error { +func printRole(role *rbac.Role, w io.Writer, options printers.PrintOptions) error { return printObjectMeta(role.ObjectMeta, w, options, true) } // Prints the Role in a human-friendly format. -func printRoleList(list *rbac.RoleList, w io.Writer, options PrintOptions) error { +func printRoleList(list *rbac.RoleList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printRole(&list.Items[i], w, options); err != nil { return err @@ -1464,7 +1360,7 @@ func printRoleList(list *rbac.RoleList, w io.Writer, options PrintOptions) error return nil } -func printRoleBinding(roleBinding *rbac.RoleBinding, w io.Writer, options PrintOptions) error { +func printRoleBinding(roleBinding *rbac.RoleBinding, w io.Writer, options printers.PrintOptions) error { meta := roleBinding.ObjectMeta name := formatResourceName(options.Kind, meta.Name, options.WithKind) @@ -1503,7 +1399,7 @@ func printRoleBinding(roleBinding *rbac.RoleBinding, w io.Writer, options PrintO } // Prints the RoleBinding in a human-friendly format. -func printRoleBindingList(list *rbac.RoleBindingList, w io.Writer, options PrintOptions) error { +func printRoleBindingList(list *rbac.RoleBindingList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printRoleBinding(&list.Items[i], w, options); err != nil { return err @@ -1512,7 +1408,7 @@ func printRoleBindingList(list *rbac.RoleBindingList, w io.Writer, options Print return nil } -func printClusterRole(clusterRole *rbac.ClusterRole, w io.Writer, options PrintOptions) error { +func printClusterRole(clusterRole *rbac.ClusterRole, w io.Writer, options printers.PrintOptions) error { if options.WithNamespace { return fmt.Errorf("clusterRole is not namespaced") } @@ -1520,7 +1416,7 @@ func printClusterRole(clusterRole *rbac.ClusterRole, w io.Writer, options PrintO } // Prints the ClusterRole in a human-friendly format. -func printClusterRoleList(list *rbac.ClusterRoleList, w io.Writer, options PrintOptions) error { +func printClusterRoleList(list *rbac.ClusterRoleList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printClusterRole(&list.Items[i], w, options); err != nil { return err @@ -1529,7 +1425,7 @@ func printClusterRoleList(list *rbac.ClusterRoleList, w io.Writer, options Print return nil } -func printClusterRoleBinding(clusterRoleBinding *rbac.ClusterRoleBinding, w io.Writer, options PrintOptions) error { +func printClusterRoleBinding(clusterRoleBinding *rbac.ClusterRoleBinding, w io.Writer, options printers.PrintOptions) error { meta := clusterRoleBinding.ObjectMeta name := formatResourceName(options.Kind, meta.Name, options.WithKind) @@ -1566,7 +1462,7 @@ func printClusterRoleBinding(clusterRoleBinding *rbac.ClusterRoleBinding, w io.W } // Prints the ClusterRoleBinding in a human-friendly format. -func printClusterRoleBindingList(list *rbac.ClusterRoleBindingList, w io.Writer, options PrintOptions) error { +func printClusterRoleBindingList(list *rbac.ClusterRoleBindingList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printClusterRoleBinding(&list.Items[i], w, options); err != nil { return err @@ -1575,7 +1471,7 @@ func printClusterRoleBindingList(list *rbac.ClusterRoleBindingList, w io.Writer, return nil } -func printCertificateSigningRequest(csr *certificates.CertificateSigningRequest, w io.Writer, options PrintOptions) error { +func printCertificateSigningRequest(csr *certificates.CertificateSigningRequest, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, csr.Name, options.WithKind) meta := csr.ObjectMeta @@ -1627,7 +1523,7 @@ func extractCSRStatus(csr *certificates.CertificateSigningRequest) (string, erro return status, nil } -func printCertificateSigningRequestList(list *certificates.CertificateSigningRequestList, w io.Writer, options PrintOptions) error { +func printCertificateSigningRequestList(list *certificates.CertificateSigningRequestList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printCertificateSigningRequest(&list.Items[i], w, options); err != nil { return err @@ -1636,7 +1532,7 @@ func printCertificateSigningRequestList(list *certificates.CertificateSigningReq return nil } -func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintOptions) error { +func printComponentStatus(item *api.ComponentStatus, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, item.Name, options.WithKind) if options.WithNamespace { @@ -1668,7 +1564,7 @@ func printComponentStatus(item *api.ComponentStatus, w io.Writer, options PrintO return err } -func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, options PrintOptions) error { +func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printComponentStatus(&item, w, options); err != nil { return err @@ -1678,7 +1574,7 @@ func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, option return nil } -func printThirdPartyResource(rsrc *extensions.ThirdPartyResource, w io.Writer, options PrintOptions) error { +func printThirdPartyResource(rsrc *extensions.ThirdPartyResource, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, rsrc.Name, options.WithKind) versions := make([]string, len(rsrc.Versions)) @@ -1693,7 +1589,7 @@ func printThirdPartyResource(rsrc *extensions.ThirdPartyResource, w io.Writer, o return nil } -func printThirdPartyResourceList(list *extensions.ThirdPartyResourceList, w io.Writer, options PrintOptions) error { +func printThirdPartyResourceList(list *extensions.ThirdPartyResourceList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printThirdPartyResource(&item, w, options); err != nil { return err @@ -1710,7 +1606,7 @@ func truncate(str string, maxLen int) string { return str } -func printThirdPartyResourceData(rsrc *extensions.ThirdPartyResourceData, w io.Writer, options PrintOptions) error { +func printThirdPartyResourceData(rsrc *extensions.ThirdPartyResourceData, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, rsrc.Name, options.WithKind) l := labels.FormatLabels(rsrc.Labels) @@ -1724,7 +1620,7 @@ func printThirdPartyResourceData(rsrc *extensions.ThirdPartyResourceData, w io.W return nil } -func printThirdPartyResourceDataList(list *extensions.ThirdPartyResourceDataList, w io.Writer, options PrintOptions) error { +func printThirdPartyResourceDataList(list *extensions.ThirdPartyResourceDataList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printThirdPartyResourceData(&item, w, options); err != nil { return err @@ -1734,7 +1630,7 @@ func printThirdPartyResourceDataList(list *extensions.ThirdPartyResourceDataList return nil } -func printDeployment(deployment *extensions.Deployment, w io.Writer, options PrintOptions) error { +func printDeployment(deployment *extensions.Deployment, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, deployment.Name, options.WithKind) if options.WithNamespace { @@ -1774,7 +1670,7 @@ func printDeployment(deployment *extensions.Deployment, w io.Writer, options Pri return err } -func printDeploymentList(list *extensions.DeploymentList, w io.Writer, options PrintOptions) error { +func printDeploymentList(list *extensions.DeploymentList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printDeployment(&item, w, options); err != nil { return err @@ -1843,7 +1739,7 @@ func formatHPAMetrics(specs []autoscaling.MetricSpec, statuses []autoscaling.Met return ret } -func printHorizontalPodAutoscaler(hpa *autoscaling.HorizontalPodAutoscaler, w io.Writer, options PrintOptions) error { +func printHorizontalPodAutoscaler(hpa *autoscaling.HorizontalPodAutoscaler, w io.Writer, options printers.PrintOptions) error { namespace := hpa.Namespace name := formatResourceName(options.Kind, hpa.Name, options.WithKind) @@ -1882,7 +1778,7 @@ func printHorizontalPodAutoscaler(hpa *autoscaling.HorizontalPodAutoscaler, w io return err } -func printHorizontalPodAutoscalerList(list *autoscaling.HorizontalPodAutoscalerList, w io.Writer, options PrintOptions) error { +func printHorizontalPodAutoscalerList(list *autoscaling.HorizontalPodAutoscalerList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printHorizontalPodAutoscaler(&list.Items[i], w, options); err != nil { return err @@ -1891,7 +1787,7 @@ func printHorizontalPodAutoscalerList(list *autoscaling.HorizontalPodAutoscalerL return nil } -func printConfigMap(configMap *api.ConfigMap, w io.Writer, options PrintOptions) error { +func printConfigMap(configMap *api.ConfigMap, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, configMap.Name, options.WithKind) namespace := configMap.Namespace @@ -1911,7 +1807,7 @@ func printConfigMap(configMap *api.ConfigMap, w io.Writer, options PrintOptions) return err } -func printConfigMapList(list *api.ConfigMapList, w io.Writer, options PrintOptions) error { +func printConfigMapList(list *api.ConfigMapList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printConfigMap(&list.Items[i], w, options); err != nil { return err @@ -1920,7 +1816,7 @@ func printConfigMapList(list *api.ConfigMapList, w io.Writer, options PrintOptio return nil } -func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, options PrintOptions) error { +func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, item.Name, options.WithKind) _, err := fmt.Fprintf(w, "%s\t%t\t%v\t%s\t%s\t%s\t%s\t%t\t%v\n", name, item.Spec.Privileged, @@ -1929,7 +1825,7 @@ func printPodSecurityPolicy(item *extensions.PodSecurityPolicy, w io.Writer, opt return err } -func printPodSecurityPolicyList(list *extensions.PodSecurityPolicyList, w io.Writer, options PrintOptions) error { +func printPodSecurityPolicyList(list *extensions.PodSecurityPolicyList, w io.Writer, options printers.PrintOptions) error { for _, item := range list.Items { if err := printPodSecurityPolicy(&item, w, options); err != nil { return err @@ -1939,7 +1835,7 @@ func printPodSecurityPolicyList(list *extensions.PodSecurityPolicyList, w io.Wri return nil } -func printNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, options PrintOptions) error { +func printNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, options printers.PrintOptions) error { name := formatResourceName(options.Kind, networkPolicy.Name, options.WithKind) namespace := networkPolicy.Namespace @@ -1959,7 +1855,7 @@ func printNetworkPolicy(networkPolicy *extensions.NetworkPolicy, w io.Writer, op return err } -func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, options PrintOptions) error { +func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, options printers.PrintOptions) error { for i := range list.Items { if err := printNetworkPolicy(&list.Items[i], w, options); err != nil { return err @@ -1968,7 +1864,7 @@ func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, opt return nil } -func printStorageClass(sc *storage.StorageClass, w io.Writer, options PrintOptions) error { +func printStorageClass(sc *storage.StorageClass, w io.Writer, options printers.PrintOptions) error { name := sc.Name if storageutil.IsDefaultAnnotation(sc.ObjectMeta) { @@ -1989,7 +1885,7 @@ func printStorageClass(sc *storage.StorageClass, w io.Writer, options PrintOptio return nil } -func printStorageClassList(scList *storage.StorageClassList, w io.Writer, options PrintOptions) error { +func printStorageClassList(scList *storage.StorageClassList, w io.Writer, options printers.PrintOptions) error { for _, sc := range scList.Items { if err := printStorageClass(&sc, w, options); err != nil { return err @@ -1998,7 +1894,7 @@ func printStorageClassList(scList *storage.StorageClassList, w io.Writer, option return nil } -func printStatus(status *metav1.Status, w io.Writer, options PrintOptions) error { +func printStatus(status *metav1.Status, w io.Writer, options printers.PrintOptions) error { if _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", status.Status, status.Reason, status.Message); err != nil { return err } @@ -2068,149 +1964,6 @@ func layoutContainers(containers []api.Container, w io.Writer) error { return nil } -func formatLabelHeaders(columnLabels []string) []string { - formHead := make([]string, len(columnLabels)) - for i, l := range columnLabels { - p := strings.Split(l, "/") - formHead[i] = strings.ToUpper((p[len(p)-1])) - } - return formHead -} - -// headers for --show-labels=true -func formatShowLabelsHeader(showLabels bool, t reflect.Type) []string { - if showLabels { - if t.String() != "*api.ThirdPartyResource" && t.String() != "*api.ThirdPartyResourceList" { - return []string{"LABELS"} - } - } - return nil -} - -// 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 { - // 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 !found { - w = GetNewTabWriter(output) - defer w.Flush() - } - - // 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. - obj, _ = DecodeUnknownObject(obj) - - t := reflect.TypeOf(obj) - if handler := h.handlerMap[t]; handler != nil { - if !h.options.NoHeaders && t != h.lastType { - headers := handler.columns - if h.options.Wide { - headers = append(headers, handler.columnsWithWide...) - } - headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...) - // LABELS is always the last column. - headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...) - if h.options.WithNamespace { - headers = append(withNamespacePrefixColumns, headers...) - } - h.printHeader(headers, w) - h.lastType = t - } - args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.options)} - resultValue := handler.printFunc.Call(args)[0] - if resultValue.IsNil() { - return nil - } - return resultValue.Interface().(error) - } - - if _, err := meta.Accessor(obj); err == nil { - if !h.options.NoHeaders && t != h.lastType { - headers := []string{"NAME", "KIND"} - headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...) - // LABELS is always the last column. - headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...) - if h.options.WithNamespace { - headers = append(withNamespacePrefixColumns, headers...) - } - h.printHeader(headers, w) - h.lastType = t - } - - // we don't recognize this type, but we can still attempt to print some reasonable information about. - unstructured, ok := obj.(runtime.Unstructured) - if !ok { - return fmt.Errorf("error: unknown type %#v", obj) - } - // if the error isn't nil, report the "I don't recognize this" error - if err := printUnstructured(unstructured, w, h.options); err != nil { - return err - } - return nil - } - - // we failed all reasonable printing efforts, report failure - return fmt.Errorf("error: unknown type %#v", obj) -} - -func printUnstructured(unstructured runtime.Unstructured, w io.Writer, options PrintOptions) error { - metadata, err := meta.Accessor(unstructured) - if err != nil { - return err - } - - if options.WithNamespace { - if _, err := fmt.Fprintf(w, "%s\t", metadata.GetNamespace()); err != nil { - return err - } - } - - content := unstructured.UnstructuredContent() - kind := "" - if objKind, ok := content["kind"]; ok { - if str, ok := objKind.(string); ok { - kind = str - } - } - if objAPIVersion, ok := content["apiVersion"]; ok { - if str, ok := objAPIVersion.(string); ok { - version, err := schema.ParseGroupVersion(str) - if err != nil { - return err - } - kind = kind + "." + version.Version + "." + version.Group - } - } - name := formatResourceName(options.Kind, metadata.GetName(), options.WithKind) - - if _, err := fmt.Fprintf(w, "%s\t%s", name, kind); err != nil { - return err - } - if _, err := fmt.Fprint(w, AppendLabels(metadata.GetLabels(), options.ColumnLabels)); err != nil { - return err - } - if _, err := fmt.Fprint(w, AppendAllLabels(options.ShowLabels, metadata.GetLabels())); err != nil { - return err - } - - return nil -} - -func tabbedString(f func(io.Writer) error) (string, error) { - out := new(tabwriter.Writer) - buf := &bytes.Buffer{} - out.Init(buf, 0, 8, 1, '\t', 0) - - err := f(out) - if err != nil { - return "", err - } - - out.Flush() - str := string(buf.String()) - return str, nil -} - // formatEventSource formats EventSource as a comma separated string excluding Host when empty func formatEventSource(es api.EventSource) string { EventSourceString := []string{es.Component} diff --git a/pkg/printers/internalversion/internalversion_test.go b/pkg/printers/internalversion/printers_test.go similarity index 92% rename from pkg/printers/internalversion/internalversion_test.go rename to pkg/printers/internalversion/printers_test.go index 2c6d8fe318e..8905c8bdafe 100644 --- a/pkg/printers/internalversion/internalversion_test.go +++ b/pkg/printers/internalversion/printers_test.go @@ -26,8 +26,11 @@ import ( "testing" "time" + "github.com/ghodss/yaml" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml" @@ -42,8 +45,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/policy" kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing" - - "github.com/ghodss/yaml" + "k8s.io/kubernetes/pkg/printers" ) func init() { @@ -60,8 +62,8 @@ var testData = kubectltesting.TestStruct{ func TestVersionedPrinter(t *testing.T) { original := &kubectltesting.TestStruct{Key: "value"} - p := NewVersionedPrinter( - ResourcePrinterFunc(func(obj runtime.Object, w io.Writer) error { + p := printers.NewVersionedPrinter( + printers.ResourcePrinterFunc(func(obj runtime.Object, w io.Writer) error { if obj == original { t.Fatalf("object should not be identical: %#v", obj) } @@ -79,7 +81,7 @@ func TestVersionedPrinter(t *testing.T) { } func TestPrintDefault(t *testing.T) { - printer, found, err := GetPrinter("", "", false, false) + printer, found, err := printers.GetStandardPrinter("", "", false, false, nil, nil, nil) if err != nil { t.Fatalf("unexpected error: %#v", err) } @@ -134,12 +136,12 @@ func TestPrinter(t *testing.T) { } for _, test := range printerTests { buf := bytes.NewBuffer([]byte{}) - printer, generic, err := GetPrinter(test.Format, test.FormatArgument, false, true) + printer, generic, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, true, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}) if err != nil { t.Errorf("in %s, unexpected error: %#v", test.Name, err) } if generic && len(test.OutputVersions) > 0 { - printer = NewVersionedPrinter(printer, api.Scheme, test.OutputVersions...) + printer = printers.NewVersionedPrinter(printer, api.Scheme, test.OutputVersions...) } if err := printer.PrintObj(test.Input, buf); err != nil { t.Errorf("in %s, unexpected error: %#v", test.Name, err) @@ -164,14 +166,14 @@ func TestBadPrinter(t *testing.T) { {"bad jsonpath", "jsonpath", "{.Name", fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")}, } for _, test := range badPrinterTests { - _, _, err := GetPrinter(test.Format, test.FormatArgument, false, false) + _, _, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}) if err == nil || err.Error() != test.Error.Error() { t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err) } } } -func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) { +func testPrinter(t *testing.T, printer printers.ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) { buf := bytes.NewBuffer([]byte{}) err := printer.PrintObj(&testData, buf) @@ -216,11 +218,11 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data } func TestYAMLPrinter(t *testing.T) { - testPrinter(t, &YAMLPrinter{}, yaml.Unmarshal) + testPrinter(t, &printers.YAMLPrinter{}, yaml.Unmarshal) } func TestJSONPrinter(t *testing.T) { - testPrinter(t, &JSONPrinter{}, json.Unmarshal) + testPrinter(t, &printers.JSONPrinter{}, json.Unmarshal) } func TestFormatResourceName(t *testing.T) { @@ -240,7 +242,7 @@ func TestFormatResourceName(t *testing.T) { } } -func PrintCustomType(obj *TestPrintType, w io.Writer, options PrintOptions) error { +func PrintCustomType(obj *TestPrintType, w io.Writer, options printers.PrintOptions) error { data := obj.Data kind := options.Kind if options.WithKind { @@ -250,13 +252,13 @@ func PrintCustomType(obj *TestPrintType, w io.Writer, options PrintOptions) erro return err } -func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options PrintOptions) error { +func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options printers.PrintOptions) error { return fmt.Errorf("ErrorPrintHandler error") } func TestCustomTypePrinting(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(PrintOptions{}) + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) printer.Handler(columns, nil, PrintCustomType) obj := TestPrintType{"test object"} @@ -273,7 +275,7 @@ func TestCustomTypePrinting(t *testing.T) { func TestCustomTypePrintingWithKind(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(PrintOptions{}) + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) printer.Handler(columns, nil, PrintCustomType) printer.EnsurePrintWithKind("test") @@ -291,7 +293,7 @@ func TestCustomTypePrintingWithKind(t *testing.T) { func TestPrintHandlerError(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(PrintOptions{}) + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) printer.Handler(columns, nil, ErrorPrintHandler) obj := TestPrintType{"test object"} buffer := &bytes.Buffer{} @@ -302,7 +304,7 @@ func TestPrintHandlerError(t *testing.T) { } func TestUnknownTypePrinting(t *testing.T) { - printer := NewHumanReadablePrinter(PrintOptions{}) + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) buffer := &bytes.Buffer{} err := printer.PrintObj(&TestUnknownType{}, buffer) if err == nil { @@ -312,7 +314,7 @@ func TestUnknownTypePrinting(t *testing.T) { func TestTemplatePanic(t *testing.T) { tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}` - printer, err := NewTemplatePrinter([]byte(tmpl)) + printer, err := printers.NewTemplatePrinter([]byte(tmpl)) if err != nil { t.Fatalf("tmpl fail: %v", err) } @@ -357,7 +359,7 @@ func TestNamePrinter(t *testing.T) { }, "pod/foo\npod/bar\n"}, } - printer, _, _ := GetPrinter("name", "", false, false) + printer, _, _ := printers.GetStandardPrinter("name", "", false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}) for name, item := range tests { buff := &bytes.Buffer{} err := printer.PrintObj(item.obj, buff) @@ -467,12 +469,12 @@ func TestTemplateStrings(t *testing.T) { } // The point of this test is to verify that the below template works. tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}` - p, err := NewTemplatePrinter([]byte(tmpl)) + p, err := printers.NewTemplatePrinter([]byte(tmpl)) if err != nil { t.Fatalf("tmpl fail: %v", err) } - printer := NewVersionedPrinter(p, api.Scheme, api.Registry.GroupOrDie(api.GroupName).GroupVersion) + printer := printers.NewVersionedPrinter(p, api.Scheme, api.Registry.GroupOrDie(api.GroupName).GroupVersion) for name, item := range table { buffer := &bytes.Buffer{} @@ -496,44 +498,47 @@ func TestPrinters(t *testing.T) { var ( err error - templatePrinter ResourcePrinter - templatePrinter2 ResourcePrinter - jsonpathPrinter ResourcePrinter + templatePrinter printers.ResourcePrinter + templatePrinter2 printers.ResourcePrinter + jsonpathPrinter printers.ResourcePrinter ) - templatePrinter, err = NewTemplatePrinter([]byte("{{.name}}")) + templatePrinter, err = printers.NewTemplatePrinter([]byte("{{.name}}")) if err != nil { t.Fatal(err) } - templatePrinter = NewVersionedPrinter(templatePrinter, api.Scheme, v1.SchemeGroupVersion) + templatePrinter = printers.NewVersionedPrinter(templatePrinter, api.Scheme, v1.SchemeGroupVersion) - templatePrinter2, err = NewTemplatePrinter([]byte("{{len .items}}")) + templatePrinter2, err = printers.NewTemplatePrinter([]byte("{{len .items}}")) if err != nil { t.Fatal(err) } - templatePrinter2 = NewVersionedPrinter(templatePrinter2, api.Scheme, v1.SchemeGroupVersion) + templatePrinter2 = printers.NewVersionedPrinter(templatePrinter2, api.Scheme, v1.SchemeGroupVersion) - jsonpathPrinter, err = NewJSONPathPrinter("{.metadata.name}") + jsonpathPrinter, err = printers.NewJSONPathPrinter("{.metadata.name}") if err != nil { t.Fatal(err) } - jsonpathPrinter = NewVersionedPrinter(jsonpathPrinter, api.Scheme, v1.SchemeGroupVersion) + jsonpathPrinter = printers.NewVersionedPrinter(jsonpathPrinter, api.Scheme, v1.SchemeGroupVersion) - printers := map[string]ResourcePrinter{ - "humanReadable": NewHumanReadablePrinter(PrintOptions{ + allPrinters := map[string]printers.ResourcePrinter{ + "humanReadable": printers.NewHumanReadablePrinter(printers.PrintOptions{ NoHeaders: true, }), - "humanReadableHeaders": NewHumanReadablePrinter(PrintOptions{}), - "json": &JSONPrinter{}, - "yaml": &YAMLPrinter{}, + "humanReadableHeaders": printers.NewHumanReadablePrinter(printers.PrintOptions{}), + "json": &printers.JSONPrinter{}, + "yaml": &printers.YAMLPrinter{}, "template": templatePrinter, "template2": templatePrinter2, "jsonpath": jsonpathPrinter, - "name": &NamePrinter{ - Typer: api.Scheme, - Decoder: api.Codecs.UniversalDecoder(), + "name": &printers.NamePrinter{ + Typer: api.Scheme, + Decoders: []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, + Mapper: api.Registry.RESTMapper(api.Registry.EnabledVersions()...), }, } + AddHandlers((allPrinters["humanReadable"]).(*printers.HumanReadablePrinter)) + AddHandlers((allPrinters["humanReadableHeaders"]).(*printers.HumanReadablePrinter)) objects := map[string]runtime.Object{ "pod": &api.Pod{ObjectMeta: om("pod")}, "emptyPodList": &api.PodList{}, @@ -550,7 +555,7 @@ func TestPrinters(t *testing.T) { "jsonpath": sets.NewString("emptyPodList", "nonEmptyPodList", "endpoints"), } - for pName, p := range printers { + for pName, p := range allPrinters { for oName, obj := range objects { b := &bytes.Buffer{} if err := p.PrintObj(obj, b); err != nil { @@ -566,7 +571,8 @@ func TestPrinters(t *testing.T) { func TestPrintEventsResultSorted(t *testing.T) { // Arrange - printer := NewHumanReadablePrinter(PrintOptions{}) + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + AddHandlers(printer) obj := api.EventList{ Items: []api.Event{ @@ -610,7 +616,8 @@ func TestPrintEventsResultSorted(t *testing.T) { } func TestPrintNodeStatus(t *testing.T) { - printer := NewHumanReadablePrinter(PrintOptions{}) + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{}) + AddHandlers(printer) table := []struct { node api.Node status string @@ -729,10 +736,11 @@ func TestPrintNodeStatus(t *testing.T) { } func TestPrintNodeOSImage(t *testing.T) { - printer := NewHumanReadablePrinter(PrintOptions{ + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ ColumnLabels: []string{}, Wide: true, }) + AddHandlers(printer) table := []struct { node api.Node @@ -773,10 +781,11 @@ func TestPrintNodeOSImage(t *testing.T) { } func TestPrintNodeKernelVersion(t *testing.T) { - printer := NewHumanReadablePrinter(PrintOptions{ + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ ColumnLabels: []string{}, Wide: true, }) + AddHandlers(printer) table := []struct { node api.Node @@ -817,9 +826,10 @@ func TestPrintNodeKernelVersion(t *testing.T) { } func TestPrintNodeExternalIP(t *testing.T) { - printer := NewHumanReadablePrinter(PrintOptions{ + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ Wide: true, }) + AddHandlers(printer) table := []struct { node api.Node externalIP string @@ -900,7 +910,7 @@ func TestPrintHunmanReadableIngressWithColumnLabels(t *testing.T) { }, } buff := bytes.Buffer{} - printIngress(&ingress, &buff, PrintOptions{ + printIngress(&ingress, &buff, printers.PrintOptions{ ColumnLabels: []string{"app_name"}, }) output := string(buff.Bytes()) @@ -1025,7 +1035,7 @@ func TestPrintHumanReadableService(t *testing.T) { for _, svc := range tests { for _, wide := range []bool{false, true} { buff := bytes.Buffer{} - printService(&svc, &buff, PrintOptions{Wide: wide}) + printService(&svc, &buff, printers.PrintOptions{Wide: wide}) output := string(buff.Bytes()) ip := svc.Spec.ClusterIP if !strings.Contains(output, ip) { @@ -1209,9 +1219,10 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { for _, test := range table { if test.isNamespaced { // Expect output to include namespace when requested. - printer := NewHumanReadablePrinter(PrintOptions{ + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ WithNamespace: true, }) + AddHandlers(printer) buffer := &bytes.Buffer{} err := printer.PrintObj(test.obj, buffer) if err != nil { @@ -1223,7 +1234,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) { } } else { // Expect error when trying to get all namespaces for un-namespaced object. - printer := NewHumanReadablePrinter(PrintOptions{ + printer := printers.NewHumanReadablePrinter(printers.PrintOptions{ WithNamespace: true, }) buffer := &bytes.Buffer{} @@ -1319,9 +1330,8 @@ func TestPrintPod(t *testing.T) { } buf := bytes.NewBuffer([]byte{}) - printer := HumanReadablePrinter{} for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{ShowAll: true}) + printPod(&test.pod, buf, printers.PrintOptions{ShowAll: true}) // We ignore time if !strings.HasPrefix(buf.String(), test.expect) { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) @@ -1413,9 +1423,8 @@ func TestPrintNonTerminatedPod(t *testing.T) { } buf := bytes.NewBuffer([]byte{}) - printer := HumanReadablePrinter{} for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{}) + printPod(&test.pod, buf, printers.PrintOptions{}) // We ignore time if !strings.HasPrefix(buf.String(), test.expect) { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) @@ -1474,9 +1483,8 @@ func TestPrintPodWithLabels(t *testing.T) { } buf := bytes.NewBuffer([]byte{}) - printer := HumanReadablePrinter{} for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{ColumnLabels: test.labelColumns}) + printPod(&test.pod, buf, printers.PrintOptions{ColumnLabels: test.labelColumns}) // We ignore time 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()) @@ -1554,13 +1562,13 @@ func TestPrintDeployment(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printDeployment(&test.deployment, buf, PrintOptions{}) + printDeployment(&test.deployment, buf, printers.PrintOptions{}) if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) } buf.Reset() // print deployment with '-o wide' option - printDeployment(&test.deployment, buf, PrintOptions{Wide: true}) + printDeployment(&test.deployment, buf, printers.PrintOptions{Wide: true}) if buf.String() != test.wideExpect { t.Fatalf("Expected: %s, got: %s", test.wideExpect, buf.String()) } @@ -1596,7 +1604,7 @@ func TestPrintDaemonSet(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printDaemonSet(&test.ds, buf, PrintOptions{}) + printDaemonSet(&test.ds, buf, printers.PrintOptions{}) if !strings.HasPrefix(buf.String(), test.startsWith) { t.Fatalf("Expected to start with %s but got %s", test.startsWith, buf.String()) } @@ -1644,7 +1652,7 @@ func TestPrintJob(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printJob(&test.job, buf, PrintOptions{}) + printJob(&test.job, buf, printers.PrintOptions{}) if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) } @@ -2014,7 +2022,7 @@ func TestPrintHPA(t *testing.T) { buff := bytes.NewBuffer([]byte{}) for _, test := range tests { - err := printHorizontalPodAutoscaler(&test.hpa, buff, PrintOptions{}) + err := printHorizontalPodAutoscaler(&test.hpa, buff, printers.PrintOptions{}) if err != nil { t.Errorf("expected %q, got error: %v", test.expected, err) buff.Reset() @@ -2079,10 +2087,8 @@ func TestPrintPodShowLabels(t *testing.T) { } buf := bytes.NewBuffer([]byte{}) - printer := HumanReadablePrinter{} - for _, test := range tests { - printer.printPod(&test.pod, buf, PrintOptions{ShowLabels: test.showLabels}) + printPod(&test.pod, buf, printers.PrintOptions{ShowLabels: test.showLabels}) // We ignore time 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()) @@ -2132,7 +2138,7 @@ func TestPrintService(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printService(&test.service, buf, PrintOptions{}) + printService(&test.service, buf, printers.PrintOptions{}) // We ignore time if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s %d", test.expect, buf.String(), strings.Compare(test.expect, buf.String())) @@ -2165,7 +2171,7 @@ func TestPrintPodDisruptionBudget(t *testing.T) { buf := bytes.NewBuffer([]byte{}) for _, test := range tests { - printPodDisruptionBudget(&test.pdb, buf, PrintOptions{}) + printPodDisruptionBudget(&test.pdb, buf, printers.PrintOptions{}) if buf.String() != test.expect { t.Fatalf("Expected: %s, got: %s", test.expect, buf.String()) } @@ -2190,7 +2196,7 @@ func TestAllowMissingKeys(t *testing.T) { } for _, test := range tests { buf := bytes.NewBuffer([]byte{}) - printer, _, err := GetPrinter(test.Format, test.Template, false, test.AllowMissingTemplateKeys) + printer, _, err := printers.GetStandardPrinter(test.Format, test.Template, false, test.AllowMissingTemplateKeys, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}) if err != nil { t.Errorf("in %s, unexpected error: %#v", test.Name, err) } diff --git a/pkg/printers/internalversion/sorted_resource_name_list_test.go b/pkg/printers/internalversion/sorted_resource_name_list_test.go index cf999c7f36f..8989691e707 100644 --- a/pkg/printers/internalversion/sorted_resource_name_list_test.go +++ b/pkg/printers/internalversion/sorted_resource_name_list_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package kubectl +package internalversion import ( "reflect" diff --git a/pkg/printers/tabwriter.go b/pkg/printers/tabwriter.go index eb272f5038c..6e3980a052f 100644 --- a/pkg/printers/tabwriter.go +++ b/pkg/printers/tabwriter.go @@ -27,7 +27,6 @@ const ( tabwriterPadding = 3 tabwriterPadChar = ' ' tabwriterFlags = 0 - loadBalancerWidth = 16 ) // GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text. From a9c03f292b22932d19af161c8faf17f29b829624 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Tue, 21 Feb 2017 11:03:14 -0500 Subject: [PATCH 3/8] UnstructuredList should return 'items' set to the children The set of items is not mutable (can't add or remove items) but the list now is. Needs to be improved to make mutability is clear. --- .../apis/meta/v1/unstructured/unstructured.go | 19 ++++++++-- .../meta/v1/unstructured/unstructured_test.go | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_test.go diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go index bb16abe4300..318bea86d6a 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go @@ -73,11 +73,24 @@ func (obj *Unstructured) UnstructuredContent() map[string]interface{} { } return obj.Object } + +// UnstructuredContent returns a map contain an overlay of the Items field onto +// the Object field. Items always overwrites overlay. Changing "items" in the +// returned object will affect items in the underlying Items field, but changing +// the "items" slice itself will have no effect. +// TODO: expose SetUnstructuredContent on runtime.Unstructured that allows +// items to be changed. func (obj *UnstructuredList) UnstructuredContent() map[string]interface{} { - if obj.Object == nil { - obj.Object = make(map[string]interface{}) + out := obj.Object + if out == nil { + out = make(map[string]interface{}) } - return obj.Object + items := make([]interface{}, len(obj.Items)) + for i, item := range obj.Items { + items[i] = item.Object + } + out["items"] = items + return out } // MarshalJSON ensures that the unstructured object produces proper diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_test.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_test.go new file mode 100644 index 00000000000..54596629db5 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_test.go @@ -0,0 +1,36 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package unstructured + +import "testing" + +func TestUnstructuredList(t *testing.T) { + list := &UnstructuredList{ + Object: map[string]interface{}{"kind": "List", "apiVersion": "v1"}, + Items: []*Unstructured{ + {Object: map[string]interface{}{"kind": "Pod", "apiVersion": "v1", "metadata": map[string]interface{}{"name": "test"}}}, + }, + } + content := list.UnstructuredContent() + items := content["items"].([]interface{}) + if len(items) != 1 { + t.Fatalf("unexpected items: %#v", items) + } + if getNestedField(items[0].(map[string]interface{}), "metadata", "name") != "test" { + t.Fatalf("unexpected fields: %#v", items[0]) + } +} From 90fdd067e93c1bd9d496556bc5cd25d6ea33fe64 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 19 Feb 2017 17:55:15 -0500 Subject: [PATCH 4/8] Restrict imports for printers --- pkg/printers/.import-restrictions | 10 ++++++++++ pkg/printers/internalversion/.import-restrictions | 4 ++++ 2 files changed, 14 insertions(+) create mode 100644 pkg/printers/.import-restrictions create mode 100644 pkg/printers/internalversion/.import-restrictions diff --git a/pkg/printers/.import-restrictions b/pkg/printers/.import-restrictions new file mode 100644 index 00000000000..5ec78945023 --- /dev/null +++ b/pkg/printers/.import-restrictions @@ -0,0 +1,10 @@ +{ + "Rules": [ + { + "SelectorRegexp": "k8s[.]io/kubernetes/pkg/(api$|apis/)", + "ForbiddenPrefixes": [ + "k8s.io/kubernetes/pkg/printers" + ] + } + ] +} diff --git a/pkg/printers/internalversion/.import-restrictions b/pkg/printers/internalversion/.import-restrictions new file mode 100644 index 00000000000..ce172cfbd9d --- /dev/null +++ b/pkg/printers/internalversion/.import-restrictions @@ -0,0 +1,4 @@ +{ + "Rules": [ + ] +} From 19ae89dcd84d32924f6630b793ec124a52276456 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 19 Feb 2017 17:39:43 -0500 Subject: [PATCH 5/8] command Factory should provide Printers The factory knows all possible types, and should abstract the process of creating all printers. A future refactor can further reduce the dependencies between printer code and internal types. --- pkg/kubectl/cmd/testing/fake.go | 27 ++++++--- pkg/kubectl/cmd/util/factory.go | 20 ++++--- pkg/kubectl/cmd/util/factory_builder.go | 53 ++++++++++++++++- pkg/kubectl/cmd/util/factory_client_access.go | 12 ++-- .../cmd/util/factory_object_mapping.go | 59 ++++--------------- pkg/kubectl/cmd/util/helpers.go | 5 +- pkg/kubectl/cmd/util/printing.go | 13 ++-- 7 files changed, 114 insertions(+), 75 deletions(-) diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 3ec2be83235..0ce1591dc52 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -41,6 +41,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" ) type InternalType struct { @@ -214,13 +215,15 @@ type TestFactory struct { Typer runtime.ObjectTyper Client kubectl.RESTClient UnstructuredClient kubectl.RESTClient - Describer kubectl.Describer - Printer kubectl.ResourcePrinter + Describer printers.Describer + Printer printers.ResourcePrinter + CommandPrinter printers.ResourcePrinter Validator validation.Schema Namespace string ClientConfig *restclient.Config Err error Command string + GenericPrinter bool ClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error) UnstructuredClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error) @@ -331,11 +334,15 @@ func (f *FakeFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (r return f.tf.UnstructuredClient, f.tf.Err } -func (f *FakeFactory) Describer(*meta.RESTMapping) (kubectl.Describer, error) { +func (f *FakeFactory) Describer(*meta.RESTMapping) (printers.Describer, error) { return f.tf.Describer, f.tf.Err } -func (f *FakeFactory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { +func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) { + return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err +} + +func (f *FakeFactory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } @@ -451,7 +458,7 @@ func (f *FakeFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, ob return nil } -func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) { +func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } @@ -587,11 +594,15 @@ func (f *fakeAPIFactory) UnstructuredClientForMapping(m *meta.RESTMapping) (reso return f.tf.UnstructuredClient, f.tf.Err } -func (f *fakeAPIFactory) Describer(*meta.RESTMapping) (kubectl.Describer, error) { +func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) { + return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err +} + +func (f *fakeAPIFactory) Describer(*meta.RESTMapping) (printers.Describer, error) { return f.tf.Describer, f.tf.Err } -func (f *fakeAPIFactory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { +func (f *fakeAPIFactory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } @@ -664,7 +675,7 @@ func (f *fakeAPIFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, return printer.PrintObj(obj, out) } -func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) { +func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 6e213a2038b..e19ce2e600c 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -51,6 +51,7 @@ import ( coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" ) const ( @@ -136,7 +137,8 @@ type ClientAccessFactory interface { // BindExternalFlags adds any flags defined by external projects (not part of pflags) BindExternalFlags(flags *pflag.FlagSet) - DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions + // TODO: Break the dependency on cmd here. + DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions // DefaultResourceFilterFunc returns a collection of FilterFuncs suitable for filtering specific resource types. DefaultResourceFilterFunc() kubectl.Filters @@ -144,7 +146,7 @@ type ClientAccessFactory interface { SuggestedPodTemplateResources() []schema.GroupResource // Returns a Printer for formatting objects of the given type or an error. - Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) + Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) // Pauser marks the object in the info as paused. Currently supported only for Deployments. // Returns the patched object in bytes and any error that occured during the encoding or // in case the object is already paused. @@ -193,7 +195,7 @@ type ObjectMappingFactory interface { // Returns a RESTClient for working with Unstructured objects. UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) // Returns a Describer for displaying the specified RESTMapping type or an error. - Describer(mapping *meta.RESTMapping) (kubectl.Describer, error) + Describer(mapping *meta.RESTMapping) (printers.Describer, error) // LogsForObject returns a request for the logs associated with the provided object LogsForObject(object, options runtime.Object) (*restclient.Request, error) @@ -211,10 +213,6 @@ type ObjectMappingFactory interface { // AttachablePodForObject returns the pod to which to attach given an object. AttachablePodForObject(object runtime.Object) (*api.Pod, error) - // PrinterForMapping returns a printer suitable for displaying the provided resource type. - // Requires that printer flags have been added to cmd (see AddPrinterFlags). - PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) - // Returns a schema that can validate objects stored on disk. Validator(validate bool, cacheDir string) (validation.Schema, error) // SwaggerSchema returns the schema declaration for the provided group version kind. @@ -224,6 +222,14 @@ type ObjectMappingFactory interface { // BuilderFactory holds the second level of factory methods. These functions depend upon ObjectMappingFactory and ClientAccessFactory methods. // Generally they depend upon client mapper functions type BuilderFactory interface { + // PrinterForCommand returns the default printer for the command. It requires that certain options + // are declared on the command (see AddPrinterFlags). Returns a printer, true if the printer is + // generic (is not internal), or an error if a printer could not be found. + // TODO: Break the dependency on cmd here. + PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) + // PrinterForMapping returns a printer suitable for displaying the provided resource type. + // Requires that printer flags have been added to cmd (see AddPrinterFlags). + PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) // PrintObject prints an api object given command line flags to modify the output format PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error // One stop shopping for a Builder diff --git a/pkg/kubectl/cmd/util/factory_builder.go b/pkg/kubectl/cmd/util/factory_builder.go index c11652d13e5..4667e5bc560 100644 --- a/pkg/kubectl/cmd/util/factory_builder.go +++ b/pkg/kubectl/cmd/util/factory_builder.go @@ -19,13 +19,16 @@ limitations under the License. package util import ( + "fmt" "io" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" ) type ring2Factory struct { @@ -42,6 +45,54 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac return f } +func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) { + mapper, typer := f.objectMappingFactory.Object() + // TODO: used by the custom column implementation and the name implementation, break this dependency + decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme} + return PrinterForCommand(cmd, mapper, typer, decoders) +} + +func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { + printer, generic, err := f.PrinterForCommand(cmd) + if err != nil { + return nil, err + } + + // Make sure we output versioned data for generic printers + if generic { + if mapping == nil { + return nil, fmt.Errorf("no serialization format found") + } + version := mapping.GroupVersionKind.GroupVersion() + if version.Empty() { + return nil, fmt.Errorf("no serialization format found") + } + + printer = printers.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion()) + } else { + // Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper + columnLabel, err := cmd.Flags().GetStringSlice("label-columns") + if err != nil { + columnLabel = []string{} + } + printer, err = f.clientAccessFactory.Printer(mapping, printers.PrintOptions{ + NoHeaders: GetFlagBool(cmd, "no-headers"), + WithNamespace: withNamespace, + Wide: GetWideFlag(cmd), + ShowAll: GetFlagBool(cmd, "show-all"), + ShowLabels: GetFlagBool(cmd, "show-labels"), + AbsoluteTimestamps: isWatch(cmd), + ColumnLabels: columnLabel, + }) + if err != nil { + return nil, err + } + printer = maybeWrapSortingPrinter(cmd, printer) + } + + return printer, nil +} + func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error { // try to get a typed object _, typer := f.objectMappingFactory.Object() @@ -66,7 +117,7 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, o return err } - printer, err := f.objectMappingFactory.PrinterForMapping(cmd, mapping, false) + printer, err := f.PrinterForMapping(cmd, mapping, false) if err != nil { return err } diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index 4c271c8ace2..605a5bdf9c0 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -49,6 +49,8 @@ import ( "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" ) type ring0Factory struct { @@ -360,12 +362,12 @@ func (f *ring0Factory) BindExternalFlags(flags *pflag.FlagSet) { flags.AddGoFlagSet(flag.CommandLine) } -func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions { +func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions { columnLabel, err := cmd.Flags().GetStringSlice("label-columns") if err != nil { columnLabel = []string{} } - opts := &kubectl.PrintOptions{ + opts := &printers.PrintOptions{ NoHeaders: GetFlagBool(cmd, "no-headers"), WithNamespace: withNamespace, Wide: GetWideFlag(cmd), @@ -392,8 +394,10 @@ func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource { } } -func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { - return kubectl.NewHumanReadablePrinter(options), nil +func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) { + p := printers.NewHumanReadablePrinter(options) + printersinternal.AddHandlers(p) + return p, nil } func (f *ring0Factory) Pauser(info *resource.Info) ([]byte, error) { diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index b54c447fcea..9e2d43639ab 100644 --- a/pkg/kubectl/cmd/util/factory_object_mapping.go +++ b/pkg/kubectl/cmd/util/factory_object_mapping.go @@ -27,7 +27,6 @@ import ( "time" "github.com/emicklei/go-restful/swagger" - "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,6 +47,8 @@ import ( "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" ) type ring1Factory struct { @@ -58,10 +59,12 @@ func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMapp f := &ring1Factory{ clientAccessFactory: clientAccessFactory, } - return f } +// TODO: This method should return an error now that it can fail. Alternatively, it needs to +// return lazy implementations of mapper and typer that don't hit the wire until they are +// invoked. func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) { mapper := api.Registry.RESTMapper() discoveryClient, err := f.clientAccessFactory.DiscoveryClient() @@ -143,7 +146,7 @@ func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) ( return restclient.RESTClientFor(cfg) } -func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, error) { +func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (printers.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() if mapping.GroupVersionKind.Group == federation.GroupName { fedClientSet, err := f.clientAccessFactory.FederationClientSetForVersion(&mappingVersion) @@ -151,7 +154,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, return nil, err } if mapping.GroupVersionKind.Kind == "Cluster" { - return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil + return &printersinternal.ClusterDescriber{Interface: fedClientSet}, nil } } @@ -166,7 +169,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, } // try to get a describer - if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok { + if describer, ok := printersinternal.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok { return describer, nil } // if this is a kind we don't have a describer for yet, go generic if possible @@ -178,7 +181,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, } // helper function to make a generic describer, or return an error -func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RESTMapping) (kubectl.Describer, error) { +func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RESTMapping) (printers.Describer, error) { clientConfig, err := clientAccessFactory.ClientConfig() if err != nil { return nil, err @@ -202,7 +205,7 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES } eventsClient := clientSet.Core() - return kubectl.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil + return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil } func (f *ring1Factory) LogsForObject(object, options runtime.Object) (*restclient.Request, error) { @@ -361,48 +364,6 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod, return pod, err } -func (f *ring1Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) { - printer, generic, err := PrinterForCommand(cmd) - if err != nil { - return nil, err - } - - // Make sure we output versioned data for generic printers - if generic { - if mapping == nil { - return nil, fmt.Errorf("no serialization format found") - } - version := mapping.GroupVersionKind.GroupVersion() - if version.Empty() { - return nil, fmt.Errorf("no serialization format found") - } - - printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion()) - - } else { - // Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper - columnLabel, err := cmd.Flags().GetStringSlice("label-columns") - if err != nil { - columnLabel = []string{} - } - printer, err = f.clientAccessFactory.Printer(mapping, kubectl.PrintOptions{ - NoHeaders: GetFlagBool(cmd, "no-headers"), - WithNamespace: withNamespace, - Wide: GetWideFlag(cmd), - ShowAll: GetFlagBool(cmd, "show-all"), - ShowLabels: GetFlagBool(cmd, "show-labels"), - AbsoluteTimestamps: isWatch(cmd), - ColumnLabels: columnLabel, - }) - if err != nil { - return nil, err - } - printer = maybeWrapSortingPrinter(cmd, printer) - } - - return printer, nil -} - func (f *ring1Factory) Validator(validate bool, cacheDir string) (validation.Schema, error) { if validate { discovery, err := f.clientAccessFactory.DiscoveryClient() diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 80ea52efa97..b2bda9d9769 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -47,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" utilexec "k8s.io/kubernetes/pkg/util/exec" ) @@ -672,7 +673,7 @@ func MustPrintWithKinds(objs []runtime.Object, infos []*resource.Info, sorter *k // FilterResourceList receives a list of runtime objects. // If any objects are filtered, that number is returned along with a modified list. -func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *kubectl.PrintOptions) (int, []runtime.Object, error) { +func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *printers.PrintOptions) (int, []runtime.Object, error) { items, err := meta.ExtractList(obj) if err != nil { return 0, []runtime.Object{obj}, utilerrors.NewAggregate([]error{err}) @@ -697,7 +698,7 @@ func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterO return filterCount, list, nil } -func PrintFilterCount(hiddenObjNum int, resource string, options *kubectl.PrintOptions) { +func PrintFilterCount(hiddenObjNum int, resource string, options *printers.PrintOptions) { if !options.NoHeaders && !options.ShowAll && hiddenObjNum > 0 { glog.V(2).Infof(" info: %d completed object(s) was(were) not shown in %s list. Pass --show-all to see all objects.\n\n", hiddenObjNum, resource) } diff --git a/pkg/kubectl/cmd/util/printing.go b/pkg/kubectl/cmd/util/printing.go index 9e309322d9a..62729316249 100644 --- a/pkg/kubectl/cmd/util/printing.go +++ b/pkg/kubectl/cmd/util/printing.go @@ -22,8 +22,10 @@ import ( "strings" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "github.com/spf13/cobra" ) @@ -105,7 +107,7 @@ func ValidateOutputArgs(cmd *cobra.Command) error { // PrinterForCommand returns the default printer for this command. // Requires that printer flags have been added to cmd (see AddPrinterFlags). -func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error) { +func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime.ObjectTyper, decoders []runtime.Decoder) (printers.ResourcePrinter, bool, error) { outputFormat := GetFlagString(cmd, "output") // templates are logically optional for specifying a format. @@ -131,7 +133,10 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error if cmd.Flags().Lookup("allow-missing-template-keys") != nil { allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys") } - printer, generic, err := kubectl.GetPrinter(outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys) + printer, generic, err := printers.GetStandardPrinter( + outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys, + mapper, typer, decoders, + ) if err != nil { return nil, generic, err } @@ -144,7 +149,7 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error // object passed is non-generic, it attempts to print the object using a HumanReadablePrinter. // Requires that printer flags have been added to cmd (see AddPrinterFlags). func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Factory, out io.Writer) error { - printer, generic, err := PrinterForCommand(cmd) + printer, generic, err := f.PrinterForCommand(cmd) if err != nil { return err } @@ -157,7 +162,7 @@ func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Fact return printer.PrintObj(info.Object, out) } -func maybeWrapSortingPrinter(cmd *cobra.Command, printer kubectl.ResourcePrinter) kubectl.ResourcePrinter { +func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinter) printers.ResourcePrinter { sorting, err := cmd.Flags().GetString("sort-by") if err != nil { // error can happen on missing flag or bad flag type. In either case, this command didn't intent to sort From 7cdb0eb89f204be1250fc4e3352da5361ede9fb0 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 19 Feb 2017 17:37:48 -0500 Subject: [PATCH 6/8] NamePrinter should not hardcode scheme --- pkg/printers/internalversion/printers_test.go | 6 ++-- pkg/printers/name.go | 30 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/pkg/printers/internalversion/printers_test.go b/pkg/printers/internalversion/printers_test.go index 8905c8bdafe..55e5a98158f 100644 --- a/pkg/printers/internalversion/printers_test.go +++ b/pkg/printers/internalversion/printers_test.go @@ -131,7 +131,7 @@ func TestPrinter(t *testing.T) { {"test jsonpath", "jsonpath", "{.metadata.name}", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"}, {"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"}, {"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""}, - {"test name", "name", "", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pod/foo\n"}, + {"test name", "name", "", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pods/foo\n"}, {"emits versioned objects", "template", "{{.kind}}", testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"}, } for _, test := range printerTests { @@ -342,7 +342,7 @@ func TestNamePrinter(t *testing.T) { Name: "foo", }, }, - "pod/foo\n"}, + "pods/foo\n"}, "List": { &v1.List{ TypeMeta: metav1.TypeMeta{ @@ -357,7 +357,7 @@ func TestNamePrinter(t *testing.T) { }, }, }, - "pod/foo\npod/bar\n"}, + "pods/foo\npods/bar\n"}, } printer, _, _ := printers.GetStandardPrinter("name", "", false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}) for name, item := range tests { diff --git a/pkg/printers/name.go b/pkg/printers/name.go index 192eed4d941..22d2ba1707d 100644 --- a/pkg/printers/name.go +++ b/pkg/printers/name.go @@ -21,15 +21,15 @@ import ( "io" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" utilerrors "k8s.io/apimachinery/pkg/util/errors" ) // NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object. type NamePrinter struct { - Decoder runtime.Decoder - Typer runtime.ObjectTyper + Decoders []runtime.Decoder + Typer runtime.ObjectTyper + Mapper meta.RESTMapper } func (p *NamePrinter) AfterPrint(w io.Writer, res string) error { @@ -44,7 +44,7 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { if err != nil { return err } - if errs := runtime.DecodeList(items, p.Decoder, unstructured.UnstructuredJSONScheme); len(errs) > 0 { + if errs := runtime.DecodeList(items, p.Decoders...); len(errs) > 0 { return utilerrors.NewAggregate(errs) } for _, obj := range items { @@ -62,22 +62,24 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { } } - if kind := obj.GetObjectKind().GroupVersionKind(); len(kind.Kind) == 0 { - // this is the old code. It's unnecessary on decoded external objects, but on internal objects - // you may have to do it. Tests are definitely calling it with internals and I'm not sure who else - // is + kind := obj.GetObjectKind().GroupVersionKind() + if len(kind.Kind) == 0 { if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil { - // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper - _, resource := meta.KindToResource(gvks[0]) - fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) + for _, gvk := range gvks { + if mappings, err := p.Mapper.RESTMappings(gvk.GroupKind(), gvk.Version); err == nil && len(mappings) > 0 { + fmt.Fprintf(w, "%s/%s\n", mappings[0].Resource, name) + } + } } else { fmt.Fprintf(w, "/%s\n", name) } } else { - // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper - _, resource := meta.KindToResource(kind) - fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) + if mappings, err := p.Mapper.RESTMappings(kind.GroupKind(), kind.Version); err == nil && len(mappings) > 0 { + fmt.Fprintf(w, "%s/%s\n", mappings[0].Resource, name) + } else { + fmt.Fprintf(w, "/%s\n", name) + } } return nil From 2aa4abb73b305c7c868107831bc007aa1c952f86 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 19 Feb 2017 17:56:11 -0500 Subject: [PATCH 7/8] Refactor commands to use new factory method --- cmd/kubeadm/app/cmd/token.go | 4 +- pkg/kubectl/cmd/annotate.go | 3 +- pkg/kubectl/cmd/clusterinfo_dump.go | 7 +-- pkg/kubectl/cmd/cmd_test.go | 39 ++++++++++----- pkg/kubectl/cmd/config/config.go | 2 +- pkg/kubectl/cmd/config/get_contexts.go | 5 +- pkg/kubectl/cmd/config/view.go | 18 ++++--- pkg/kubectl/cmd/convert.go | 8 ++-- pkg/kubectl/cmd/delete.go | 3 +- pkg/kubectl/cmd/describe.go | 12 +++-- pkg/kubectl/cmd/edit.go | 13 ++--- pkg/kubectl/cmd/expose_test.go | 4 +- pkg/kubectl/cmd/get.go | 9 ++-- pkg/kubectl/cmd/get_test.go | 55 ++++++++++++---------- pkg/kubectl/cmd/label.go | 3 +- pkg/kubectl/cmd/patch.go | 3 +- pkg/kubectl/cmd/set/set_image_test.go | 10 ++-- pkg/kubectl/cmd/testing/fake.go | 4 +- pkg/kubectl/history.go | 21 ++++++++- pkg/kubectl/kubectl.go | 13 ----- pkg/kubectl/metricsutil/metrics_printer.go | 6 +-- pkg/kubectl/resource_filter.go | 7 +-- pkg/kubectl/rollback.go | 5 +- pkg/kubectl/sorting_printer.go | 5 +- 24 files changed, 153 insertions(+), 106 deletions(-) diff --git a/cmd/kubeadm/app/cmd/token.go b/cmd/kubeadm/app/cmd/token.go index 848a3be7b45..b4360dfb651 100644 --- a/cmd/kubeadm/app/cmd/token.go +++ b/cmd/kubeadm/app/cmd/token.go @@ -37,7 +37,7 @@ import ( kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token" bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api" - "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/printers" ) func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command { @@ -207,7 +207,7 @@ func RunListTokens(out io.Writer, errW io.Writer, cmd *cobra.Command) error { if err != nil { return fmt.Errorf("error parsing expiration time [%v]", err) } - expires = kubectl.ShortHumanDuration(expireTime.Sub(time.Now())) + expires = printers.ShortHumanDuration(expireTime.Sub(time.Now())) } fmt.Fprintf(w, "%s\t%s\t%s\n", tokenId, tokenutil.BearerToken(td), expires) } diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index dd65ac0761c..f61ff45ac2a 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -36,6 +36,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -101,7 +102,7 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs, argAliases := []string{}, []string{} - p, err := f.Printer(nil, kubectl.PrintOptions{ + p, err := f.Printer(nil, printers.PrintOptions{ ColumnLabels: []string{}, }) cmdutil.CheckErr(err) diff --git a/pkg/kubectl/cmd/clusterinfo_dump.go b/pkg/kubectl/cmd/clusterinfo_dump.go index 3c8133bd459..dabdac3e96a 100644 --- a/pkg/kubectl/cmd/clusterinfo_dump.go +++ b/pkg/kubectl/cmd/clusterinfo_dump.go @@ -26,9 +26,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -93,10 +93,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, args []string, out i return err } - printer, _, err := kubectl.GetPrinter("json", "", false, true) - if err != nil { - return err - } + printer := &printers.JSONPrinter{} nodes, err := clientset.Core().Nodes().List(metav1.ListOptions{}) if err != nil { diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 27ead0a2b0a..9901f2b752d 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -35,9 +35,10 @@ import ( "k8s.io/client-go/rest/fake" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" - "k8s.io/kubernetes/pkg/kubectl" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/printers" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" "k8s.io/kubernetes/pkg/util/strings" ) @@ -97,12 +98,12 @@ func (t *testPrinter) AfterPrint(output io.Writer, res string) error { type testDescriber struct { Name, Namespace string - Settings kubectl.DescriberSettings + Settings printers.DescriberSettings Output string Err error } -func (t *testDescriber) Describe(namespace, name string, describerSettings kubectl.DescriberSettings) (output string, err error) { +func (t *testDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) { t.Namespace, t.Name = namespace, name t.Settings = describerSettings return t.Output, t.Err @@ -146,10 +147,12 @@ func stringBody(body string) io.ReadCloser { func Example_printReplicationControllerWithNamespace() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ WithNamespace: true, ColumnLabels: []string{}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -197,10 +200,12 @@ func Example_printReplicationControllerWithNamespace() { func Example_printMultiContainersReplicationControllerWithWide() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ Wide: true, ColumnLabels: []string{}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -250,9 +255,11 @@ func Example_printMultiContainersReplicationControllerWithWide() { func Example_printReplicationController() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ ColumnLabels: []string{}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -302,10 +309,12 @@ func Example_printReplicationController() { func Example_printPodWithWideFormat() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ Wide: true, ColumnLabels: []string{}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -343,10 +352,12 @@ func Example_printPodWithWideFormat() { func Example_printPodWithShowLabels() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ ShowLabels: true, ColumnLabels: []string{}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -479,9 +490,11 @@ func newAllPhasePodList() *api.PodList { func Example_printPodHideTerminated() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ ColumnLabels: []string{}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -512,10 +525,12 @@ func Example_printPodHideTerminated() { func Example_printPodShowAll() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ ShowAll: true, ColumnLabels: []string{}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -539,10 +554,12 @@ func Example_printPodShowAll() { func Example_printServiceWithNamespacesAndLabels() { f, tf, _, ns := cmdtesting.NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{ + p := printers.NewHumanReadablePrinter(printers.PrintOptions{ WithNamespace: true, ColumnLabels: []string{"l1"}, }) + printersinternal.AddHandlers(p) + tf.Printer = p tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, diff --git a/pkg/kubectl/cmd/config/config.go b/pkg/kubectl/cmd/config/config.go index 639fb481237..fe49488444f 100644 --- a/pkg/kubectl/cmd/config/config.go +++ b/pkg/kubectl/cmd/config/config.go @@ -52,7 +52,7 @@ func NewCmdConfig(pathOptions *clientcmd.PathOptions, out, errOut io.Writer) *co // file paths are common to all sub commands cmd.PersistentFlags().StringVar(&pathOptions.LoadingRules.ExplicitPath, pathOptions.ExplicitFileFlag, pathOptions.LoadingRules.ExplicitPath, "use a particular kubeconfig file") - cmd.AddCommand(NewCmdConfigView(out, pathOptions)) + cmd.AddCommand(NewCmdConfigView(out, errOut, pathOptions)) cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions)) cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions)) cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions)) diff --git a/pkg/kubectl/cmd/config/get_contexts.go b/pkg/kubectl/cmd/config/get_contexts.go index 0576a21a92c..5c52014f699 100644 --- a/pkg/kubectl/cmd/config/get_contexts.go +++ b/pkg/kubectl/cmd/config/get_contexts.go @@ -23,13 +23,14 @@ import ( "text/tabwriter" "github.com/spf13/cobra" + utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -108,7 +109,7 @@ func (o GetContextsOptions) RunGetContexts() error { out, found := o.out.(*tabwriter.Writer) if !found { - out = kubectl.GetNewTabWriter(o.out) + out = printers.GetNewTabWriter(o.out) defer out.Flush() } diff --git a/pkg/kubectl/cmd/config/view.go b/pkg/kubectl/cmd/config/view.go index e3c9b28fb00..4c7cdb665d3 100644 --- a/pkg/kubectl/cmd/config/view.go +++ b/pkg/kubectl/cmd/config/view.go @@ -23,13 +23,15 @@ import ( "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apiserver/pkg/util/flag" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/tools/clientcmd/api/latest" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -55,7 +57,7 @@ var ( kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'`) ) -func NewCmdConfigView(out io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command { +func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command { options := &ViewOptions{ConfigAccess: ConfigAccess} // Default to yaml defaultOutputFormat := "yaml" @@ -69,17 +71,19 @@ func NewCmdConfigView(out io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra options.Complete() outputFormat := cmdutil.GetFlagString(cmd, "output") if outputFormat == "wide" { - fmt.Printf("--output wide is not available in kubectl config view; reset to default output format (%s)\n\n", defaultOutputFormat) + fmt.Fprintf(errOut, "--output wide is not available in kubectl config view; reset to default output format (%s)\n\n", defaultOutputFormat) + // TODO: once printing is abstracted, this should be handled at flag declaration time cmd.Flags().Set("output", defaultOutputFormat) } if outputFormat == "" { - fmt.Printf("Reset to default output format (%s) as --output is empty\n", defaultOutputFormat) + fmt.Fprintf(errOut, "Reset to default output format (%s) as --output is empty\n", defaultOutputFormat) + // TODO: once printing is abstracted, this should be handled at flag declaration time cmd.Flags().Set("output", defaultOutputFormat) } - printer, _, err := cmdutil.PrinterForCommand(cmd) + printer, _, err := cmdutil.PrinterForCommand(cmd, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, []runtime.Decoder{latest.Codec}) cmdutil.CheckErr(err) - printer = kubectl.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion) + printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion) cmdutil.CheckErr(options.Run(out, printer)) }, @@ -97,7 +101,7 @@ func NewCmdConfigView(out io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra return cmd } -func (o ViewOptions) Run(out io.Writer, printer kubectl.ResourcePrinter) error { +func (o ViewOptions) Run(out io.Writer, printer printers.ResourcePrinter) error { config, err := o.loadConfig() if err != nil { return err diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index 519f465a6c8..a4697d7d0dd 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -25,10 +25,10 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" "github.com/spf13/cobra" @@ -96,7 +96,7 @@ type ConvertOptions struct { encoder runtime.Encoder out io.Writer - printer kubectl.ResourcePrinter + printer printers.ResourcePrinter outputVersion schema.GroupVersion } @@ -160,9 +160,11 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C } else { outputFormat = "template" } + // TODO: once printing is abstracted, this should be handled at flag declaration time + cmd.Flags().Set("output", outputFormat) } o.encoder = f.JSONEncoder() - o.printer, _, err = kubectl.GetPrinter(outputFormat, templateFile, false, cmdutil.GetFlagBool(cmd, "allow-missing-template-keys")) + o.printer, _, err = f.PrinterForCommand(cmd) if err != nil { return err } diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 7440edc834a..1eada79b4b4 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -32,6 +32,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -117,7 +118,7 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs, argAliases := []string{}, []string{} - p, err := f.Printer(nil, kubectl.PrintOptions{ + p, err := f.Printer(nil, printers.PrintOptions{ ColumnLabels: []string{}, }) cmdutil.CheckErr(err) diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 37c5ce89887..c71bda26bb8 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -33,6 +33,8 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -72,9 +74,11 @@ var ( func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command { options := &resource.FilenameOptions{} - describerSettings := &kubectl.DescriberSettings{} + describerSettings := &printers.DescriberSettings{} - validArgs := kubectl.DescribableResources() + // TODO: this should come from the factory, and may need to be loaded from the server, and so is probably + // going to have to be removed + validArgs := printersinternal.DescribableResources() argAliases := kubectl.ResourceAliases(validArgs) cmd := &cobra.Command{ @@ -98,7 +102,7 @@ func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command { return cmd } -func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions, describerSettings *kubectl.DescriberSettings) error { +func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions, describerSettings *printers.DescriberSettings) error { selector := cmdutil.GetFlagString(cmd, "selector") allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces") cmdNamespace, enforceNamespace, err := f.DefaultNamespace() @@ -172,7 +176,7 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a return utilerrors.NewAggregate(allErrs) } -func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *kubectl.DescriberSettings, out io.Writer, originalError error) error { +func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *printers.DescriberSettings, out io.Writer, originalError error) error { mapper, typer, err := f.UnstructuredObject() if err != nil { return err diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index a6de724706a..04555d2cdd3 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -43,6 +43,7 @@ import ( cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/crlf" "k8s.io/kubernetes/pkg/util/i18n" @@ -64,9 +65,9 @@ var ( Editing is done with the API version used to fetch the resource. To edit using a specific API version, fully-qualify the resource, version, and group. - + The default format is YAML. To edit in JSON, specify "-o json". - + The flag --windows-line-endings can be used to force Windows line endings, otherwise the default for your operating system will be used. @@ -92,7 +93,7 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs, argAliases := []string{}, []string{} - p, err := f.Printer(nil, kubectl.PrintOptions{ + p, err := f.Printer(nil, printers.PrintOptions{ ColumnLabels: []string{}, }) cmdutil.CheckErr(err) @@ -343,14 +344,14 @@ func getPrinter(cmd *cobra.Command) (*editPrinterOptions, error) { switch format := cmdutil.GetFlagString(cmd, "output"); format { case "json": return &editPrinterOptions{ - printer: &kubectl.JSONPrinter{}, + printer: &printers.JSONPrinter{}, ext: ".json", addHeader: false, }, nil // If flag -o is not specified, use yaml as default case "yaml", "": return &editPrinterOptions{ - printer: &kubectl.YAMLPrinter{}, + printer: &printers.YAMLPrinter{}, ext: ".yaml", addHeader: true, }, nil @@ -589,7 +590,7 @@ func (h *editHeader) flush() { } type editPrinterOptions struct { - printer kubectl.ResourcePrinter + printer printers.ResourcePrinter ext string addHeader bool } diff --git a/pkg/kubectl/cmd/expose_test.go b/pkg/kubectl/cmd/expose_test.go index 331d8f89d72..fdf67367fe1 100644 --- a/pkg/kubectl/cmd/expose_test.go +++ b/pkg/kubectl/cmd/expose_test.go @@ -28,8 +28,8 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/rest/fake" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" + "k8s.io/kubernetes/pkg/printers" ) func TestRunExposeService(t *testing.T) { @@ -460,7 +460,7 @@ func TestRunExposeService(t *testing.T) { for _, test := range tests { f, tf, codec, ns := cmdtesting.NewAPIFactory() - tf.Printer = &kubectl.JSONPrinter{} + tf.Printer = &printers.JSONPrinter{} tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index fb85104f7ff..d11bd55b16f 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" "k8s.io/kubernetes/pkg/util/interrupt" ) @@ -93,7 +94,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman // retrieve a list of handled resources from printer as valid args validArgs, argAliases := []string{}, []string{} - p, err := f.Printer(nil, kubectl.PrintOptions{ + p, err := f.Printer(nil, printers.PrintOptions{ ColumnLabels: []string{}, }) cmdutil.CheckErr(err) @@ -300,7 +301,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ return err } - printer, generic, err := cmdutil.PrinterForCommand(cmd) + printer, generic, err := f.PrinterForCommand(cmd) if err != nil { return err } @@ -419,7 +420,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ // use the default printer for each object printer = nil var lastMapping *meta.RESTMapping - w := kubectl.GetNewTabWriter(out) + w := printers.GetNewTabWriter(out) filteredResourceCount := 0 if resource.MultipleTypesRequested(args) || cmdutil.MustPrintWithKinds(objs, infos, sorter) { @@ -475,7 +476,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ } } - if resourcePrinter, found := printer.(*kubectl.HumanReadablePrinter); found { + if resourcePrinter, found := printer.(*printers.HumanReadablePrinter); found { resourceName := resourcePrinter.GetResourceKind() if mapping != nil { if resourceName == "" { diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/get_test.go index 39200512d1f..7a55f9a4f81 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/get_test.go @@ -29,6 +29,7 @@ import ( apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer/json" @@ -459,7 +460,8 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { pods, svc, _ := testData() f, tf, codec, _ := cmdtesting.NewAPIFactory() - tf.Printer = &testPrinter{} + tf.CommandPrinter = &testPrinter{} + tf.GenericPrinter = true tf.UnstructuredClient = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: unstructuredSerializer, @@ -486,34 +488,37 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) { cmd.Flags().Set("output", "json") cmd.Run(cmd, []string{"pods,services"}) - if tf.Printer.(*testPrinter).Objects != nil { - t.Errorf("unexpected print to default printer") + actual := tf.CommandPrinter.(*testPrinter).Objects + fn := func(obj runtime.Object) *unstructured.Unstructured { + data, err := runtime.Encode(api.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"}), obj) + if err != nil { + panic(err) + } + out := &unstructured.Unstructured{Object: make(map[string]interface{})} + if err := encjson.Unmarshal(data, &out.Object); err != nil { + panic(err) + } + return out } - out, err := runtime.Decode(codec, buf.Bytes()) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - list, err := meta.ExtractList(out) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if errs := runtime.DecodeList(list, codec); len(errs) > 0 { - t.Fatalf("unexpected error: %v", errs) - } - if err := meta.SetList(out, list); err != nil { - t.Fatalf("unexpected error: %v", err) - } - - expected := &api.List{ - Items: []runtime.Object{ - &pods.Items[0], - &pods.Items[1], - &svc.Items[0], + expected := &unstructured.UnstructuredList{ + Object: map[string]interface{}{"kind": "List", "apiVersion": "v1", "metadata": map[string]interface{}{}, "selfLink": "", "resourceVersion": ""}, + Items: []*unstructured.Unstructured{ + fn(&pods.Items[0]), + fn(&pods.Items[1]), + fn(&svc.Items[0]), }, } - if !reflect.DeepEqual(expected, out) { - t.Errorf("unexpected output: %#v", out) + actualBytes, err := encjson.Marshal(actual[0]) + if err != nil { + t.Fatal(err) + } + expectedBytes, err := encjson.Marshal(expected) + if err != nil { + t.Fatal(err) + } + if string(actualBytes) != string(expectedBytes) { + t.Errorf("unexpected object:\n%s\n%s", expectedBytes, actualBytes) } } diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index 5c84b8babbb..14b20b7718d 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -39,6 +39,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -99,7 +100,7 @@ func NewCmdLabel(f cmdutil.Factory, out io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs, argAliases := []string{}, []string{} - p, err := f.Printer(nil, kubectl.PrintOptions{ + p, err := f.Printer(nil, printers.PrintOptions{ ColumnLabels: []string{}, }) cmdutil.CheckErr(err) diff --git a/pkg/kubectl/cmd/patch.go b/pkg/kubectl/cmd/patch.go index 6115279c391..33236fbdea2 100644 --- a/pkg/kubectl/cmd/patch.go +++ b/pkg/kubectl/cmd/patch.go @@ -39,6 +39,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/util/i18n" ) @@ -81,7 +82,7 @@ func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command { // retrieve a list of handled resources from printer as valid args validArgs, argAliases := []string{}, []string{} - p, err := f.Printer(nil, kubectl.PrintOptions{ + p, err := f.Printer(nil, printers.PrintOptions{ ColumnLabels: []string{}, }) cmdutil.CheckErr(err) diff --git a/pkg/kubectl/cmd/set/set_image_test.go b/pkg/kubectl/cmd/set/set_image_test.go index a83dc71627f..400903a2bac 100644 --- a/pkg/kubectl/cmd/set/set_image_test.go +++ b/pkg/kubectl/cmd/set/set_image_test.go @@ -22,16 +22,17 @@ import ( "strings" "testing" + "k8s.io/apimachinery/pkg/runtime" restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" "k8s.io/kubernetes/pkg/api" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" - cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" ) func TestImageLocal(t *testing.T) { - f, tf, _, ns := cmdtesting.NewAPIFactory() + f, tf, codec, ns := cmdtesting.NewAPIFactory() tf.Client = &fake.RESTClient{ APIRegistry: api.Registry, NegotiatedSerializer: ns, @@ -47,7 +48,8 @@ func TestImageLocal(t *testing.T) { cmd := NewCmdImage(f, buf, buf) cmd.SetOutput(buf) cmd.Flags().Set("output", "name") - tf.Printer, _, _ = cmdutil.PrinterForCommand(cmd) + mapper, typer := f.Object() + tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper} opts := ImageOptions{FilenameOptions: resource.FilenameOptions{ Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}}, @@ -63,7 +65,7 @@ func TestImageLocal(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - if !strings.Contains(buf.String(), "replicationcontroller/cassandra") { + if !strings.Contains(buf.String(), "replicationcontrollers/cassandra") { t.Errorf("did not set image: %s", buf.String()) } } diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 0ce1591dc52..d615e186ad5 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -466,8 +466,8 @@ func (f *FakeFactory) NewBuilder() *resource.Builder { return nil } -func (f *FakeFactory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions { - return &kubectl.PrintOptions{} +func (f *FakeFactory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions { + return &printers.PrintOptions{} } func (f *FakeFactory) DefaultResourceFilterFunc() kubectl.Filters { diff --git a/pkg/kubectl/history.go b/pkg/kubectl/history.go index b64a34d7938..b13491fadde 100644 --- a/pkg/kubectl/history.go +++ b/pkg/kubectl/history.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "io" + "text/tabwriter" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -30,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" sliceutil "k8s.io/kubernetes/pkg/util/slice" ) @@ -55,6 +57,7 @@ type DeploymentHistoryViewer struct { } // ViewHistory returns a revision-to-replicaset map as the revision history of a deployment +// TODO: this should be a describer func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) { versionedClient := versionedClientsetForDeployment(h.c) deployment, err := versionedClient.Extensions().Deployments(namespace).Get(name, metav1.GetOptions{}) @@ -101,7 +104,7 @@ func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision i if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil { return "", fmt.Errorf("failed to convert podtemplate, %v", err) } - DescribePodTemplate(internalTemplate, buf) + printersinternal.DescribePodTemplate(internalTemplate, buf) return buf.String(), nil } @@ -126,6 +129,22 @@ func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision i }) } +// TODO: copied here until this becomes a describer +func tabbedString(f func(io.Writer) error) (string, error) { + out := new(tabwriter.Writer) + buf := &bytes.Buffer{} + out.Init(buf, 0, 8, 1, '\t', 0) + + err := f(out) + if err != nil { + return "", err + } + + out.Flush() + str := string(buf.String()) + return str, nil +} + // getChangeCause returns the change-cause annotation of the input object func getChangeCause(obj runtime.Object) string { accessor, err := meta.Accessor(obj) diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index 3f812dddf0a..5b6594a3aaa 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -24,7 +24,6 @@ import ( "strings" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/kubernetes/pkg/api" ) const ( @@ -35,18 +34,6 @@ type NamespaceInfo struct { Namespace string } -func listOfImages(spec *api.PodSpec) []string { - images := make([]string, 0, len(spec.Containers)) - for _, container := range spec.Containers { - images = append(images, container.Image) - } - return images -} - -func makeImageList(spec *api.PodSpec) string { - return strings.Join(listOfImages(spec), ",") -} - // ResourceShortcuts represents a structure that holds the information how to // transition from resource's shortcut to its full name. type ResourceShortcuts struct { diff --git a/pkg/kubectl/metricsutil/metrics_printer.go b/pkg/kubectl/metricsutil/metrics_printer.go index cbc67b32549..576926a6ba4 100644 --- a/pkg/kubectl/metricsutil/metrics_printer.go +++ b/pkg/kubectl/metricsutil/metrics_printer.go @@ -23,7 +23,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metricsapi "k8s.io/heapster/metrics/apis/metrics/v1alpha1" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/printers" ) var ( @@ -55,7 +55,7 @@ func (printer *TopCmdPrinter) PrintNodeMetrics(metrics []metricsapi.NodeMetrics, if len(metrics) == 0 { return nil } - w := kubectl.GetNewTabWriter(printer.out) + w := printers.GetNewTabWriter(printer.out) defer w.Flush() printColumnNames(w, NodeColumns) @@ -78,7 +78,7 @@ func (printer *TopCmdPrinter) PrintPodMetrics(metrics []metricsapi.PodMetrics, p if len(metrics) == 0 { return nil } - w := kubectl.GetNewTabWriter(printer.out) + w := printers.GetNewTabWriter(printer.out) defer w.Flush() if withNamespace { diff --git a/pkg/kubectl/resource_filter.go b/pkg/kubectl/resource_filter.go index 5443274ae62..34b5dba9157 100644 --- a/pkg/kubectl/resource_filter.go +++ b/pkg/kubectl/resource_filter.go @@ -20,12 +20,13 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/printers" ) // FilterFunc is a function that knows how to filter a specific resource kind. // It receives a generic runtime.Object which must be type-checked by the function. // Returns a boolean value true if a resource is filtered, or false otherwise. -type FilterFunc func(runtime.Object, PrintOptions) bool +type FilterFunc func(runtime.Object, printers.PrintOptions) bool // Filters is a collection of filter funcs type Filters []FilterFunc @@ -38,7 +39,7 @@ func NewResourceFilter() Filters { // filterPods returns true if a pod should be skipped. // defaults to true for terminated pods -func filterPods(obj runtime.Object, options PrintOptions) bool { +func filterPods(obj runtime.Object, options printers.PrintOptions) bool { switch p := obj.(type) { case *v1.Pod: reason := string(p.Status.Phase) @@ -57,7 +58,7 @@ func filterPods(obj runtime.Object, options PrintOptions) bool { } // Filter loops through a collection of FilterFuncs until it finds one that can filter the given resource -func (f Filters) Filter(obj runtime.Object, opts *PrintOptions) (bool, error) { +func (f Filters) Filter(obj runtime.Object, opts *printers.PrintOptions) (bool, error) { // check if the object is unstructured. If so, let's attempt to convert it to a type we can understand // before apply filter func. obj, _ = DecodeUnknownObject(obj) diff --git a/pkg/kubectl/rollback.go b/pkg/kubectl/rollback.go index 16c174f0f49..32f9140669d 100644 --- a/pkg/kubectl/rollback.go +++ b/pkg/kubectl/rollback.go @@ -33,6 +33,7 @@ import ( externalextensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" sliceutil "k8s.io/kubernetes/pkg/util/slice" ) @@ -170,7 +171,7 @@ func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRe if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil { return "", fmt.Errorf("failed to convert podtemplate, %v", err) } - DescribePodTemplate(internalTemplate, buf) + printersinternal.DescribePodTemplate(internalTemplate, buf) return buf.String(), nil } @@ -188,6 +189,6 @@ func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRe if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil { return "", fmt.Errorf("failed to convert podtemplate, %v", err) } - DescribePodTemplate(internalTemplate, buf) + printersinternal.DescribePodTemplate(internalTemplate, buf) return buf.String(), nil } diff --git a/pkg/kubectl/sorting_printer.go b/pkg/kubectl/sorting_printer.go index 381998c9f16..6243b6c0e68 100644 --- a/pkg/kubectl/sorting_printer.go +++ b/pkg/kubectl/sorting_printer.go @@ -31,13 +31,14 @@ import ( "k8s.io/client-go/util/integer" "k8s.io/client-go/util/jsonpath" "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/printers" ) // Sorting printer sorts list types before delegating to another printer. // Non-list types are simply passed through type SortingPrinter struct { SortField string - Delegate ResourcePrinter + Delegate printers.ResourcePrinter Decoder runtime.Decoder } @@ -101,7 +102,7 @@ func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput stri } } - field, err := massageJSONPath(fieldInput) + field, err := printers.RelaxedJSONPathExpression(fieldInput) if err != nil { return nil, err } From 651188d68753d9379f19308f754842e354e789c8 Mon Sep 17 00:00:00 2001 From: Clayton Coleman Date: Sun, 19 Feb 2017 18:09:03 -0500 Subject: [PATCH 8/8] generated: bazel --- cmd/kubeadm/app/cmd/BUILD | 2 +- hack/.linted_packages | 1 + pkg/BUILD | 1 + pkg/kubectl/BUILD | 35 +--------------- pkg/kubectl/cmd/BUILD | 5 +++ pkg/kubectl/cmd/config/BUILD | 4 +- pkg/kubectl/cmd/set/BUILD | 1 + pkg/kubectl/cmd/testing/BUILD | 1 + pkg/kubectl/cmd/util/BUILD | 2 + pkg/kubectl/metricsutil/BUILD | 2 +- pkg/printers/BUILD | 66 ++++++++++++++++++++++++++++++ pkg/printers/internalversion/BUILD | 1 - vendor/BUILD | 7 ++++ 13 files changed, 91 insertions(+), 37 deletions(-) create mode 100644 pkg/printers/BUILD diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index b41ce591770..b2384f73e6d 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -39,8 +39,8 @@ go_library( "//cmd/kubeadm/app/util/kubeconfig:go_default_library", "//cmd/kubeadm/app/util/token:go_default_library", "//pkg/bootstrap/api:go_default_library", - "//pkg/kubectl:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", + "//pkg/printers:go_default_library", "//pkg/util/initsystem:go_default_library", "//pkg/version:go_default_library", "//vendor:github.com/blang/semver", diff --git a/hack/.linted_packages b/hack/.linted_packages index 65ece82c7f8..ed41208ca32 100644 --- a/hack/.linted_packages +++ b/hack/.linted_packages @@ -180,6 +180,7 @@ pkg/kubelet/volumemanager/cache pkg/kubelet/volumemanager/populator pkg/kubelet/volumemanager/reconciler pkg/labels +pkg/printers pkg/proxy/config pkg/proxy/healthcheck pkg/quota diff --git a/pkg/BUILD b/pkg/BUILD index 1307dffd852..58528cb8bfa 100644 --- a/pkg/BUILD +++ b/pkg/BUILD @@ -92,6 +92,7 @@ filegroup( "//pkg/labels:all-srcs", "//pkg/master:all-srcs", "//pkg/metrics:all-srcs", + "//pkg/printers:all-srcs", "//pkg/probe:all-srcs", "//pkg/proxy:all-srcs", "//pkg/quota:all-srcs", diff --git a/pkg/kubectl/BUILD b/pkg/kubectl/BUILD index c530c94d945..e1e3011dcd2 100644 --- a/pkg/kubectl/BUILD +++ b/pkg/kubectl/BUILD @@ -17,9 +17,7 @@ go_library( "cluster.go", "clusterrolebinding.go", "configmap.go", - "custom_column_printer.go", "deployment.go", - "describe.go", "doc.go", "explain.go", "generate.go", @@ -31,7 +29,6 @@ go_library( "proxy_server.go", "quota.go", "resource_filter.go", - "resource_printer.go", "rolebinding.go", "rollback.go", "rolling_updater.go", @@ -44,31 +41,24 @@ go_library( "service.go", "service_basic.go", "serviceaccount.go", - "sorted_resource_name_list.go", "sorting_printer.go", "stop.go", "versioned_client.go", ], tags = ["automanaged"], deps = [ - "//federation/apis/federation:go_default_library", "//federation/apis/federation/v1beta1:go_default_library", - "//federation/client/clientset_generated/federation_internalclientset:go_default_library", "//pkg/api:go_default_library", "//pkg/api/annotations:go_default_library", - "//pkg/api/events:go_default_library", "//pkg/api/util:go_default_library", "//pkg/api/v1:go_default_library", "//pkg/apis/apps:go_default_library", "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/batch:go_default_library", - "//pkg/apis/certificates:go_default_library", "//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions/v1beta1:go_default_library", "//pkg/apis/policy:go_default_library", "//pkg/apis/rbac:go_default_library", - "//pkg/apis/storage:go_default_library", - "//pkg/apis/storage/util:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library", "//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library", "//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library", @@ -81,14 +71,12 @@ go_library( "//pkg/client/unversioned:go_default_library", "//pkg/controller/deployment/util:go_default_library", "//pkg/credentialprovider:go_default_library", - "//pkg/fieldpath:go_default_library", "//pkg/kubectl/resource:go_default_library", - "//pkg/kubelet/qos:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/printers/internalversion:go_default_library", "//pkg/util:go_default_library", - "//pkg/util/node:go_default_library", "//pkg/util/slice:go_default_library", "//vendor:github.com/emicklei/go-restful/swagger", - "//vendor:github.com/ghodss/yaml", "//vendor:github.com/golang/glog", "//vendor:github.com/spf13/cobra", "//vendor:github.com/spf13/pflag", @@ -104,12 +92,10 @@ go_library( "//vendor:k8s.io/apimachinery/pkg/types", "//vendor:k8s.io/apimachinery/pkg/util/errors", "//vendor:k8s.io/apimachinery/pkg/util/intstr", - "//vendor:k8s.io/apimachinery/pkg/util/sets", "//vendor:k8s.io/apimachinery/pkg/util/uuid", "//vendor:k8s.io/apimachinery/pkg/util/validation", "//vendor:k8s.io/apimachinery/pkg/util/wait", "//vendor:k8s.io/apimachinery/pkg/watch", - "//vendor:k8s.io/client-go/dynamic", "//vendor:k8s.io/client-go/rest", "//vendor:k8s.io/client-go/util/integer", "//vendor:k8s.io/client-go/util/jsonpath", @@ -121,15 +107,12 @@ go_test( srcs = [ "cluster_test.go", "configmap_test.go", - "custom_column_printer_test.go", "deployment_test.go", - "describe_test.go", "generate_test.go", "kubectl_test.go", "namespace_test.go", "proxy_server_test.go", "quota_test.go", - "resource_printer_test.go", "rolling_updater_test.go", "rollout_status_test.go", "run_test.go", @@ -140,46 +123,32 @@ go_test( "service_basic_test.go", "service_test.go", "serviceaccount_test.go", - "sorted_resource_name_list_test.go", "sorting_printer_test.go", "stop_test.go", ], library = ":go_default_library", tags = ["automanaged"], deps = [ - "//federation/apis/federation:go_default_library", "//federation/apis/federation/v1beta1:go_default_library", - "//federation/client/clientset_generated/federation_internalclientset/fake:go_default_library", "//pkg/api:go_default_library", "//pkg/api/testapi:go_default_library", "//pkg/api/testing:go_default_library", "//pkg/api/v1:go_default_library", - "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/batch:go_default_library", "//pkg/apis/extensions:go_default_library", - "//pkg/apis/extensions/v1beta1:go_default_library", - "//pkg/apis/policy:go_default_library", - "//pkg/apis/storage:go_default_library", - "//pkg/client/clientset_generated/clientset/fake:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/client/clientset_generated/internalclientset/fake:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library", "//pkg/controller/deployment/util:go_default_library", - "//pkg/kubectl/testing:go_default_library", - "//pkg/util:go_default_library", - "//vendor:github.com/ghodss/yaml", "//vendor:github.com/spf13/cobra", - "//vendor:k8s.io/apimachinery/pkg/api/equality", "//vendor:k8s.io/apimachinery/pkg/api/errors", "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", "//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime/schema", - "//vendor:k8s.io/apimachinery/pkg/runtime/serializer/yaml", - "//vendor:k8s.io/apimachinery/pkg/util/diff", "//vendor:k8s.io/apimachinery/pkg/util/intstr", "//vendor:k8s.io/apimachinery/pkg/util/sets", "//vendor:k8s.io/apimachinery/pkg/watch", diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index 95645cd6b92..b249ac2596f 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -90,6 +90,8 @@ go_library( "//pkg/kubectl/resource:go_default_library", "//pkg/kubelet/server/remotecommand:go_default_library", "//pkg/kubelet/types:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/printers/internalversion:go_default_library", "//pkg/util/crlf:go_default_library", "//pkg/util/exec:go_default_library", "//pkg/util/i18n:go_default_library", @@ -198,6 +200,8 @@ go_test( "//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/resource:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/printers/internalversion:go_default_library", "//pkg/util/strings:go_default_library", "//pkg/util/term:go_default_library", "//vendor:github.com/spf13/cobra", @@ -208,6 +212,7 @@ go_test( "//vendor:k8s.io/apimachinery/pkg/api/meta", "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", "//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime/schema", "//vendor:k8s.io/apimachinery/pkg/runtime/serializer/json", diff --git a/pkg/kubectl/cmd/config/BUILD b/pkg/kubectl/cmd/config/BUILD index 025d8902b70..c5f250b9032 100644 --- a/pkg/kubectl/cmd/config/BUILD +++ b/pkg/kubectl/cmd/config/BUILD @@ -28,11 +28,13 @@ go_library( ], tags = ["automanaged"], deps = [ - "//pkg/kubectl:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", + "//pkg/printers:go_default_library", "//pkg/util/i18n:go_default_library", "//vendor:github.com/spf13/cobra", + "//vendor:k8s.io/apimachinery/pkg/api/meta", + "//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/util/errors", "//vendor:k8s.io/apimachinery/pkg/util/sets", "//vendor:k8s.io/apiserver/pkg/util/flag", diff --git a/pkg/kubectl/cmd/set/BUILD b/pkg/kubectl/cmd/set/BUILD index fe0fee4dda5..90aaf6f715d 100644 --- a/pkg/kubectl/cmd/set/BUILD +++ b/pkg/kubectl/cmd/set/BUILD @@ -56,6 +56,7 @@ go_test( "//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/resource:go_default_library", + "//pkg/printers:go_default_library", "//vendor:github.com/spf13/cobra", "//vendor:github.com/stretchr/testify/assert", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", diff --git a/pkg/kubectl/cmd/testing/BUILD b/pkg/kubectl/cmd/testing/BUILD index d8835d8bcfc..97c30c66791 100644 --- a/pkg/kubectl/cmd/testing/BUILD +++ b/pkg/kubectl/cmd/testing/BUILD @@ -20,6 +20,7 @@ go_library( "//pkg/kubectl:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/resource:go_default_library", + "//pkg/printers:go_default_library", "//vendor:github.com/emicklei/go-restful/swagger", "//vendor:github.com/spf13/cobra", "//vendor:github.com/spf13/pflag", diff --git a/pkg/kubectl/cmd/util/BUILD b/pkg/kubectl/cmd/util/BUILD index 7df57bacab3..884cd375f5c 100644 --- a/pkg/kubectl/cmd/util/BUILD +++ b/pkg/kubectl/cmd/util/BUILD @@ -38,6 +38,8 @@ go_library( "//pkg/controller:go_default_library", "//pkg/kubectl:go_default_library", "//pkg/kubectl/resource:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/printers/internalversion:go_default_library", "//pkg/util/exec:go_default_library", "//pkg/version:go_default_library", "//vendor:github.com/emicklei/go-restful/swagger", diff --git a/pkg/kubectl/metricsutil/BUILD b/pkg/kubectl/metricsutil/BUILD index 68ab2115154..378a5b623d4 100644 --- a/pkg/kubectl/metricsutil/BUILD +++ b/pkg/kubectl/metricsutil/BUILD @@ -18,7 +18,7 @@ go_library( "//pkg/api:go_default_library", "//pkg/api/validation:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", - "//pkg/kubectl:go_default_library", + "//pkg/printers:go_default_library", "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/labels", diff --git a/pkg/printers/BUILD b/pkg/printers/BUILD new file mode 100644 index 00000000000..2fa84d674a0 --- /dev/null +++ b/pkg/printers/BUILD @@ -0,0 +1,66 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = [ + "common.go", + "customcolumn.go", + "humanreadable.go", + "interface.go", + "json.go", + "jsonpath.go", + "name.go", + "printers.go", + "tabwriter.go", + "template.go", + "versioned.go", + ], + tags = ["automanaged"], + deps = [ + "//vendor:github.com/ghodss/yaml", + "//vendor:github.com/golang/glog", + "//vendor:k8s.io/apimachinery/pkg/api/meta", + "//vendor:k8s.io/apimachinery/pkg/labels", + "//vendor:k8s.io/apimachinery/pkg/runtime", + "//vendor:k8s.io/apimachinery/pkg/runtime/schema", + "//vendor:k8s.io/apimachinery/pkg/util/errors", + "//vendor:k8s.io/client-go/util/jsonpath", + ], +) + +go_test( + name = "go_default_xtest", + srcs = ["customcolumn_test.go"], + tags = ["automanaged"], + deps = [ + "//pkg/api:go_default_library", + "//pkg/api/v1:go_default_library", + "//pkg/printers:go_default_library", + "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + "//vendor:k8s.io/apimachinery/pkg/runtime", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//pkg/printers/internalversion:all-srcs", + ], + tags = ["automanaged"], +) diff --git a/pkg/printers/internalversion/BUILD b/pkg/printers/internalversion/BUILD index d8c73ed6845..18e7dfa067d 100644 --- a/pkg/printers/internalversion/BUILD +++ b/pkg/printers/internalversion/BUILD @@ -62,7 +62,6 @@ go_library( "//pkg/api:go_default_library", "//pkg/api/annotations:go_default_library", "//pkg/api/events:go_default_library", - "//pkg/api/v1:go_default_library", "//pkg/apis/apps:go_default_library", "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/batch:go_default_library", diff --git a/vendor/BUILD b/vendor/BUILD index bca564119a4..3b756adae29 100644 --- a/vendor/BUILD +++ b/vendor/BUILD @@ -17119,3 +17119,10 @@ go_library( "//vendor:k8s.io/kube-aggregator/pkg/apis/apiregistration/install", ], ) + +go_test( + name = "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured_test", + srcs = ["k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_test.go"], + library = ":k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", + tags = ["automanaged"], +)