From 2158473474b4873d20efbc433e9262c8a7c5d777 Mon Sep 17 00:00:00 2001 From: Fabiano Franz Date: Tue, 25 Apr 2017 18:55:01 -0300 Subject: [PATCH] Plugins are loaded under the 'kubectl plugin' command --- docs/.generated_docs | 3 ++ docs/man/man1/kubectl-plugin.1 | 3 ++ docs/user-guide/kubectl/kubectl_plugin.md | 3 ++ docs/yaml/kubectl/kubectl_plugin.yaml | 3 ++ hack/make-rules/test-cmd-util.sh | 26 ++++++++------ pkg/kubectl/cmd/cmd.go | 19 +--------- pkg/kubectl/cmd/plugin.go | 42 +++++++++++++++++++++++ 7 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 docs/man/man1/kubectl-plugin.1 create mode 100644 docs/user-guide/kubectl/kubectl_plugin.md create mode 100644 docs/yaml/kubectl/kubectl_plugin.yaml diff --git a/docs/.generated_docs b/docs/.generated_docs index fac2e50ee0..a06f8132fd 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -73,6 +73,7 @@ docs/man/man1/kubectl-label.1 docs/man/man1/kubectl-logs.1 docs/man/man1/kubectl-options.1 docs/man/man1/kubectl-patch.1 +docs/man/man1/kubectl-plugin.1 docs/man/man1/kubectl-port-forward.1 docs/man/man1/kubectl-proxy.1 docs/man/man1/kubectl-replace.1 @@ -162,6 +163,7 @@ docs/user-guide/kubectl/kubectl_label.md docs/user-guide/kubectl/kubectl_logs.md docs/user-guide/kubectl/kubectl_options.md docs/user-guide/kubectl/kubectl_patch.md +docs/user-guide/kubectl/kubectl_plugin.md docs/user-guide/kubectl/kubectl_port-forward.md docs/user-guide/kubectl/kubectl_proxy.md docs/user-guide/kubectl/kubectl_replace.md @@ -211,6 +213,7 @@ docs/yaml/kubectl/kubectl_label.yaml docs/yaml/kubectl/kubectl_logs.yaml docs/yaml/kubectl/kubectl_options.yaml docs/yaml/kubectl/kubectl_patch.yaml +docs/yaml/kubectl/kubectl_plugin.yaml docs/yaml/kubectl/kubectl_port-forward.yaml docs/yaml/kubectl/kubectl_proxy.yaml docs/yaml/kubectl/kubectl_replace.yaml diff --git a/docs/man/man1/kubectl-plugin.1 b/docs/man/man1/kubectl-plugin.1 new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/man/man1/kubectl-plugin.1 @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/user-guide/kubectl/kubectl_plugin.md b/docs/user-guide/kubectl/kubectl_plugin.md new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/user-guide/kubectl/kubectl_plugin.md @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/yaml/kubectl/kubectl_plugin.yaml b/docs/yaml/kubectl/kubectl_plugin.yaml new file mode 100644 index 0000000000..b6fd7a0f98 --- /dev/null +++ b/docs/yaml/kubectl/kubectl_plugin.yaml @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index 8f0a68c454..e5161b212e 100644 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -3684,19 +3684,25 @@ __EOF__ ########### kube::log::status "Testing kubectl plugins" - # single plugins path + # top-level plugin command output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins kubectl -h 2>&1) + kube::test::if_has_string "${output_message}" 'plugin\s\+Runs a command-line plugin' + + # no plugins + output_message=$(! kubectl plugin 2>&1) + kube::test::if_has_string "${output_message}" 'no plugins installed' + + # single plugins path + output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins kubectl plugin 2>&1) kube::test::if_has_string "${output_message}" 'echo\s\+Echoes for test-cmd' kube::test::if_has_string "${output_message}" 'get\s\+The wonderful new plugin-based get!' kube::test::if_has_string "${output_message}" 'error\s\+The tremendous plugin that always fails!' kube::test::if_has_not_string "${output_message}" 'The hello plugin' kube::test::if_has_not_string "${output_message}" 'Incomplete plugin' - - # when overriding existing command, both appear in help. TODO handle this to not register plugins that override existing cmd. - kube::test::if_has_string "${output_message}" 'get\s\+Display one or many resources' + kube::test::if_has_not_string "${output_message}" 'no plugins installed' # multiple plugins path - output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl -h 2>&1) + output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl plugin -h 2>&1) kube::test::if_has_string "${output_message}" 'echo\s\+Echoes for test-cmd' kube::test::if_has_string "${output_message}" 'get\s\+The wonderful new plugin-based get!' kube::test::if_has_string "${output_message}" 'error\s\+The tremendous plugin that always fails!' @@ -3709,18 +3715,18 @@ __EOF__ kube::test::if_has_not_string "$output_message{output_message}" 'The wonderful new plugin-based get' # plugin help - output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl hello -h 2>&1) + output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl plugin hello -h 2>&1) kube::test::if_has_string "${output_message}" 'The hello plugin is a new plugin used by test-cmd to test multiple plugin locations.' kube::test::if_has_string "${output_message}" 'Usage:' # run plugin - output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl hello 2>&1) + output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl plugin hello 2>&1) kube::test::if_has_string "${output_message}" '#hello#' - output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl echo 2>&1) + output_message=$(KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/:test/fixtures/pkg/kubectl/plugins2/ kubectl plugin echo 2>&1) kube::test::if_has_string "${output_message}" 'This plugin works!' - output_message=$(! KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/ kubectl hello 2>&1) + output_message=$(! KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/ kubectl plugin hello 2>&1) kube::test::if_has_string "${output_message}" 'unknown command' - output_message=$(! KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/ kubectl error 2>&1) + output_message=$(! KUBECTL_PLUGINS_PATH=test/fixtures/pkg/kubectl/plugins/ kubectl plugin error 2>&1) kube::test::if_has_string "${output_message}" 'error: exit status 1' kube::test::clear_all diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 995ec765a5..4c9c353f81 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -347,24 +347,6 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob }, }, } - - // Loads plugins and create commands for each plugin identified - loadedPlugins, loadErr := f.PluginLoader().Load() - if loadErr != nil { - glog.V(1).Infof("Unable to load plugins: %v", loadErr) - } - pluginRunner := f.PluginRunner() - if len(loadedPlugins) > 0 { - pluginCmds := []*cobra.Command{} - for _, p := range loadedPlugins { - pluginCmds = append(pluginCmds, NewCmdForPlugin(p, pluginRunner, in, out, err)) - } - groups = append(groups, templates.CommandGroup{ - Message: "Plugins:", - Commands: pluginCmds, - }) - } - groups.Add(cmds) filters := []string{ @@ -386,6 +368,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob } cmds.AddCommand(cmdconfig.NewCmdConfig(clientcmd.NewDefaultPathOptions(), out, err)) + cmds.AddCommand(NewCmdPlugin(f, in, out, err)) cmds.AddCommand(NewCmdVersion(f, out)) cmds.AddCommand(NewCmdApiVersions(f, out)) cmds.AddCommand(NewCmdOptions()) diff --git a/pkg/kubectl/cmd/plugin.go b/pkg/kubectl/cmd/plugin.go index d3cb93a1c5..808eb5508f 100644 --- a/pkg/kubectl/cmd/plugin.go +++ b/pkg/kubectl/cmd/plugin.go @@ -17,15 +17,57 @@ limitations under the License. package cmd import ( + "fmt" "io" "os" + "github.com/golang/glog" "github.com/spf13/cobra" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/plugins" + "k8s.io/kubernetes/pkg/util/i18n" ) +var ( + plugin_long = templates.LongDesc(` + Runs a command-line plugin. + + Plugins are subcommands that are not part of the major command-line distribution + and can even be provided by third-parties. Please refer to the documentation and + examples for more information about how to install and write your own plugins.`) +) + +// NewCmdPlugin creates the command that is the top-level for plugin commands. +func NewCmdPlugin(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command { + // Loads plugins and create commands for each plugin identified + loadedPlugins, loadErr := f.PluginLoader().Load() + if loadErr != nil { + glog.V(1).Infof("Unable to load plugins: %v", loadErr) + } + + cmd := &cobra.Command{ + Use: "plugin NAME", + Short: i18n.T("Runs a command-line plugin"), + Long: plugin_long, + Run: func(cmd *cobra.Command, args []string) { + if len(loadedPlugins) == 0 { + cmdutil.CheckErr(fmt.Errorf("no plugins installed.")) + } + cmdutil.DefaultSubCommandRun(err)(cmd, args) + }, + } + + if len(loadedPlugins) > 0 { + pluginRunner := f.PluginRunner() + for _, p := range loadedPlugins { + cmd.AddCommand(NewCmdForPlugin(p, pluginRunner, in, out, err)) + } + } + + return cmd +} + // NewCmdForPlugin creates a command capable of running the provided plugin. func NewCmdForPlugin(plugin *plugins.Plugin, runner plugins.PluginRunner, in io.Reader, out, errout io.Writer) *cobra.Command { if !plugin.IsValid() {