Implementing autocompletion for karmadactl

Signed-off-by: zhzhuang-zju <m17799853869@163.com>
This commit is contained in:
zhzhuang-zju 2024-09-20 09:42:40 +08:00
parent 4ac95b95a1
commit a684edd354
27 changed files with 839 additions and 63 deletions

View File

@ -23,6 +23,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -57,5 +58,7 @@ func NewCmdAnnotate(f util.Factory, parentCommand string, ioStreams genericioopt
} }
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags()) options.AddNamespaceFlag(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd return cmd
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -83,6 +84,10 @@ func NewCmdAPIResources(f util.Factory, parentCommand string, ioStreams generici
cmd.Flags().StringVar(&o.SortBy, "sort-by", o.SortBy, "If non-empty, sort list of resources using specified field. The field can be either 'name' or 'kind'.") cmd.Flags().StringVar(&o.SortBy, "sort-by", o.SortBy, "If non-empty, sort list of resources using specified field. The field can be either 'name' or 'kind'.")
cmd.Flags().BoolVar(&o.Cached, "cached", o.Cached, "Use the cached list of resources if available.") cmd.Flags().BoolVar(&o.Cached, "cached", o.Cached, "Use the cached list of resources if available.")
cmd.Flags().StringSliceVar(&o.Categories, "categories", o.Categories, "Limit to resources that belong to the specified categories.") cmd.Flags().StringSliceVar(&o.Categories, "categories", o.Categories, "Limit to resources that belong to the specified categories.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -62,6 +63,11 @@ func NewCmdAPIVersions(f util.Factory, parentCommand string, ioStreams genericio
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
cmd.Flags().VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.") cmd.Flags().VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.")
cmd.Flags().StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1") cmd.Flags().StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -35,6 +35,7 @@ import (
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/util/names" "github.com/karmada-io/karmada/pkg/util/names"
) )
@ -93,6 +94,7 @@ func NewCmdApply(f util.Factory, parentCommand string, streams genericiooptions.
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Example: fmt.Sprintf(applyExample, parentCommand), Example: fmt.Sprintf(applyExample, parentCommand),
ValidArgsFunction: utilcomp.ResourceTypeAndNameCompletionFunc(f),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(f, cmd, parentCommand, args); err != nil { if err := o.Complete(f, cmd, parentCommand, args); err != nil {
return err return err
@ -113,6 +115,11 @@ func NewCmdApply(f util.Factory, parentCommand string, streams genericiooptions.
options.AddNamespaceFlag(flags) options.AddNamespaceFlag(flags)
flags.BoolVarP(&o.AllClusters, "all-clusters", "", o.AllClusters, "If present, propagates a group of resources to all member clusters.") flags.BoolVarP(&o.AllClusters, "all-clusters", "", o.AllClusters, "If present, propagates a group of resources to all member clusters.")
flags.StringSliceVarP(&o.Clusters, "cluster", "C", o.Clusters, "If present, propagates a group of resources to specified clusters.") flags.StringSliceVarP(&o.Clusters, "cluster", "C", o.Clusters, "If present, propagates a group of resources to specified clusters.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -24,11 +24,11 @@ import (
"k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/cli-runtime/pkg/genericiooptions"
kubectlattach "k8s.io/kubectl/pkg/cmd/attach" kubectlattach "k8s.io/kubectl/pkg/cmd/attach"
cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/templates"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -64,7 +64,7 @@ func NewCmdAttach(f util.Factory, parentCommand string, streams genericiooptions
Short: "Attach to a running container", Short: "Attach to a running container",
Long: "Attach to a process that is already running inside an existing container.", Long: "Attach to a process that is already running inside an existing container.",
Example: fmt.Sprintf(attachExample, parentCommand), Example: fmt.Sprintf(attachExample, parentCommand),
ValidArgsFunction: completion.PodResourceNameCompletionFunc(f), ValidArgsFunction: utilcomp.PodResourceNameCompletionFunc(f),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Validate())
@ -85,6 +85,11 @@ func NewCmdAttach(f util.Factory, parentCommand string, streams genericiooptions
cmd.Flags().BoolVarP(&o.Quiet, "quiet", "q", o.Quiet, "Only print output from the remote session") cmd.Flags().BoolVarP(&o.Quiet, "quiet", "q", o.Quiet, "Only print output from the remote session")
cmd.Flags().VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.") cmd.Flags().VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.")
cmd.Flags().StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1") cmd.Flags().StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -0,0 +1,143 @@
/*
Copyright 2024 The Karmada 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 completion
import (
"fmt"
"io"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
)
const defaultBoilerPlate = `
# Copyright 2024 The Karmada 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.
`
var (
completionLong = templates.LongDesc(i18n.T(`
Output shell completion code for the specified shell (bash, zsh).
The shell code must be evaluated to provide interactive
completion of kubectl commands. This can be done by sourcing it from
the .bash_profile.
Note for zsh users: zsh completions are only supported in versions of zsh >= 5.2.`))
completionExample = templates.Examples(i18n.T(`
# Installing bash completion on Linux
## If bash-completion is not installed on Linux, install the 'bash-completion' package
1. apt-get install bash-completion
2. source /usr/share/bash-completion/bash_completion
## Load the %[1]s completion code for bash into the current shell
source <(%[1]s completion bash)
## Or, write bash completion code to a file and source it from .bash_profile
1. %[1]s completion bash > ~/.kube/completion.bash.inc
2. echo "source '$HOME/.kube/completion.bash.inc'" >> $HOME/.bash_profile
3. source $HOME/.bash_profile
# Load the %[1]s completion code for zsh into the current shell
source <(%[1]s completion zsh)
# Set the %[1]s completion code for zsh to autoload on startup
%[1]s completion zsh > "${fpath[1]}/%[1]s"`))
)
var (
// TODO: support output shell completion code for more specified shell, like `fish` and `powershell`.
completionShells = map[string]func(out io.Writer, boilerPlate string, cmd *cobra.Command) error{
"bash": runCompletionBash,
"zsh": runCompletionZsh,
}
)
// NewCmdCompletion creates the `completion` command
func NewCmdCompletion(parentCommand string, out io.Writer, boilerPlate string) *cobra.Command {
var shells []string
for s := range completionShells {
shells = append(shells, s)
}
cmd := &cobra.Command{
Use: "completion SHELL",
DisableFlagsInUseLine: true,
Short: "Output shell completion code for the specified shell (bash, zsh)",
Long: completionLong,
Example: fmt.Sprintf(completionExample, parentCommand),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(RunCompletion(out, boilerPlate, cmd, args))
},
ValidArgs: shells,
}
return cmd
}
// RunCompletion checks given arguments and executes command
func RunCompletion(out io.Writer, boilerPlate string, cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return cmdutil.UsageErrorf(cmd, "Shell not specified.")
}
if len(args) > 1 {
return cmdutil.UsageErrorf(cmd, "Too many arguments. Expected only the shell type.")
}
run, found := completionShells[args[0]]
if !found {
return cmdutil.UsageErrorf(cmd, "Unsupported shell type %q.", args[0])
}
return run(out, boilerPlate, cmd.Parent())
}
func runCompletionBash(out io.Writer, boilerPlate string, cmd *cobra.Command) error {
if len(boilerPlate) == 0 {
boilerPlate = defaultBoilerPlate
}
if _, err := out.Write([]byte(boilerPlate)); err != nil {
return err
}
return cmd.GenBashCompletionV2(out, true)
}
func runCompletionZsh(out io.Writer, boilerPlate string, cmd *cobra.Command) error {
zshHead := fmt.Sprintf("#compdef %[1]s\ncompdef _%[1]s %[1]s\n", cmd.Name())
if _, err := out.Write([]byte(zshHead)); err != nil {
return err
}
if len(boilerPlate) == 0 {
boilerPlate = defaultBoilerPlate
}
if _, err := out.Write([]byte(boilerPlate)); err != nil {
return err
}
return cmd.GenZshCompletion(out)
}

View File

@ -33,6 +33,7 @@ import (
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -61,6 +62,7 @@ const (
// NewCmdCordon defines the `cordon` command that mark cluster as unschedulable. // NewCmdCordon defines the `cordon` command that mark cluster as unschedulable.
func NewCmdCordon(f util.Factory, parentCommand string) *cobra.Command { func NewCmdCordon(f util.Factory, parentCommand string) *cobra.Command {
opts := CommandCordonOption{} opts := CommandCordonOption{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "cordon CLUSTER", Use: "cordon CLUSTER",
Short: "Mark cluster as unschedulable", Short: "Mark cluster as unschedulable",
@ -68,6 +70,7 @@ func NewCmdCordon(f util.Factory, parentCommand string) *cobra.Command {
Example: fmt.Sprintf(cordonExample, parentCommand), Example: fmt.Sprintf(cordonExample, parentCommand),
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.SpecifiedResourceTypeAndNameCompletionFunc(f, []string{"cluster"}),
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
if err := opts.Complete(args); err != nil { if err := opts.Complete(args); err != nil {
return err return err
@ -86,12 +89,14 @@ func NewCmdCordon(f util.Factory, parentCommand string) *cobra.Command {
options.AddKubeConfigFlags(flags) options.AddKubeConfigFlags(flags)
flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.") flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }
// NewCmdUncordon defines the `uncordon` command that mark cluster as schedulable. // NewCmdUncordon defines the `uncordon` command that mark cluster as schedulable.
func NewCmdUncordon(f util.Factory, parentCommand string) *cobra.Command { func NewCmdUncordon(f util.Factory, parentCommand string) *cobra.Command {
opts := CommandCordonOption{} opts := CommandCordonOption{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "uncordon CLUSTER", Use: "uncordon CLUSTER",
Short: "Mark cluster as schedulable", Short: "Mark cluster as schedulable",
@ -99,6 +104,7 @@ func NewCmdUncordon(f util.Factory, parentCommand string) *cobra.Command {
Example: fmt.Sprintf(uncordonExample, parentCommand), Example: fmt.Sprintf(uncordonExample, parentCommand),
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.SpecifiedResourceTypeAndNameCompletionFunc(f, []string{"cluster"}),
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
if err := opts.Complete(args); err != nil { if err := opts.Complete(args); err != nil {
return err return err
@ -117,6 +123,7 @@ func NewCmdUncordon(f util.Factory, parentCommand string) *cobra.Command {
options.AddKubeConfigFlags(flags) options.AddKubeConfigFlags(flags)
flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.") flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }

View File

@ -26,6 +26,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -55,5 +56,9 @@ func NewCmdCreate(f util.Factory, parentCommand string, ioStreams genericiooptio
} }
options.AddKubeConfigFlags(cmd.PersistentFlags()) options.AddKubeConfigFlags(cmd.PersistentFlags())
options.AddNamespaceFlag(cmd.PersistentFlags()) options.AddNamespaceFlag(cmd.PersistentFlags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd return cmd
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -98,5 +99,8 @@ func NewCmdDelete(f util.Factory, parentCommand string, ioStreams genericiooptio
} }
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags()) options.AddNamespaceFlag(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd return cmd
} }

View File

@ -26,6 +26,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -74,6 +75,7 @@ func NewCmdDescribe(f util.Factory, parentCommand string, streams genericiooptio
Long: fmt.Sprintf(describeLong, parentCommand), Long: fmt.Sprintf(describeLong, parentCommand),
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.ResourceTypeAndNameCompletionFunc(f),
Example: fmt.Sprintf(describeExample, parentCommand), Example: fmt.Sprintf(describeExample, parentCommand),
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
if err := o.Complete(f, args, kubedescribeFlags, parentCommand); err != nil { if err := o.Complete(f, args, kubedescribeFlags, parentCommand); err != nil {
@ -101,6 +103,10 @@ func NewCmdDescribe(f util.Factory, parentCommand string, streams genericiooptio
flags.VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.") flags.VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.")
flags.StringVarP(&o.Cluster, "cluster", "C", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1") flags.StringVarP(&o.Cluster, "cluster", "C", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -56,5 +57,8 @@ func NewCmdEdit(f util.Factory, parentCommand string, ioStreams genericiooptions
} }
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags()) options.AddNamespaceFlag(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd return cmd
} }

View File

@ -28,6 +28,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
const ( const (
@ -80,6 +81,7 @@ func NewCmdExec(f util.Factory, parentCommand string, streams genericiooptions.I
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Example: fmt.Sprintf(execExample, parentCommand), Example: fmt.Sprintf(execExample, parentCommand),
ValidArgsFunction: utilcomp.PodResourceNameCompletionFunc(f),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
argsLenAtDash := cmd.ArgsLenAtDash() argsLenAtDash := cmd.ArgsLenAtDash()
if err := o.Complete(f, cmd, args, argsLenAtDash); err != nil { if err := o.Complete(f, cmd, args, argsLenAtDash); err != nil {
@ -111,6 +113,13 @@ func NewCmdExec(f util.Factory, parentCommand string, streams genericiooptions.I
flags.BoolVarP(&o.KubectlExecOptions.Quiet, "quiet", "q", o.KubectlExecOptions.Quiet, "Only print output from the remote session") flags.BoolVarP(&o.KubectlExecOptions.Quiet, "quiet", "q", o.KubectlExecOptions.Quiet, "Only print output from the remote session")
flags.VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.") flags.VarP(&o.OperationScope, "operation-scope", "s", "Used to control the operation scope of the command. The optional values are karmada and members. Defaults to karmada.")
flags.StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1") flags.StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is members, for example: --operation-scope=members --cluster=member1")
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc("container", utilcomp.ContainerCompletionFunc(f)))
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -90,6 +91,11 @@ func NewCmdExplain(f util.Factory, parentCommand string, streams genericiooption
// Only enable --output as a valid flag if the feature is enabled // Only enable --output as a valid flag if the feature is enabled
flags.StringVar(&o.OutputFormat, "output", plaintextTemplateName, "Format in which to render the schema. Valid values are: (plaintext, plaintext-openapiv2).") flags.StringVar(&o.OutputFormat, "output", plaintextTemplateName, "Format in which to render the schema. Valid values are: (plaintext, plaintext-openapiv2).")
flags.StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is member clusters, for example: --operation-scope=all --cluster=member1") flags.StringVar(&o.Cluster, "cluster", "", "Used to specify a target member cluster and only takes effect when the command's operation scope is member clusters, for example: --operation-scope=all --cluster=member1")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -130,13 +130,13 @@ func NewCmdGet(f util.Factory, parentCommand string, streams genericiooptions.IO
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Example: fmt.Sprintf(getExample, parentCommand), Example: fmt.Sprintf(getExample, parentCommand),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(f); err != nil { if err := o.Complete(f, cmd); err != nil {
return err return err
} }
if err := o.Validate(cmd); err != nil { if err := o.Validate(cmd); err != nil {
return err return err
} }
if err := o.Run(f, cmd, args); err != nil { if err := o.Run(f, args); err != nil {
return err return err
} }
return nil return nil
@ -167,7 +167,7 @@ func NewCmdGet(f util.Factory, parentCommand string, streams genericiooptions.IO
type CommandGetOptions struct { type CommandGetOptions struct {
Clusters []string Clusters []string
OperationScope options.OperationScope OperationScope options.OperationScope
targetMemberClusters []string TargetMemberClusters []string
PrintFlags *get.PrintFlags PrintFlags *get.PrintFlags
ToPrinter func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error) ToPrinter func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error)
@ -198,7 +198,7 @@ type CommandGetOptions struct {
genericiooptions.IOStreams genericiooptions.IOStreams
karmadaClient karmadaclientset.Interface KarmadaClient karmadaclientset.Interface
} }
// NewCommandGetOptions returns a CommandGetOptions with default chunk size 500. // NewCommandGetOptions returns a CommandGetOptions with default chunk size 500.
@ -212,9 +212,7 @@ func NewCommandGetOptions(streams genericiooptions.IOStreams) *CommandGetOptions
} }
// Complete takes the command arguments and infers any remaining options. // Complete takes the command arguments and infers any remaining options.
func (g *CommandGetOptions) Complete(f util.Factory) error { func (g *CommandGetOptions) Complete(f util.Factory, cmd *cobra.Command) error {
newScheme := gclient.NewSchema()
err := g.handleNamespaceScopeFlags(f) err := g.handleNamespaceScopeFlags(f)
if err != nil { if err != nil {
return err return err
@ -225,12 +223,54 @@ func (g *CommandGetOptions) Complete(f util.Factory) error {
templateArg = *g.PrintFlags.TemplateFlags.TemplateArgument templateArg = *g.PrintFlags.TemplateFlags.TemplateArgument
} }
outputOption := cmd.Flags().Lookup("output").Value.String()
if strings.Contains(outputOption, "custom-columns") || outputOption == "yaml" || strings.Contains(outputOption, "json") {
g.ServerPrint = false
}
// human readable printers have special conversion rules, so we determine if we're using one. // human readable printers have special conversion rules, so we determine if we're using one.
if (len(*g.PrintFlags.OutputFormat) == 0 && len(templateArg) == 0) || *g.PrintFlags.OutputFormat == "wide" { if (len(*g.PrintFlags.OutputFormat) == 0 && len(templateArg) == 0) || *g.PrintFlags.OutputFormat == "wide" {
g.IsHumanReadablePrinter = true g.IsHumanReadablePrinter = true
} }
g.ToPrinter = func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) { g.ToPrinter = g.getResourcePrinter()
karmadaClient, err := f.KarmadaClientSet()
if err != nil {
return err
}
g.KarmadaClient = karmadaClient
return g.HandleClusterScopeFlags()
}
// Validate checks the set of flags provided by the user.
func (g *CommandGetOptions) Validate(cmd *cobra.Command) error {
if cmdutil.GetFlagBool(cmd, "show-labels") {
outputOption := cmd.Flags().Lookup("output").Value.String()
if outputOption != "" && outputOption != "wide" {
return fmt.Errorf("--show-labels option cannot be used with %s printer", outputOption)
}
}
if g.OutputWatchEvents && !(g.Watch || g.WatchOnly) {
return fmt.Errorf("--output-watch-events option can only be used with --watch or --watch-only")
}
if err := options.VerifyOperationScopeFlags(g.OperationScope); err != nil {
return err
}
if options.ContainMembersScope(g.OperationScope) && len(g.Clusters) > 0 {
clusters, err := g.KarmadaClient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
return util.VerifyClustersExist(g.Clusters, clusters)
}
return nil
}
func (g *CommandGetOptions) getResourcePrinter() func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
newScheme := gclient.NewSchema()
return func(mapping *meta.RESTMapping, outputObjects *bool, withNamespace bool, withKind bool) (printers.ResourcePrinterFunc, error) {
// make a new copy of current flags / opts before mutating // make a new copy of current flags / opts before mutating
printFlags := g.PrintFlags.Copy() printFlags := g.PrintFlags.Copy()
@ -263,51 +303,20 @@ func (g *CommandGetOptions) Complete(f util.Factory) error {
return printer.PrintObj, nil return printer.PrintObj, nil
} }
karmadaClient, err := f.KarmadaClientSet()
if err != nil {
return err
}
g.karmadaClient = karmadaClient
return g.handleClusterScopeFlags()
} }
// Validate checks the set of flags provided by the user. // HandleClusterScopeFlags used to handle flags related to cluster scope.
func (g *CommandGetOptions) Validate(cmd *cobra.Command) error { func (g *CommandGetOptions) HandleClusterScopeFlags() error {
if cmdutil.GetFlagBool(cmd, "show-labels") {
outputOption := cmd.Flags().Lookup("output").Value.String()
if outputOption != "" && outputOption != "wide" {
return fmt.Errorf("--show-labels option cannot be used with %s printer", outputOption)
}
}
if g.OutputWatchEvents && !(g.Watch || g.WatchOnly) {
return fmt.Errorf("--output-watch-events option can only be used with --watch or --watch-only")
}
if err := options.VerifyOperationScopeFlags(g.OperationScope); err != nil {
return err
}
if options.ContainMembersScope(g.OperationScope) && len(g.Clusters) > 0 {
clusters, err := g.karmadaClient.ClusterV1alpha1().Clusters().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
return util.VerifyClustersExist(g.Clusters, clusters)
}
return nil
}
func (g *CommandGetOptions) handleClusterScopeFlags() error {
var err error var err error
switch g.OperationScope { switch g.OperationScope {
case options.KarmadaControlPlane: case options.KarmadaControlPlane:
g.targetMemberClusters = []string{} g.TargetMemberClusters = []string{}
case options.Members, options.All: case options.Members, options.All:
if len(g.Clusters) == 0 { if len(g.Clusters) == 0 {
g.targetMemberClusters, err = LoadRegisteredClusters(g.karmadaClient) g.TargetMemberClusters, err = LoadRegisteredClusters(g.KarmadaClient)
return err return err
} }
g.targetMemberClusters = g.Clusters g.TargetMemberClusters = g.Clusters
return nil return nil
} }
return nil return nil
@ -339,7 +348,7 @@ type WatchObj struct {
} }
// Run performs the get operation. // Run performs the get operation.
func (g *CommandGetOptions) Run(f util.Factory, cmd *cobra.Command, args []string) error { func (g *CommandGetOptions) Run(f util.Factory, args []string) error {
mux := sync.Mutex{} mux := sync.Mutex{}
var wg sync.WaitGroup var wg sync.WaitGroup
@ -347,24 +356,19 @@ func (g *CommandGetOptions) Run(f util.Factory, cmd *cobra.Command, args []strin
var watchObjs []WatchObj var watchObjs []WatchObj
var allErrs []error var allErrs []error
outputOption := cmd.Flags().Lookup("output").Value.String()
if strings.Contains(outputOption, "custom-columns") || outputOption == "yaml" || strings.Contains(outputOption, "json") {
g.ServerPrint = false
}
if options.ContainKarmadaScope(g.OperationScope) { if options.ContainKarmadaScope(g.OperationScope) {
g.getObjInfo(&mux, f, "Karmada", true, &objs, &watchObjs, &allErrs, args) g.getObjInfo(&mux, f, "Karmada", true, &objs, &watchObjs, &allErrs, args)
} }
if len(g.targetMemberClusters) != 0 { if len(g.TargetMemberClusters) != 0 {
wg.Add(len(g.targetMemberClusters)) wg.Add(len(g.TargetMemberClusters))
for idx := range g.targetMemberClusters { for idx := range g.TargetMemberClusters {
memberFactory, err := f.FactoryForMemberCluster(g.targetMemberClusters[idx]) memberFactory, err := f.FactoryForMemberCluster(g.TargetMemberClusters[idx])
if err != nil { if err != nil {
return err return err
} }
go func() { go func() {
g.getObjInfo(&mux, memberFactory, g.targetMemberClusters[idx], false, &objs, &watchObjs, &allErrs, args) g.getObjInfo(&mux, memberFactory, g.TargetMemberClusters[idx], false, &objs, &watchObjs, &allErrs, args)
wg.Done() wg.Done()
}() }()
} }
@ -476,7 +480,7 @@ func (g *CommandGetOptions) printIfNotFindResource(written int, allErrs *[]error
if written != 0 || g.IgnoreNotFound || len(*allErrs) != 0 { if written != 0 || g.IgnoreNotFound || len(*allErrs) != 0 {
return return
} }
if !options.ContainKarmadaScope(g.OperationScope) && len(g.targetMemberClusters) == 0 { if !options.ContainKarmadaScope(g.OperationScope) && len(g.TargetMemberClusters) == 0 {
fmt.Fprintln(g.ErrOut, "No member Clusters found in Karmada control plane") fmt.Fprintln(g.ErrOut, "No member Clusters found in Karmada control plane")
return return
} }

View File

@ -34,6 +34,7 @@ import (
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/karmadactl/util/genericresource" "github.com/karmada-io/karmada/pkg/karmadactl/util/genericresource"
"github.com/karmada-io/karmada/pkg/util/gclient" "github.com/karmada-io/karmada/pkg/util/gclient"
"github.com/karmada-io/karmada/pkg/util/helper" "github.com/karmada-io/karmada/pkg/util/helper"
@ -131,6 +132,7 @@ func NewCmdInterpret(f util.Factory, parentCommand string, streams genericioopti
cmdutil.AddJsonFilenameFlag(flags, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files containing the customizations") cmdutil.AddJsonFilenameFlag(flags, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files containing the customizations")
flags.BoolVarP(&o.FilenameOptions.Recursive, "recursive", "R", false, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.") flags.BoolVarP(&o.FilenameOptions.Recursive, "recursive", "R", false, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }

View File

@ -33,6 +33,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
cmdutil "github.com/karmada-io/karmada/pkg/karmadactl/util" cmdutil "github.com/karmada-io/karmada/pkg/karmadactl/util"
"github.com/karmada-io/karmada/pkg/karmadactl/util/apiclient" "github.com/karmada-io/karmada/pkg/karmadactl/util/apiclient"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/util" "github.com/karmada-io/karmada/pkg/util"
) )
@ -77,6 +78,7 @@ func NewCmdJoin(f cmdutil.Factory, parentCommand string) *cobra.Command {
opts.AddFlags(flags) opts.AddFlags(flags)
options.AddKubeConfigFlags(flags) options.AddKubeConfigFlags(flags)
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }

View File

@ -34,6 +34,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/apply" "github.com/karmada-io/karmada/pkg/karmadactl/apply"
"github.com/karmada-io/karmada/pkg/karmadactl/attach" "github.com/karmada-io/karmada/pkg/karmadactl/attach"
"github.com/karmada-io/karmada/pkg/karmadactl/cmdinit" "github.com/karmada-io/karmada/pkg/karmadactl/cmdinit"
"github.com/karmada-io/karmada/pkg/karmadactl/completion"
"github.com/karmada-io/karmada/pkg/karmadactl/cordon" "github.com/karmada-io/karmada/pkg/karmadactl/cordon"
"github.com/karmada-io/karmada/pkg/karmadactl/create" "github.com/karmada-io/karmada/pkg/karmadactl/create"
"github.com/karmada-io/karmada/pkg/karmadactl/deinit" "github.com/karmada-io/karmada/pkg/karmadactl/deinit"
@ -56,6 +57,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/top" "github.com/karmada-io/karmada/pkg/karmadactl/top"
"github.com/karmada-io/karmada/pkg/karmadactl/unjoin" "github.com/karmada-io/karmada/pkg/karmadactl/unjoin"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/version/sharedcommand" "github.com/karmada-io/karmada/pkg/version/sharedcommand"
) )
@ -91,12 +93,21 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
_ = flag.CommandLine.Parse(nil) _ = flag.CommandLine.Parse(nil)
f := util.NewFactory(options.DefaultConfigFlags) f := util.NewFactory(options.DefaultConfigFlags)
ioStreams := genericiooptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} ioStreams := genericiooptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr}
// Avoid import cycle by setting ValidArgsFunction here instead of in NewCmdGet()
getCmd := get.NewCmdGet(f, parentCommand, ioStreams)
getCmd.ValidArgsFunction = utilcomp.ResourceTypeAndNameCompletionFunc(f)
utilcomp.RegisterCompletionFuncForClustersFlag(getCmd)
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(getCmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(getCmd, f)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(getCmd)
groups := templates.CommandGroups{ groups := templates.CommandGroups{
{ {
Message: "Basic Commands:", Message: "Basic Commands:",
Commands: []*cobra.Command{ Commands: []*cobra.Command{
explain.NewCmdExplain(f, parentCommand, ioStreams), explain.NewCmdExplain(f, parentCommand, ioStreams),
get.NewCmdGet(f, parentCommand, ioStreams), getCmd,
create.NewCmdCreate(f, parentCommand, ioStreams), create.NewCmdCreate(f, parentCommand, ioStreams),
karmadactldelete.NewCmdDelete(f, parentCommand, ioStreams), karmadactldelete.NewCmdDelete(f, parentCommand, ioStreams),
edit.NewCmdEdit(f, parentCommand, ioStreams), edit.NewCmdEdit(f, parentCommand, ioStreams),
@ -146,6 +157,7 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
Commands: []*cobra.Command{ Commands: []*cobra.Command{
label.NewCmdLabel(f, parentCommand, ioStreams), label.NewCmdLabel(f, parentCommand, ioStreams),
annotate.NewCmdAnnotate(f, parentCommand, ioStreams), annotate.NewCmdAnnotate(f, parentCommand, ioStreams),
completion.NewCmdCompletion(parentCommand, ioStreams.Out, ""),
}, },
}, },
{ {
@ -165,6 +177,8 @@ func NewKarmadaCtlCommand(cmdUse, parentCommand string) *cobra.Command {
templates.ActsAsRootCommand(rootCmd, filters, groups...) templates.ActsAsRootCommand(rootCmd, filters, groups...)
utilcomp.SetFactoryForCompletion(f)
return rootCmd return rootCmd
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -56,5 +57,8 @@ func NewCmdLabel(f util.Factory, parentCommand string, ioStreams genericiooption
} }
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags()) options.AddNamespaceFlag(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd return cmd
} }

View File

@ -27,6 +27,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
const ( const (
@ -79,6 +80,7 @@ func NewCmdLogs(f util.Factory, parentCommand string, streams genericiooptions.I
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Example: fmt.Sprintf(logsExample, parentCommand), Example: fmt.Sprintf(logsExample, parentCommand),
ValidArgsFunction: utilcomp.PodResourceNameAndContainerCompletionFunc(f),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(cmd, args, f); err != nil { if err := o.Complete(cmd, args, f); err != nil {
return err return err
@ -102,6 +104,9 @@ func NewCmdLogs(f util.Factory, parentCommand string, streams genericiooptions.I
flags.StringVarP(&o.Cluster, "cluster", "C", "", "Specify a member cluster") flags.StringVarP(&o.Cluster, "cluster", "C", "", "Specify a member cluster")
o.KubectlLogsOptions.AddFlags(cmd) o.KubectlLogsOptions.AddFlags(cmd)
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -52,5 +53,8 @@ func NewCmdPatch(f util.Factory, parentCommand string, ioStreams genericiooption
} }
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags()) options.AddNamespaceFlag(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd return cmd
} }

View File

@ -46,6 +46,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/get" "github.com/karmada-io/karmada/pkg/karmadactl/get"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/declarative" "github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/declarative"
"github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/webhook" "github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/webhook"
"github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/webhook/request" "github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/webhook/request"
@ -103,6 +104,7 @@ func NewCmdPromote(f util.Factory, parentCommand string) *cobra.Command {
Example: fmt.Sprintf(promoteExample, parentCommand), Example: fmt.Sprintf(promoteExample, parentCommand),
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.ResourceTypeAndNameCompletionFunc(f),
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
if err := opts.Complete(f, args); err != nil { if err := opts.Complete(f, args); err != nil {
return err return err
@ -125,6 +127,9 @@ func NewCmdPromote(f util.Factory, parentCommand string) *cobra.Command {
options.AddKubeConfigFlags(flag) options.AddKubeConfigFlags(flag)
options.AddNamespaceFlag(flag) options.AddNamespaceFlag(flag)
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd return cmd
} }

View File

@ -37,6 +37,7 @@ import (
"github.com/karmada-io/karmada/pkg/generated/clientset/versioned/scheme" "github.com/karmada-io/karmada/pkg/generated/clientset/versioned/scheme"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/util/lifted" "github.com/karmada-io/karmada/pkg/util/lifted"
) )
@ -84,6 +85,7 @@ func NewCmdTaint(f util.Factory, parentCommand string) *cobra.Command {
Example: fmt.Sprintf(taintExample, parentCommand), Example: fmt.Sprintf(taintExample, parentCommand),
SilenceUsage: true, SilenceUsage: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.SpecifiedResourceTypeAndNameCompletionFunc(f, []string{"cluster"}),
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
if err := opts.Complete(f, args); err != nil { if err := opts.Complete(f, args); err != nil {
return err return err
@ -107,6 +109,7 @@ func NewCmdTaint(f util.Factory, parentCommand string) *cobra.Command {
flags.BoolVar(&opts.overwrite, "overwrite", opts.overwrite, "If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.") flags.BoolVar(&opts.overwrite, "overwrite", opts.overwrite, "If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.")
flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.") flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }

View File

@ -39,6 +39,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
tokenutil "github.com/karmada-io/karmada/pkg/karmadactl/util/bootstraptoken" tokenutil "github.com/karmada-io/karmada/pkg/karmadactl/util/bootstraptoken"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
var ( var (
@ -135,6 +136,7 @@ func NewCmdTokenCreate(f util.Factory, out io.Writer, tokenOpts *CommandTokenOpt
cmd.Flags().StringSliceVar(&tokenOpts.Groups, "groups", tokenutil.DefaultGroups, fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q", bootstrapapi.BootstrapGroupPattern)) cmd.Flags().StringSliceVar(&tokenOpts.Groups, "groups", tokenutil.DefaultGroups, fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q", bootstrapapi.BootstrapGroupPattern))
cmd.Flags().StringVar(&tokenOpts.Description, "description", tokenOpts.Description, "A human friendly description of how this token is used.") cmd.Flags().StringVar(&tokenOpts.Description, "description", tokenOpts.Description, "A human friendly description of how this token is used.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }
@ -158,6 +160,7 @@ func NewCmdTokenList(f util.Factory, out io.Writer, errW io.Writer, tokenOpts *C
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }
@ -190,6 +193,7 @@ func NewCmdTokenDelete(f util.Factory, out io.Writer, tokenOpts *CommandTokenOpt
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }

View File

@ -31,7 +31,6 @@ import (
"k8s.io/cli-runtime/pkg/genericiooptions" "k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/templates"
metricsapi "k8s.io/metrics/pkg/apis/metrics" metricsapi "k8s.io/metrics/pkg/apis/metrics"
@ -42,6 +41,7 @@ import (
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
// NodeOptions contains all the options for running the top-node cli command. // NodeOptions contains all the options for running the top-node cli command.
@ -104,13 +104,16 @@ func NewCmdTopNode(f util.Factory, parentCommand string, o *NodeOptions, streams
Short: i18n.T("Display resource (CPU/memory) usage of nodes"), Short: i18n.T("Display resource (CPU/memory) usage of nodes"),
Long: topNodeLong, Long: topNodeLong,
Example: fmt.Sprintf(topNodeExample, parentCommand), Example: fmt.Sprintf(topNodeExample, parentCommand),
ValidArgsFunction: completion.ResourceNameCompletionFunc(f, "node"), ValidArgsFunction: utilcomp.ResourceNameCompletionFunc(f, "node"),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunTopNode(f)) cmdutil.CheckErr(o.RunTopNode(f))
}, },
Aliases: []string{"nodes", "no"}, Aliases: []string{"nodes", "no"},
Annotations: map[string]string{
"parent": "top", // used for completion code to set default operation scope.
},
} }
cmdutil.AddLabelSelectorFlagVar(cmd, &o.Selector) cmdutil.AddLabelSelectorFlagVar(cmd, &o.Selector)
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
@ -120,6 +123,8 @@ func NewCmdTopNode(f util.Factory, parentCommand string, o *NodeOptions, streams
cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.") cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.")
cmd.Flags().BoolVar(&o.ShowCapacity, "show-capacity", o.ShowCapacity, "Print node resources based on Capacity instead of Allocatable(default) of the nodes.") cmd.Flags().BoolVar(&o.ShowCapacity, "show-capacity", o.ShowCapacity, "Print node resources based on Capacity instead of Allocatable(default) of the nodes.")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForClustersFlag(cmd)
return cmd return cmd
} }

View File

@ -33,7 +33,6 @@ import (
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/klog/v2" "k8s.io/klog/v2"
cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/util/templates"
metricsapi "k8s.io/metrics/pkg/apis/metrics" metricsapi "k8s.io/metrics/pkg/apis/metrics"
@ -44,6 +43,7 @@ import (
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util" "github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
) )
// PodOptions contains the options to the top command. // PodOptions contains the options to the top command.
@ -115,13 +115,16 @@ func NewCmdTopPod(f util.Factory, parentCommand string, o *PodOptions, streams g
Short: i18n.T("Display resource (CPU/memory) usage of pods of member clusters"), Short: i18n.T("Display resource (CPU/memory) usage of pods of member clusters"),
Long: topPodLong, Long: topPodLong,
Example: fmt.Sprintf(topPodExample, parentCommand), Example: fmt.Sprintf(topPodExample, parentCommand),
ValidArgsFunction: completion.ResourceNameCompletionFunc(f, "pod"), ValidArgsFunction: utilcomp.ResourceNameCompletionFunc(f, "pod"),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate()) cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunTopPod(f)) cmdutil.CheckErr(o.RunTopPod(f))
}, },
Aliases: []string{"pods", "po"}, Aliases: []string{"pods", "po"},
Annotations: map[string]string{
"parent": "top", // used for completion code to set default operation scope.
},
} }
cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector) cmdutil.AddLabelSelectorFlagVar(cmd, &o.LabelSelector)
options.AddKubeConfigFlags(cmd.Flags()) options.AddKubeConfigFlags(cmd.Flags())
@ -134,6 +137,10 @@ func NewCmdTopPod(f util.Factory, parentCommand string, o *PodOptions, streams g
cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "If present, print output without headers.") cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "If present, print output without headers.")
cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.") cmd.Flags().BoolVar(&o.UseProtocolBuffers, "use-protocol-buffers", o.UseProtocolBuffers, "Enables using protocol-buffers to access Metrics API.")
cmd.Flags().BoolVar(&o.Sum, "sum", o.Sum, "Print the sum of the resource usage") cmd.Flags().BoolVar(&o.Sum, "sum", o.Sum, "Print the sum of the resource usage")
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForClustersFlag(cmd)
return cmd return cmd
} }

View File

@ -35,6 +35,7 @@ import (
"github.com/karmada-io/karmada/pkg/karmadactl/options" "github.com/karmada-io/karmada/pkg/karmadactl/options"
cmdutil "github.com/karmada-io/karmada/pkg/karmadactl/util" cmdutil "github.com/karmada-io/karmada/pkg/karmadactl/util"
"github.com/karmada-io/karmada/pkg/karmadactl/util/apiclient" "github.com/karmada-io/karmada/pkg/karmadactl/util/apiclient"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/util" "github.com/karmada-io/karmada/pkg/util"
"github.com/karmada-io/karmada/pkg/util/names" "github.com/karmada-io/karmada/pkg/util/names"
) )
@ -86,6 +87,7 @@ func NewCmdUnjoin(f cmdutil.Factory, parentCommand string) *cobra.Command {
opts.AddFlags(flags) opts.AddFlags(flags)
options.AddKubeConfigFlags(flags) options.AddKubeConfigFlags(flags)
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd return cmd
} }

View File

@ -0,0 +1,505 @@
/*
Copyright 2024 The Karmada 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 completion
import (
"bytes"
"context"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/kubectl/pkg/cmd/apiresources"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/polymorphichelpers"
"k8s.io/kubectl/pkg/scheme"
"github.com/karmada-io/karmada/pkg/karmadactl/get"
"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
)
var factory util.Factory
// SetFactoryForCompletion Store the factory which is needed by the completion functions.
// Not all commands have access to the factory, so cannot pass it to the completion functions.
func SetFactoryForCompletion(f util.Factory) {
factory = f
}
// ResourceTypeAndNameCompletionFunc Returns a completion function that completes resource types
// and resource names that match the toComplete prefix. It supports the <type>/<name> form.
func ResourceTypeAndNameCompletionFunc(f util.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return resourceTypeAndNameCompletionFunc(f, nil, true)
}
// SpecifiedResourceTypeAndNameCompletionFunc Returns a completion function that completes resource
// types limited to the specified allowedTypes, and resource names that match the toComplete prefix.
// It allows for multiple resources. It supports the <type>/<name> form.
func SpecifiedResourceTypeAndNameCompletionFunc(f util.Factory, allowedTypes []string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return resourceTypeAndNameCompletionFunc(f, allowedTypes, true)
}
// ResourceNameCompletionFunc Returns a completion function that completes as a first argument
// the resource names specified by the resourceType parameter, and which match the toComplete prefix.
// This function does NOT support the <type>/<name> form: it is meant to be used by commands
// that don't support that form. For commands that apply to pods and that support the <type>/<name>
// form, please use PodResourceNameCompletionFunc()
func ResourceNameCompletionFunc(f util.Factory, resourceType string) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
if len(args) == 0 {
comps = CompGetResource(f, cmd, resourceType, toComplete)
}
return comps, cobra.ShellCompDirectiveNoFileComp
}
}
// PodResourceNameCompletionFunc Returns a completion function that completes:
// 1- pod names that match the toComplete prefix
// 2- resource types containing pods which match the toComplete prefix
func PodResourceNameCompletionFunc(f util.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
directive := cobra.ShellCompDirectiveNoFileComp
if len(args) == 0 {
comps, directive = doPodResourceCompletion(f, cmd, toComplete)
}
return comps, directive
}
}
// PodResourceNameAndContainerCompletionFunc Returns a completion function that completes, as a first argument:
// 1- pod names that match the toComplete prefix
// 2- resource types containing pods which match the toComplete prefix
// and as a second argument the containers within the specified pod.
func PodResourceNameAndContainerCompletionFunc(f util.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
directive := cobra.ShellCompDirectiveNoFileComp
if len(args) == 0 {
comps, directive = doPodResourceCompletion(f, cmd, toComplete)
} else if len(args) == 1 {
podName := convertResourceNameToPodName(f, args[0])
comps = CompGetContainers(f, cmd, podName, toComplete)
}
return comps, directive
}
}
// ContainerCompletionFunc Returns a completion function that completes the containers within the
// pod specified by the first argument. The resource containing the pod can be specified in
// the <type>/<name> form.
func ContainerCompletionFunc(f util.Factory) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
// We need the pod name to be able to complete the container names, it must be in args[0].
// That first argument can also be of the form <type>/<name> so we need to convert it.
if len(args) > 0 {
podName := convertResourceNameToPodName(f, args[0])
comps = CompGetContainers(f, cmd, podName, toComplete)
}
return comps, cobra.ShellCompDirectiveNoFileComp
}
}
// CompGetResource gets the list of the resource specified which begin with `toComplete`.
func CompGetResource(f util.Factory, cmd *cobra.Command, resourceName string, toComplete string) []string {
template := "{{ range .items }}{{ .metadata.name }} {{ end }}"
return CompGetFromTemplate(&template, f, cmd, "", []string{resourceName}, toComplete)
}
// CompGetContainers gets the list of containers of the specified pod which begin with `toComplete`.
func CompGetContainers(f util.Factory, cmd *cobra.Command, podName string, toComplete string) []string {
template := "{{ range .spec.initContainers }}{{ .name }} {{end}}{{ range .spec.containers }}{{ .name }} {{ end }}"
return CompGetFromTemplate(&template, f, cmd, "", []string{"pod", podName}, toComplete)
}
// CompGetFromTemplate executes a Get operation using the specified template and args and returns the results
// which begin with `toComplete`.
func CompGetFromTemplate(template *string, f util.Factory, cmd *cobra.Command, namespace string, args []string, toComplete string) []string {
buf := new(bytes.Buffer)
streams := genericiooptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: io.Discard}
o := get.NewCommandGetOptions(streams)
// Get the list of names of the specified resource
o.PrintFlags.TemplateFlags.GoTemplatePrintFlags.TemplateArgument = template
format := "go-template"
o.PrintFlags.OutputFormat = &format
// Do the steps Complete() would have done.
// We cannot actually call Complete() or Validate() as these function check for
// the presence of flags, which, in our case won't be there
if namespace != "" {
o.Namespace = namespace
o.ExplicitNamespace = true
} else {
var err error
o.Namespace, o.ExplicitNamespace, err = f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return nil
}
}
o.ToPrinter = func(_ *meta.RESTMapping, _ *bool, _ bool, _ bool) (printers.ResourcePrinterFunc, error) {
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return nil, err
}
return printer.PrintObj, nil
}
o.OperationScope = options.KarmadaControlPlane
// currently, the operation-scope of command `top`, `logs` and `promote` is members.
if cmd.Annotations["parent"] == "top" || cmd.Name() == "logs" || cmd.Name() == "promote" {
o.OperationScope = options.Members
}
operationScopeFlag := cmd.Flag("operation-scope")
if operationScopeFlag != nil {
o.OperationScope = options.OperationScope(operationScopeFlag.Value.String())
}
o.Clusters, _ = cmd.Flags().GetStringSlice("clusters")
clusterFlag := cmd.Flag("cluster")
if clusterFlag != nil {
cluster := clusterFlag.Value.String()
if len(cluster) != 0 {
o.Clusters = []string{cluster}
}
}
o.KarmadaClient, _ = f.KarmadaClientSet()
if err := o.HandleClusterScopeFlags(); err != nil {
return nil
}
if err := o.Run(f, args); err != nil {
return nil
}
var comps []string
resources := strings.Split(buf.String(), " ")
for _, res := range resources {
if res != "" && strings.HasPrefix(res, toComplete) {
comps = append(comps, res)
}
}
return comps
}
// ListContextsInConfig returns a list of context names which begin with `toComplete`
func ListContextsInConfig(toComplete string) []string {
config, err := factory.ToRawKubeConfigLoader().RawConfig()
if err != nil {
return nil
}
var ret []string
for name := range config.Contexts {
if strings.HasPrefix(name, toComplete) {
ret = append(ret, name)
}
}
return ret
}
// ListClustersInConfig returns a list of cluster names which begin with `toComplete`
func ListClustersInConfig(toComplete string) []string {
set, err := factory.KarmadaClientSet()
if err != nil {
return nil
}
list, err := set.ClusterV1alpha1().Clusters().List(context.Background(), metav1.ListOptions{})
if err != nil {
return nil
}
var ret []string
for _, cluster := range list.Items {
if strings.HasPrefix(cluster.Name, toComplete) {
ret = append(ret, cluster.Name)
}
}
return ret
}
// compGetResourceList returns the list of api resources which begin with `toComplete`.
func compGetResourceList(restClientGetter genericclioptions.RESTClientGetter, cmd *cobra.Command, toComplete string) []string {
buf := new(bytes.Buffer)
streams := genericiooptions.IOStreams{In: os.Stdin, Out: buf, ErrOut: io.Discard}
// TODO: Using karmadactlapiresources.CommandAPIResourcesOptions to adapt to the operation scope.
o := apiresources.NewAPIResourceOptions(streams)
if err := o.Complete(restClientGetter, cmd, nil); err != nil {
return nil
}
// Get the list of resources
o.Output = "name"
o.Cached = true
o.Verbs = []string{"get"}
// TODO:Should set --request-timeout=5s
// Ignore errors as the output may still be valid
if err := o.RunAPIResources(); err != nil {
return nil
}
// Resources can be a comma-separated list. The last element is then
// the one we should complete. For example if toComplete=="pods,secre"
// we should return "pods,secrets"
prefix := ""
suffix := toComplete
lastIdx := strings.LastIndex(toComplete, ",")
if lastIdx != -1 {
prefix = toComplete[0 : lastIdx+1]
suffix = toComplete[lastIdx+1:]
}
var comps []string
resources := strings.Split(buf.String(), "\n")
for _, res := range resources {
if res != "" && strings.HasPrefix(res, suffix) {
comps = append(comps, fmt.Sprintf("%s%s", prefix, res))
}
}
return comps
}
// resourceTypeAndNameCompletionFunc Returns a completion function that completes resource types
// and resource names that match the toComplete prefix. It supports the <type>/<name> form.
//
//nolint:gocyclo
func resourceTypeAndNameCompletionFunc(f util.Factory, allowedTypes []string, allowRepeat bool) func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
directive := cobra.ShellCompDirectiveNoFileComp
if len(args) > 0 && !strings.Contains(args[0], "/") {
// The first argument is of the form <type> (e.g., pods)
// All following arguments should be a resource name.
if allowRepeat || len(args) == 1 {
comps = CompGetResource(f, cmd, args[0], toComplete)
// Remove choices already on the command-line
if len(args) > 1 {
comps = cmdutil.Difference(comps, args[1:])
}
}
} else {
slashIdx := strings.Index(toComplete, "/")
if slashIdx == -1 {
if len(args) == 0 {
// We are completing the first argument. We default to the normal
// <type> form (not the form <type>/<name>).
// So we suggest resource types and let the shell add a space after
// the completion.
if len(allowedTypes) == 0 {
comps = compGetResourceList(f, cmd, toComplete)
} else {
for _, c := range allowedTypes {
if strings.HasPrefix(c, toComplete) {
comps = append(comps, c)
}
}
}
} else {
// Here we know the first argument contains a / (<type>/<name>).
// All other arguments must also use that form.
if allowRepeat {
// Since toComplete does not already contain a / we know we are completing a
// resource type. Disable adding a space after the completion, and add the /
directive |= cobra.ShellCompDirectiveNoSpace
if len(allowedTypes) == 0 {
typeComps := compGetResourceList(f, cmd, toComplete)
for _, c := range typeComps {
comps = append(comps, fmt.Sprintf("%s/", c))
}
} else {
for _, c := range allowedTypes {
if strings.HasPrefix(c, toComplete) {
comps = append(comps, fmt.Sprintf("%s/", c))
}
}
}
}
}
} else {
// We are completing an argument of the form <type>/<name>
// and since the / is already present, we are completing the resource name.
if allowRepeat || len(args) == 0 {
resourceType := toComplete[:slashIdx]
toComplete = toComplete[slashIdx+1:]
nameComps := CompGetResource(f, cmd, resourceType, toComplete)
for _, c := range nameComps {
comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c))
}
// Remove choices already on the command-line.
if len(args) > 0 {
comps = cmdutil.Difference(comps, args[0:])
}
}
}
}
return comps, directive
}
}
// doPodResourceCompletion Returns completions of:
// 1- pod names that match the toComplete prefix
// 2- resource types containing pods which match the toComplete prefix
func doPodResourceCompletion(f util.Factory, cmd *cobra.Command, toComplete string) ([]string, cobra.ShellCompDirective) {
var comps []string
directive := cobra.ShellCompDirectiveNoFileComp
slashIdx := strings.Index(toComplete, "/")
if slashIdx == -1 {
// Standard case, complete pod names
comps = CompGetResource(f, cmd, "pod", toComplete)
// Also include resource choices for the <type>/<name> form,
// but only for resources that contain pods
resourcesWithPods := []string{
"daemonsets",
"deployments",
"pods",
"jobs",
"replicasets",
"replicationcontrollers",
"services",
"statefulsets"}
if len(comps) == 0 {
// If there are no pods to complete, we will only be completing
// <type>/. We should disable adding a space after the /.
directive |= cobra.ShellCompDirectiveNoSpace
}
for _, resource := range resourcesWithPods {
if strings.HasPrefix(resource, toComplete) {
comps = append(comps, fmt.Sprintf("%s/", resource))
}
}
} else {
// Dealing with the <type>/<name> form, use the specified resource type
resourceType := toComplete[:slashIdx]
toComplete = toComplete[slashIdx+1:]
nameComps := CompGetResource(f, cmd, resourceType, toComplete)
for _, c := range nameComps {
comps = append(comps, fmt.Sprintf("%s/%s", resourceType, c))
}
}
return comps, directive
}
// convertResourceNameToPodName Converts a resource name to a pod name.
// If the resource name is of the form <type>/<name>, we use
// polymorphichelpers.AttachablePodForObjectFn(), if not, the resource name
// is already a pod name.
func convertResourceNameToPodName(f cmdutil.Factory, resourceName string) string {
var podName string
if !strings.Contains(resourceName, "/") {
// When we don't have the <type>/<name> form, the resource name is the pod name
podName = resourceName
} else {
// if the resource name is of the form <type>/<name>, we need to convert it to a pod name
ns, _, err := f.ToRawKubeConfigLoader().Namespace()
if err != nil {
return ""
}
resourceWithPod, err := f.NewBuilder().
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
ContinueOnError().
NamespaceParam(ns).DefaultNamespace().
ResourceNames("pods", resourceName).
Do().Object()
if err != nil {
return ""
}
// For shell completion, use a short timeout
forwardablePod, err := polymorphichelpers.AttachablePodForObjectFn(f, resourceWithPod, 100*time.Millisecond)
if err != nil {
return ""
}
podName = forwardablePod.Name
}
return podName
}
// RegisterCompletionFuncForNamespaceFlag registers CompletionFunc for flag namespace.
func RegisterCompletionFuncForNamespaceFlag(cmd *cobra.Command, f util.Factory) {
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
"namespace",
func(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return CompGetResource(f, cmd, "namespace", toComplete), cobra.ShellCompDirectiveNoFileComp
}))
}
// RegisterCompletionFuncForClusterFlag registers CompletionFunc for flag cluster.
func RegisterCompletionFuncForClusterFlag(cmd *cobra.Command) {
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
"cluster",
func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp
}))
}
// RegisterCompletionFuncForClustersFlag registers CompletionFunc for flag clusters.
func RegisterCompletionFuncForClustersFlag(cmd *cobra.Command) {
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
"clusters",
func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return ListClustersInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp
}))
}
// RegisterCompletionFuncForKarmadaContextFlag registers CompletionFunc for flag karmada-context.
func RegisterCompletionFuncForKarmadaContextFlag(cmd *cobra.Command) {
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
"karmada-context",
func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return ListContextsInConfig(toComplete), cobra.ShellCompDirectiveNoFileComp
}))
}
// RegisterCompletionFuncForOperationScopeFlag registers CompletionFunc for flag operation-scope.
func RegisterCompletionFuncForOperationScopeFlag(cmd *cobra.Command, supportScope ...options.OperationScope) {
cmdutil.CheckErr(cmd.RegisterFlagCompletionFunc(
"operation-scope",
func(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var ret []string
if len(supportScope) == 0 {
supportScope = []options.OperationScope{options.KarmadaControlPlane, options.Members, options.All}
}
for _, scope := range supportScope {
if strings.HasPrefix(scope.String(), toComplete) {
ret = append(ret, scope.String())
}
}
return ret, cobra.ShellCompDirectiveNoFileComp
}))
}