1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package completion
18
19import (
20	"io"
21
22	"github.com/spf13/cobra"
23
24	cmdutil "k8s.io/kubectl/pkg/cmd/util"
25	"k8s.io/kubectl/pkg/util/i18n"
26	"k8s.io/kubectl/pkg/util/templates"
27)
28
29const defaultBoilerPlate = `
30# Copyright 2016 The Kubernetes Authors.
31#
32# Licensed under the Apache License, Version 2.0 (the "License");
33# you may not use this file except in compliance with the License.
34# You may obtain a copy of the License at
35#
36#     http://www.apache.org/licenses/LICENSE-2.0
37#
38# Unless required by applicable law or agreed to in writing, software
39# distributed under the License is distributed on an "AS IS" BASIS,
40# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41# See the License for the specific language governing permissions and
42# limitations under the License.
43`
44
45var (
46	completionLong = templates.LongDesc(i18n.T(`
47		Output shell completion code for the specified shell (bash or zsh).
48		The shell code must be evaluated to provide interactive
49		completion of kubectl commands.  This can be done by sourcing it from
50		the .bash_profile.
51
52		Detailed instructions on how to do this are available here:
53
54        for macOS:
55        https://kubernetes.io/docs/tasks/tools/install-kubectl-macos/#enable-shell-autocompletion
56
57        for linux:
58        https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#enable-shell-autocompletion
59
60        for windows:
61        https://kubernetes.io/docs/tasks/tools/install-kubectl-windows/#enable-shell-autocompletion
62
63		Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2.`))
64
65	completionExample = templates.Examples(i18n.T(`
66		# Installing bash completion on macOS using homebrew
67		## If running Bash 3.2 included with macOS
68		    brew install bash-completion
69		## or, if running Bash 4.1+
70		    brew install bash-completion@2
71		## If kubectl is installed via homebrew, this should start working immediately
72		## If you've installed via other means, you may need add the completion to your completion directory
73		    kubectl completion bash > $(brew --prefix)/etc/bash_completion.d/kubectl
74
75
76		# Installing bash completion on Linux
77		## If bash-completion is not installed on Linux, install the 'bash-completion' package
78		## via your distribution's package manager.
79		## Load the kubectl completion code for bash into the current shell
80		    source <(kubectl completion bash)
81		## Write bash completion code to a file and source it from .bash_profile
82		    kubectl completion bash > ~/.kube/completion.bash.inc
83		    printf "
84		      # Kubectl shell completion
85		      source '$HOME/.kube/completion.bash.inc'
86		      " >> $HOME/.bash_profile
87		    source $HOME/.bash_profile
88
89		# Load the kubectl completion code for zsh[1] into the current shell
90		    source <(kubectl completion zsh)
91		# Set the kubectl completion code for zsh[1] to autoload on startup
92		    kubectl completion zsh > "${fpath[1]}/_kubectl"`))
93)
94
95var (
96	completionShells = map[string]func(out io.Writer, boilerPlate string, cmd *cobra.Command) error{
97		"bash": runCompletionBash,
98		"zsh":  runCompletionZsh,
99	}
100)
101
102// NewCmdCompletion creates the `completion` command
103func NewCmdCompletion(out io.Writer, boilerPlate string) *cobra.Command {
104	shells := []string{}
105	for s := range completionShells {
106		shells = append(shells, s)
107	}
108
109	cmd := &cobra.Command{
110		Use:                   "completion SHELL",
111		DisableFlagsInUseLine: true,
112		Short:                 i18n.T("Output shell completion code for the specified shell (bash or zsh)"),
113		Long:                  completionLong,
114		Example:               completionExample,
115		Run: func(cmd *cobra.Command, args []string) {
116			cmdutil.CheckErr(RunCompletion(out, boilerPlate, cmd, args))
117		},
118		ValidArgs: shells,
119	}
120
121	return cmd
122}
123
124// RunCompletion checks given arguments and executes command
125func RunCompletion(out io.Writer, boilerPlate string, cmd *cobra.Command, args []string) error {
126	if len(args) == 0 {
127		return cmdutil.UsageErrorf(cmd, "Shell not specified.")
128	}
129	if len(args) > 1 {
130		return cmdutil.UsageErrorf(cmd, "Too many arguments. Expected only the shell type.")
131	}
132	run, found := completionShells[args[0]]
133	if !found {
134		return cmdutil.UsageErrorf(cmd, "Unsupported shell type %q.", args[0])
135	}
136
137	return run(out, boilerPlate, cmd.Parent())
138}
139
140func runCompletionBash(out io.Writer, boilerPlate string, kubectl *cobra.Command) error {
141	if len(boilerPlate) == 0 {
142		boilerPlate = defaultBoilerPlate
143	}
144	if _, err := out.Write([]byte(boilerPlate)); err != nil {
145		return err
146	}
147
148	return kubectl.GenBashCompletion(out)
149}
150
151func runCompletionZsh(out io.Writer, boilerPlate string, kubectl *cobra.Command) error {
152	zshHead := "#compdef kubectl\ncompdef _kubectl kubectl\n"
153	out.Write([]byte(zshHead))
154
155	if len(boilerPlate) == 0 {
156		boilerPlate = defaultBoilerPlate
157	}
158	if _, err := out.Write([]byte(boilerPlate)); err != nil {
159		return err
160	}
161
162	return kubectl.GenZshCompletion(out)
163}
164