Change:
<!-- Please only use this template for submitting enhancement requests --What would you like to be : I type <kbdTab</kbd<kbdTab</kbdI would like to see kubectl plugins in the list of suggested subcommands. For example, if I have installed , subcommand completions should suggest . the plugin itself also supports command-line completion, <kbdTab</kbd<kbdTab</kbdshould show suggestions from the plugin command completion. Why is this needed: Plugins fill in niches in the kubectl workflow, so that they are used rather seldomly. This means that plugin users need to memorize plugin names and their options over long times without using them. Therefore, plugin usefulness would be greatly enhanced by suggesting plugin sub-commands and their options.
Reiterating the mentions to trigger a notification: <detailsIn response to : Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
In fact, already supports command completion. You can run for more information about it. If you are using bash shelljust run , and then it will work!
Hi , but I'm not talking about command completion in general. I'm talking about command completion for plugins. For example, the new plugin system allows to call a plugin at location as . It would be nice to get suggested/completed, when typing <kbdTab</kbd. Further, if plugin names are completed, so should be options for those plugins -- provided the plugin offers some form of command completion.
I try to implement completion for kubectl plugins!
Were you able to make progress on this? I have a working patch for the completion of the plugin name (part 1 of this issue). I can make a PR for it if it can help.
This is a great first step! I do think however, that the real value is auto-completion for the plugin options. I can roughly remember what plugins I have, but not what options they offer. That's the real value here, I think. This will probably need a contract for how plugins make completion available (e.g. by following the kubectl standard of ). WDYT?
I've been thinking about how to do the plugin options completion. Based on the patch I have now, I believe what will be needed is to be able to ask the plugin what the possible completions can be (based on the user-specified parameters). So the plugin won't need to generate a bash or zsh completion script (which is difficult for the author of the plugin) but just output possible completions. This also has the benefit of being independent of which shell is used. One way is to do as you mention and have the plugin support as a way to ask the plugin what the completions would be. Although it won't behave in the same fashion as (as it won't generate a completion script, and is shell-independent), I like that option because it simply requires a plugin author to provide a different script called which the bash completion logic can call. Another way would be to ask the plugin itself to support a flag such as which would return the possible completions. I don't like this because if the plugin doesn't support this flag, we'd be calling it and who knows what it would do in that unexpected case. The use of makes it easy to check of presence or absence of completion support. So I suggest that the contract with plugins be that a plugin that supports completion would provide two scripts and that the bash completion script call to get the list of completions. Any thoughts?
Hi guys, I've already implemented completion for plugins (subcommands only, doesn't include options) as However it needs to be refactored more. You can try completion for plugins with the following steps: First, prepare the repository, and build a kubectl. Install kubectl-hello plugin for completion testing. Load the kubectl completion code. You can see completion for some plugins.
Cool! You've used the same technique I did. However, may I suggest re-using the code from the plugin package instead of copying it?
Well, you are absolutely right in saying that we can't just call a plugin with some flag to produce the completion script, because who knows what will happen. Therefore, option completion for plugins has to go the route via an external completion script, like However, I'm pretty sure that those completion files need to be bash/zsh scripts, because they need to process contextual information. For example, say I have a plugin with sub-commands Then I want completion to work as follows: As you say, this will have to be maintained by the plugin authors. Can you take a typical cobra command completion script as basis and work out how kubectl-plugin-completion and this external script need to work together? You could even try to have kubectl as its own plugin, like and or so :grinning:
kubectl already has some dynamic completion code to allow to complete pod names for example, which actually calls kubectl itself. I think I can use the same technique for plugin parameter completion. So here's an example of what I'm thinking: Then when the user types the kubectl completion script will call . This call will return which will be given to the shell by the kubectl completion script. I haven't actually tested this example, so I could be wrong. I'll try implementing it to see if I'm missing something.
Thank you for supporting me ;) I add you as co-author to commit when I send a PR.
Thanks
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
Stale issues rot after 30d of inactivity. Mark the issue as fresh with . Rotten issues close after an additional 30d of inactivity. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle rotten
Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . Send feedback to sig-testing, kubernetes/test-infra and/or . /close
Closing this issue. <detailsIn response to : Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . /close Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/reopen
Reopened this issue. <detailsIn response to : /reopen Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/remove-lifecycle rotten
I've been working on auto-completion for the Helm project and I have a proposal with a PR that fully supports auto-completion for Helm plugins. I believe the fundamental aspects of the solution would apply just as well to kubectl. Some adaptation would be needed to adjust to how kubectl supports plugins, but I think it wouldn't be very hard.
Is there an ETA on this? If I see it correctly, this is currently preventing plugin authors to implement completions as the generated completion suggestions are based off the root command and not the subcommand aka plugin. I've been trying all day to make this work but failed and that's when I stumbled over this issue..
Several major issues I see here are: We don't want files like as proposed in previous comment. We had a requirement prior to kubectl 1.12 and it didn't look great at all. The simplicity of plugins is valuable to us. (Also, unlike Helm plugins, kubectl plugins don't live at a particular directory, or follow a directory structure.)Plugin completions most likely would be integrated into commands, which a lot of people put in their or file. We need to keep this command fast. So what if the command called a special command on each plugin, in parallel, to inquire if it has completion available (e.g.), with a short deadline, and cached the successful results (maybe keyed with on plugin binary’s stat data, or checksum). That way, cmd could also provide data about plugins, while periodically refreshing the cached completion scripts.
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
any updates on this one ? is it complete/done?
No update, still open..
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-contributor-experience at . /lifecycle stale
/remove-lifecycle stale
/lifecycle frozen
Sorry to revive this thread a bit but while working on another cli using the same cobra completion system by (implemented by i believe) I found out that it would not be too hard or too disruptive to have a small patch on the shell completion directly. For example with kubectl if we have a patch like this (using zsh but I am guessing the same technique will apply for bash or others) : the patch will check the first command () and the second command for example "krew" exist as and then use it directly for completion otherwise do the normal completion 'thingy'. the only downside is that the initial plugin would not show i.e: if you do you won't get krew as the plugin name, except if we add all plugin to the Root Command like the pull request from does. I don't think it would be too intensive since it is a zsh/bash builtin and in memory. I have made a small asciinema to demonstrate how it works I guess if we want this we may need to do it directly in cobra, do you think a patch like this could get accepted ? (I had somehow difficulties to get PR reviewer and merged in cobra last time I tried)
Thanks for the suggestion. The idea is good but doesn't generalize perfectly. The command is available for programs that use Cobra but kubectl plugins can be something else (a script, a Python program, a Go program that doesn't use Cobra, etc). So we couldn't do something like this, unless we required every plugin to provide a command. I'm working on porting Helm's plugin completion solution to kubectl and should have something for the community to try soon. Plugins using Cobra will then be able to use your suggestion and get completion super easily.
That make sense I forgot about non go (and non cobra) plugins, thanks I look forward to see how the helm completion extracted will look like !
I've posted something for people to try out in
It would be nice if some plugin developers try out the solution proposed in and provide feedback. since uses Cobra, the solution is very easy. Here is how to try it out: First build kubectl with the PR. Then create your completion script like this: put this new on $PATH. And now test using the newly built (I build mine at ). For completion of flags (which I realize is the real value for the plugin), kubectl needs to first move to Cobra 1.3.
I would be interested in your opinion on the proposed solution. It will not work out-of-the-box with Krew, but I haven't found a way to do better with respect to Krew. You had mentioned this previously, and although it would be a nice solution, I am concerned it would cause problems to any plugin that ignores all arguments and runs no matter what. With such a plugin, calling a command would trigger the plugin to run and possibly perform undesired actions when completions are requested.
I'm sorry about slow responses. Finally had time to read it and parse these. You're right we shouldn't randomly call a plugin binary without knowing it supports completion. From : kubectl already has a mechanism to find all plugins on $PATH, so I agree that at minimum, kubectl<kbdTab</kbdcan and should show plugins installed on the system (even if the plugins themselves don't support shell completion) I think this is a good model. Just like how kubectl scans $PATH for , it can scan to see if a plugin supports completion. (It's important to define if that binary takes additional positional args or --options) To make the (2) a reality, I support adding an additional field to the Krew plugin manifest. The shape of that would be up for a discussion (I imagine it might be (a) a separate binary that comes in the plugin archive, or (b) allow linking to the plugin binary itself and the plugin reads argv[0] and prints completion, which can be done with (a) anyway). I think it's doable. I'm not very familiar with how shell completion works in practice, when it's called, how lazy can it be loaded, what are performance implications etc, but if you'd like to drive this effort, I think we can discuss a design on Krew side as well. Please bring this up in a SIG CLI meeting to see what the folks think about your proposal.
brilliant! Thanks for taking the time to think about this. Your suggestion to adapt krew sounds great to me. I will continue with the PR on kubectl's side and if it gets accepted we can look at krew.
The as proposed in accepts the exact same arguments as the standard executable. The new completion executable will examine those arguments and will determine what the shell completion choices following them should be.
do you have an insight into whether a plugin developer would develop and ship a separate executable for the completion, or would they simply link the main plugin executable and see if the argv[0] is the kubectl_complete-*? I assume the answer largely also depends on whether the plugin was authored in Go or bash (basically the only two languages I've ever seen a plugin submission in out of 150+).
I think plugins will choose to ship a separate executable. I feel it keeps the two aspects nicely separate (the plugin functionality and the shell completion functionality). Also, it is very easy for a separate executable to call back the main plugin if the developer wishes to do so, so I find that solution the most flexible. For example, for plugins using the Cobra library, the script would call back the executable like this: BTW, I have looked into the 179 plugins in the krew index and I found that 50% (yes, half!) of them used Cobra (some of them indirectly by calling back ). This means that shell completion should be very easy to implement for all these plugins.
I'm interested in this support and thanks for adding this feature. One question: in which version this is planned to be released? Looking forward to this.

Discussion:
Run: runHelp, // Hook before and after Run initialize and write profiles to disk, // respectively. <del> PersistentPreRunE: func(*cobra.Command, []string) error { </del> <ins> PersistentPreRunE: func(cmd *cobra.Command, args []string) error { </ins> rest.SetDefaultWarningHandler(warningHandler) <ins> if cmd.Name() == cobra.ShellCompRequestCmd { // This is the __complete or __completeNoDesc command which // indicates shell completion has been requested. plugin.SetupPluginCompletion(cmd, args) } </ins> return initProfiling() }, PersistentPostRunE: func(*cobra.Command, []string) error {

} func (o *PluginListOptions) Run() error { <del> pluginsFound := false isFirstFile := true pluginErrors := []error{} </del> <ins> plugins, pluginErrors := o.ListPlugins() if len(plugins) > 0 { fmt.Fprintf(o.Out, "The following compatible plugins are available:nn") } else { pluginErrors = append(pluginErrors, fmt.Errorf("error: unable to find any kubectl plugins in your PATH")) } </ins> pluginWarnings := 0 <ins> for _, pluginPath := range plugins { if o.NameOnly { fmt.Fprintf(o.Out, "%sn", filepath.Base(pluginPath)) } else { fmt.Fprintf(o.Out, "%sn", pluginPath) } if errs := o.Verifier.Verify(pluginPath); len(errs) != 0 { for _, err := range errs { fmt.Fprintf(o.ErrOut, "  - %sn", err) pluginWarnings++ } } } if pluginWarnings > 0 { if pluginWarnings == 1 { pluginErrors = append(pluginErrors, fmt.Errorf("error: one plugin warning was found")) } else { pluginErrors = append(pluginErrors, fmt.Errorf("error: %v plugin warnings were found", pluginWarnings)) } } if len(pluginErrors) > 0 { errs := bytes.NewBuffer(nil) for _, e := range pluginErrors { fmt.Fprintln(errs, e) } return fmt.Errorf("%s", errs.String()) } return nil } // ListPlugins returns list of plugin paths. func (o *PluginListOptions) ListPlugins() ([]string, []error) { plugins := []string{} errors := []error{} </ins> for _, dir := range uniquePathsList(o.PluginPaths) { if len(strings.TrimSpace(dir)) == 0 {

continue } <del> pluginErrors = append(pluginErrors, fmt.Errorf("error: unable to read directory %q in your PATH: %v", dir, err)) </del> <ins> errors = append(errors, fmt.Errorf("error: unable to read directory %q in your PATH: %v", dir, err)) </ins> continue }

