Merge pull request #1737 from ijc/plugins-using-PersistentPreRunE

Fix regression for CLI plugins using PersistentPreRunE
This commit is contained in:
Sebastiaan van Stijn 2019-03-14 15:51:13 +01:00 committed by GitHub
commit 7f1176b8aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 9 deletions

View File

@ -46,11 +46,20 @@ func main() {
var ( var (
who, context string who, context string
debug bool preRun, debug bool
) )
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "helloworld", Use: "helloworld",
Short: "A basic Hello World plugin for tests", Short: "A basic Hello World plugin for tests",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if err := plugin.PersistentPreRunE(cmd, args); err != nil {
return err
}
if preRun {
fmt.Fprintf(dockerCli.Err(), "Plugin PersistentPreRunE called")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
if debug { if debug {
fmt.Fprintf(dockerCli.Err(), "Plugin debug mode enabled") fmt.Fprintf(dockerCli.Err(), "Plugin debug mode enabled")
@ -79,6 +88,7 @@ func main() {
flags := cmd.Flags() flags := cmd.Flags()
flags.StringVar(&who, "who", "", "Who are we addressing?") flags.StringVar(&who, "who", "", "Who are we addressing?")
flags.BoolVar(&preRun, "pre-run", false, "Log from prerun hook")
// These are intended to deliberately clash with the CLIs own top // These are intended to deliberately clash with the CLIs own top
// level arguments. // level arguments.
flags.BoolVarP(&debug, "debug", "D", false, "Enable debug") flags.BoolVarP(&debug, "debug", "D", false, "Enable debug")

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"sync"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager" "github.com/docker/cli/cli-plugins/manager"
@ -13,14 +14,26 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
// PersistentPreRunE must be called by any plugin command (or
// subcommand) which uses the cobra `PersistentPreRun*` hook. Plugins
// which do not make use of `PersistentPreRun*` do not need to call
// this (although it remains safe to do so). Plugins are recommended
// to use `PersistenPreRunE` to enable the error to be
// returned. Should not be called outside of a command's
// PersistentPreRunE hook and must not be run unless Run has been
// called.
var PersistentPreRunE func(*cobra.Command, []string) error
func runPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) error { func runPlugin(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) error {
tcmd := newPluginCommand(dockerCli, plugin, meta) tcmd := newPluginCommand(dockerCli, plugin, meta)
// Doing this here avoids also calling it for the metadata var persistentPreRunOnce sync.Once
// command which needlessly initializes the client and tries PersistentPreRunE = func(_ *cobra.Command, _ []string) error {
// to connect to the daemon. var err error
plugin.PersistentPreRunE = func(_ *cobra.Command, _ []string) error { persistentPreRunOnce.Do(func() {
return tcmd.Initialize(withPluginClientConn(plugin.Name())) err = tcmd.Initialize(withPluginClientConn(plugin.Name()))
})
return err
} }
cmd, _, err := tcmd.HandleGlobalFlags() cmd, _, err := tcmd.HandleGlobalFlags()
@ -98,6 +111,7 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
Short: fullname + " is a Docker CLI plugin", Short: fullname + " is a Docker CLI plugin",
SilenceUsage: true, SilenceUsage: true,
SilenceErrors: true, SilenceErrors: true,
PersistentPreRunE: PersistentPreRunE,
TraverseChildren: true, TraverseChildren: true,
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
} }
@ -122,6 +136,10 @@ func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: manager.MetadataSubcommandName, Use: manager.MetadataSubcommandName,
Hidden: true, Hidden: true,
// Suppress the global/parent PersistentPreRunE, which
// needlessly initializes the client and tries to
// connect to the daemon.
PersistentPreRun: func(cmd *cobra.Command, args []string) {},
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
enc.SetEscapeHTML(false) enc.SetEscapeHTML(false)

View File

@ -177,10 +177,10 @@ func TestCliInitialized(t *testing.T) {
run, _, cleanup := prepare(t) run, _, cleanup := prepare(t)
defer cleanup() defer cleanup()
res := icmd.RunCmd(run("helloworld", "apiversion")) res := icmd.RunCmd(run("helloworld", "--pre-run", "apiversion"))
res.Assert(t, icmd.Success) res.Assert(t, icmd.Success)
assert.Assert(t, res.Stdout() != "") assert.Assert(t, res.Stdout() != "")
assert.Assert(t, is.Equal(res.Stderr(), "")) assert.Assert(t, is.Equal(res.Stderr(), "Plugin PersistentPreRunE called"))
} }
// TestPluginErrorCode tests when the plugin return with a given exit status. // TestPluginErrorCode tests when the plugin return with a given exit status.

View File

@ -6,6 +6,7 @@ A basic Hello World plugin for tests
Options: Options:
-c, --context string Is it Christmas? -c, --context string Is it Christmas?
-D, --debug Enable debug -D, --debug Enable debug
--pre-run Log from prerun hook
--who string Who are we addressing? --who string Who are we addressing?
Commands: Commands: