...

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

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

     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 describe
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	"github.com/spf13/cobra"
    24  
    25  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    26  	"k8s.io/apimachinery/pkg/api/meta"
    27  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  	"k8s.io/cli-runtime/pkg/genericiooptions"
    30  	"k8s.io/cli-runtime/pkg/resource"
    31  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    32  	"k8s.io/kubectl/pkg/describe"
    33  	"k8s.io/kubectl/pkg/util/completion"
    34  	"k8s.io/kubectl/pkg/util/i18n"
    35  	"k8s.io/kubectl/pkg/util/templates"
    36  )
    37  
    38  var (
    39  	describeLong = templates.LongDesc(i18n.T(`
    40  		Show details of a specific resource or group of resources.
    41  
    42  		Print a detailed description of the selected resources, including related resources such
    43  		as events or controllers. You may select a single object by name, all objects of that
    44  		type, provide a name prefix, or label selector. For example:
    45  
    46  		    $ kubectl describe TYPE NAME_PREFIX
    47  
    48  		will first check for an exact match on TYPE and NAME_PREFIX. If no such resource
    49  		exists, it will output details for every resource that has a name prefixed with NAME_PREFIX.`))
    50  
    51  	describeExample = templates.Examples(i18n.T(`
    52  		# Describe a node
    53  		kubectl describe nodes kubernetes-node-emt8.c.myproject.internal
    54  
    55  		# Describe a pod
    56  		kubectl describe pods/nginx
    57  
    58  		# Describe a pod identified by type and name in "pod.json"
    59  		kubectl describe -f pod.json
    60  
    61  		# Describe all pods
    62  		kubectl describe pods
    63  
    64  		# Describe pods by label name=myLabel
    65  		kubectl describe pods -l name=myLabel
    66  
    67  		# Describe all pods managed by the 'frontend' replication controller
    68  		# (rc-created pods get the name of the rc as a prefix in the pod name)
    69  		kubectl describe pods frontend`))
    70  )
    71  
    72  // DescribeFlags directly reflect the information that CLI is gathering via flags. They will be converted to Options,
    73  // which reflect the runtime requirements for the command.
    74  type DescribeFlags struct {
    75  	Factory           cmdutil.Factory
    76  	Selector          string
    77  	AllNamespaces     bool
    78  	FilenameOptions   *resource.FilenameOptions
    79  	DescriberSettings *describe.DescriberSettings
    80  	genericiooptions.IOStreams
    81  }
    82  
    83  // NewDescribeFlags returns a default DescribeFlags
    84  func NewDescribeFlags(f cmdutil.Factory, streams genericiooptions.IOStreams) *DescribeFlags {
    85  	return &DescribeFlags{
    86  		Factory:         f,
    87  		FilenameOptions: &resource.FilenameOptions{},
    88  		DescriberSettings: &describe.DescriberSettings{
    89  			ShowEvents: true,
    90  			ChunkSize:  cmdutil.DefaultChunkSize,
    91  		},
    92  		IOStreams: streams,
    93  	}
    94  }
    95  
    96  // AddFlags registers flags for a cli
    97  func (flags *DescribeFlags) AddFlags(cmd *cobra.Command) {
    98  	cmdutil.AddFilenameOptionFlags(cmd, flags.FilenameOptions, "containing the resource to describe")
    99  	cmdutil.AddLabelSelectorFlagVar(cmd, &flags.Selector)
   100  	cmd.Flags().BoolVarP(&flags.AllNamespaces, "all-namespaces", "A", flags.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
   101  	cmd.Flags().BoolVar(&flags.DescriberSettings.ShowEvents, "show-events", flags.DescriberSettings.ShowEvents, "If true, display events related to the described object.")
   102  	cmdutil.AddChunkSizeFlag(cmd, &flags.DescriberSettings.ChunkSize)
   103  }
   104  
   105  // ToOptions converts from CLI inputs to runtime input
   106  func (flags *DescribeFlags) ToOptions(parent string, args []string) (*DescribeOptions, error) {
   107  
   108  	var err error
   109  	namespace, enforceNamespace, err := flags.Factory.ToRawKubeConfigLoader().Namespace()
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	if flags.AllNamespaces {
   115  		enforceNamespace = false
   116  	}
   117  
   118  	if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(flags.FilenameOptions.Filenames, flags.FilenameOptions.Kustomize) {
   119  		return nil, fmt.Errorf("You must specify the type of resource to describe. %s\n", cmdutil.SuggestAPIResources(parent))
   120  	}
   121  
   122  	builderArgs := args
   123  
   124  	describer := func(mapping *meta.RESTMapping) (describe.ResourceDescriber, error) {
   125  		return describe.DescriberFn(flags.Factory, mapping)
   126  	}
   127  
   128  	o := &DescribeOptions{
   129  		Selector:          flags.Selector,
   130  		Namespace:         namespace,
   131  		Describer:         describer,
   132  		NewBuilder:        flags.Factory.NewBuilder,
   133  		BuilderArgs:       builderArgs,
   134  		EnforceNamespace:  enforceNamespace,
   135  		AllNamespaces:     flags.AllNamespaces,
   136  		FilenameOptions:   flags.FilenameOptions,
   137  		DescriberSettings: flags.DescriberSettings,
   138  		IOStreams:         flags.IOStreams,
   139  	}
   140  
   141  	return o, nil
   142  }
   143  
   144  func NewCmdDescribe(parent string, f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   145  	flags := NewDescribeFlags(f, streams)
   146  
   147  	cmd := &cobra.Command{
   148  		Use:                   "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)",
   149  		DisableFlagsInUseLine: true,
   150  		Short:                 i18n.T("Show details of a specific resource or group of resources"),
   151  		Long:                  describeLong + "\n\n" + cmdutil.SuggestAPIResources(parent),
   152  		Example:               describeExample,
   153  		ValidArgsFunction:     completion.ResourceTypeAndNameCompletionFunc(f),
   154  		Run: func(cmd *cobra.Command, args []string) {
   155  			o, err := flags.ToOptions(parent, args)
   156  			cmdutil.CheckErr(err)
   157  			cmdutil.CheckErr(o.Validate())
   158  			cmdutil.CheckErr(o.Run())
   159  		},
   160  	}
   161  
   162  	flags.AddFlags(cmd)
   163  
   164  	return cmd
   165  }
   166  
   167  func (o *DescribeOptions) Validate() error {
   168  	return nil
   169  }
   170  
   171  func (o *DescribeOptions) Run() error {
   172  	r := o.NewBuilder().
   173  		Unstructured().
   174  		ContinueOnError().
   175  		NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
   176  		FilenameParam(o.EnforceNamespace, o.FilenameOptions).
   177  		LabelSelectorParam(o.Selector).
   178  		ResourceTypeOrNameArgs(true, o.BuilderArgs...).
   179  		RequestChunksOf(o.DescriberSettings.ChunkSize).
   180  		Flatten().
   181  		Do()
   182  	err := r.Err()
   183  	if err != nil {
   184  		return err
   185  	}
   186  
   187  	allErrs := []error{}
   188  	infos, err := r.Infos()
   189  	if err != nil {
   190  		if apierrors.IsNotFound(err) && len(o.BuilderArgs) == 2 {
   191  			return o.DescribeMatchingResources(err, o.BuilderArgs[0], o.BuilderArgs[1])
   192  		}
   193  		allErrs = append(allErrs, err)
   194  	}
   195  
   196  	errs := sets.NewString()
   197  	first := true
   198  	for _, info := range infos {
   199  		mapping := info.ResourceMapping()
   200  		describer, err := o.Describer(mapping)
   201  		if err != nil {
   202  			if errs.Has(err.Error()) {
   203  				continue
   204  			}
   205  			allErrs = append(allErrs, err)
   206  			errs.Insert(err.Error())
   207  			continue
   208  		}
   209  		s, err := describer.Describe(info.Namespace, info.Name, *o.DescriberSettings)
   210  		if err != nil {
   211  			if errs.Has(err.Error()) {
   212  				continue
   213  			}
   214  			allErrs = append(allErrs, err)
   215  			errs.Insert(err.Error())
   216  			continue
   217  		}
   218  		if first {
   219  			first = false
   220  			fmt.Fprint(o.Out, s)
   221  		} else {
   222  			fmt.Fprintf(o.Out, "\n\n%s", s)
   223  		}
   224  	}
   225  
   226  	if len(infos) == 0 && len(allErrs) == 0 {
   227  		// if we wrote no output, and had no errors, be sure we output something.
   228  		if o.AllNamespaces {
   229  			fmt.Fprintln(o.ErrOut, "No resources found")
   230  		} else {
   231  			fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace)
   232  		}
   233  	}
   234  
   235  	return utilerrors.NewAggregate(allErrs)
   236  }
   237  
   238  func (o *DescribeOptions) DescribeMatchingResources(originalError error, resource, prefix string) error {
   239  	r := o.NewBuilder().
   240  		Unstructured().
   241  		NamespaceParam(o.Namespace).DefaultNamespace().
   242  		ResourceTypeOrNameArgs(true, resource).
   243  		SingleResourceType().
   244  		RequestChunksOf(o.DescriberSettings.ChunkSize).
   245  		Flatten().
   246  		Do()
   247  	mapping, err := r.ResourceMapping()
   248  	if err != nil {
   249  		return err
   250  	}
   251  	describer, err := o.Describer(mapping)
   252  	if err != nil {
   253  		return err
   254  	}
   255  	infos, err := r.Infos()
   256  	if err != nil {
   257  		return err
   258  	}
   259  	isFound := false
   260  	for ix := range infos {
   261  		info := infos[ix]
   262  		if strings.HasPrefix(info.Name, prefix) {
   263  			isFound = true
   264  			s, err := describer.Describe(info.Namespace, info.Name, *o.DescriberSettings)
   265  			if err != nil {
   266  				return err
   267  			}
   268  			fmt.Fprintf(o.Out, "%s\n", s)
   269  		}
   270  	}
   271  	if !isFound {
   272  		return originalError
   273  	}
   274  	return nil
   275  }
   276  
   277  type DescribeOptions struct {
   278  	CmdParent string
   279  	Selector  string
   280  	Namespace string
   281  
   282  	Describer  func(*meta.RESTMapping) (describe.ResourceDescriber, error)
   283  	NewBuilder func() *resource.Builder
   284  
   285  	BuilderArgs []string
   286  
   287  	EnforceNamespace bool
   288  	AllNamespaces    bool
   289  
   290  	DescriberSettings *describe.DescriberSettings
   291  	FilenameOptions   *resource.FilenameOptions
   292  
   293  	genericiooptions.IOStreams
   294  }
   295  

View as plain text