...

Source file src/k8s.io/cli-runtime/pkg/genericclioptions/print_flags.go

Documentation: k8s.io/cli-runtime/pkg/genericclioptions

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package genericclioptions
    18  
    19  import (
    20  	"fmt"
    21  	"sort"
    22  	"strings"
    23  
    24  	"github.com/spf13/cobra"
    25  
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/cli-runtime/pkg/printers"
    28  )
    29  
    30  // NoCompatiblePrinterError is a struct that contains error information.
    31  // It will be constructed when a invalid printing format is provided
    32  type NoCompatiblePrinterError struct {
    33  	OutputFormat   *string
    34  	AllowedFormats []string
    35  	Options        interface{}
    36  }
    37  
    38  func (e NoCompatiblePrinterError) Error() string {
    39  	output := ""
    40  	if e.OutputFormat != nil {
    41  		output = *e.OutputFormat
    42  	}
    43  
    44  	sort.Strings(e.AllowedFormats)
    45  	return fmt.Sprintf("unable to match a printer suitable for the output format %q, allowed formats are: %s", output, strings.Join(e.AllowedFormats, ","))
    46  }
    47  
    48  // IsNoCompatiblePrinterError returns true if it is a not a compatible printer
    49  // otherwise it will return false
    50  func IsNoCompatiblePrinterError(err error) bool {
    51  	if err == nil {
    52  		return false
    53  	}
    54  
    55  	_, ok := err.(NoCompatiblePrinterError)
    56  	return ok
    57  }
    58  
    59  // PrintFlags composes common printer flag structs
    60  // used across all commands, and provides a method
    61  // of retrieving a known printer based on flag values provided.
    62  type PrintFlags struct {
    63  	JSONYamlPrintFlags   *JSONYamlPrintFlags
    64  	NamePrintFlags       *NamePrintFlags
    65  	TemplatePrinterFlags *KubeTemplatePrintFlags
    66  
    67  	TypeSetterPrinter *printers.TypeSetterPrinter
    68  
    69  	OutputFormat *string
    70  
    71  	// OutputFlagSpecified indicates whether the user specifically requested a certain kind of output.
    72  	// Using this function allows a sophisticated caller to change the flag binding logic if they so desire.
    73  	OutputFlagSpecified func() bool
    74  }
    75  
    76  // Complete sets NamePrintFlags operation flag from successTemplate
    77  func (f *PrintFlags) Complete(successTemplate string) error {
    78  	return f.NamePrintFlags.Complete(successTemplate)
    79  }
    80  
    81  // AllowedFormats returns slice of string of allowed JSONYaml/Name/Template printing format
    82  func (f *PrintFlags) AllowedFormats() []string {
    83  	ret := []string{}
    84  	ret = append(ret, f.JSONYamlPrintFlags.AllowedFormats()...)
    85  	ret = append(ret, f.NamePrintFlags.AllowedFormats()...)
    86  	ret = append(ret, f.TemplatePrinterFlags.AllowedFormats()...)
    87  	return ret
    88  }
    89  
    90  // ToPrinter returns a printer capable of
    91  // handling --output or --template printing.
    92  // Returns false if the specified outputFormat does not match a supported format.
    93  // Supported format types can be found in pkg/printers/printers.go
    94  func (f *PrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
    95  	outputFormat := ""
    96  	if f.OutputFormat != nil {
    97  		outputFormat = *f.OutputFormat
    98  	}
    99  	// For backwards compatibility we want to support a --template argument given, even when no --output format is provided.
   100  	// If no explicit output format has been provided via the --output flag, fallback
   101  	// to honoring the --template argument.
   102  	templateFlagSpecified := f.TemplatePrinterFlags != nil &&
   103  		f.TemplatePrinterFlags.TemplateArgument != nil &&
   104  		len(*f.TemplatePrinterFlags.TemplateArgument) > 0
   105  	outputFlagSpecified := f.OutputFlagSpecified != nil && f.OutputFlagSpecified()
   106  	if templateFlagSpecified && !outputFlagSpecified {
   107  		outputFormat = "go-template"
   108  	}
   109  
   110  	if f.JSONYamlPrintFlags != nil {
   111  		if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
   112  			return f.TypeSetterPrinter.WrapToPrinter(p, err)
   113  		}
   114  	}
   115  
   116  	if f.NamePrintFlags != nil {
   117  		if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
   118  			return f.TypeSetterPrinter.WrapToPrinter(p, err)
   119  		}
   120  	}
   121  
   122  	if f.TemplatePrinterFlags != nil {
   123  		if p, err := f.TemplatePrinterFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
   124  			return f.TypeSetterPrinter.WrapToPrinter(p, err)
   125  		}
   126  	}
   127  
   128  	return nil, NoCompatiblePrinterError{OutputFormat: f.OutputFormat, AllowedFormats: f.AllowedFormats()}
   129  }
   130  
   131  // AddFlags receives a *cobra.Command reference and binds
   132  // flags related to JSON/Yaml/Name/Template printing to it
   133  func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
   134  	f.JSONYamlPrintFlags.AddFlags(cmd)
   135  	f.NamePrintFlags.AddFlags(cmd)
   136  	f.TemplatePrinterFlags.AddFlags(cmd)
   137  
   138  	if f.OutputFormat != nil {
   139  		cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf(`Output format. One of: (%s).`, strings.Join(f.AllowedFormats(), ", ")))
   140  		if f.OutputFlagSpecified == nil {
   141  			f.OutputFlagSpecified = func() bool {
   142  				return cmd.Flag("output").Changed
   143  			}
   144  		}
   145  	}
   146  }
   147  
   148  // WithDefaultOutput sets a default output format if one is not provided through a flag value
   149  func (f *PrintFlags) WithDefaultOutput(output string) *PrintFlags {
   150  	f.OutputFormat = &output
   151  	return f
   152  }
   153  
   154  // WithTypeSetter sets a wrapper than will surround the returned printer with a printer to type resources
   155  func (f *PrintFlags) WithTypeSetter(scheme *runtime.Scheme) *PrintFlags {
   156  	f.TypeSetterPrinter = printers.NewTypeSetter(scheme)
   157  	return f
   158  }
   159  
   160  // NewPrintFlags returns a default *PrintFlags
   161  func NewPrintFlags(operation string) *PrintFlags {
   162  	outputFormat := ""
   163  
   164  	return &PrintFlags{
   165  		OutputFormat: &outputFormat,
   166  
   167  		JSONYamlPrintFlags:   NewJSONYamlPrintFlags(),
   168  		NamePrintFlags:       NewNamePrintFlags(operation),
   169  		TemplatePrinterFlags: NewKubeTemplatePrintFlags(),
   170  	}
   171  }
   172  

View as plain text