continue } <del> if isFirstFile { fmt.Fprintf(o.Out, "The following compatible plugins are available:nn") pluginsFound = true isFirstFile = false } pluginPath := f.Name() if !o.NameOnly { pluginPath = filepath.Join(dir, pluginPath) } fmt.Fprintf(o.Out, "%sn", pluginPath) if errs := o.Verifier.Verify(filepath.Join(dir, f.Name())); len(errs) != 0 { for _, err := range errs { fmt.Fprintf(o.ErrOut, "  - %sn", err) pluginWarnings++ } } } } if !pluginsFound { pluginErrors = append(pluginErrors, fmt.Errorf("error: unable to find any kubectl plugins in your PATH")) } if pluginWarnings > 0 { if pluginWarnings == 1 { pluginErrors = append(pluginErrors, fmt.Errorf("error: one plugin warning was found")) } else { pluginErrors = append(pluginErrors, fmt.Errorf("error: %v plugin warnings were found", pluginWarnings)) } } if len(pluginErrors) > 0 { errs := bytes.NewBuffer(nil) for _, e := range pluginErrors { fmt.Fprintln(errs, e) </del> <ins> plugins = append(plugins, filepath.Join(dir, f.Name())) </ins> } <del> return fmt.Errorf("%s", errs.String()) </del> } <del> return nil </del> <ins> return plugins, errors </ins> } // pathVerifier receives a path and determines if it is valid or not

