diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 3ec2be83235..0ce1591dc52 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -41,6 +41,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" ) type InternalType struct { @@ -214,13 +215,15 @@ type TestFactory struct { Typer runtime.ObjectTyper Client kubectl.RESTClient UnstructuredClient kubectl.RESTClient - Describer kubectl.Describer - Printer kubectl.ResourcePrinter + Describer printers.Describer + Printer printers.ResourcePrinter + CommandPrinter printers.ResourcePrinter Validator validation.Schema Namespace string ClientConfig *restclient.Config Err error Command string + GenericPrinter bool ClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error) UnstructuredClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error) @@ -331,11 +334,15 @@ func (f *FakeFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (r return f.tf.UnstructuredClient, f.tf.Err } -func (f *FakeFactory) Describer(*meta.RESTMapping) (kubectl.Describer, error) { +func (f *FakeFactory) Describer(*meta.RESTMapping) (printers.Describer, error) { return f.tf.Describer, f.tf.Err } -func (f *FakeFactory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { +func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) { + return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err +} + +func (f *FakeFactory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } @@ -451,7 +458,7 @@ func (f *FakeFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, ob return nil } -func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) { +func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } @@ -587,11 +594,15 @@ func (f *fakeAPIFactory) UnstructuredClientForMapping(m *meta.RESTMapping) (reso return f.tf.UnstructuredClient, f.tf.Err } -func (f *fakeAPIFactory) Describer(*meta.RESTMapping) (kubectl.Describer, error) { +func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) { + return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err +} + +func (f *fakeAPIFactory) Describer(*meta.RESTMapping) (printers.Describer, error) { return f.tf.Describer, f.tf.Err } -func (f *fakeAPIFactory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { +func (f *fakeAPIFactory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } @@ -664,7 +675,7 @@ func (f *fakeAPIFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, return printer.PrintObj(obj, out) } -func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) { +func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 6e213a2038b..e19ce2e600c 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -51,6 +51,7 @@ import ( coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" ) const ( @@ -136,7 +137,8 @@ type ClientAccessFactory interface { // BindExternalFlags adds any flags defined by external projects (not part of pflags) BindExternalFlags(flags *pflag.FlagSet) - DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions + // TODO: Break the dependency on cmd here. + DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions // DefaultResourceFilterFunc returns a collection of FilterFuncs suitable for filtering specific resource types. DefaultResourceFilterFunc() kubectl.Filters @@ -144,7 +146,7 @@ type ClientAccessFactory interface { SuggestedPodTemplateResources() []schema.GroupResource // Returns a Printer for formatting objects of the given type or an error. - Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) + Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) // Pauser marks the object in the info as paused. Currently supported only for Deployments. // Returns the patched object in bytes and any error that occured during the encoding or // in case the object is already paused. @@ -193,7 +195,7 @@ type ObjectMappingFactory interface { // Returns a RESTClient for working with Unstructured objects. UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) // Returns a Describer for displaying the specified RESTMapping type or an error. - Describer(mapping *meta.RESTMapping) (kubectl.Describer, error) + Describer(mapping *meta.RESTMapping) (printers.Describer, error) // LogsForObject returns a request for the logs associated with the provided object LogsForObject(object, options runtime.Object) (*restclient.Request, error) @@ -211,10 +213,6 @@ type ObjectMappingFactory interface { // AttachablePodForObject returns the pod to which to attach given an object. AttachablePodForObject(object runtime.Object) (*api.Pod, error) - // PrinterForMapping returns a printer suitable for displaying the provided resource type. - // Requires that printer flags have been added to cmd (see AddPrinterFlags). - PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) - // Returns a schema that can validate objects stored on disk. Validator(validate bool, cacheDir string) (validation.Schema, error) // SwaggerSchema returns the schema declaration for the provided group version kind. @@ -224,6 +222,14 @@ type ObjectMappingFactory interface { // BuilderFactory holds the second level of factory methods. These functions depend upon ObjectMappingFactory and ClientAccessFactory methods. // Generally they depend upon client mapper functions type BuilderFactory interface { + // PrinterForCommand returns the default printer for the command. It requires that certain options + // are declared on the command (see AddPrinterFlags). Returns a printer, true if the printer is + // generic (is not internal), or an error if a printer could not be found. + // TODO: Break the dependency on cmd here. + PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) + // PrinterForMapping returns a printer suitable for displaying the provided resource type. + // Requires that printer flags have been added to cmd (see AddPrinterFlags). + PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) // PrintObject prints an api object given command line flags to modify the output format PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error // One stop shopping for a Builder diff --git a/pkg/kubectl/cmd/util/factory_builder.go b/pkg/kubectl/cmd/util/factory_builder.go index c11652d13e5..4667e5bc560 100644 --- a/pkg/kubectl/cmd/util/factory_builder.go +++ b/pkg/kubectl/cmd/util/factory_builder.go @@ -19,13 +19,16 @@ limitations under the License. package util import ( + "fmt" "io" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" ) type ring2Factory struct { @@ -42,6 +45,54 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac return f } +func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) { + mapper, typer := f.objectMappingFactory.Object() + // TODO: used by the custom column implementation and the name implementation, break this dependency + decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme} + return PrinterForCommand(cmd, mapper, typer, decoders) +} + +func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { + printer, generic, err := f.PrinterForCommand(cmd) + if err != nil { + return nil, err + } + + // Make sure we output versioned data for generic printers + if generic { + if mapping == nil { + return nil, fmt.Errorf("no serialization format found") + } + version := mapping.GroupVersionKind.GroupVersion() + if version.Empty() { + return nil, fmt.Errorf("no serialization format found") + } + + printer = printers.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion()) + } else { + // Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper + columnLabel, err := cmd.Flags().GetStringSlice("label-columns") + if err != nil { + columnLabel = []string{} + } + printer, err = f.clientAccessFactory.Printer(mapping, printers.PrintOptions{ + NoHeaders: GetFlagBool(cmd, "no-headers"), + WithNamespace: withNamespace, + Wide: GetWideFlag(cmd), + ShowAll: GetFlagBool(cmd, "show-all"), + ShowLabels: GetFlagBool(cmd, "show-labels"), + AbsoluteTimestamps: isWatch(cmd), + ColumnLabels: columnLabel, + }) + if err != nil { + return nil, err + } + printer = maybeWrapSortingPrinter(cmd, printer) + } + + return printer, nil +} + func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error { // try to get a typed object _, typer := f.objectMappingFactory.Object() @@ -66,7 +117,7 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, o return err } - printer, err := f.objectMappingFactory.PrinterForMapping(cmd, mapping, false) + printer, err := f.PrinterForMapping(cmd, mapping, false) if err != nil { return err } diff --git a/pkg/kubectl/cmd/util/factory_client_access.go b/pkg/kubectl/cmd/util/factory_client_access.go index 4c271c8ace2..605a5bdf9c0 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -49,6 +49,8 @@ import ( "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" ) type ring0Factory struct { @@ -360,12 +362,12 @@ func (f *ring0Factory) BindExternalFlags(flags *pflag.FlagSet) { flags.AddGoFlagSet(flag.CommandLine) } -func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions { +func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions { columnLabel, err := cmd.Flags().GetStringSlice("label-columns") if err != nil { columnLabel = []string{} } - opts := &kubectl.PrintOptions{ + opts := &printers.PrintOptions{ NoHeaders: GetFlagBool(cmd, "no-headers"), WithNamespace: withNamespace, Wide: GetWideFlag(cmd), @@ -392,8 +394,10 @@ func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource { } } -func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) { - return kubectl.NewHumanReadablePrinter(options), nil +func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) { + p := printers.NewHumanReadablePrinter(options) + printersinternal.AddHandlers(p) + return p, nil } func (f *ring0Factory) Pauser(info *resource.Info) ([]byte, error) { diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index b54c447fcea..9e2d43639ab 100644 --- a/pkg/kubectl/cmd/util/factory_object_mapping.go +++ b/pkg/kubectl/cmd/util/factory_object_mapping.go @@ -27,7 +27,6 @@ import ( "time" "github.com/emicklei/go-restful/swagger" - "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,6 +47,8 @@ import ( "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" + printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" ) type ring1Factory struct { @@ -58,10 +59,12 @@ func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMapp f := &ring1Factory{ clientAccessFactory: clientAccessFactory, } - return f } +// TODO: This method should return an error now that it can fail. Alternatively, it needs to +// return lazy implementations of mapper and typer that don't hit the wire until they are +// invoked. func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) { mapper := api.Registry.RESTMapper() discoveryClient, err := f.clientAccessFactory.DiscoveryClient() @@ -143,7 +146,7 @@ func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) ( return restclient.RESTClientFor(cfg) } -func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, error) { +func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (printers.Describer, error) { mappingVersion := mapping.GroupVersionKind.GroupVersion() if mapping.GroupVersionKind.Group == federation.GroupName { fedClientSet, err := f.clientAccessFactory.FederationClientSetForVersion(&mappingVersion) @@ -151,7 +154,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, return nil, err } if mapping.GroupVersionKind.Kind == "Cluster" { - return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil + return &printersinternal.ClusterDescriber{Interface: fedClientSet}, nil } } @@ -166,7 +169,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, } // try to get a describer - if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok { + if describer, ok := printersinternal.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok { return describer, nil } // if this is a kind we don't have a describer for yet, go generic if possible @@ -178,7 +181,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, } // helper function to make a generic describer, or return an error -func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RESTMapping) (kubectl.Describer, error) { +func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RESTMapping) (printers.Describer, error) { clientConfig, err := clientAccessFactory.ClientConfig() if err != nil { return nil, err @@ -202,7 +205,7 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES } eventsClient := clientSet.Core() - return kubectl.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil + return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil } func (f *ring1Factory) LogsForObject(object, options runtime.Object) (*restclient.Request, error) { @@ -361,48 +364,6 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod, return pod, err } -func (f *ring1Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) { - printer, generic, err := PrinterForCommand(cmd) - if err != nil { - return nil, err - } - - // Make sure we output versioned data for generic printers - if generic { - if mapping == nil { - return nil, fmt.Errorf("no serialization format found") - } - version := mapping.GroupVersionKind.GroupVersion() - if version.Empty() { - return nil, fmt.Errorf("no serialization format found") - } - - printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion()) - - } else { - // Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper - columnLabel, err := cmd.Flags().GetStringSlice("label-columns") - if err != nil { - columnLabel = []string{} - } - printer, err = f.clientAccessFactory.Printer(mapping, kubectl.PrintOptions{ - NoHeaders: GetFlagBool(cmd, "no-headers"), - WithNamespace: withNamespace, - Wide: GetWideFlag(cmd), - ShowAll: GetFlagBool(cmd, "show-all"), - ShowLabels: GetFlagBool(cmd, "show-labels"), - AbsoluteTimestamps: isWatch(cmd), - ColumnLabels: columnLabel, - }) - if err != nil { - return nil, err - } - printer = maybeWrapSortingPrinter(cmd, printer) - } - - return printer, nil -} - func (f *ring1Factory) Validator(validate bool, cacheDir string) (validation.Schema, error) { if validate { discovery, err := f.clientAccessFactory.DiscoveryClient() diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 80ea52efa97..b2bda9d9769 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -47,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" utilexec "k8s.io/kubernetes/pkg/util/exec" ) @@ -672,7 +673,7 @@ func MustPrintWithKinds(objs []runtime.Object, infos []*resource.Info, sorter *k // FilterResourceList receives a list of runtime objects. // If any objects are filtered, that number is returned along with a modified list. -func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *kubectl.PrintOptions) (int, []runtime.Object, error) { +func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *printers.PrintOptions) (int, []runtime.Object, error) { items, err := meta.ExtractList(obj) if err != nil { return 0, []runtime.Object{obj}, utilerrors.NewAggregate([]error{err}) @@ -697,7 +698,7 @@ func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterO return filterCount, list, nil } -func PrintFilterCount(hiddenObjNum int, resource string, options *kubectl.PrintOptions) { +func PrintFilterCount(hiddenObjNum int, resource string, options *printers.PrintOptions) { if !options.NoHeaders && !options.ShowAll && hiddenObjNum > 0 { glog.V(2).Infof(" info: %d completed object(s) was(were) not shown in %s list. Pass --show-all to see all objects.\n\n", hiddenObjNum, resource) } diff --git a/pkg/kubectl/cmd/util/printing.go b/pkg/kubectl/cmd/util/printing.go index 9e309322d9a..62729316249 100644 --- a/pkg/kubectl/cmd/util/printing.go +++ b/pkg/kubectl/cmd/util/printing.go @@ -22,8 +22,10 @@ import ( "strings" "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/printers" "github.com/spf13/cobra" ) @@ -105,7 +107,7 @@ func ValidateOutputArgs(cmd *cobra.Command) error { // PrinterForCommand returns the default printer for this command. // Requires that printer flags have been added to cmd (see AddPrinterFlags). -func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error) { +func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime.ObjectTyper, decoders []runtime.Decoder) (printers.ResourcePrinter, bool, error) { outputFormat := GetFlagString(cmd, "output") // templates are logically optional for specifying a format. @@ -131,7 +133,10 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error if cmd.Flags().Lookup("allow-missing-template-keys") != nil { allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys") } - printer, generic, err := kubectl.GetPrinter(outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys) + printer, generic, err := printers.GetStandardPrinter( + outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys, + mapper, typer, decoders, + ) if err != nil { return nil, generic, err } @@ -144,7 +149,7 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error // object passed is non-generic, it attempts to print the object using a HumanReadablePrinter. // Requires that printer flags have been added to cmd (see AddPrinterFlags). func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Factory, out io.Writer) error { - printer, generic, err := PrinterForCommand(cmd) + printer, generic, err := f.PrinterForCommand(cmd) if err != nil { return err } @@ -157,7 +162,7 @@ func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Fact return printer.PrintObj(info.Object, out) } -func maybeWrapSortingPrinter(cmd *cobra.Command, printer kubectl.ResourcePrinter) kubectl.ResourcePrinter { +func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinter) printers.ResourcePrinter { sorting, err := cmd.Flags().GetString("sort-by") if err != nil { // error can happen on missing flag or bad flag type. In either case, this command didn't intent to sort