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