From d3773b4b0618dd549c73a372c0a228eeac53a40e Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Mon, 30 Oct 2017 15:54:44 -0400 Subject: [PATCH] consolidate printer OutputOpts w PrintOpts This patch removes the use of printers.OutputOptions in favor of only having a single struct for setting / passing printer options set by user flags. --- pkg/kubectl/cmd/cmd_test.go | 2 +- pkg/kubectl/cmd/config/view.go | 3 +- pkg/kubectl/cmd/convert.go | 2 +- pkg/kubectl/cmd/resource/get.go | 43 ++++++------ pkg/kubectl/cmd/testing/fake.go | 18 ++--- pkg/kubectl/cmd/util/factory.go | 7 +- pkg/kubectl/cmd/util/factory_builder.go | 30 ++------ pkg/kubectl/cmd/util/factory_client_access.go | 18 ----- pkg/kubectl/cmd/util/printing.go | 70 +++++++++---------- pkg/printers/interface.go | 19 +++-- pkg/printers/internalversion/printers_test.go | 64 ++++++++--------- pkg/printers/printers.go | 10 +-- 12 files changed, 124 insertions(+), 162 deletions(-) diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index bf94b5ec77..dcfe0d490d 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -548,7 +548,7 @@ func Example_printPodHideTerminated() { podList := newAllPhasePodList() // filter pods filterFuncs := f.DefaultResourceFilterFunc() - filterOpts := f.DefaultResourceFilterOptions(cmd, false) + filterOpts := cmdutil.ExtractCmdPrintOptions(cmd, false) _, filteredPodList, errs := cmdutil.FilterResourceList(podList, filterFuncs, filterOpts) if errs != nil { fmt.Printf("Unexpected filter error: %v\n", errs) diff --git a/pkg/kubectl/cmd/config/view.go b/pkg/kubectl/cmd/config/view.go index c12418e9c1..70c8516f6e 100644 --- a/pkg/kubectl/cmd/config/view.go +++ b/pkg/kubectl/cmd/config/view.go @@ -81,7 +81,8 @@ func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess cmd.Flags().Set("output", defaultOutputFormat) } - printer, err := cmdutil.PrinterForCommand(cmd, nil, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, nil, []runtime.Decoder{latest.Codec}, printers.PrintOptions{}) + printOpts := cmdutil.ExtractCmdPrintOptions(cmd, false) + printer, err := cmdutil.PrinterForOptions(meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, nil, []runtime.Decoder{latest.Codec}, *printOpts) cmdutil.CheckErr(err) printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion) diff --git a/pkg/kubectl/cmd/convert.go b/pkg/kubectl/cmd/convert.go index 2af8be7b8d..44bf2a47e5 100644 --- a/pkg/kubectl/cmd/convert.go +++ b/pkg/kubectl/cmd/convert.go @@ -162,7 +162,7 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C cmd.Flags().Set("output", outputFormat) } o.encoder = f.JSONEncoder() - o.printer, err = f.PrinterForCommand(cmd, o.local, nil, printers.PrintOptions{}) + o.printer, err = f.PrinterForOptions(printers.PrintOptions{}) return err } diff --git a/pkg/kubectl/cmd/resource/get.go b/pkg/kubectl/cmd/resource/get.go index 1dd910218f..ee596e166b 100644 --- a/pkg/kubectl/cmd/resource/get.go +++ b/pkg/kubectl/cmd/resource/get.go @@ -254,12 +254,13 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str return err } - printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{}) + printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces) + printer, err := f.PrinterForOptions(*printOpts) if err != nil { return err } - filterOpts := f.DefaultResourceFilterOptions(cmd, options.AllNamespaces) + filterOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces) filterFuncs := f.DefaultResourceFilterFunc() if r.TargetsSingleItems() { filterFuncs = nil @@ -330,14 +331,14 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str printWithNamespace = false } - var outputOpts *printers.OutputOptions + printOpts := *cmdutil.ExtractCmdPrintOptions(cmd, printWithNamespace) // if cmd does not specify output format and useOpenAPIPrintColumnFlagLabel flag is true, // then get the default output options for this mapping from OpenAPI schema. if !cmdSpecifiesOutputFmt(cmd) && useOpenAPIPrintColumns { - outputOpts, _ = outputOptsForMappingFromOpenAPI(f, mapping) + outputOptsForMappingFromOpenAPI(f, mapping, &printOpts) } - printer, err = f.PrinterForMapping(cmd, false, outputOpts, mapping, printWithNamespace) + printer, err = f.PrinterForMapping(printOpts, mapping) if err != nil { if !errs.Has(err.Error()) { errs.Insert(err.Error()) @@ -470,7 +471,7 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s return i18n.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", len(infos)) } - filterOpts := f.DefaultResourceFilterOptions(cmd, options.AllNamespaces) + filterOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces) filterFuncs := f.DefaultResourceFilterFunc() if r.TargetsSingleItems() { filterFuncs = nil @@ -478,7 +479,8 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s info := infos[0] mapping := info.ResourceMapping() - printer, err := f.PrinterForMapping(cmd, false, nil, mapping, options.AllNamespaces) + printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces) + printer, err := f.PrinterForMapping(*printOpts, mapping) if err != nil { return err } @@ -662,43 +664,44 @@ func cmdSpecifiesOutputFmt(cmd *cobra.Command) bool { // outputOptsForMappingFromOpenAPI looks for the output format metatadata in the // openapi schema and returns the output options for the mapping if found. -func outputOptsForMappingFromOpenAPI(f cmdutil.Factory, mapping *meta.RESTMapping) (*printers.OutputOptions, bool) { +func outputOptsForMappingFromOpenAPI(f cmdutil.Factory, mapping *meta.RESTMapping, printOpts *printers.PrintOptions) bool { // user has not specified any output format, check if OpenAPI has // default specification to print this resource type api, err := f.OpenAPISchema() if err != nil { // Error getting schema - return nil, false + return false } // Found openapi metadata for this resource schema := api.LookupResource(mapping.GroupVersionKind) if schema == nil { // Schema not found, return empty columns - return nil, false + return false } columns, found := openapi.GetPrintColumns(schema.GetExtensions()) if !found { // Extension not found, return empty columns - return nil, false + return false } - return outputOptsFromStr(columns) + return outputOptsFromStr(columns, printOpts) } // outputOptsFromStr parses the print-column metadata and generates printer.OutputOptions object. -func outputOptsFromStr(columnStr string) (*printers.OutputOptions, bool) { +func outputOptsFromStr(columnStr string, printOpts *printers.PrintOptions) bool { if columnStr == "" { - return nil, false + return false } parts := strings.SplitN(columnStr, "=", 2) if len(parts) < 2 { - return nil, false + return false } - return &printers.OutputOptions{ - FmtType: parts[0], - FmtArg: parts[1], - AllowMissingKeys: true, - }, true + + printOpts.OutputFormatType = parts[0] + printOpts.OutputFormatArgument = parts[1] + printOpts.AllowMissingKeys = true + + return true } diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 5f55d24720..434ff8f3e2 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -352,17 +352,17 @@ func (f *FakeFactory) Describer(*meta.RESTMapping) (printers.Describer, error) { return f.tf.Describer, f.tf.Err } -func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) { +func (f *FakeFactory) PrinterForOptions(options printers.PrintOptions) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } func (f *FakeFactory) PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, out io.Writer) error { - printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{}) + printer, err := f.PrinterForOptions(printers.PrintOptions{}) if err != nil { return err } if !printer.IsGeneric() { - printer, err = f.PrinterForMapping(cmd, false, nil, nil, false) + printer, err = f.PrinterForMapping(printers.PrintOptions{}, nil) if err != nil { return err } @@ -513,7 +513,7 @@ func (f *FakeFactory) PrintObject(cmd *cobra.Command, isLocal bool, mapper meta. return nil } -func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { +func (f *FakeFactory) PrinterForMapping(printOpts printers.PrintOptions, mapping *meta.RESTMapping) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } @@ -744,17 +744,17 @@ func (f *fakeAPIFactory) UnstructuredClientForMapping(m *meta.RESTMapping) (reso return f.tf.UnstructuredClient, f.tf.Err } -func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) { +func (f *fakeAPIFactory) PrinterForOptions(options printers.PrintOptions) (printers.ResourcePrinter, error) { return f.tf.Printer, f.tf.Err } func (f *fakeAPIFactory) PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, out io.Writer) error { - printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{}) + printer, err := f.PrinterForOptions(printers.PrintOptions{}) if err != nil { return err } if !printer.IsGeneric() { - printer, err = f.PrinterForMapping(cmd, false, nil, nil, false) + printer, err = f.PrinterForMapping(printers.PrintOptions{}, nil) if err != nil { return err } @@ -851,14 +851,14 @@ func (f *fakeAPIFactory) PrintObject(cmd *cobra.Command, isLocal bool, mapper me return err } - printer, err := f.PrinterForMapping(cmd, isLocal, nil, mapping, false) + printer, err := f.PrinterForMapping(printers.PrintOptions{}, mapping) if err != nil { return err } return printer.PrintObj(obj, out) } -func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { +func (f *fakeAPIFactory) PrinterForMapping(outputOpts printers.PrintOptions, mapping *meta.RESTMapping) (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 7ed72e4c6c..aaab089752 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -140,8 +140,6 @@ type ClientAccessFactory interface { // BindExternalFlags adds any flags defined by external projects (not part of pflags) BindExternalFlags(flags *pflag.FlagSet) - // 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 @@ -232,13 +230,12 @@ 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, or an error if a printer // could not be found. - // TODO: Break the dependency on cmd here. - PrinterForCommand(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) + PrinterForOptions(options printers.PrintOptions) (printers.ResourcePrinter, error) // PrinterForMapping returns a printer suitable for displaying the provided resource type. // Requires that printer flags have been added to cmd (see AddPrinterFlags). // Returns a printer, true if the printer is generic (is not internal), or // an error if a printer could not be found. - PrinterForMapping(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) + PrinterForMapping(options printers.PrintOptions, mapping *meta.RESTMapping) (printers.ResourcePrinter, error) // PrintObject prints an api object given command line flags to modify the output format PrintObject(cmd *cobra.Command, isLocal bool, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error // PrintResourceInfoForCommand receives a *cobra.Command and a *resource.Info and diff --git a/pkg/kubectl/cmd/util/factory_builder.go b/pkg/kubectl/cmd/util/factory_builder.go index 0851670508..64c27d9528 100644 --- a/pkg/kubectl/cmd/util/factory_builder.go +++ b/pkg/kubectl/cmd/util/factory_builder.go @@ -47,7 +47,7 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac return f } -func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) { +func (f *ring2Factory) PrinterForOptions(options printers.PrintOptions) (printers.ResourcePrinter, error) { var mapper meta.RESTMapper var typer runtime.ObjectTyper @@ -56,27 +56,11 @@ func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, isLocal bool, outpu // TODO: used by the custom column implementation and the name implementation, break this dependency decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme} encoder := f.clientAccessFactory.JSONEncoder() - return PrinterForCommand(cmd, outputOpts, mapper, typer, encoder, decoders, options) + return PrinterForOptions(mapper, typer, encoder, decoders, options) } -func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) { - // 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{} - } - - options := 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, - } - - printer, err := f.PrinterForCommand(cmd, isLocal, outputOpts, options) +func (f *ring2Factory) PrinterForMapping(options printers.PrintOptions, mapping *meta.RESTMapping) (printers.ResourcePrinter, error) { + printer, err := f.PrinterForOptions(options) if err != nil { return nil, err } @@ -140,7 +124,7 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, isLocal bool, mapper meta return err } - printer, err := f.PrinterForMapping(cmd, isLocal, nil, mapping, false) + printer, err := f.PrinterForMapping(printers.PrintOptions{}, mapping) if err != nil { return err } @@ -148,12 +132,12 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, isLocal bool, mapper meta } func (f *ring2Factory) PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, out io.Writer) error { - printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{}) + printer, err := f.PrinterForOptions(printers.PrintOptions{}) if err != nil { return err } if !printer.IsGeneric() { - printer, err = f.PrinterForMapping(cmd, false, nil, nil, false) + printer, err = f.PrinterForMapping(printers.PrintOptions{}, nil) 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 76a752310e..bf354932a8 100644 --- a/pkg/kubectl/cmd/util/factory_client_access.go +++ b/pkg/kubectl/cmd/util/factory_client_access.go @@ -411,24 +411,6 @@ func (f *ring0Factory) BindExternalFlags(flags *pflag.FlagSet) { flags.AddGoFlagSet(flag.CommandLine) } -func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions { - columnLabel, err := cmd.Flags().GetStringSlice("label-columns") - if err != nil { - columnLabel = []string{} - } - opts := &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, - } - - return opts -} - func (f *ring0Factory) DefaultResourceFilterFunc() kubectl.Filters { return kubectl.NewResourceFilter() } diff --git a/pkg/kubectl/cmd/util/printing.go b/pkg/kubectl/cmd/util/printing.go index c051efe799..968a573238 100644 --- a/pkg/kubectl/cmd/util/printing.go +++ b/pkg/kubectl/cmd/util/printing.go @@ -81,23 +81,11 @@ func ValidateOutputArgs(cmd *cobra.Command) error { return nil } -// PrinterForCommand returns the printer for the outputOptions (if given) or +// printerForOptions returns the printer for the outputOptions (if given) or // returns the default printer for the command. Requires that printer flags have // been added to cmd (see AddPrinterFlags). -// TODO: remove the dependency on cmd object -func PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options printers.PrintOptions) (printers.ResourcePrinter, error) { - - if outputOpts == nil { - outputOpts = extractOutputOptions(cmd) - } - - // this function may be invoked by a command that did not call AddPrinterFlags first, so we need - // to be safe about how we access the no-headers flag - noHeaders := false - if cmd.Flags().Lookup("no-headers") != nil { - noHeaders = GetFlagBool(cmd, "no-headers") - } - printer, err := printers.GetStandardPrinter(outputOpts, noHeaders, mapper, typer, encoder, decoders, options) +func PrinterForOptions(mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options printers.PrintOptions) (printers.ResourcePrinter, error) { + printer, err := printers.GetStandardPrinter(mapper, typer, encoder, decoders, options) if err != nil { return nil, err } @@ -109,19 +97,39 @@ func PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, m printersinternal.AddHandlers(humanReadablePrinter) } - return maybeWrapSortingPrinter(cmd, printer), nil + return maybeWrapSortingPrinter(printer, options), nil } -// extractOutputOptions parses printer specific commandline args and returns -// printers.OutputsOptions object. -func extractOutputOptions(cmd *cobra.Command) *printers.OutputOptions { +// ExtractCmdPrintOptions parses printer specific commandline args and +// returns a PrintOptions object. +// Requires that printer flags have been added to cmd (see AddPrinterFlags) +func ExtractCmdPrintOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions { flags := cmd.Flags() + columnLabel, err := flags.GetStringSlice("label-columns") + if err != nil { + columnLabel = []string{} + } + + options := &printers.PrintOptions{ + NoHeaders: GetFlagBool(cmd, "no-headers"), + Wide: GetWideFlag(cmd), + ShowAll: GetFlagBool(cmd, "show-all"), + ShowLabels: GetFlagBool(cmd, "show-labels"), + AbsoluteTimestamps: isWatch(cmd), + ColumnLabels: columnLabel, + WithNamespace: withNamespace, + } + var outputFormat string if flags.Lookup("output") != nil { outputFormat = GetFlagString(cmd, "output") } + if flags.Lookup("sort-by") != nil { + options.SortBy = GetFlagString(cmd, "sort-by") + } + // templates are logically optional for specifying a format. // TODO once https://github.com/kubernetes/kubernetes/issues/12668 is fixed, this should fall back to GetFlagString var templateFile string @@ -146,29 +154,21 @@ func extractOutputOptions(cmd *cobra.Command) *printers.OutputOptions { // this function may be invoked by a command that did not call AddPrinterFlags first, so we need // to be safe about how we access the allow-missing-template-keys flag - allowMissingTemplateKeys := false if flags.Lookup("allow-missing-template-keys") != nil { - allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys") + options.AllowMissingKeys = GetFlagBool(cmd, "allow-missing-template-keys") } - return &printers.OutputOptions{ - FmtType: outputFormat, - FmtArg: templateFile, - AllowMissingKeys: allowMissingTemplateKeys, - } + options.OutputFormatType = outputFormat + options.OutputFormatArgument = templateFile + + return options } -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 - return printer - } - - if len(sorting) != 0 { +func maybeWrapSortingPrinter(printer printers.ResourcePrinter, printOpts printers.PrintOptions) printers.ResourcePrinter { + if len(printOpts.SortBy) != 0 { return &kubectl.SortingPrinter{ Delegate: printer, - SortField: fmt.Sprintf("{%s}", sorting), + SortField: fmt.Sprintf("{%s}", printOpts.SortBy), } } return printer diff --git a/pkg/printers/interface.go b/pkg/printers/interface.go index 5271215864..5bda6c6175 100644 --- a/pkg/printers/interface.go +++ b/pkg/printers/interface.go @@ -57,6 +57,10 @@ func (fn ResourcePrinterFunc) IsGeneric() bool { } type PrintOptions struct { + // supported Format types can be found in pkg/printers/printers.go + OutputFormatType string + OutputFormatArgument string + NoHeaders bool WithNamespace bool WithKind bool @@ -66,6 +70,11 @@ type PrintOptions struct { AbsoluteTimestamps bool Kind string ColumnLabels []string + + SortBy string + + // indicates if it is OK to ignore missing keys for rendering an output template. + AllowMissingKeys bool } // Describer generates output for the named resource or an error @@ -100,13 +109,3 @@ type ErrNoDescriber struct { func (e ErrNoDescriber) Error() string { return fmt.Sprintf("no describer has been defined for %v", e.Types) } - -// OutputOptions represents resource output options which is used to generate a resource printer. -type OutputOptions struct { - // supported Format types can be found in pkg/printers/printers.go - FmtType string - FmtArg string - - // indicates if it is OK to ignore missing keys for rendering an output template. - AllowMissingKeys bool -} diff --git a/pkg/printers/internalversion/printers_test.go b/pkg/printers/internalversion/printers_test.go index 6c1cf7ecea..8662c91fc3 100644 --- a/pkg/printers/internalversion/printers_test.go +++ b/pkg/printers/internalversion/printers_test.go @@ -96,7 +96,7 @@ func TestPrintDefault(t *testing.T) { } for _, test := range printerTests { - printer, err := printers.GetStandardPrinter(&printers.OutputOptions{AllowMissingKeys: false}, false, nil, nil, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{}) + printer, err := printers.GetStandardPrinter(nil, nil, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{AllowMissingKeys: false}) if err != nil { t.Errorf("in %s, unexpected error: %#v", test.Name, err) } @@ -254,24 +254,24 @@ func TestPrinter(t *testing.T) { printerTests := []struct { Name string - OutputOpts *printers.OutputOptions + PrintOpts *printers.PrintOptions Input runtime.Object OutputVersions []schema.GroupVersion Expect string }{ - {"test json", &printers.OutputOptions{FmtType: "json", AllowMissingKeys: true}, simpleTest, nil, "{\n \"Data\": \"foo\"\n}\n"}, - {"test yaml", &printers.OutputOptions{FmtType: "yaml", AllowMissingKeys: true}, simpleTest, nil, "Data: foo\n"}, - {"test template", &printers.OutputOptions{FmtType: "template", FmtArg: "{{if .id}}{{.id}}{{end}}{{if .metadata.name}}{{.metadata.name}}{{end}}", AllowMissingKeys: true}, + {"test json", &printers.PrintOptions{OutputFormatType: "json", AllowMissingKeys: true}, simpleTest, nil, "{\n \"Data\": \"foo\"\n}\n"}, + {"test yaml", &printers.PrintOptions{OutputFormatType: "yaml", AllowMissingKeys: true}, simpleTest, nil, "Data: foo\n"}, + {"test template", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{if .id}}{{.id}}{{end}}{{if .metadata.name}}{{.metadata.name}}{{end}}", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"}, - {"test jsonpath", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.metadata.name}", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"}, - {"test jsonpath list", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.items[*].metadata.name}", AllowMissingKeys: true}, podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"}, - {"test jsonpath empty list", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.items[*].metadata.name}", AllowMissingKeys: true}, emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""}, - {"test name", &printers.OutputOptions{FmtType: "name", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pods/foo\n"}, - {"emits versioned objects", &printers.OutputOptions{FmtType: "template", FmtArg: "{{.kind}}", AllowMissingKeys: true}, testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"}, + {"test jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.metadata.name}", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"}, + {"test jsonpath list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"}, + {"test jsonpath empty list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""}, + {"test name", &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pods/foo\n"}, + {"emits versioned objects", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.kind}}", AllowMissingKeys: true}, testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"}, } for _, test := range printerTests { buf := bytes.NewBuffer([]byte{}) - printer, err := printers.GetStandardPrinter(test.OutputOpts, false, legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{}) + printer, err := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts) if err != nil { t.Errorf("in %s, unexpected error: %#v", test.Name, err) } @@ -290,18 +290,18 @@ func TestPrinter(t *testing.T) { func TestBadPrinter(t *testing.T) { badPrinterTests := []struct { - Name string - OutputOpts *printers.OutputOptions - Error error + Name string + PrintOpts *printers.PrintOptions + Error error }{ - {"empty template", &printers.OutputOptions{FmtType: "template", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")}, - {"bad template", &printers.OutputOptions{FmtType: "template", FmtArg: "{{ .Name", AllowMissingKeys: false}, fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")}, - {"bad templatefile", &printers.OutputOptions{FmtType: "templatefile", AllowMissingKeys: false}, fmt.Errorf("templatefile format specified but no template file given")}, - {"bad jsonpath", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.Name", AllowMissingKeys: false}, fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")}, - {"unknown format", &printers.OutputOptions{FmtType: "anUnknownFormat", FmtArg: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")}, + {"empty template", &printers.PrintOptions{OutputFormatType: "template", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")}, + {"bad template", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{ .Name", AllowMissingKeys: false}, fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")}, + {"bad templatefile", &printers.PrintOptions{OutputFormatType: "templatefile", AllowMissingKeys: false}, fmt.Errorf("templatefile format specified but no template file given")}, + {"bad jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.Name", AllowMissingKeys: false}, fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")}, + {"unknown format", &printers.PrintOptions{OutputFormatType: "anUnknownFormat", OutputFormatArgument: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")}, } for _, test := range badPrinterTests { - _, err := printers.GetStandardPrinter(test.OutputOpts, false, legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{}) + _, err := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts) if err == nil || err.Error() != test.Error.Error() { t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err) } @@ -494,8 +494,8 @@ func TestNamePrinter(t *testing.T) { }, "pods/foo\npods/bar\n"}, } - outputOpts := &printers.OutputOptions{FmtType: "name", AllowMissingKeys: false} - printer, _ := printers.GetStandardPrinter(outputOpts, false, legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{}) + printOpts := &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: false} + printer, _ := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *printOpts) for name, item := range tests { buff := &bytes.Buffer{} err := printer.PrintObj(item.obj, buff) @@ -2793,20 +2793,20 @@ func TestPrintPodDisruptionBudget(t *testing.T) { func TestAllowMissingKeys(t *testing.T) { tests := []struct { - Name string - OutputOpts *printers.OutputOptions - Input runtime.Object - Expect string - Error string + Name string + PrintOpts *printers.PrintOptions + Input runtime.Object + Expect string + Error string }{ - {"test template, allow missing keys", &printers.OutputOptions{FmtType: "template", FmtArg: "{{.blarg}}", AllowMissingKeys: true}, &api.Pod{}, "", ""}, - {"test template, strict", &printers.OutputOptions{FmtType: "template", FmtArg: "{{.blarg}}", AllowMissingKeys: false}, &api.Pod{}, "", `error executing template "{{.blarg}}": template: output:1:2: executing "output" at <.blarg>: map has no entry for key "blarg"`}, - {"test jsonpath, allow missing keys", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.blarg}", AllowMissingKeys: true}, &api.Pod{}, "", ""}, - {"test jsonpath, strict", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.blarg}", AllowMissingKeys: false}, &api.Pod{}, "", "error executing jsonpath \"{.blarg}\": blarg is not found\n"}, + {"test template, allow missing keys", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.blarg}}", AllowMissingKeys: true}, &api.Pod{}, "", ""}, + {"test template, strict", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.blarg}}", AllowMissingKeys: false}, &api.Pod{}, "", `error executing template "{{.blarg}}": template: output:1:2: executing "output" at <.blarg>: map has no entry for key "blarg"`}, + {"test jsonpath, allow missing keys", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.blarg}", AllowMissingKeys: true}, &api.Pod{}, "", ""}, + {"test jsonpath, strict", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.blarg}", AllowMissingKeys: false}, &api.Pod{}, "", "error executing jsonpath \"{.blarg}\": blarg is not found\n"}, } for _, test := range tests { buf := bytes.NewBuffer([]byte{}) - printer, err := printers.GetStandardPrinter(test.OutputOpts, false, legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{}) + printer, err := printers.GetStandardPrinter(legacyscheme.Registry.RESTMapper(legacyscheme.Registry.EnabledVersions()...), legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.EnabledVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts) if err != nil { t.Errorf("in %s, unexpected error: %#v", test.Name, err) } diff --git a/pkg/printers/printers.go b/pkg/printers/printers.go index c956170355..3ef696ed9b 100644 --- a/pkg/printers/printers.go +++ b/pkg/printers/printers.go @@ -29,12 +29,8 @@ import ( // a printer or an error. The printer is agnostic to schema versions, so you must // send arguments to PrintObj in the version you wish them to be shown using a // VersionedPrinter (typically when generic is true). -func GetStandardPrinter(outputOpts *OutputOptions, noHeaders bool, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) { - if outputOpts == nil { - return nil, fmt.Errorf("no output options specified") - } - - format, formatArgument, allowMissingTemplateKeys := outputOpts.FmtType, outputOpts.FmtArg, outputOpts.AllowMissingKeys +func GetStandardPrinter(mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) { + format, formatArgument, allowMissingTemplateKeys := options.OutputFormatType, options.OutputFormatArgument, options.AllowMissingKeys var printer ResourcePrinter switch format { @@ -106,7 +102,7 @@ func GetStandardPrinter(outputOpts *OutputOptions, noHeaders bool, mapper meta.R case "custom-columns": var err error - if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, decoders[0], noHeaders); err != nil { + if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, decoders[0], options.NoHeaders); err != nil { return nil, err }