Make kubectl bash completion namespace aware and add noun aliases

- add namespace filtering to bash completion
- add --namespace flag bash completion
- add bash completion noun aliases
- adapt to new cobra package structure
This commit is contained in:
Dr. Stefan Schimanski 2016-03-20 19:14:25 +01:00
parent 8af3be87e4
commit 7e0bb885f1
8 changed files with 99 additions and 17 deletions

View File

@ -21,7 +21,7 @@ import (
"io/ioutil"
"os"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
"k8s.io/kubernetes/cmd/genutils"
"k8s.io/kubernetes/pkg/kubectl/cmd"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -48,5 +48,5 @@ func main() {
os.Setenv("HOME", "/home/username")
// TODO os.Stdin should really be something like ioutil.Discard, but a Reader
kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
cobra.GenMarkdownTree(kubectl, outDir)
doc.GenMarkdownTree(kubectl, outDir)
}

View File

@ -20,7 +20,7 @@ import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
"k8s.io/kubernetes/cmd/genutils"
apiservapp "k8s.io/kubernetes/cmd/kube-apiserver/app"
cmapp "k8s.io/kubernetes/cmd/kube-controller-manager/app"
@ -51,23 +51,23 @@ func main() {
case "kube-apiserver":
// generate docs for kube-apiserver
apiserver := apiservapp.NewAPIServerCommand()
cobra.GenMarkdownTree(apiserver, outDir)
doc.GenMarkdownTree(apiserver, outDir)
case "kube-controller-manager":
// generate docs for kube-controller-manager
controllermanager := cmapp.NewControllerManagerCommand()
cobra.GenMarkdownTree(controllermanager, outDir)
doc.GenMarkdownTree(controllermanager, outDir)
case "kube-proxy":
// generate docs for kube-proxy
proxy := proxyapp.NewProxyCommand()
cobra.GenMarkdownTree(proxy, outDir)
doc.GenMarkdownTree(proxy, outDir)
case "kube-scheduler":
// generate docs for kube-scheduler
scheduler := schapp.NewSchedulerCommand()
cobra.GenMarkdownTree(scheduler, outDir)
doc.GenMarkdownTree(scheduler, outDir)
case "kubelet":
// generate docs for kubelet
kubelet := kubeletapp.NewKubeletCommand()
cobra.GenMarkdownTree(kubelet, outDir)
doc.GenMarkdownTree(kubelet, outDir)
default:
fmt.Fprintf(os.Stderr, "Module %s is not supported", module)
os.Exit(1)

View File

@ -30,12 +30,47 @@ import (
const (
bash_completion_func = `# call kubectl get $1,
__kubectl_namespace_flag()
{
local ret two_word_ns
ret=""
two_word_ns=false
for w in "${words[@]}"; do
if [ "$two_word_ns" = true ]; then
ret="--namespace=${w}"
two_word_ns=false
continue
fi
case "${w}" in
--namespace=*)
ret=${w}
;;
--namespace)
two_word_ns=true
;;
--all-namespaces)
ret=${w}
;;
esac
done
echo $ret
}
__kubectl_get_namespaces()
{
local template kubectl_out
template="{{ range .items }}{{ .metadata.name }} {{ end }}"
if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
fi
}
__kubectl_parse_get()
{
local template
template="{{ range .items }}{{ .metadata.name }} {{ end }}"
local kubectl_out
if kubectl_out=$(kubectl get -o template --template="${template}" "$1" 2>/dev/null); then
if kubectl_out=$(kubectl get $(__kubectl_namespace_flag) -o template --template="${template}" "$1" 2>/dev/null); then
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
fi
}
@ -71,7 +106,7 @@ __kubectl_get_containers()
fi
local last=${nouns[${len} -1]}
local kubectl_out
if kubectl_out=$(kubectl get -o template --template="${template}" pods "${last}" 2>/dev/null); then
if kubectl_out=$(kubectl get $(__kubectl_namespace_flag) -o template --template="${template}" pods "${last}" 2>/dev/null); then
COMPREPLY=( $( compgen -W "${kubectl_out[*]}" -- "$cur" ) )
fi
}
@ -196,6 +231,14 @@ Find more information at https://github.com/kubernetes/kubernetes.`,
cmds.AddCommand(NewCmdExplain(f, out))
cmds.AddCommand(NewCmdConvert(f, out))
if cmds.Flag("namespace").Annotations == nil {
cmds.Flag("namespace").Annotations = map[string][]string{}
}
cmds.Flag("namespace").Annotations[cobra.BashCompCustom] = append(
cmds.Flag("namespace").Annotations[cobra.BashCompCustom],
"__kubectl_get_namespaces",
)
return cmds
}

View File

@ -71,11 +71,12 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command {
options := &DeleteOptions{}
// retrieve a list of handled resources from printer as valid args
validArgs := []string{}
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, false, false, false, false, false, false, []string{})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
cmd := &cobra.Command{
@ -88,7 +89,8 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command {
err := RunDelete(f, out, cmd, args, options)
cmdutil.CheckErr(err)
},
ValidArgs: validArgs,
ValidArgs: validArgs,
ArgAliases: argAliases,
}
usage := "Filename, directory, or URL to a file containing the resource to delete."
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)

View File

@ -74,6 +74,9 @@ kubectl describe pods frontend`
func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command {
options := &DescribeOptions{}
validArgs := kubectl.DescribableResources()
argAliases := kubectl.ResourceAliases(validArgs)
cmd := &cobra.Command{
Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)",
Short: "Show details of a specific resource or group of resources",
@ -83,7 +86,8 @@ func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command {
err := RunDescribe(f, out, cmd, args, options)
cmdutil.CheckErr(err)
},
ValidArgs: kubectl.DescribableResources(),
ValidArgs: validArgs,
ArgAliases: argAliases,
}
usage := "Filename, directory, or URL to a file containing the resource to describe"
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)

View File

@ -74,11 +74,12 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command {
options := &GetOptions{}
// retrieve a list of handled resources from printer as valid args
validArgs := []string{}
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, false, false, false, false, false, false, []string{})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
cmd := &cobra.Command{
@ -90,7 +91,8 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command {
err := RunGet(f, out, cmd, args, options)
cmdutil.CheckErr(err)
},
ValidArgs: validArgs,
ValidArgs: validArgs,
ArgAliases: argAliases,
}
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")

View File

@ -73,11 +73,12 @@ func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command {
options := &LabelOptions{}
// retrieve a list of handled resources from printer as valid args
validArgs := []string{}
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, false, false, false, false, false, false, []string{})
cmdutil.CheckErr(err)
if p != nil {
validArgs = p.HandledResources()
argAliases = kubectl.ResourceAliases(validArgs)
}
cmd := &cobra.Command{
@ -89,7 +90,8 @@ func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command {
err := RunLabel(f, out, cmd, args, options)
cmdutil.CheckErr(err)
},
ValidArgs: validArgs,
ValidArgs: validArgs,
ArgAliases: argAliases,
}
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().Bool("overwrite", false, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.")

View File

@ -175,6 +175,35 @@ func expandResourceShortcut(resource unversioned.GroupVersionResource) unversion
return resource
}
// ResourceAliases returns the resource shortcuts and plural forms for the given resources.
func ResourceAliases(rs []string) []string {
as := make([]string, 0, len(rs))
plurals := make(map[string]struct{}, len(rs))
for _, r := range rs {
var plural string
switch {
case r == "endpoints":
plural = r // exception. "endpoint" does not exist. Why?
case strings.HasSuffix(r, "y"):
plural = r[0:len(r)-1] + "ies"
case strings.HasSuffix(r, "s"):
plural = r + "es"
default:
plural = r + "s"
}
as = append(as, plural)
plurals[plural] = struct{}{}
}
for sf, r := range shortForms {
if _, found := plurals[r]; found {
as = append(as, sf)
}
}
return as
}
// parseFileSource parses the source given. Acceptable formats include:
//
// 1. source-path: the basename will become the key name