From 53f018120ae8d93531245ba56d4acdfa4bd837ba Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Mon, 17 Dec 2018 16:23:55 +0000 Subject: [PATCH] =?UTF-8?q?Integrate=20CLI=20plugins=20with=20`docker=20?= =?UTF-8?q?=C2=ABplugin=C2=BB=20--help`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To achieve this we hook in at the beginning of our custom `HelpFunc` and detect the plugin case by adding stub commands. Signed-off-by: Ian Campbell --- cmd/docker/docker.go | 26 ++++++++++++++++ e2e/cli-plugins/run_test.go | 60 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index f4b94652ec..1ebf30a89d 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -123,6 +123,21 @@ func setupHelpCommand(dockerCli *command.DockerCli, rootCmd, helpCmd *cobra.Comm } } +func tryRunPluginHelp(dockerCli command.Cli, ccmd *cobra.Command, cargs []string) error { + root := ccmd.Root() + pluginmanager.AddPluginCommandStubs(dockerCli, root, false) + + cmd, _, err := root.Traverse(cargs) + if err != nil { + return err + } + helpcmd, err := pluginmanager.PluginRunCommand(dockerCli, cmd.Name(), root) + if err != nil { + return err + } + return helpcmd.Run() +} + func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag.FlagSet, opts *cliflags.ClientOptions) { defaultHelpFunc := cmd.HelpFunc() cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) { @@ -130,6 +145,17 @@ func setHelpFunc(dockerCli *command.DockerCli, cmd *cobra.Command, flags *pflag. ccmd.Println(err) return } + if len(args) >= 1 { + err := tryRunPluginHelp(dockerCli, ccmd, args) + if err == nil { // Successfully ran the plugin + return + } + if !pluginmanager.IsNotFound(err) { + ccmd.Println(err) + return + } + } + if err := isSupported(ccmd, dockerCli); err != nil { ccmd.Println(err) return diff --git a/e2e/cli-plugins/run_test.go b/e2e/cli-plugins/run_test.go index 34bd84c9a8..30a13cf79e 100644 --- a/e2e/cli-plugins/run_test.go +++ b/e2e/cli-plugins/run_test.go @@ -35,6 +35,22 @@ func TestHelpNonexisting(t *testing.T) { golden.Assert(t, res.Stderr(), "docker-help-nonexistent-err.golden") } +// TestNonexistingHelp ensures correct behaviour when invoking a +// nonexistent plugin with `--help`. +func TestNonexistingHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("nonexistent", "--help")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + // This should actually be the whole docker help + // output, so spot check instead having of a golden + // with everything in, which will change too frequently. + Out: "Usage: docker [OPTIONS] COMMAND\n\nA self-sufficient runtime for containers", + }) +} + // TestRunBad ensures correct behaviour when running an existent but invalid plugin func TestRunBad(t *testing.T) { run, cleanup := prepare(t) @@ -61,6 +77,22 @@ func TestHelpBad(t *testing.T) { golden.Assert(t, res.Stderr(), "docker-help-badmeta-err.golden") } +// TestBadHelp ensures correct behaviour when invoking an +// existent but invalid plugin with `--help`. +func TestBadHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("badmeta", "--help")) + res.Assert(t, icmd.Expected{ + ExitCode: 0, + // This should be literally the whole docker help + // output, so spot check instead of a golden with + // everything in which will change all the time. + Out: "Usage: docker [OPTIONS] COMMAND\n\nA self-sufficient runtime for containers", + }) +} + // TestRunGood ensures correct behaviour when running a valid plugin func TestRunGood(t *testing.T) { run, cleanup := prepare(t) @@ -86,6 +118,20 @@ func TestHelpGood(t *testing.T) { assert.Assert(t, is.Equal(res.Stderr(), "")) } +// TestGoodHelp ensures correct behaviour when calling a valid plugin +// with `--help`. A global argument is used to ensure it does not +// interfere. +func TestGoodHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("-D", "helloworld", "--help")) + res.Assert(t, icmd.Success) + // This is the same golden file as `TestHelpGood`, above. + golden.Assert(t, res.Stdout(), "docker-help-helloworld.golden") + assert.Assert(t, is.Equal(res.Stderr(), "")) +} + // TestRunGoodSubcommand ensures correct behaviour when running a valid plugin with a subcommand func TestRunGoodSubcommand(t *testing.T) { run, cleanup := prepare(t) @@ -110,3 +156,17 @@ func TestHelpGoodSubcommand(t *testing.T) { golden.Assert(t, res.Stdout(), "docker-help-helloworld-goodbye.golden") assert.Assert(t, is.Equal(res.Stderr(), "")) } + +// TestGoodSubcommandHelp ensures correct behaviour when calling a valid plugin +// with a subcommand and `--help`. A global argument is used to ensure it does not +// interfere. +func TestGoodSubcommandHelp(t *testing.T) { + run, cleanup := prepare(t) + defer cleanup() + + res := icmd.RunCmd(run("-D", "helloworld", "goodbye", "--help")) + res.Assert(t, icmd.Success) + // This is the same golden file as `TestHelpGoodSubcommand`, above. + golden.Assert(t, res.Stdout(), "docker-help-helloworld-goodbye.golden") + assert.Assert(t, is.Equal(res.Stderr(), "")) +}