<ins> /* Copyright 2022 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package plugin import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "strconv" "strings" "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" ) // SetupPluginCompletion adds a Cobra command to the command tree for each // plugin.  This is only done when performing shell completion that relate // to plugins. func SetupPluginCompletion(cmd *cobra.Command, args []string) { if len(args) > 0 { if strings.HasPrefix(args[0], "-") { // Plugins are not supported if the first argument is a flag, // so no need to add them in that case. return } if len(args) == 1 { // We are completing a subcommand at the first level so // we should include all plugins names. addPluginCommands(cmd) return } // We have more than one argument. // Check if we know the first level subcommand. // If we don't it could be a plugin and we'll need to add // the plugin commands for completion to work. found := false for _, subCmd := range cmd.Root().Commands() { if args[0] == subCmd.Name() { found = true break } } if !found { // We don't know the subcommand for which completion // is being called: it could be a plugin. // // When using a plugin, the kubectl global flags are not supported. // Therefore, when doing completion, we need to remove these flags // to avoid them being included in the completion choices. // This must be done *before* adding the plugin commands so that // when creating those plugin commands, the flags don't exist. cmd.Root().ResetFlags() cobra.CompDebugln("Cleared global flags for plugin completion", true) addPluginCommands(cmd) } } } // addPluginCommand adds a Cobra command to the command tree // for each plugin so that the completion logic knows about the plugins func addPluginCommands(cmd *cobra.Command) { kubectl := cmd.Root() streams := genericclioptions.IOStreams{ In:     &bytes.Buffer{}, Out:    ioutil.Discard, ErrOut: ioutil.Discard, } o := &PluginListOptions{IOStreams: streams} o.Complete(kubectl) plugins, _ := o.ListPlugins() for _, plugin := range plugins { plugin = filepath.Base(plugin) args := []string{} // Plugins are named "kubectl-<name>" or with more - such as // "kubectl-<name>-<subcmd1>..." for _, arg := range strings.Split(plugin, "-")[1:] { // Underscores (_) in plugin's filename are replaced with dashes(-) // e.g. foo_bar -> foo-bar args = append(args, strings.Replace(arg, "_", "-", -1)) } // In order to avoid that the same plugin command is added more than once, // find the lowest command given args from the root command parentCmd, remainingArgs, _ := kubectl.Find(args) if parentCmd == nil { parentCmd = kubectl } for _, remainingArg := range remainingArgs { cmd := &cobra.Command{ Use: remainingArg, // Add a description that will be shown with completion choices. // Make each one different by including the plugin name to avoid // all plugins being grouped in a single line during completion for zsh. Short:              fmt.Sprintf("The command %s is a plugin installed by the user", remainingArg), DisableFlagParsing: true, // Allow plugins to provide their own completion choices ValidArgsFunction: pluginCompletion, // A Run is required for it to be a valid command Run: func(cmd *cobra.Command, args []string) {}, } parentCmd.AddCommand(cmd) parentCmd = cmd } } } // pluginCompletion deals with shell completion beyond the plugin name, it allows to complete // plugin arguments and flags. // It will look on $PATH for a specific executable file that will provide completions // for the plugin in question. // // When called, this completion executable should print the completion choices to stdout. // The arguments passed to the executable file will be the arguments for the plugin currently // on the command-line.  For example, if a user types: // //	kubectl myplugin arg1 arg2 a<TAB> // // the completion executable will be called with arguments: "arg1" "arg2" "a". // And if a user types: // //	kubectl myplugin arg1 arg2 <TAB> // // the completion executable will be called with arguments: "arg1" "arg2" "".  Notice the empty // last argument which indicates that a new word should be completed but that the user has not // typed anything for it yet. // // Kubectl's plugin completion logic supports Cobra's ShellCompDirective system.  This means a plugin // can optionally print :<value of a shell completion directive> as its very last line to provide // directives to the shell on how to perform completion.  If this directive is not present, the // cobra.ShellCompDirectiveDefault will be used. Please see Cobra's documentation for more details: // https://github.com/spf13/cobra/blob/master/shell_completions.md#dynamic-completion-of-nouns // // The completion executable should be named kubectl_complete-<plugin>.  For example, for a plugin // named kubectl-get_all, the completion file should be named kubectl_complete-get_all.  The completion // executable must have executable permissions set on it and must be on $PATH. func pluginCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { // Recreate the plugin name from the commandPath pluginName := strings.Replace(strings.Replace(cmd.CommandPath(), "-", "_", -1), " ", "-", -1) path, found := lookupCompletionExec(pluginName) if !found { cobra.CompDebugln(fmt.Sprintf("Plugin %s does not provide a matching completion executable", pluginName), true) return nil, cobra.ShellCompDirectiveDefault } args = append(args, toComplete) cobra.CompDebugln(fmt.Sprintf("About to call: %s %s", path, strings.Join(args, " ")), true) return getPluginCompletions(path, args, os.Environ()) } // lookupCompletionExec will look for the existence of an executable // that can provide completion for the given plugin name. // The first filepath to match is returned, or a boolean false if // such an executable is not found. func lookupCompletionExec(pluginName string) (string, bool) { // Convert the plugin name into the plugin completion name by inserting "_complete" before the first -. // For example, convert kubectl-get_all to kubectl_complete-get_all pluginCompExec := strings.Replace(pluginName, "-", "_complete-", 1) cobra.CompDebugln(fmt.Sprintf("About to look for: %s", pluginCompExec), true) path, err := exec.LookPath(pluginCompExec) if err != nil || len(path) == 0 { return "", false } return path, true } // getPluginCompletions receives an executable's filepath, a slice // of arguments, and a slice of environment variables // to relay to the executable. // The executable is responsible for printing the completions of the // plugin for the current set of arguments. func getPluginCompletions(executablePath string, cmdArgs, environment []string) ([]string, cobra.ShellCompDirective) { buf := new(bytes.Buffer) prog := exec.Command(executablePath, cmdArgs...) prog.Stdin = os.Stdin prog.Stdout = buf prog.Stderr = os.Stderr prog.Env = environment var comps []string directive := cobra.ShellCompDirectiveDefault if err := prog.Run(); err == nil { for _, comp := range strings.Split(buf.String(), "n") { // Remove any empty lines if len(comp) > 0 { comps = append(comps, comp) } } // Check if the last line of output is of the form :<integer>, which // indicates a Cobra ShellCompDirective.  We do this for plugins // that use Cobra or the ones that wish to use this directive to // communicate a special behavior for the shell. if len(comps) > 0 { lastLine := comps[len(comps)-1] if len(lastLine) > 1 && lastLine[0] == ':' { if strInt, err := strconv.Atoi(lastLine[1:]); err == nil { directive = cobra.ShellCompDirective(strInt) comps = comps[:len(comps)-1] } } } } return comps, directive } </ins>

"fmt" "io/ioutil" "os" <ins> "path/filepath" "reflect" </ins> "strings" "testing"

} } <ins> func TestListPlugins(t *testing.T) { pluginPath, _ := filepath.Abs("./testdata") expectPlugins := []string{ filepath.Join(pluginPath, "kubectl-foo"), filepath.Join(pluginPath, "kubectl-version"), } verifier := newFakePluginPathVerifier() ioStreams, _, _, _ := genericclioptions.NewTestIOStreams() pluginPaths := []string{pluginPath} o := &PluginListOptions{ Verifier:  verifier, IOStreams: ioStreams, PluginPaths: pluginPaths, } plugins, errs := o.ListPlugins() if len(errs) > 0 { t.Fatalf("unexpected errors: %v", errs) } if !reflect.DeepEqual(expectPlugins, plugins) { t.Fatalf("saw unexpected plugins. Expecting %v, got %v", expectPlugins, plugins) } } </ins> type duplicatePathError struct { path string }

It can also be used as a means of showcasing usage of the cli-runtime set of utilities to aid in third-party plugin development. <ins> ## Shell completion This plugin supports shell completion when used through kubectl.  To enable shell completion for the plugin you must copy the file `./kubectl_complete-ns` somewhere on `$PATH` and give it executable permissions. The `./kubectl_complete-ns` script shows a hybrid approach to providing completions: 1. it uses the builtin `__complete` command provided by [Cobra](https://github.com/spf13/cobra) for flags 1. it calls `kubectl` to obtain the list of namespaces to complete arguments (note that a more elegant approach would be to have the `kubectl-ns` program itself provide completion of arguments by implementing Cobra's `ValidArgsFunction` to fetch the list of namespaces, but it would then be a less varied example) One can then do things like: ``` $ kubectl ns <TAB> default          kube-node-lease  kube-public      kube-system $ kubectl ns --<TAB> --as                        -- Username to impersonate for the operation. User could be a regular user or a service account in a namespace. --as-group                  -- Group to impersonate for the operation, this flag can be repeated to specify multiple groups. --as-uid                    -- UID to impersonate for the operation. --cache-dir                 -- Default cache directory [...] ``` Note: kubectl v1.26 or higher is required for shell completion to work for plugins. </ins> ## Cleanup You can "uninstall" this plugin from kubectl by simply removing it from your PATH:

<ins> #!/usr/bin/env bash # If we are completing a flag, use Cobra's builtin completion system. # To know if we are completing a flag we need the last argument starts with a `-` and does not contain an `=` args=("$@") lastArg=${args[((${#args[@]}-1))]} if [[ "$lastArg" == -* ]]; then if [[ "$lastArg" != *=* ]]; then kubectl ns __complete "$@" fi else # TODO Make sure we are not completing the value of a flag. # TODO Only complete a single argument. # Both are pretty hard to do in a shell script.  The better way to do this would be to let # Cobra do all the completions by using `cobra.ValidArgsFunction` in the program. # But the below, although imperfect, is a nice example for plugins that don't use Cobra. # We are probably completing an argument.  This plugin only accepts namespaces, let's fetch them. kubectl get namespaces --output go-template='{{ range .items }}{{ .metadata.name }}{{"n"}}{{ end }}' # Turn off file completion.  See the ShellCompDirective documentation within # https://github.com/spf13/cobra/blob/main/shell_completions.md#completion-of-nouns echo :4 fi </ins>