1
16
17 package explain
18
19 import (
20 "fmt"
21
22 "github.com/spf13/cobra"
23
24 "k8s.io/apimachinery/pkg/api/meta"
25 "k8s.io/apimachinery/pkg/runtime/schema"
26 "k8s.io/cli-runtime/pkg/genericiooptions"
27 openapiclient "k8s.io/client-go/openapi"
28 cmdutil "k8s.io/kubectl/pkg/cmd/util"
29 "k8s.io/kubectl/pkg/explain"
30 openapiv3explain "k8s.io/kubectl/pkg/explain/v2"
31 "k8s.io/kubectl/pkg/util/i18n"
32 "k8s.io/kubectl/pkg/util/openapi"
33 "k8s.io/kubectl/pkg/util/templates"
34 )
35
36 var (
37 explainLong = templates.LongDesc(i18n.T(`
38 Describe fields and structure of various resources.
39
40 This command describes the fields associated with each supported API resource.
41 Fields are identified via a simple JSONPath identifier:
42
43 <type>.<fieldName>[.<fieldName>]
44
45 Information about each field is retrieved from the server in OpenAPI format.`))
46
47 explainExamples = templates.Examples(i18n.T(`
48 # Get the documentation of the resource and its fields
49 kubectl explain pods
50
51 # Get all the fields in the resource
52 kubectl explain pods --recursive
53
54 # Get the explanation for deployment in supported api versions
55 kubectl explain deployments --api-version=apps/v1
56
57 # Get the documentation of a specific field of a resource
58 kubectl explain pods.spec.containers
59
60 # Get the documentation of resources in different format
61 kubectl explain deployment --output=plaintext-openapiv2`))
62
63 plaintextTemplateName = "plaintext"
64 plaintextOpenAPIV2TemplateName = "plaintext-openapiv2"
65 )
66
67 type ExplainOptions struct {
68 genericiooptions.IOStreams
69
70 CmdParent string
71 APIVersion string
72 Recursive bool
73
74 args []string
75
76 Mapper meta.RESTMapper
77 openAPIGetter openapi.OpenAPIResourcesGetter
78
79
80 OutputFormat string
81
82
83 OpenAPIV3Client openapiclient.Client
84 }
85
86 func NewExplainOptions(parent string, streams genericiooptions.IOStreams) *ExplainOptions {
87 return &ExplainOptions{
88 IOStreams: streams,
89 CmdParent: parent,
90 OutputFormat: plaintextTemplateName,
91 }
92 }
93
94
95 func NewCmdExplain(parent string, f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
96 o := NewExplainOptions(parent, streams)
97
98 cmd := &cobra.Command{
99 Use: "explain TYPE [--recursive=FALSE|TRUE] [--api-version=api-version-group] [--output=plaintext|plaintext-openapiv2]",
100 DisableFlagsInUseLine: true,
101 Short: i18n.T("Get documentation for a resource"),
102 Long: explainLong + "\n\n" + cmdutil.SuggestAPIResources(parent),
103 Example: explainExamples,
104 Run: func(cmd *cobra.Command, args []string) {
105 cmdutil.CheckErr(o.Complete(f, cmd, args))
106 cmdutil.CheckErr(o.Validate())
107 cmdutil.CheckErr(o.Run())
108 },
109 }
110 cmd.Flags().BoolVar(&o.Recursive, "recursive", o.Recursive, "When true, print the name of all the fields recursively. Otherwise, print the available fields with their description.")
111 cmd.Flags().StringVar(&o.APIVersion, "api-version", o.APIVersion, "Use given api-version (group/version) of the resource.")
112
113
114 cmd.Flags().StringVar(&o.OutputFormat, "output", plaintextTemplateName, "Format in which to render the schema. Valid values are: (plaintext, plaintext-openapiv2).")
115
116 return cmd
117 }
118
119 func (o *ExplainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
120 var err error
121 o.Mapper, err = f.ToRESTMapper()
122 if err != nil {
123 return err
124 }
125
126
127 o.OpenAPIV3Client, err = f.OpenAPIV3Client()
128 if err != nil {
129 return err
130 }
131
132
133 o.openAPIGetter = f
134 o.args = args
135 return nil
136 }
137
138 func (o *ExplainOptions) Validate() error {
139 if len(o.args) == 0 {
140 return fmt.Errorf("You must specify the type of resource to explain. %s\n", cmdutil.SuggestAPIResources(o.CmdParent))
141 }
142 if len(o.args) > 1 {
143 return fmt.Errorf("We accept only this format: explain RESOURCE\n")
144 }
145
146 return nil
147 }
148
149
150 func (o *ExplainOptions) Run() error {
151 var fullySpecifiedGVR schema.GroupVersionResource
152 var fieldsPath []string
153 var err error
154 if len(o.APIVersion) == 0 {
155 fullySpecifiedGVR, fieldsPath, err = explain.SplitAndParseResourceRequestWithMatchingPrefix(o.args[0], o.Mapper)
156 if err != nil {
157 return err
158 }
159 } else {
160
161
162
163 fullySpecifiedGVR, fieldsPath, err = explain.SplitAndParseResourceRequest(o.args[0], o.Mapper)
164 if err != nil {
165 return err
166 }
167 }
168
169
170 switch o.OutputFormat {
171 case plaintextOpenAPIV2TemplateName:
172 return o.renderOpenAPIV2(fullySpecifiedGVR, fieldsPath)
173 case plaintextTemplateName:
174
175 if _, err := o.OpenAPIV3Client.Paths(); err != nil {
176
177 return o.renderOpenAPIV2(fullySpecifiedGVR, fieldsPath)
178 }
179
180 fallthrough
181 default:
182 if len(o.APIVersion) > 0 {
183 apiVersion, err := schema.ParseGroupVersion(o.APIVersion)
184 if err != nil {
185 return err
186 }
187 fullySpecifiedGVR.Group = apiVersion.Group
188 fullySpecifiedGVR.Version = apiVersion.Version
189 }
190
191 return openapiv3explain.PrintModelDescription(
192 fieldsPath,
193 o.Out,
194 o.OpenAPIV3Client,
195 fullySpecifiedGVR,
196 o.Recursive,
197 o.OutputFormat,
198 )
199 }
200 }
201
202 func (o *ExplainOptions) renderOpenAPIV2(
203 fullySpecifiedGVR schema.GroupVersionResource,
204 fieldsPath []string,
205 ) error {
206 var err error
207
208 gvk, _ := o.Mapper.KindFor(fullySpecifiedGVR)
209 if gvk.Empty() {
210 gvk, err = o.Mapper.KindFor(fullySpecifiedGVR.GroupResource().WithVersion(""))
211 if err != nil {
212 return err
213 }
214 }
215
216 if len(o.APIVersion) != 0 {
217 apiVersion, err := schema.ParseGroupVersion(o.APIVersion)
218 if err != nil {
219 return err
220 }
221 gvk = apiVersion.WithKind(gvk.Kind)
222 }
223
224 resources, err := o.openAPIGetter.OpenAPISchema()
225 if err != nil {
226 return err
227 }
228 schema := resources.LookupResource(gvk)
229 if schema == nil {
230 return fmt.Errorf("couldn't find resource for %q", gvk)
231 }
232
233 return explain.PrintModelDescription(fieldsPath, o.Out, schema, gvk, o.Recursive)
234 }
235
View as plain text