cmd/docker: Add --cloud with configurable context resolution - #7343
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
Lower-confidence findings (not posted inline)
- [medium] cmd/docker/docker.go:514 — os.Args mutated globally before tcmd.Initialize(), affecting processAliases (confidence: weak 52/100)
Plugins had no generic way to declare support for optional CLI contracts, so each one would need its own top-level metadata field. Add Features, keyed by feature name. Values are arbitrary JSON so a feature can carry more than a boolean. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Callers that choose the context from configuration had to do it before Initialize. That meant loading the config file and context store themselves and handling --config a second time. WithContextResolver runs a callback inside Initialize after the config file and context store are loaded, but before telemetry and endpoint initialization. A non-empty result replaces the selected context, an empty result keeps normal selection, and an error aborts initialization. The API client and endpoint are resolved lazily, so they use the overridden context. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Nested plugin help requests can unexpectedly provision a context or fail before displaying help.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Adds configurable cloud-context resolution before Docker client initialization.
Changes:
- Adds
--cloudwith plugin capability discovery and context provisioning. - Forwards resolved contexts to downstream plugins.
- Adds resolver metadata, initialization hooks, and integration tests.
| File | Description |
|---|---|
cmd/docker/docker.go |
Integrates cloud resolution into startup and help. |
cmd/docker/cloud.go |
Implements provider discovery, resolution, and argument rewriting. |
cmd/docker/cloud_test.go |
Tests cloud resolution and plugin forwarding. |
cli/command/cli.go |
Runs context resolvers during initialization. |
cli/command/cli_test.go |
Tests resolver initialization behavior. |
cli/command/cli_options.go |
Adds the context-resolver option. |
cli-plugins/metadata/metadata.go |
Adds plugin feature metadata. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if err != nil || cmd == rootCmd || pluginmanager.IsPluginCommand(cmd) { | ||
| // Plugin flags are opaque. Only recognize help immediately after the | ||
| // plugin name; deeper help is available through "docker help PLUGIN". | ||
| return len(args) > 1 && (args[1] == "--help" || args[1] == "-h" || args[1] == "--help=true"), nil |
There was a problem hiding this comment.
Yeah not sure theres a better way. We can't just scan the whole command line.. what about something like compose run web some-command--help ?
There was a problem hiding this comment.
I don't think there's a good way around this due to the early context resolution that needs to happen before invoking the plugin. We could do a naive/full scan for --help and skip context resolution entirely perhaps?
There was a problem hiding this comment.
-h might be too broad. For --help that would break for usages where for example you call a container with a cmd that involves --help (although thats probably a big edge case).
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
Lower-confidence findings (not posted inline)
- [low]
cmd/docker/docker.go:519 —WithContextResolvercallback mutatesos.Argsas a global side-effect before returning (confidence: weak 52/100) - [low]
cmd/docker/cloud.go:195 —cloudHelpRequestreturns false for a single unknown subcommand, causing cloud provisioning for invalid commands (confidence: weak 52/100)
Dismissed security findings (review manually)
cmd/docker/cloud.go:135 — Plugin executable path from discovery used inexec.CommandContextwith#nosec G204suppression (verifier mitigation: consistent with the existing docker CLI plugin execution model; all plugins execute user-installed binaries with the same trust level; the extracloudResolverFeaturemetadata check adds an additional validation step)
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
Lower-confidence findings (not posted inline)
- [low] cmd/docker/cloud.go:221 —
cloudPluginArgsshares*pflag.Flagpointers viaAddFlagSet, soParseAllsetsChanged=trueon the original root command's flags; notably--cloudis parsed but excluded from the rewrittenos.Args, so it remainsChanged=trueon the root command's actualFlagSetafter cobra re-parses (cobra never resetsChanged, only sets it). In practice this is benign since no code betweencloudPluginArgsandcmd.ExecuteContextreads root-flagChangedstate — but it is a latent inconsistency. (confidence: 🟠 weak 52/100)
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
| err = flags.ParseAll(remaining, func(flag *pflag.Flag, value string) error { | ||
| if flag.Name == "help" { | ||
| var err error | ||
| if help, err = strconv.ParseBool(value); err != nil { | ||
| return fmt.Errorf("invalid argument %q for \"--help\" flag: %w", value, err) | ||
| } | ||
| } | ||
| return nil |
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
Resolve it through the offload plugin by default, with a provider override in features.cloud in the Docker config. Require the selected plugin to declare the cloud-context-resolver feature in its metadata before showing the flag in help and completion or invoking its __resolve-context operation. Resolve through WithContextResolver, after the config file and context store are loaded but before client initialization, without wrapping or re-executing Docker. Forward --context to downstream plugins so they need not support --cloud or repeat provisioning. Skip provisioning for help, version, and completion requests. Report unknown flags and missing flag values before provisioning, so a mistyped invocation fails without creating cloud resources or contacting the engine selected before resolution. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
| command.WithEnableGlobalTracerProvider(), | ||
| command.WithContextResolver(func(cli *command.DockerCli) (string, error) { | ||
| var err error | ||
| os.Args, err = processCloud(ctx, cli, cmd, args, os.Args) |
There was a problem hiding this comment.
[medium] os.Args is mutated inside the WithContextResolver callback; mutation survives if processCloud returns (modifiedArgs, error)
In Go, a multi-return assignment os.Args, err = processCloud(...) always assigns both return values simultaneously. If processCloud returns a partially-rewritten args slice and a non-nil error, the callback returns ("", err), causing Initialize to return that error — but os.Args is already overwritten with the mutated value. The caller in runDocker propagates the error upward and exits, leaving the process-wide os.Args permanently modified.
Because os.Args is a package-level global, any code or goroutine that reads it after the callback fires (including shell-completion paths or signal handlers) sees the rewritten form even when the --cloud operation ultimately failed.
The standard Go idiom is to keep the original value and only commit the change after success:
| os.Args, err = processCloud(ctx, cli, cmd, args, os.Args) | |
| newArgs, err := processCloud(ctx, cli, cmd, args, os.Args) | |
| if err != nil { | |
| return "", err | |
| } | |
| os.Args = newArgs |
| Confidence | Score |
|---|---|
| 🟡 moderate | 75/100 |
There was a problem hiding this comment.
Technically correct, but I think it's fine to ignore this one; if an error happens, it's a fail

Resolve it through the offload plugin by default, with a provider override in features.cloud in the Docker config.
Require the selected plugin to advertise CloudContextResolver in its metadata before showing the flag in help and completion or invoking its __resolve-context operation.
Resolve before client initialization without wrapping or re-executing Docker.
Forward --context to downstream plugins so they need not support --cloud or repeat provisioning.
Skip provisioning for help, version, and completion requests.
Summary
Release notes (optional)
A picture of a cute animal (not mandatory but encouraged)