diff --git a/pkg/karmadactl/describe.go b/pkg/karmadactl/describe.go index 8c7da18dc..7b8d35060 100644 --- a/pkg/karmadactl/describe.go +++ b/pkg/karmadactl/describe.go @@ -2,15 +2,11 @@ package karmadactl import ( "fmt" - "strings" "github.com/spf13/cobra" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/resource" + kubectldescribe "k8s.io/kubectl/pkg/cmd/describe" cmdutil "k8s.io/kubectl/pkg/cmd/util" "k8s.io/kubectl/pkg/describe" "k8s.io/kubectl/pkg/util/templates" @@ -39,16 +35,16 @@ var ( // NewCmdDescribe new describe command. func NewCmdDescribe(karmadaConfig KarmadaConfig, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command { - o := &CommandDescribeOptions{ - FilenameOptions: &resource.FilenameOptions{}, - DescriberSettings: &describe.DescriberSettings{ - ShowEvents: true, - ChunkSize: cmdutil.DefaultChunkSize, + o := &DescribeOptions{ + KubectlDescribeOptions: &kubectldescribe.DescribeOptions{ + FilenameOptions: &resource.FilenameOptions{}, + DescriberSettings: &describe.DescriberSettings{ + ShowEvents: true, + ChunkSize: cmdutil.DefaultChunkSize, + }, + CmdParent: parentCommand, + IOStreams: streams, }, - - CmdParent: parentCommand, - - IOStreams: streams, } cmd := &cobra.Command{ @@ -57,7 +53,7 @@ func NewCmdDescribe(karmadaConfig KarmadaConfig, parentCommand string, streams g SilenceUsage: true, Example: fmt.Sprintf(describeExample, parentCommand), RunE: func(cmd *cobra.Command, args []string) error { - if err := o.Complete(karmadaConfig, args); err != nil { + if err := o.Complete(karmadaConfig, cmd, args); err != nil { return err } if err := o.Run(); err != nil { @@ -70,45 +66,29 @@ func NewCmdDescribe(karmadaConfig KarmadaConfig, parentCommand string, streams g o.GlobalCommandOptions.AddFlags(cmd.Flags()) usage := "containing the resource to describe" - cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, usage) + cmdutil.AddFilenameOptionFlags(cmd, o.KubectlDescribeOptions.FilenameOptions, usage) cmd.Flags().StringVarP(&o.Cluster, "cluster", "C", "", "Specify a member cluster") cmd.Flags().StringVarP(&o.Namespace, "namespace", "n", o.Namespace, "If present, the namespace scope for this CLI request") - cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") - cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") - cmd.Flags().BoolVar(&o.DescriberSettings.ShowEvents, "show-events", o.DescriberSettings.ShowEvents, "If true, display events related to the described object.") - cmdutil.AddChunkSizeFlag(cmd, &o.DescriberSettings.ChunkSize) + cmd.Flags().StringVarP(&o.KubectlDescribeOptions.Selector, "selector", "l", o.KubectlDescribeOptions.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") + cmd.Flags().BoolVarP(&o.KubectlDescribeOptions.AllNamespaces, "all-namespaces", "A", o.KubectlDescribeOptions.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") + cmd.Flags().BoolVar(&o.KubectlDescribeOptions.DescriberSettings.ShowEvents, "show-events", o.KubectlDescribeOptions.DescriberSettings.ShowEvents, "If true, display events related to the described object.") + cmdutil.AddChunkSizeFlag(cmd, &o.KubectlDescribeOptions.DescriberSettings.ChunkSize) return cmd } -// CommandDescribeOptions contains the input to the describe command. -type CommandDescribeOptions struct { +// DescribeOptions contains the input to the describe command. +type DescribeOptions struct { // global flags options.GlobalCommandOptions - - Cluster string - CmdParent string - Selector string - Namespace string - - Describer func(*meta.RESTMapping) (describe.ResourceDescriber, error) - NewBuilder func() *resource.Builder - - BuilderArgs []string - - EnforceNamespace bool - AllNamespaces bool - - DescriberSettings *describe.DescriberSettings - FilenameOptions *resource.FilenameOptions - - genericclioptions.IOStreams + // flags specific to describe + KubectlDescribeOptions *kubectldescribe.DescribeOptions + Namespace string + Cluster string } // Complete ensures that options are valid and marshals them if necessary -func (o *CommandDescribeOptions) Complete(karmadaConfig KarmadaConfig, args []string) error { - var err error - +func (o *DescribeOptions) Complete(karmadaConfig KarmadaConfig, cmd *cobra.Command, args []string) error { if len(o.Cluster) == 0 { return fmt.Errorf("must specify a cluster") } @@ -118,146 +98,15 @@ func (o *CommandDescribeOptions) Complete(karmadaConfig KarmadaConfig, args []st return fmt.Errorf("failed to get control plane rest config. context: %s, kube-config: %s, error: %v", o.KarmadaContext, o.KubeConfig, err) } - clusterInfo, err := getClusterInfo(karmadaRestConfig, o.Cluster, o.KubeConfig, o.KarmadaContext) if err != nil { return err } - - f := getFactory(o.Cluster, clusterInfo, "") - - namespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace() - if err != nil { - return err - } - - if o.Namespace == "" { - o.Namespace = namespace - } - o.EnforceNamespace = enforceNamespace - - if o.AllNamespaces { - o.EnforceNamespace = false - } - - if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) { - return fmt.Errorf("must specify the type of resource to describe. %s", cmdutil.SuggestAPIResources(o.CmdParent)) - } - - o.BuilderArgs = args - - o.Describer = func(mapping *meta.RESTMapping) (describe.ResourceDescriber, error) { - return describe.DescriberFn(f, mapping) - } - - o.NewBuilder = f.NewBuilder - - return nil + f := getFactory(o.Cluster, clusterInfo, o.Namespace) + return o.KubectlDescribeOptions.Complete(f, cmd, args) } // Run describe information of resources -func (o *CommandDescribeOptions) Run() error { - r := o.NewBuilder(). - Unstructured(). - ContinueOnError(). - NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces). - FilenameParam(o.EnforceNamespace, o.FilenameOptions). - LabelSelectorParam(o.Selector). - ResourceTypeOrNameArgs(true, o.BuilderArgs...). - RequestChunksOf(o.DescriberSettings.ChunkSize). - Flatten(). - Do() - err := r.Err() - if err != nil { - return err - } - - allErrs := []error{} - infos, err := r.Infos() - if err != nil { - if apierrors.IsNotFound(err) && len(o.BuilderArgs) == 2 { - return o.describeMatchingResources(err, o.BuilderArgs[0], o.BuilderArgs[1]) - } - allErrs = append(allErrs, err) - } - - errs := sets.NewString() - first := true - for _, info := range infos { - mapping := info.ResourceMapping() - describer, err := o.Describer(mapping) - if err != nil { - if errs.Has(err.Error()) { - continue - } - allErrs = append(allErrs, err) - errs.Insert(err.Error()) - continue - } - s, err := describer.Describe(info.Namespace, info.Name, *o.DescriberSettings) - if err != nil { - if errs.Has(err.Error()) { - continue - } - allErrs = append(allErrs, err) - errs.Insert(err.Error()) - continue - } - if first { - first = false - fmt.Fprint(o.Out, s) - } else { - fmt.Fprintf(o.Out, "\n\n%s", s) - } - } - - if len(infos) == 0 && len(allErrs) == 0 { - // if we wrote no output, and had no errors, be sure we output something. - if o.AllNamespaces { - fmt.Fprintln(o.ErrOut, "No resources found") - } else { - fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace) - } - } - - return utilerrors.NewAggregate(allErrs) -} - -func (o *CommandDescribeOptions) describeMatchingResources(originalError error, resource, prefix string) error { - r := o.NewBuilder(). - Unstructured(). - NamespaceParam(o.Namespace).DefaultNamespace(). - ResourceTypeOrNameArgs(true, resource). - SingleResourceType(). - RequestChunksOf(o.DescriberSettings.ChunkSize). - Flatten(). - Do() - mapping, err := r.ResourceMapping() - if err != nil { - return err - } - describer, err := o.Describer(mapping) - if err != nil { - return err - } - infos, err := r.Infos() - if err != nil { - return err - } - isFound := false - for ix := range infos { - info := infos[ix] - if strings.HasPrefix(info.Name, prefix) { - isFound = true - s, err := describer.Describe(info.Namespace, info.Name, *o.DescriberSettings) - if err != nil { - return err - } - fmt.Fprintf(o.Out, "%s\n", s) - } - } - if !isFound { - return originalError - } - return nil +func (o *DescribeOptions) Run() error { + return o.KubectlDescribeOptions.Run() } diff --git a/vendor/k8s.io/kubectl/pkg/cmd/describe/describe.go b/vendor/k8s.io/kubectl/pkg/cmd/describe/describe.go new file mode 100644 index 000000000..6ca80e632 --- /dev/null +++ b/vendor/k8s.io/kubectl/pkg/cmd/describe/describe.go @@ -0,0 +1,259 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package describe + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + utilerrors "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/resource" + cmdutil "k8s.io/kubectl/pkg/cmd/util" + "k8s.io/kubectl/pkg/describe" + "k8s.io/kubectl/pkg/util/completion" + "k8s.io/kubectl/pkg/util/i18n" + "k8s.io/kubectl/pkg/util/templates" +) + +var ( + describeLong = templates.LongDesc(i18n.T(` + Show details of a specific resource or group of resources. + + Print a detailed description of the selected resources, including related resources such + as events or controllers. You may select a single object by name, all objects of that + type, provide a name prefix, or label selector. For example: + + $ kubectl describe TYPE NAME_PREFIX + + will first check for an exact match on TYPE and NAME_PREFIX. If no such resource + exists, it will output details for every resource that has a name prefixed with NAME_PREFIX.`)) + + describeExample = templates.Examples(i18n.T(` + # Describe a node + kubectl describe nodes kubernetes-node-emt8.c.myproject.internal + + # Describe a pod + kubectl describe pods/nginx + + # Describe a pod identified by type and name in "pod.json" + kubectl describe -f pod.json + + # Describe all pods + kubectl describe pods + + # Describe pods by label name=myLabel + kubectl describe po -l name=myLabel + + # Describe all pods managed by the 'frontend' replication controller + # (rc-created pods get the name of the rc as a prefix in the pod name) + kubectl describe pods frontend`)) +) + +type DescribeOptions struct { + CmdParent string + Selector string + Namespace string + + Describer func(*meta.RESTMapping) (describe.ResourceDescriber, error) + NewBuilder func() *resource.Builder + + BuilderArgs []string + + EnforceNamespace bool + AllNamespaces bool + + DescriberSettings *describe.DescriberSettings + FilenameOptions *resource.FilenameOptions + + genericclioptions.IOStreams +} + +func NewCmdDescribe(parent string, f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + o := &DescribeOptions{ + FilenameOptions: &resource.FilenameOptions{}, + DescriberSettings: &describe.DescriberSettings{ + ShowEvents: true, + ChunkSize: cmdutil.DefaultChunkSize, + }, + + CmdParent: parent, + + IOStreams: streams, + } + + cmd := &cobra.Command{ + Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)", + DisableFlagsInUseLine: true, + Short: i18n.T("Show details of a specific resource or group of resources"), + Long: describeLong + "\n\n" + cmdutil.SuggestAPIResources(parent), + Example: describeExample, + ValidArgsFunction: completion.ResourceTypeAndNameCompletionFunc(f), + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(o.Complete(f, cmd, args)) + cmdutil.CheckErr(o.Run()) + }, + } + usage := "containing the resource to describe" + cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, usage) + cmdutil.AddLabelSelectorFlagVar(cmd, &o.Selector) + cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") + cmd.Flags().BoolVar(&o.DescriberSettings.ShowEvents, "show-events", o.DescriberSettings.ShowEvents, "If true, display events related to the described object.") + cmdutil.AddChunkSizeFlag(cmd, &o.DescriberSettings.ChunkSize) + return cmd +} + +func (o *DescribeOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { + var err error + o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace() + if err != nil { + return err + } + + if o.AllNamespaces { + o.EnforceNamespace = false + } + + if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames, o.FilenameOptions.Kustomize) { + return fmt.Errorf("You must specify the type of resource to describe. %s\n", cmdutil.SuggestAPIResources(o.CmdParent)) + } + + o.BuilderArgs = args + + o.Describer = func(mapping *meta.RESTMapping) (describe.ResourceDescriber, error) { + return describe.DescriberFn(f, mapping) + } + + o.NewBuilder = f.NewBuilder + + return nil +} + +func (o *DescribeOptions) Validate(args []string) error { + return nil +} + +func (o *DescribeOptions) Run() error { + r := o.NewBuilder(). + Unstructured(). + ContinueOnError(). + NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces). + FilenameParam(o.EnforceNamespace, o.FilenameOptions). + LabelSelectorParam(o.Selector). + ResourceTypeOrNameArgs(true, o.BuilderArgs...). + RequestChunksOf(o.DescriberSettings.ChunkSize). + Flatten(). + Do() + err := r.Err() + if err != nil { + return err + } + + allErrs := []error{} + infos, err := r.Infos() + if err != nil { + if apierrors.IsNotFound(err) && len(o.BuilderArgs) == 2 { + return o.DescribeMatchingResources(err, o.BuilderArgs[0], o.BuilderArgs[1]) + } + allErrs = append(allErrs, err) + } + + errs := sets.NewString() + first := true + for _, info := range infos { + mapping := info.ResourceMapping() + describer, err := o.Describer(mapping) + if err != nil { + if errs.Has(err.Error()) { + continue + } + allErrs = append(allErrs, err) + errs.Insert(err.Error()) + continue + } + s, err := describer.Describe(info.Namespace, info.Name, *o.DescriberSettings) + if err != nil { + if errs.Has(err.Error()) { + continue + } + allErrs = append(allErrs, err) + errs.Insert(err.Error()) + continue + } + if first { + first = false + fmt.Fprint(o.Out, s) + } else { + fmt.Fprintf(o.Out, "\n\n%s", s) + } + } + + if len(infos) == 0 && len(allErrs) == 0 { + // if we wrote no output, and had no errors, be sure we output something. + if o.AllNamespaces { + fmt.Fprintln(o.ErrOut, "No resources found") + } else { + fmt.Fprintf(o.ErrOut, "No resources found in %s namespace.\n", o.Namespace) + } + } + + return utilerrors.NewAggregate(allErrs) +} + +func (o *DescribeOptions) DescribeMatchingResources(originalError error, resource, prefix string) error { + r := o.NewBuilder(). + Unstructured(). + NamespaceParam(o.Namespace).DefaultNamespace(). + ResourceTypeOrNameArgs(true, resource). + SingleResourceType(). + RequestChunksOf(o.DescriberSettings.ChunkSize). + Flatten(). + Do() + mapping, err := r.ResourceMapping() + if err != nil { + return err + } + describer, err := o.Describer(mapping) + if err != nil { + return err + } + infos, err := r.Infos() + if err != nil { + return err + } + isFound := false + for ix := range infos { + info := infos[ix] + if strings.HasPrefix(info.Name, prefix) { + isFound = true + s, err := describer.Describe(info.Namespace, info.Name, *o.DescriberSettings) + if err != nil { + return err + } + fmt.Fprintf(o.Out, "%s\n", s) + } + } + if !isFound { + return originalError + } + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 6ce039639..be583c0ba 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1414,6 +1414,7 @@ k8s.io/kubectl/pkg/apps k8s.io/kubectl/pkg/cmd/apiresources k8s.io/kubectl/pkg/cmd/apply k8s.io/kubectl/pkg/cmd/delete +k8s.io/kubectl/pkg/cmd/describe k8s.io/kubectl/pkg/cmd/exec k8s.io/kubectl/pkg/cmd/get k8s.io/kubectl/pkg/cmd/logs