1/*
2Copyright 2018 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package get
18
19import (
20	"fmt"
21	"strings"
22
23	"github.com/spf13/cobra"
24
25	"k8s.io/apimachinery/pkg/api/meta"
26	"k8s.io/apimachinery/pkg/runtime/schema"
27	"k8s.io/cli-runtime/pkg/genericclioptions"
28	"k8s.io/cli-runtime/pkg/printers"
29	"k8s.io/kubectl/pkg/util/openapi"
30)
31
32// PrintFlags composes common printer flag structs
33// used in the Get command.
34type PrintFlags struct {
35	JSONYamlPrintFlags *genericclioptions.JSONYamlPrintFlags
36	NamePrintFlags     *genericclioptions.NamePrintFlags
37	CustomColumnsFlags *CustomColumnsPrintFlags
38	HumanReadableFlags *HumanPrintFlags
39	TemplateFlags      *genericclioptions.KubeTemplatePrintFlags
40
41	NoHeaders    *bool
42	OutputFormat *string
43}
44
45// SetKind sets the Kind option of humanreadable flags
46func (f *PrintFlags) SetKind(kind schema.GroupKind) {
47	f.HumanReadableFlags.SetKind(kind)
48}
49
50// EnsureWithNamespace ensures that humanreadable flags return
51// a printer capable of printing with a "namespace" column.
52func (f *PrintFlags) EnsureWithNamespace() error {
53	return f.HumanReadableFlags.EnsureWithNamespace()
54}
55
56// EnsureWithKind ensures that humanreadable flags return
57// a printer capable of including resource kinds.
58func (f *PrintFlags) EnsureWithKind() error {
59	return f.HumanReadableFlags.EnsureWithKind()
60}
61
62// Copy returns a copy of PrintFlags for mutation
63func (f *PrintFlags) Copy() PrintFlags {
64	printFlags := *f
65	return printFlags
66}
67
68// AllowedFormats is the list of formats in which data can be displayed
69func (f *PrintFlags) AllowedFormats() []string {
70	formats := f.JSONYamlPrintFlags.AllowedFormats()
71	formats = append(formats, f.NamePrintFlags.AllowedFormats()...)
72	formats = append(formats, f.TemplateFlags.AllowedFormats()...)
73	formats = append(formats, f.CustomColumnsFlags.AllowedFormats()...)
74	formats = append(formats, f.HumanReadableFlags.AllowedFormats()...)
75	return formats
76}
77
78// UseOpenAPIColumns modifies the output format, as well as the
79// "allowMissingKeys" option for template printers, to values
80// defined in the OpenAPI schema of a resource.
81func (f *PrintFlags) UseOpenAPIColumns(api openapi.Resources, mapping *meta.RESTMapping) error {
82	// Found openapi metadata for this resource
83	schema := api.LookupResource(mapping.GroupVersionKind)
84	if schema == nil {
85		// Schema not found, return empty columns
86		return nil
87	}
88
89	columns, found := openapi.GetPrintColumns(schema.GetExtensions())
90	if !found {
91		// Extension not found, return empty columns
92		return nil
93	}
94
95	parts := strings.SplitN(columns, "=", 2)
96	if len(parts) < 2 {
97		return nil
98	}
99
100	allowMissingKeys := true
101	f.OutputFormat = &parts[0]
102	f.TemplateFlags.TemplateArgument = &parts[1]
103	f.TemplateFlags.AllowMissingKeys = &allowMissingKeys
104	return nil
105}
106
107// ToPrinter attempts to find a composed set of PrintFlags suitable for
108// returning a printer based on current flag values.
109func (f *PrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
110	outputFormat := ""
111	if f.OutputFormat != nil {
112		outputFormat = *f.OutputFormat
113	}
114
115	noHeaders := false
116	if f.NoHeaders != nil {
117		noHeaders = *f.NoHeaders
118	}
119	f.HumanReadableFlags.NoHeaders = noHeaders
120	f.CustomColumnsFlags.NoHeaders = noHeaders
121
122	// for "get.go" we want to support a --template argument given, even when no --output format is provided
123	if f.TemplateFlags.TemplateArgument != nil && len(*f.TemplateFlags.TemplateArgument) > 0 && len(outputFormat) == 0 {
124		outputFormat = "go-template"
125	}
126
127	if p, err := f.TemplateFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
128		return p, err
129	}
130
131	if f.TemplateFlags.TemplateArgument != nil {
132		f.CustomColumnsFlags.TemplateArgument = *f.TemplateFlags.TemplateArgument
133	}
134
135	if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
136		return p, err
137	}
138
139	if p, err := f.HumanReadableFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
140		return p, err
141	}
142
143	if p, err := f.CustomColumnsFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
144		return p, err
145	}
146
147	if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) {
148		return p, err
149	}
150
151	return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: f.AllowedFormats()}
152}
153
154// AddFlags receives a *cobra.Command reference and binds
155// flags related to humanreadable and template printing.
156func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
157	f.JSONYamlPrintFlags.AddFlags(cmd)
158	f.NamePrintFlags.AddFlags(cmd)
159	f.TemplateFlags.AddFlags(cmd)
160	f.HumanReadableFlags.AddFlags(cmd)
161	f.CustomColumnsFlags.AddFlags(cmd)
162
163	if f.OutputFormat != nil {
164		cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf("Output format. One of: %s See custom columns [https://kubernetes.io/docs/reference/kubectl/overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [https://kubernetes.io/docs/reference/kubectl/jsonpath/].", strings.Join(f.AllowedFormats(), "|")))
165	}
166	if f.NoHeaders != nil {
167		cmd.Flags().BoolVar(f.NoHeaders, "no-headers", *f.NoHeaders, "When using the default or custom-column output format, don't print headers (default print headers).")
168	}
169}
170
171// NewGetPrintFlags returns flags associated with humanreadable,
172// template, and "name" printing, with default values set.
173func NewGetPrintFlags() *PrintFlags {
174	outputFormat := ""
175	noHeaders := false
176
177	return &PrintFlags{
178		OutputFormat: &outputFormat,
179		NoHeaders:    &noHeaders,
180
181		JSONYamlPrintFlags: genericclioptions.NewJSONYamlPrintFlags(),
182		NamePrintFlags:     genericclioptions.NewNamePrintFlags(""),
183		TemplateFlags:      genericclioptions.NewKubeTemplatePrintFlags(),
184
185		HumanReadableFlags: NewHumanPrintFlags(),
186		CustomColumnsFlags: NewCustomColumnsPrintFlags(),
187	}
188}
189