diff --git a/cmd/gendocs/gen_kubectl_docs.go b/cmd/gendocs/gen_kubectl_docs.go index 7ba04693c75..d30e7efa04f 100644 --- a/cmd/gendocs/gen_kubectl_docs.go +++ b/cmd/gendocs/gen_kubectl_docs.go @@ -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) } diff --git a/cmd/genkubedocs/gen_kube_docs.go b/cmd/genkubedocs/gen_kube_docs.go index afef74e1a08..6a631af2057 100644 --- a/cmd/genkubedocs/gen_kube_docs.go +++ b/cmd/genkubedocs/gen_kube_docs.go @@ -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) diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 0c47e3b5a43..3b716f2be98 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -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 } diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index a6c07e243e0..ca0652b1a55 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -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) diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 85e80da48c0..0f55f998818 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -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) diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 493fff9f4ae..3345fb846da 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -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") diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index 4693959c264..b6a28d0888a 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -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.") diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index 2b6b0007a7f..7caf86ddd79 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -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