Skip to content

Commit

Permalink
Merge pull request #5533 from zhzhuang-zju/autocompletion
Browse files Browse the repository at this point in the history
Implementing autocompletion for karmadactl
  • Loading branch information
karmada-bot authored Sep 20, 2024
2 parents 836b878 + a684edd commit b8edec1
Show file tree
Hide file tree
Showing 27 changed files with 839 additions and 63 deletions.
3 changes: 3 additions & 0 deletions pkg/karmadactl/annotate/annotate.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -57,5 +58,7 @@ func NewCmdAnnotate(f util.Factory, parentCommand string, ioStreams genericioopt
}
options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags())
utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd
}
5 changes: 5 additions & 0 deletions pkg/karmadactl/apiresources/apiresources.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -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().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.")

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd
}

Expand Down
6 changes: 6 additions & 0 deletions pkg/karmadactl/apiresources/apiversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -62,6 +63,11 @@ func NewCmdAPIVersions(f util.Factory, parentCommand string, ioStreams genericio
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().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
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/karmadactl/apply/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
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/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
"github.com/karmada-io/karmada/pkg/util/names"
)

Expand Down Expand Up @@ -93,6 +94,7 @@ func NewCmdApply(f util.Factory, parentCommand string, streams genericiooptions.
SilenceUsage: true,
DisableFlagsInUseLine: true,
Example: fmt.Sprintf(applyExample, parentCommand),
ValidArgsFunction: utilcomp.ResourceTypeAndNameCompletionFunc(f),
RunE: func(cmd *cobra.Command, args []string) error {
if err := o.Complete(f, cmd, parentCommand, args); err != nil {
return err
Expand All @@ -113,6 +115,11 @@ func NewCmdApply(f util.Factory, parentCommand string, streams genericiooptions.
options.AddNamespaceFlag(flags)
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.")

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)

return cmd
}

Expand Down
9 changes: 7 additions & 2 deletions pkg/karmadactl/attach/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import (
"k8s.io/cli-runtime/pkg/genericiooptions"
kubectlattach "k8s.io/kubectl/pkg/cmd/attach"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/completion"
"k8s.io/kubectl/pkg/util/templates"

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -64,7 +64,7 @@ func NewCmdAttach(f util.Factory, parentCommand string, streams genericiooptions
Short: "Attach to a running container",
Long: "Attach to a process that is already running inside an existing container.",
Example: fmt.Sprintf(attachExample, parentCommand),
ValidArgsFunction: completion.PodResourceNameCompletionFunc(f),
ValidArgsFunction: utilcomp.PodResourceNameCompletionFunc(f),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
Expand All @@ -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().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")

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
utilcomp.RegisterCompletionFuncForOperationScopeFlag(cmd, options.KarmadaControlPlane, options.Members)
utilcomp.RegisterCompletionFuncForClusterFlag(cmd)
return cmd
}

Expand Down
143 changes: 143 additions & 0 deletions pkg/karmadactl/completion/completion.go
Original file line number Diff line number Diff line change
@@ -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)
}
7 changes: 7 additions & 0 deletions pkg/karmadactl/cordon/cordon.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
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/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -61,13 +62,15 @@ const (
// NewCmdCordon defines the `cordon` command that mark cluster as unschedulable.
func NewCmdCordon(f util.Factory, parentCommand string) *cobra.Command {
opts := CommandCordonOption{}

cmd := &cobra.Command{
Use: "cordon CLUSTER",
Short: "Mark cluster as unschedulable",
Long: cordonLong,
Example: fmt.Sprintf(cordonExample, parentCommand),
SilenceUsage: true,
DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.SpecifiedResourceTypeAndNameCompletionFunc(f, []string{"cluster"}),
RunE: func(_ *cobra.Command, args []string) error {
if err := opts.Complete(args); err != nil {
return err
Expand All @@ -86,19 +89,22 @@ func NewCmdCordon(f util.Factory, parentCommand string) *cobra.Command {
options.AddKubeConfigFlags(flags)
flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.")

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd
}

// NewCmdUncordon defines the `uncordon` command that mark cluster as schedulable.
func NewCmdUncordon(f util.Factory, parentCommand string) *cobra.Command {
opts := CommandCordonOption{}

cmd := &cobra.Command{
Use: "uncordon CLUSTER",
Short: "Mark cluster as schedulable",
Long: uncordonLong,
Example: fmt.Sprintf(uncordonExample, parentCommand),
SilenceUsage: true,
DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.SpecifiedResourceTypeAndNameCompletionFunc(f, []string{"cluster"}),
RunE: func(_ *cobra.Command, args []string) error {
if err := opts.Complete(args); err != nil {
return err
Expand All @@ -117,6 +123,7 @@ func NewCmdUncordon(f util.Factory, parentCommand string) *cobra.Command {
options.AddKubeConfigFlags(flags)
flags.BoolVar(&opts.DryRun, "dry-run", false, "Run the command in dry-run mode, without making any server requests.")

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
return cmd
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/karmadactl/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -55,5 +56,9 @@ func NewCmdCreate(f util.Factory, parentCommand string, ioStreams genericiooptio
}
options.AddKubeConfigFlags(cmd.PersistentFlags())
options.AddNamespaceFlag(cmd.PersistentFlags())

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)

return cmd
}
4 changes: 4 additions & 0 deletions pkg/karmadactl/delete/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -98,5 +99,8 @@ func NewCmdDelete(f util.Factory, parentCommand string, ioStreams genericiooptio
}
options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags())

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd
}
6 changes: 6 additions & 0 deletions pkg/karmadactl/describe/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -74,6 +75,7 @@ func NewCmdDescribe(f util.Factory, parentCommand string, streams genericiooptio
Long: fmt.Sprintf(describeLong, parentCommand),
SilenceUsage: true,
DisableFlagsInUseLine: true,
ValidArgsFunction: utilcomp.ResourceTypeAndNameCompletionFunc(f),
Example: fmt.Sprintf(describeExample, parentCommand),
RunE: func(_ *cobra.Command, args []string) error {
if err := o.Complete(f, args, kubedescribeFlags, parentCommand); err != nil {
Expand Down Expand Up @@ -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.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
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/karmadactl/edit/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (

"github.com/karmada-io/karmada/pkg/karmadactl/options"
"github.com/karmada-io/karmada/pkg/karmadactl/util"
utilcomp "github.com/karmada-io/karmada/pkg/karmadactl/util/completion"
)

var (
Expand Down Expand Up @@ -56,5 +57,8 @@ func NewCmdEdit(f util.Factory, parentCommand string, ioStreams genericiooptions
}
options.AddKubeConfigFlags(cmd.Flags())
options.AddNamespaceFlag(cmd.Flags())

utilcomp.RegisterCompletionFuncForKarmadaContextFlag(cmd)
utilcomp.RegisterCompletionFuncForNamespaceFlag(cmd, f)
return cmd
}
Loading

0 comments on commit b8edec1

Please sign in to comment.