Project: Soda Foundation (Runtime / Cloud Native Storage)
File: bash_completions.md

Generating bash completions from a cobra command is incredibly easy. An actual program which does so for the kubernetes kubectl binary is as follows: ```go package main import ( "io/ioutil" "os" "k8s.io/kubernetes/pkg/kubectl/cmd" "k8s.io/kubernetes/pkg/kubectl/cmd/util" ) func main() { kubectl := cmd.NewKubectlCommand(util.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) kubectl.GenBashCompletionFile("out.sh") } ``` `out.sh` will get you completions of subcommands and flags. Copy it to `/etc/bash_completion.d/` as described and reset your terminal to use autocompletion. If you make additional annotations to your code, you can get even more intelligent and flexible behavior. Some more actual code that works in kubernetes: ```bash const ( bashcompletionfunc = `kubectlparseget() { local kubectl_output out if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then out=($(echo "${kubectl_output}" | awk '{print $1}')) COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) fi } kubectlgetresource() { if [[ ${#nouns[@]} -eq 0 ]]; then return 1 fi kubectlparseget ${nouns[${#nouns[@]} -1]} if [[ $? -eq 0 ]]; then return 0 fi } custom_func() { case ${last_command} in kubectlget | kubectldescribe | kubectldelete | kubectlstop) kubectlgetresource return ;; *) ;; esac } `) ``` And then I set that in my command definition: ```go cmds := &cobra.Command{ Use: "kubectl", Short: "kubectl controls the Kubernetes cluster manager", Long: `kubectl controls the Kubernetes cluster manager. Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, Run: runHelp, BashCompletionFunction: bashcompletionfunc, } ``` The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `custom_func()` to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod ` the `customcfunc()` will run because the cobra.Command only understood "kubectl" and "get." `customfunc()` will see that the cobra.Command is "kubectlget" and will thus call another helper `kubectlgetresource()`. `kubectlgetresource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `kubectlparseget pod`. `kubectlparse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods! In the above example "pod" was assumed to already be typed. But if you want `kubectl get ` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like: ```go validArgs []string = { "pod", "node", "service", "replicationcontroller" } cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)", Short: "Display one or many resources", Long: get_long, Example: get_example, Run: func(cmd

args []string) { err := RunGet(f, out, cmd, args) util.CheckErr(err) }, ValidArgs: validArgs, } ``` Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like ```bash node pod replicationcontroller service ``` If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`: ```go argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" } cmd := &cobra.Command{ ... ValidArgs: validArgs, ArgAliases: argAliases } ``` The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by the completion algorithm if entered manually, e.g. in: ```bash backend frontend database ``` Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns in this example again instead of the replication controllers. Most of the time completions will only show subcommands. But if a flag is required to make a subcommand work, you probably want it to show up when the user types . Marking a flag as 'Required' is incredibly easy. ```go cmd.MarkFlagRequired("pod") cmd.MarkFlagRequired("container") ``` and you'll get something like ```bash -c --container= -p --pod= ``` In this example we use --filename= and expect to get a json or yaml file as the argument. To make this easier we annotate the --filename flag with valid filename extensions. ```go annotations := []string{"json", "yaml", "yml"} annotation := make(mapstring) annotation[cobra.BashCompFilenameExt] = annotations flag := &pflag.Flag{ Name: "filename", Shorthand: "f", Usage: usage, Value: value, DefValue: value.String(), Annotations: annotation, } cmd.Flags().AddFlag(flag) ``` Now when you run a command with this filename flag you'll get something like ```bash test/ example/ rpmbuild/ hello.yml test.json ``` So while there are many other files in the CWD it only shows me subdirs and those with valid extensions. Similar to the filename completion and filtering using cobra.BashCompFilenameExt, you can specify a custom flag completion function with cobra.BashCompCustom: ```go annotation := make(mapstring) annotation[cobra.BashCompCustom] = []string{"kubectlgetnamespaces"} flag := &pflag.Flag{ Name: "namespace", Usage: usage, Annotations: annotation, } cmd.Flags().AddFlag(flag) ``` In addition add the `handlenamespaceflag` implementation in the `BashCompletionFunction` value, e.g.: ```bash kubectlgetnamespaces() { local template template="{{ range .items }}{{ .metadata.name }} {{ end }}" local kubectl_out if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) ) fi } ``` You can also configure the `bash aliases` for the commands and they will also support completions. ```bash alias aliasname=origcommand complete -o default -F start_origcommand aliasname $) aliasname <tab><tab> completion firstcommand secondcommand ```