...

Source file src/k8s.io/kubectl/pkg/cmd/explain/explain.go

Documentation: k8s.io/kubectl/pkg/cmd/explain

     1  /*
     2  Copyright 2014 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 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  	// Name of the template to use with the openapiv3 template renderer.
    80  	OutputFormat string
    81  
    82  	// Client capable of fetching openapi documents from the user's cluster
    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  // NewCmdExplain returns a cobra command for swagger docs
    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  	// Only enable --output as a valid flag if the feature is enabled
   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  	// Only openapi v3 needs the discovery client.
   127  	o.OpenAPIV3Client, err = f.OpenAPIV3Client()
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	// Lazy-load the OpenAPI V2 Resources, so they're not loaded when using OpenAPI V3.
   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  // Run executes the appropriate steps to print a model's documentation
   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  		// TODO: After we figured out the new syntax to separate group and resource, allow
   161  		// the users to use it in explain (kubectl explain <group><syntax><resource>).
   162  		// Refer to issue #16039 for why we do this. Refer to PR #15808 that used "/" syntax.
   163  		fullySpecifiedGVR, fieldsPath, err = explain.SplitAndParseResourceRequest(o.args[0], o.Mapper)
   164  		if err != nil {
   165  			return err
   166  		}
   167  	}
   168  
   169  	// Fallback to openapiv2 implementation using special template name
   170  	switch o.OutputFormat {
   171  	case plaintextOpenAPIV2TemplateName:
   172  		return o.renderOpenAPIV2(fullySpecifiedGVR, fieldsPath)
   173  	case plaintextTemplateName:
   174  		// Check whether the server reponds to OpenAPIV3.
   175  		if _, err := o.OpenAPIV3Client.Paths(); err != nil {
   176  			// Use v2 renderer if server does not support v3
   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