1
0
mirror of https://github.com/docker/cli.git synced 2025-08-31 23:02:07 +03:00

cli-plugins: use docker system dial-stdio to call the daemon

This means that plugins can use whatever methods the monolithic CLI supports,
which is good for consistency.

This relies on `os.Args[0]` being something which can be executed again to
reach the same binary, since it is propagated (via an envvar) to the plugin for
this purpose. This essentially requires that the current working directory and
path are not modified by the monolithic CLI before it launches the plugin nor
by the plugin before it initializes the client. This should be the case.

Previously the fake apiclient used by `TestExperimentalCLI` was not being used,
since `cli.Initialize` was unconditionally overwriting it with a real one
(talking to a real daemon during unit testing, it seems). This wasn't expected
nor desirable and no longer happens with the new arrangements, exposing the
fact that no `pingFunc` is provided, leading to a panic. Add a `pingFunc` to
the fake client to avoid this.

Signed-off-by: Ian Campbell <ijc@docker.com>
This commit is contained in:
Ian Campbell
2019-01-31 17:50:58 +00:00
parent 8fa7c572d4
commit 891b3d953e
7 changed files with 143 additions and 23 deletions

View File

@@ -9,7 +9,9 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/connhelper"
cliflags "github.com/docker/cli/cli/flags"
"github.com/docker/docker/client"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
@@ -49,6 +51,7 @@ func Run(makeCmd func(command.Cli) *cobra.Command, meta manager.Metadata) {
// own use of that hook will shadow anything we add to the top-level
// command meaning the CLI is never Initialized.
var options struct {
name string
init, prerun sync.Once
opts *cliflags.ClientOptions
flags *pflag.FlagSet
@@ -71,11 +74,43 @@ func PersistentPreRunE(cmd *cobra.Command, args []string) error {
}
// flags must be the original top-level command flags, not cmd.Flags()
options.opts.Common.SetDefaultOptions(options.flags)
err = options.dockerCli.Initialize(options.opts)
err = options.dockerCli.Initialize(options.opts, withPluginClientConn(options.name))
})
return err
}
func withPluginClientConn(name string) command.InitializeOpt {
return command.WithInitializeClient(func(dockerCli *command.DockerCli) (client.APIClient, error) {
cmd := "docker"
if x := os.Getenv(manager.ReexecEnvvar); x != "" {
cmd = x
}
var flags []string
// Accumulate all the global arguments, that is those
// up to (but not including) the plugin's name. This
// ensures that `docker system dial-stdio` is
// evaluating the same set of `--config`, `--tls*` etc
// global options as the plugin was called with, which
// in turn is the same as what the original docker
// invocation was passed.
for _, a := range os.Args[1:] {
if a == name {
break
}
flags = append(flags, a)
}
flags = append(flags, "system", "dial-stdio")
helper, err := connhelper.GetCommandConnectionHelper(cmd, flags...)
if err != nil {
return nil, err
}
return client.NewClientWithOpts(client.WithDialContext(helper.Dialer))
})
}
func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta manager.Metadata) *cobra.Command {
name := plugin.Name()
fullname := manager.NamePrefix + name
@@ -101,6 +136,7 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
cli.DisableFlagsInUseLine(cmd)
options.init.Do(func() {
options.name = name
options.opts = opts
options.flags = flags
options.dockerCli = dockerCli
@@ -115,6 +151,8 @@ func newMetadataSubcommand(plugin *cobra.Command, meta manager.Metadata) *cobra.
cmd := &cobra.Command{
Use: manager.MetadataSubcommandName,
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 {
enc := json.NewEncoder(os.Stdout)
enc.SetEscapeHTML(false)