1# Generating Bash Completions For Your Own cobra.Command 2 3Generating bash completions from a cobra command is incredibly easy. An actual program which does so for the kubernetes kubectl binary is as follows: 4 5```go 6package main 7 8import ( 9 "io/ioutil" 10 "os" 11 12 "k8s.io/kubernetes/pkg/kubectl/cmd" 13 "k8s.io/kubernetes/pkg/kubectl/cmd/util" 14) 15 16func main() { 17 kubectl := cmd.NewKubectlCommand(util.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) 18 kubectl.GenBashCompletionFile("out.sh") 19} 20``` 21 22`out.sh` will get you completions of subcommands and flags. Copy it to `/etc/bash_completion.d/` as described [here](https://debian-administration.org/article/316/An_introduction_to_bash_completion_part_1) and reset your terminal to use autocompletion. If you make additional annotations to your code, you can get even more intelligent and flexible behavior. 23 24## Creating your own custom functions 25 26Some more actual code that works in kubernetes: 27 28```bash 29const ( 30 bash_completion_func = `__kubectl_parse_get() 31{ 32 local kubectl_output out 33 if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then 34 out=($(echo "${kubectl_output}" | awk '{print $1}')) 35 COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) 36 fi 37} 38 39__kubectl_get_resource() 40{ 41 if [[ ${#nouns[@]} -eq 0 ]]; then 42 return 1 43 fi 44 __kubectl_parse_get ${nouns[${#nouns[@]} -1]} 45 if [[ $? -eq 0 ]]; then 46 return 0 47 fi 48} 49 50__custom_func() { 51 case ${last_command} in 52 kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop) 53 __kubectl_get_resource 54 return 55 ;; 56 *) 57 ;; 58 esac 59} 60`) 61``` 62 63And then I set that in my command definition: 64 65```go 66cmds := &cobra.Command{ 67 Use: "kubectl", 68 Short: "kubectl controls the Kubernetes cluster manager", 69 Long: `kubectl controls the Kubernetes cluster manager. 70 71Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, 72 Run: runHelp, 73 BashCompletionFunction: bash_completion_func, 74} 75``` 76 77The `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 [mypod]`. If you type `kubectl get pod [tab][tab]` the `__customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods! 78 79## Have the completions code complete your 'nouns' 80 81In the above example "pod" was assumed to already be typed. But if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. Simplified code from `kubectl get` looks like: 82 83```go 84validArgs []string = { "pod", "node", "service", "replicationcontroller" } 85 86cmd := &cobra.Command{ 87 Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)", 88 Short: "Display one or many resources", 89 Long: get_long, 90 Example: get_example, 91 Run: func(cmd *cobra.Command, args []string) { 92 err := RunGet(f, out, cmd, args) 93 util.CheckErr(err) 94 }, 95 ValidArgs: validArgs, 96} 97``` 98 99Notice we put the "ValidArgs" on the "get" subcommand. Doing so will give results like 100 101```bash 102# kubectl get [tab][tab] 103node pod replicationcontroller service 104``` 105 106## Plural form and shortcuts for nouns 107 108If your nouns have a number of aliases, you can define them alongside `ValidArgs` using `ArgAliases`: 109 110```go 111argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" } 112 113cmd := &cobra.Command{ 114 ... 115 ValidArgs: validArgs, 116 ArgAliases: argAliases 117} 118``` 119 120The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by 121the completion algorithm if entered manually, e.g. in: 122 123```bash 124# kubectl get rc [tab][tab] 125backend frontend database 126``` 127 128Note that without declaring `rc` as an alias, the completion algorithm would show the list of nouns 129in this example again instead of the replication controllers. 130 131## Mark flags as required 132 133Most 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 [tab][tab]. Marking a flag as 'Required' is incredibly easy. 134 135```go 136cmd.MarkFlagRequired("pod") 137cmd.MarkFlagRequired("container") 138``` 139 140and you'll get something like 141 142```bash 143# kubectl exec [tab][tab][tab] 144-c --container= -p --pod= 145``` 146 147# Specify valid filename extensions for flags that take a filename 148 149In 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. 150 151```go 152 annotations := []string{"json", "yaml", "yml"} 153 annotation := make(map[string][]string) 154 annotation[cobra.BashCompFilenameExt] = annotations 155 156 flag := &pflag.Flag{ 157 Name: "filename", 158 Shorthand: "f", 159 Usage: usage, 160 Value: value, 161 DefValue: value.String(), 162 Annotations: annotation, 163 } 164 cmd.Flags().AddFlag(flag) 165``` 166 167Now when you run a command with this filename flag you'll get something like 168 169```bash 170# kubectl create -f 171test/ example/ rpmbuild/ 172hello.yml test.json 173``` 174 175So while there are many other files in the CWD it only shows me subdirs and those with valid extensions. 176 177# Specify custom flag completion 178 179Similar to the filename completion and filtering using cobra.BashCompFilenameExt, you can specify 180a custom flag completion function with cobra.BashCompCustom: 181 182```go 183 annotation := make(map[string][]string) 184 annotation[cobra.BashCompCustom] = []string{"__kubectl_get_namespaces"} 185 186 flag := &pflag.Flag{ 187 Name: "namespace", 188 Usage: usage, 189 Annotations: annotation, 190 } 191 cmd.Flags().AddFlag(flag) 192``` 193 194In addition add the `__handle_namespace_flag` implementation in the `BashCompletionFunction` 195value, e.g.: 196 197```bash 198__kubectl_get_namespaces() 199{ 200 local template 201 template="{{ range .items }}{{ .metadata.name }} {{ end }}" 202 local kubectl_out 203 if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then 204 COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) ) 205 fi 206} 207``` 208# Using bash aliases for commands 209 210You can also configure the `bash aliases` for the commands and they will also support completions. 211 212```bash 213alias aliasname=origcommand 214complete -o default -F __start_origcommand aliasname 215 216# and now when you run `aliasname` completion will make 217# suggestions as it did for `origcommand`. 218 219$) aliasname <tab><tab> 220completion firstcommand secondcommand 221``` 222