mirror of https://github.com/k3s-io/k3s
Denote if a printer is generic.
This fixes #38779. This allows us to avoid case in which printers.GetStandardPrinter returns nil for both printer and err removing any potential panics that may arise throughout kubectl commands. Please see #38779 and #38112 for complete context. Add comment explaining adding handlers to printers.HumanReadablePrinter also remove an unnecessary conversion of printers.HumanReadablePrinter to printers.ResourcePrinter.pull/6/head
parent
77a8c25839
commit
8442a118ea
|
@ -456,8 +456,7 @@ func TestApplyObjectOutput(t *testing.T) {
|
|||
}
|
||||
|
||||
f, tf, _, _ := cmdtesting.NewAPIFactory()
|
||||
tf.CommandPrinter = &printers.YAMLPrinter{}
|
||||
tf.GenericPrinter = true
|
||||
tf.Printer = &printers.YAMLPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
|
|
|
@ -80,8 +80,9 @@ func defaultClientConfigForVersion(version *schema.GroupVersion) *restclient.Con
|
|||
}
|
||||
|
||||
type testPrinter struct {
|
||||
Objects []runtime.Object
|
||||
Err error
|
||||
Objects []runtime.Object
|
||||
Err error
|
||||
GenericPrinter bool
|
||||
}
|
||||
|
||||
func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||
|
@ -99,6 +100,10 @@ func (t *testPrinter) AfterPrint(output io.Writer, res string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *testPrinter) IsGeneric() bool {
|
||||
return t.GenericPrinter
|
||||
}
|
||||
|
||||
type testDescriber struct {
|
||||
Name, Namespace string
|
||||
Settings printers.DescriberSettings
|
||||
|
|
|
@ -81,7 +81,7 @@ func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess
|
|||
cmd.Flags().Set("output", defaultOutputFormat)
|
||||
}
|
||||
|
||||
printer, _, err := cmdutil.PrinterForCommand(cmd, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, []runtime.Decoder{latest.Codec})
|
||||
printer, err := cmdutil.PrinterForCommand(cmd, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, nil, []runtime.Decoder{latest.Codec}, printers.PrintOptions{})
|
||||
cmdutil.CheckErr(err)
|
||||
printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion)
|
||||
|
||||
|
|
|
@ -164,7 +164,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.printer, err = f.PrinterForCommand(cmd, printers.PrintOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -46,6 +46,10 @@ func (t *testClusterRolePrinter) HandledResources() []string {
|
|||
return []string{}
|
||||
}
|
||||
|
||||
func (t *testClusterRolePrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestCreateClusterRole(t *testing.T) {
|
||||
clusterRoleName := "my-cluster-role"
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ func (t *testRolePrinter) HandledResources() []string {
|
|||
return []string{}
|
||||
}
|
||||
|
||||
func (t *testRolePrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestCreateRole(t *testing.T) {
|
||||
roleName := "my-role"
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||
return err
|
||||
}
|
||||
|
||||
printer, generic, err := f.PrinterForCommand(cmd)
|
||||
printer, err := f.PrinterForCommand(cmd, printers.PrintOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||
r.IgnoreErrors(kapierrors.IsNotFound)
|
||||
}
|
||||
|
||||
if generic {
|
||||
if printer.IsGeneric() {
|
||||
// we flattened the data from the builder, so we have individual items, but now we'd like to either:
|
||||
// 1. if there is more than one item, combine them all into a single list
|
||||
// 2. if there is a single item and that item is a list, leave it as its specific list
|
||||
|
@ -436,7 +436,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||
mapping = infos[ix].Mapping
|
||||
original = infos[ix].Object
|
||||
}
|
||||
if printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource {
|
||||
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
|
||||
if printer != nil {
|
||||
w.Flush()
|
||||
}
|
||||
|
@ -513,3 +513,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||
cmdutil.PrintFilterCount(errOut, len(objs), filteredResourceCount, len(allErrs), filterOpts, options.IgnoreNotFound)
|
||||
return utilerrors.NewAggregate(allErrs)
|
||||
}
|
||||
|
||||
func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {
|
||||
return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
|
||||
}
|
||||
|
|
|
@ -220,15 +220,16 @@ func TestGetObjectsFiltered(t *testing.T) {
|
|||
second := &pods.Items[1]
|
||||
|
||||
testCases := []struct {
|
||||
args []string
|
||||
resp runtime.Object
|
||||
flags map[string]string
|
||||
expect []runtime.Object
|
||||
args []string
|
||||
resp runtime.Object
|
||||
flags map[string]string
|
||||
expect []runtime.Object
|
||||
genericPrinter bool
|
||||
}{
|
||||
{args: []string{"pods", "foo"}, resp: first, expect: []runtime.Object{first}},
|
||||
{args: []string{"pods", "foo"}, flags: map[string]string{"show-all": "false"}, resp: first, expect: []runtime.Object{first}},
|
||||
{args: []string{"pods", "foo"}, resp: first, expect: []runtime.Object{first}, genericPrinter: true},
|
||||
{args: []string{"pods", "foo"}, flags: map[string]string{"show-all": "false"}, resp: first, expect: []runtime.Object{first}, genericPrinter: true},
|
||||
{args: []string{"pods"}, flags: map[string]string{"show-all": "true"}, resp: pods, expect: []runtime.Object{first, second}},
|
||||
{args: []string{"pods/foo"}, resp: first, expect: []runtime.Object{first}},
|
||||
{args: []string{"pods/foo"}, resp: first, expect: []runtime.Object{first}, genericPrinter: true},
|
||||
{args: []string{"pods"}, flags: map[string]string{"output": "yaml"}, resp: pods, expect: []runtime.Object{second}},
|
||||
{args: []string{}, flags: map[string]string{"filename": "../../../examples/storage/cassandra/cassandra-controller.yaml"}, resp: pods, expect: []runtime.Object{first, second}},
|
||||
|
||||
|
@ -240,7 +241,7 @@ func TestGetObjectsFiltered(t *testing.T) {
|
|||
for i, test := range testCases {
|
||||
t.Logf("%d", i)
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Printer = &testPrinter{GenericPrinter: test.genericPrinter}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
|
@ -281,7 +282,7 @@ func TestGetObjectIgnoreNotFound(t *testing.T) {
|
|||
}
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Printer = &testPrinter{GenericPrinter: true}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
|
@ -393,7 +394,7 @@ func TestGetObjectsIdentifiedByFile(t *testing.T) {
|
|||
pods, _, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Printer = &testPrinter{GenericPrinter: true}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
|
@ -561,8 +562,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
|||
pods, svc, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.CommandPrinter = &testPrinter{}
|
||||
tf.GenericPrinter = true
|
||||
tf.Printer = &testPrinter{GenericPrinter: true}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
|
@ -589,7 +589,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
|
|||
cmd.Flags().Set("output", "json")
|
||||
cmd.Run(cmd, []string{"pods,services"})
|
||||
|
||||
actual := tf.CommandPrinter.(*testPrinter).Objects
|
||||
actual := tf.Printer.(*testPrinter).Objects
|
||||
fn := func(obj runtime.Object) unstructured.Unstructured {
|
||||
data, err := runtime.Encode(api.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"}), obj)
|
||||
if err != nil {
|
||||
|
@ -716,7 +716,7 @@ func TestGetByFormatForcesFlag(t *testing.T) {
|
|||
pods, _, _ := testData()
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.Printer = &testPrinter{}
|
||||
tf.Printer = &testPrinter{GenericPrinter: true}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
|
|
|
@ -161,8 +161,7 @@ func TestPatchObjectFromFileOutput(t *testing.T) {
|
|||
svcCopy.Labels["post-patch"] = "post-patch-value"
|
||||
|
||||
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||
tf.CommandPrinter = &printers.YAMLPrinter{}
|
||||
tf.GenericPrinter = true
|
||||
tf.Printer = &printers.YAMLPrinter{}
|
||||
tf.UnstructuredClient = &fake.RESTClient{
|
||||
APIRegistry: api.Registry,
|
||||
NegotiatedSerializer: unstructuredSerializer,
|
||||
|
|
|
@ -221,13 +221,11 @@ type TestFactory struct {
|
|||
UnstructuredClient kubectl.RESTClient
|
||||
Describer printers.Describer
|
||||
Printer printers.ResourcePrinter
|
||||
CommandPrinter printers.ResourcePrinter
|
||||
Validator validation.Schema
|
||||
Namespace string
|
||||
ClientConfig *restclient.Config
|
||||
Err error
|
||||
Command string
|
||||
GenericPrinter bool
|
||||
TmpDir string
|
||||
|
||||
ClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
||||
|
@ -338,8 +336,8 @@ func (f *FakeFactory) Describer(*meta.RESTMapping) (printers.Describer, error) {
|
|||
return f.tf.Describer, f.tf.Err
|
||||
}
|
||||
|
||||
func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) {
|
||||
return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err
|
||||
func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||
return f.tf.Printer, f.tf.Err
|
||||
}
|
||||
|
||||
func (f *FakeFactory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||
|
@ -619,8 +617,8 @@ func (f *fakeAPIFactory) UnstructuredClientForMapping(m *meta.RESTMapping) (reso
|
|||
return f.tf.UnstructuredClient, f.tf.Err
|
||||
}
|
||||
|
||||
func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) {
|
||||
return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err
|
||||
func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||
return f.tf.Printer, f.tf.Err
|
||||
}
|
||||
|
||||
func (f *fakeAPIFactory) Describer(*meta.RESTMapping) (printers.Describer, error) {
|
||||
|
|
|
@ -230,10 +230,10 @@ type ObjectMappingFactory interface {
|
|||
// 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.
|
||||
// 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) (printers.ResourcePrinter, bool, error)
|
||||
PrinterForCommand(cmd *cobra.Command, 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).
|
||||
PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error)
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/kubectl/plugins"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
)
|
||||
|
||||
type ring2Factory struct {
|
||||
|
@ -47,24 +48,41 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
|
|||
return f
|
||||
}
|
||||
|
||||
func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) {
|
||||
func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||
mapper, typer, err := f.objectMappingFactory.UnstructuredObject()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
// 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)
|
||||
encoder := f.clientAccessFactory.JSONEncoder()
|
||||
return PrinterForCommand(cmd, mapper, typer, encoder, decoders, options)
|
||||
}
|
||||
|
||||
func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
|
||||
printer, generic, err := f.PrinterForCommand(cmd)
|
||||
// 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, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure we output versioned data for generic printers
|
||||
if generic {
|
||||
if printer.IsGeneric() {
|
||||
if mapping == nil {
|
||||
return nil, fmt.Errorf("no serialization format found")
|
||||
}
|
||||
|
@ -74,25 +92,17 @@ func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTM
|
|||
}
|
||||
|
||||
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{}
|
||||
// We add handlers to the printer in case it is printers.HumanReadablePrinter.
|
||||
// printers.AddHandlers expects concrete type of printers.HumanReadablePrinter
|
||||
// as its parameter because of this we have to do a type check on printer and
|
||||
// extract out concrete HumanReadablePrinter from it. We are then able to attach
|
||||
// handlers on it.
|
||||
if humanReadablePrinter, ok := printer.(*printers.HumanReadablePrinter); ok {
|
||||
printersinternal.AddHandlers(humanReadablePrinter)
|
||||
printer = humanReadablePrinter
|
||||
}
|
||||
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
|
||||
|
|
|
@ -107,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, mapper meta.RESTMapper, typer runtime.ObjectTyper, decoders []runtime.Decoder) (printers.ResourcePrinter, bool, error) {
|
||||
func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||
outputFormat := GetFlagString(cmd, "output")
|
||||
|
||||
// templates are logically optional for specifying a format.
|
||||
|
@ -133,15 +133,15 @@ func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime
|
|||
if cmd.Flags().Lookup("allow-missing-template-keys") != nil {
|
||||
allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
|
||||
}
|
||||
printer, generic, err := printers.GetStandardPrinter(
|
||||
printer, err := printers.GetStandardPrinter(
|
||||
outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys,
|
||||
mapper, typer, decoders,
|
||||
mapper, typer, encoder, decoders, options,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, generic, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return maybeWrapSortingPrinter(cmd, printer), generic, nil
|
||||
return maybeWrapSortingPrinter(cmd, printer), nil
|
||||
}
|
||||
|
||||
// PrintResourceInfoForCommand receives a *cobra.Command and a *resource.Info and
|
||||
|
@ -149,11 +149,11 @@ func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime
|
|||
// 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 := f.PrinterForCommand(cmd)
|
||||
printer, err := f.PrinterForCommand(cmd, printers.PrintOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !generic || printer == nil {
|
||||
if !printer.IsGeneric() {
|
||||
printer, err = f.PrinterForMapping(cmd, nil, false)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -60,10 +60,14 @@ func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
|||
}
|
||||
|
||||
// TODO: implement HandledResources()
|
||||
func (p *SortingPrinter) HandledResources() []string {
|
||||
func (s *SortingPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (s *SortingPrinter) IsGeneric() bool {
|
||||
return s.Delegate.IsGeneric()
|
||||
}
|
||||
|
||||
func (s *SortingPrinter) sortObj(obj runtime.Object) error {
|
||||
objs, err := meta.ExtractList(obj)
|
||||
if err != nil {
|
||||
|
|
|
@ -239,3 +239,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso
|
|||
func (s *CustomColumnsPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (s *CustomColumnsPrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -152,6 +152,10 @@ func (h *HumanReadablePrinter) AfterPrint(output io.Writer, res string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *HumanReadablePrinter) IsGeneric() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
|
||||
_, err := fmt.Fprintf(w, "Unknown object: %s", string(data))
|
||||
return err
|
||||
|
|
|
@ -31,6 +31,8 @@ type ResourcePrinter interface {
|
|||
//Can be used to print out warning/clarifications if needed
|
||||
//after all objects were printed
|
||||
AfterPrint(io.Writer, string) error
|
||||
// Identify if it is a generic printer
|
||||
IsGeneric() bool
|
||||
}
|
||||
|
||||
// ResourcePrinterFunc is a function that can print objects
|
||||
|
@ -50,6 +52,10 @@ func (fn ResourcePrinterFunc) AfterPrint(io.Writer, string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (fn ResourcePrinterFunc) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type PrintOptions struct {
|
||||
NoHeaders bool
|
||||
WithNamespace bool
|
||||
|
|
|
@ -81,12 +81,22 @@ func TestVersionedPrinter(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPrintDefault(t *testing.T) {
|
||||
printer, found, err := printers.GetStandardPrinter("", "", false, false, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %#v", err)
|
||||
printerTests := []struct {
|
||||
Name string
|
||||
Format string
|
||||
}{
|
||||
{"test wide", "wide"},
|
||||
{"test blank format", ""},
|
||||
}
|
||||
if found {
|
||||
t.Errorf("no printer should have been found: %#v / %v", printer, err)
|
||||
|
||||
for _, test := range printerTests {
|
||||
printer, err := printers.GetStandardPrinter(test.Format, "", false, false, nil, nil, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||
}
|
||||
if printer.IsGeneric() {
|
||||
t.Errorf("in %s, printer should not be generic: %#v", test.Name, printer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,11 +146,11 @@ func TestPrinter(t *testing.T) {
|
|||
}
|
||||
for _, test := range printerTests {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
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})
|
||||
printer, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, true, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||
}
|
||||
if generic && len(test.OutputVersions) > 0 {
|
||||
if printer.IsGeneric() && len(test.OutputVersions) > 0 {
|
||||
printer = printers.NewVersionedPrinter(printer, api.Scheme, test.OutputVersions...)
|
||||
}
|
||||
if err := printer.PrintObj(test.Input, buf); err != nil {
|
||||
|
@ -164,9 +174,10 @@ func TestBadPrinter(t *testing.T) {
|
|||
{"bad template", "template", "{{ .Name", fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")},
|
||||
{"bad templatefile", "templatefile", "", fmt.Errorf("templatefile format specified but no template file given")},
|
||||
{"bad jsonpath", "jsonpath", "{.Name", fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")},
|
||||
{"unknown format", "anUnknownFormat", "", fmt.Errorf("output format \"anUnknownFormat\" not recognized")},
|
||||
}
|
||||
for _, test := range badPrinterTests {
|
||||
_, _, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme})
|
||||
_, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||
if err == nil || err.Error() != test.Error.Error() {
|
||||
t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err)
|
||||
}
|
||||
|
@ -359,7 +370,7 @@ func TestNamePrinter(t *testing.T) {
|
|||
},
|
||||
"pods/foo\npods/bar\n"},
|
||||
}
|
||||
printer, _, _ := printers.GetStandardPrinter("name", "", false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme})
|
||||
printer, _ := printers.GetStandardPrinter("name", "", false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||
for name, item := range tests {
|
||||
buff := &bytes.Buffer{}
|
||||
err := printer.PrintObj(item.obj, buff)
|
||||
|
@ -2235,7 +2246,7 @@ func TestAllowMissingKeys(t *testing.T) {
|
|||
}
|
||||
for _, test := range tests {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
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})
|
||||
printer, err := printers.GetStandardPrinter(test.Format, test.Template, false, test.AllowMissingTemplateKeys, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||
}
|
||||
|
|
|
@ -63,6 +63,10 @@ func (p *JSONPrinter) HandledResources() []string {
|
|||
return []string{}
|
||||
}
|
||||
|
||||
func (p *JSONPrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// YAMLPrinter is an implementation of ResourcePrinter which outputs an object as YAML.
|
||||
// The input object is assumed to be in the internal version of an API and is converted
|
||||
// to the given version first.
|
||||
|
@ -99,3 +103,7 @@ func (p *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
|||
func (p *YAMLPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (p *YAMLPrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -155,3 +155,7 @@ func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
|||
func (p *JSONPathPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (p *JSONPathPrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -87,3 +87,7 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
|||
func (p *NamePrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (p *NamePrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -25,92 +25,102 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// GetStandardPrinter takes a format type, an optional format argument. It will return true
|
||||
// if the format is generic (untyped), otherwise it will return false. The printer
|
||||
// is agnostic to schema versions, so you must send arguments to PrintObj in the
|
||||
// version you wish them to be shown using a VersionedPrinter (typically when
|
||||
// generic is true).
|
||||
func GetStandardPrinter(format, formatArgument string, noHeaders, allowMissingTemplateKeys bool, mapper meta.RESTMapper, typer runtime.ObjectTyper, decoders []runtime.Decoder) (ResourcePrinter, bool, error) {
|
||||
// GetStandardPrinter takes a format type, an optional format argument. It will return
|
||||
// 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(format, formatArgument string, noHeaders, allowMissingTemplateKeys bool, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) {
|
||||
var printer ResourcePrinter
|
||||
switch format {
|
||||
|
||||
case "json":
|
||||
printer = &JSONPrinter{}
|
||||
|
||||
case "yaml":
|
||||
printer = &YAMLPrinter{}
|
||||
|
||||
case "name":
|
||||
printer = &NamePrinter{
|
||||
Typer: typer,
|
||||
Decoders: decoders,
|
||||
Mapper: mapper,
|
||||
}
|
||||
|
||||
case "template", "go-template":
|
||||
if len(formatArgument) == 0 {
|
||||
return nil, false, fmt.Errorf("template format specified but no template given")
|
||||
return nil, 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)
|
||||
return nil, 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")
|
||||
return nil, 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)
|
||||
return nil, 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)
|
||||
return nil, 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")
|
||||
return nil, 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)
|
||||
return nil, 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")
|
||||
return nil, 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)
|
||||
return nil, 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)
|
||||
return nil, fmt.Errorf("error parsing template %s, %v\n", string(data), err)
|
||||
}
|
||||
jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys)
|
||||
printer = jsonpathPrinter
|
||||
|
||||
case "custom-columns":
|
||||
var err error
|
||||
if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, decoders[0], noHeaders); err != nil {
|
||||
return nil, false, err
|
||||
return nil, 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)
|
||||
return nil, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
|
||||
}
|
||||
defer file.Close()
|
||||
if printer, err = NewCustomColumnsPrinterFromTemplate(file, decoders[0]); err != nil {
|
||||
return nil, false, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "wide":
|
||||
fallthrough
|
||||
case "":
|
||||
return nil, false, nil
|
||||
|
||||
printer = NewHumanReadablePrinter(encoder, decoders[0], options)
|
||||
default:
|
||||
return nil, false, fmt.Errorf("output format %q not recognized", format)
|
||||
return nil, fmt.Errorf("output format %q not recognized", format)
|
||||
}
|
||||
return printer, true, nil
|
||||
return printer, nil
|
||||
}
|
||||
|
|
|
@ -88,6 +88,10 @@ func (p *TemplatePrinter) HandledResources() []string {
|
|||
return []string{}
|
||||
}
|
||||
|
||||
func (p *TemplatePrinter) IsGeneric() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// safeExecute tries to execute the template, but catches panics and returns an error
|
||||
// should the template engine panic.
|
||||
func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
|
||||
|
|
|
@ -61,3 +61,7 @@ func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
|
|||
func (p *VersionedPrinter) HandledResources() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (p *VersionedPrinter) IsGeneric() bool {
|
||||
return p.printer.IsGeneric()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue