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.