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