Commit Graph

89 Commits

Author SHA1 Message Date
Sebastiaan van Stijn d65f0c9bbf
golangci-lint: enable exhaustive linter
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-05-19 20:14:04 +02:00
Sebastiaan van Stijn 378e754c88
use consistent alias for gotest.tools/v3/assert/cmp
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-05-16 12:36:14 +02:00
Sebastiaan van Stijn 1d768f8983
update go:build tags to go1.23 to align with vendor.mod
Go maintainers started to unconditionally update the minimum go version
for golang.org/x/ dependencies to go1.23, which means that we'll no longer
be able to support any version below that when updating those dependencies;

> all: upgrade go directive to at least 1.23.0 [generated]
>
> By now Go 1.24.0 has been released, and Go 1.22 is no longer supported
> per the Go Release Policy (https://go.dev/doc/devel/release#policy).
>
> For golang/go#69095.

This updates our minimum version to go1.23, as we won't be able to maintain
compatibility with older versions because of the above.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-17 10:43:47 +02:00
Sebastiaan van Stijn 1ed3859879
cli-plugins/manager: use lazyregexp to compile regexes on first use
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-04-10 12:22:26 +02:00
Sebastiaan van Stijn 091421f13f
cli-plugins/manager: getPluginDirs: remove redundant error-return
This function returned an error (if any) from [config.Path]. However, the
only situation in which an error could be returned was if the given path
to append to `config.Dir` was outside of the config directory. This can
only happen if the path to append would try to traverse directories (e.g.,
passing `../../cli-plugins`).

Given that we're passing a hard-coded value, that would not be the case,
so we can simplify the code to join the path directly, and don't have to
handle errors.

[config.Path]: 2d74733942/cli/config/config.go (L100-L107)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-18 12:10:38 +01:00
Sebastiaan van Stijn d1a19d4476
cli-plugins/manager: ListPlugins: return early if no candidates
Skip the other logic, which includes listing all commands provided; if
there's no plugin-candidates, those steps won't be needed.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-18 12:10:38 +01:00
Sebastiaan van Stijn 40725aea3c
cli-plugins/manager: add test for empty / non-existing plugin dirs
Verify that listPluginCandidates returns an empty result if nothing was
found.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-18 12:10:38 +01:00
Sebastiaan van Stijn fdcfd229aa
cli-plugins/manager: rename var that shadowed arg in test
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-18 12:10:38 +01:00
Sebastiaan van Stijn abd02b6a23
cli-plugins/manager: ListPlugins: pass context to error-group
This error-group was added in 89583b92b7, but
passed a context.TODO because the function didn't have a context as argument.

However, it does get the root-command passed, which holds the context, so
we can pass that.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-18 12:10:28 +01:00
Sebastiaan van Stijn 8fc0c74f9a
cli-plugins/manager: use stdlib errors, and minor cleanup
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-07 19:14:32 +01:00
Sebastiaan van Stijn 292713c887
move cli-plugins annotation consts to a separate package
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-07 12:46:11 +01:00
Sebastiaan van Stijn 4321293972
move cli-plugins metadata types/consts to a separate package
This prevents cli-plugins having to import the plugin-manager.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-07 12:38:06 +01:00
Sebastiaan van Stijn 8dcde50b6e
cli-plugins/manager: use shallower interface
The manager only requires the CLI's configuration; define a shallow interface
for this so that we don't have to import cli/command.

In addition to the CLI's configuration, `runHooks` also used the CLI's configured
StdErr output. We set the Cobra input and output streams to be the same as the
DockerCLI outputs in [newDockerCommand] and [newPluginCommand], so we can
get this from the Cobra command.

[newDockerCommand]: ea1f10b440/cmd/docker/docker.go (L148-L150)
[newPluginCommand]: ea1f10b440/cli-plugins/plugin/plugin.go (L166-L168)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-05 12:04:49 +01:00
Sebastiaan van Stijn 8bedb69f2c
cli-plugins/manager: move OTEL-related code to separate file
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-03 14:24:00 +01:00
Sebastiaan van Stijn 9dc175d6ef
cli/command: un-export ResourceAttributesEnvvar, DockerCliAttributePrefix
These utility functions were added in 8890a1c929,
and are all related to OTEL. The ResourceAttributesEnvvar const defines
the "OTEL_RESOURCE_ATTRIBUTES" environment-variable to use, which is part
of the [OpenTelemetry specification], so should be considered a well-known
env-var, and not up to us to define a const for. These code-changes were not
yet included in a release, so we don't have to deprecate.

This patch:

- Moves the utility functions to the telemetry files, so that all code related
  to OpenTelemetry is together.
- Un-exports the ResourceAttributesEnvvar to reduce our public API.
- Un-exports the DockerCliAttributePrefix to reduce depdency on cli/command
  in CLI-plugins, but adds a TODO to move telemetry-related code to a common
  (internal) package.
- Deprecates the cli-plugins/manager.ResourceAttributesEnvvar const. This
  const has no known consumers, so we could skip deprecation, but just in
  case some codebase uses this.

[OpenTelemetry specification]: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#general-sdk-configuration

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-03-03 14:21:45 +01:00
Jonathan A. Sternberg 8890a1c929
cli-plugins: remove docker.cli specific otel attributes after usage
Remove the `docker.cli` prefixed attributes from
`OTEL_RESOURCE_ATTRIBUTES` after the telemetry provider has been created
within a plugin. This prevents accidentally sending the attributes to
something downstream for the user.

This also fixes an issue with compose where the self-injected `OTEL_RESOURCE_ATTRIBUTES`
would override an existing attribute in the environment file because the
"user environment" overrode the environment file, but the "user
environment" was created by the `docker` tool rather than by the user's
environment.

When `OTEL_RESOURCE_ATTRIBUTES` is empty after pruning, the environment
variable is unset.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-19 10:19:00 -06:00
Jonathan A. Sternberg cfe0605616
cli-plugins: merge OTEL_RESOURCE_ATTRIBUTES environment variable
Merge `OTEL_RESOURCE_ATTRIBUTES` when there is one already in the
environment. This allows user-specified resource attributes to be passed
on to CLI plugins while still allowing the extra attributes added for
telemetry information.

This was the original intended use-case but it seems to have never made
it in. The reason `OTEL_RESOURCE_ATTRIBUTES` was used is because we
could combine it with user-centric ones.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2025-02-18 12:06:46 -06:00
Sebastiaan van Stijn 450768c311
cli-plugins/manager: fix "unused-receiver" linting
cli-plugins/manager/manager.go:35:7: unused-receiver: method receiver 'e' is not referenced in method's body, consider removing or renaming it as _ (revive)
    func (e errPluginNotFound) NotFound() {}
          ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-02-17 15:24:55 +01:00
Paweł Gronowski fcd94feefb
cli-plugins: Simplify addPluginCandidatesFromDir
The returned error is always nil now, so just remove it.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-11-28 14:33:49 +01:00
Paweł Gronowski 6de3d71ab6
cli-plugins: Fix searching inaccessible directories
Fix a case where one inaccessible plugin search path stops the whole
search and prevents latter paths from being scanned.

Remove a preliminary `Stat` call that verifies whether path is an actual
directory and is accessible.
It's unneeded and doesn't actually check whether the directory can be
listed or not.
`os.ReadDir` will fail in such case anyway, so just attempt to do that
and ignore any encountered error, instead of erroring out the whole
plugin candidate listing.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-11-28 14:17:11 +01:00
Sebastiaan van Stijn 7c80e4f938
update go:build tags to use go1.22
commit 4a7b04d412 configured golangci-lint
to use go1.23 semantics, which enabled the copyloopvar linter.

go1.22 now creates a copy of variables when assigned in a loop; make sure we
don't have files that may downgrade semantics to go1.21 in case that also means
disabling that feature; https://go.dev/ref/spec#Go_1.22

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-12 12:38:18 +01:00
Sebastiaan van Stijn d77760fe53
cli-plugins/manager: remove redundant capturing of loop vars (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    cli-plugins/manager/cobra.go:55:4: The copy of the 'for' variable "p" can be deleted (Go 1.22+) (copyloopvar)
                p := p
                ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:32 +01:00
Sebastiaan van Stijn 020f3a7ad9
golangci-lint: enable G204, add #nosec comments instead
There's only 3 locations where it's hit, so putting #gosec ignore comments
in those locations.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-30 15:52:18 +01:00
Sebastiaan van Stijn a6e96c758e
cli: improve output and consistency for unknown (sub)commands
Before this patch, output for invalid top-level and sub-commands differed.
For top-level commands, the CLI would print an error-message and a suggestion
to use `--help`. For missing *subcommands*, we would hit a different code-path,
and different output, which includes full "usage" / "help" output.

While it is a common convention to show usage output, and may have been
a nice gesture when docker was still young and only had a few commands
and options ("you did something wrong; here's an overview of what you
can use"), that's no longer the case, and many commands have a _very_
long output.

The result of this is that the error message, which is the relevant
information in this case - "You mis-typed something" - is lost in the
output, and hard to find (sometimes even requiring scrolling back).

The output is also confusing, because it _looks_ like something ran
successfully (most of the output is not about the error!).

Even further; the suggested resolution (try `--help` to see the correct
options) is rather redundant, because running teh command with `--help`
produces _exactly_ the same output as was just showh, baring the error
message. As a fun fact, due to the usage output being printed, the
output even contains not one, but _two_ "call to actions";

- `See 'docker volume --help'.` (under the erro message)
- `Run 'docker volume COMMAND --help' for more information on a command.`
  (under the usage output)

In short; the output is too verbose, confusing, and doesn't provide
a good UX. Let's reduce the output produced so that the focus is on the
important information.

This patch:

- Changes the usage to the short-usage.
- Changes the error-message to mention the _full_ command instead of only
  the command after `docker` (so `docker no-such-command` instead of
  `no-such-command`).
- Prefixes the error message with the binary / root-command name
  (usually `docker:`); this is something we can still decide on, but
  it's a pattern we already use in some places. The motivation for this
  is that `docker` commands can often produce output that's a combination
  of output from the CLI itself, output from the daemon, and even output
  from the container. The `docker:` prefix helps to distinguish where
  the message originated from (the `docker` CLI in this case).
- Adds an empty line between the error-message and the "call to action"
  (`Run 'docker volume --help'...` in the example below). This helps
  separating the error message ("unkown flag") from the call-to-action.

Before this patch:

Unknown top-level command:

    docker nosuchcommand foo
    docker: 'nosuchcommand' is not a docker command.
    See 'docker --help'

Unknown sub-command:

    docker volume nosuchcommand foo

    Usage:  docker volume COMMAND

    Manage volumes

    Commands:
      create      Create a volume
      inspect     Display detailed information on one or more volumes
      ls          List volumes
      prune       Remove unused local volumes
      rm          Remove one or more volumes
      update      Update a volume (cluster volumes only)

    Run 'docker volume COMMAND --help' for more information on a command.

After this patch:

Unknown top-level command:

    docker nosuchcommand foo
    docker: unknown command: docker nosuchcommand

    Run 'docker --help' for more information

Unknown sub-command:

    docker volume nosuchcommand foo
    docker: unknown command: 'docker volume nosuchcommand'

    Usage:  docker volume COMMAND

    Run 'docker volume --help' for more information

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-05 02:28:11 +02:00
Sebastiaan van Stijn 55a1f6eb73
cli-plugins/manager: add GoDoc for getPluginDirs, defaultSystemPluginDirs
Add some documentation about their purpose, and document order of preference
when resolving plugins.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-19 14:02:40 +02:00
Sebastiaan van Stijn c07cee05e2
Update go:build comments to go1.21
Match the minimum version that's specified on our vendor.mod.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-18 12:17:13 +02:00
Sebastiaan van Stijn dfec976e84
linting: fmt.Errorf can be replaced with errors.New (perfsprint)
internal/test/cli.go:175:14: fmt.Errorf can be replaced with errors.New (perfsprint)
        return nil, fmt.Errorf("no notary client available unless defined")
                    ^
    cli/command/cli.go:318:29: fmt.Errorf can be replaced with errors.New (perfsprint)
            return docker.Endpoint{}, fmt.Errorf("no context store initialized")
                                      ^
    cli/command/container/attach.go:161:11: fmt.Errorf can be replaced with errors.New (perfsprint)
                return fmt.Errorf(result.Error.Message)
                       ^
    cli/command/container/opts.go:577:16: fmt.Errorf can be replaced with errors.New (perfsprint)
                return nil, fmt.Errorf("--health-start-period cannot be negative")
                            ^
    cli/command/container/opts.go:580:16: fmt.Errorf can be replaced with errors.New (perfsprint)
                return nil, fmt.Errorf("--health-start-interval cannot be negative")
                            ^
    cli/command/container/stats.go:221:11: fmt.Errorf can be replaced with errors.New (perfsprint)
                return fmt.Errorf("filtering is not supported when specifying a list of containers")
                       ^
    cli/command/container/attach_test.go:82:17: fmt.Errorf can be replaced with errors.New (perfsprint)
            expectedErr = fmt.Errorf("unexpected error")
                          ^
    cli/command/container/create_test.go:234:40: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
                                                       ^
    cli/command/container/list_test.go:150:17: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return nil, fmt.Errorf("error listing containers")
                                ^
    cli/command/container/rm_test.go:40:31: fmt.Errorf can be replaced with errors.New (perfsprint)
                            return errdefs.NotFound(fmt.Errorf("Error: no such container: " + container))
                                                    ^
    cli/command/container/run_test.go:138:40: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return container.CreateResponse{}, fmt.Errorf("shouldn't try to pull image")
                                                       ^
    cli/command/image/pull_test.go:115:49: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return io.NopCloser(strings.NewReader("")), fmt.Errorf("shouldn't try to pull image")
                                                                ^
    cli/command/network/connect.go:88:16: fmt.Errorf can be replaced with errors.New (perfsprint)
                return nil, fmt.Errorf("invalid key/value pair format in driver options")
                            ^
    cli/command/plugin/create_test.go:96:11: fmt.Errorf can be replaced with errors.New (perfsprint)
                return fmt.Errorf("Error creating plugin")
                       ^
    cli/command/plugin/disable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return fmt.Errorf("Error disabling plugin")
                           ^
    cli/command/plugin/enable_test.go:32:12: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return fmt.Errorf("failed to enable plugin")
                           ^
    cli/command/plugin/inspect_test.go:55:22: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return nil, nil, fmt.Errorf("error inspecting plugin")
                                     ^
    cli/command/plugin/install_test.go:43:17: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return nil, fmt.Errorf("Error installing plugin")
                                ^
    cli/command/plugin/install_test.go:51:17: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return nil, fmt.Errorf("(image) when fetching")
                                ^
    cli/command/plugin/install_test.go:95:17: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return nil, fmt.Errorf("should not try to install plugin")
                                ^
    cli/command/plugin/list_test.go:35:41: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return types.PluginsListResponse{}, fmt.Errorf("error listing plugins")
                                                        ^
    cli/command/plugin/remove_test.go:27:12: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return fmt.Errorf("Error removing plugin")
                           ^
    cli/command/registry/login_test.go:36:46: fmt.Errorf can be replaced with errors.New (perfsprint)
            return registrytypes.AuthenticateOKBody{}, fmt.Errorf("Invalid Username or Password")
                                                       ^
    cli/command/registry/login_test.go:44:46: fmt.Errorf can be replaced with errors.New (perfsprint)
            return registrytypes.AuthenticateOKBody{}, fmt.Errorf(errUnknownUser)
                                                       ^
    cli/command/system/info.go:190:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("errors pretty printing info")
                   ^
    cli/command/system/prune.go:77:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
                   ^
    cli/command/system/version_test.go:19:28: fmt.Errorf can be replaced with errors.New (perfsprint)
                return types.Version{}, fmt.Errorf("no server")
                                        ^
    cli/command/trust/key_load.go:112:22: fmt.Errorf can be replaced with errors.New (perfsprint)
                    return []byte{}, fmt.Errorf("could not decrypt key")
                                     ^
    cli/command/trust/revoke.go:44:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
                   ^
    cli/command/trust/revoke.go:105:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("no signed tags to remove")
                   ^
    cli/command/trust/signer_add.go:56:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("releases is a reserved keyword, please use a different signer name")
                   ^
    cli/command/trust/signer_add.go:60:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("path to a public key must be provided using the `--key` flag")
                   ^
    opts/config.go:71:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("source is required")
                   ^
    opts/mount.go:168:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("type is required")
                   ^
    opts/mount.go:172:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("target is required")
                   ^
    opts/network.go:90:11: fmt.Errorf can be replaced with errors.New (perfsprint)
                return fmt.Errorf("network name/id is not specified")
                       ^
    opts/network.go:129:18: fmt.Errorf can be replaced with errors.New (perfsprint)
            return "", "", fmt.Errorf("invalid key value pair format in driver options")
                           ^
    opts/opts.go:404:13: fmt.Errorf can be replaced with errors.New (perfsprint)
            return 0, fmt.Errorf("value is too precise")
                      ^
    opts/opts.go:412:18: fmt.Errorf can be replaced with errors.New (perfsprint)
            return "", "", fmt.Errorf("empty string specified for links")
                           ^
    opts/parse.go:84:37: fmt.Errorf can be replaced with errors.New (perfsprint)
            return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: no policy provided before colon")
                                              ^
    opts/parse.go:89:38: fmt.Errorf can be replaced with errors.New (perfsprint)
                return container.RestartPolicy{}, fmt.Errorf("invalid restart policy format: maximum retry count must be an integer")
                                                  ^
    opts/port.go:105:13: fmt.Errorf can be replaced with errors.New (perfsprint)
                        return fmt.Errorf("hostip is not supported")
                               ^
    opts/secret.go:70:10: fmt.Errorf can be replaced with errors.New (perfsprint)
            return fmt.Errorf("source is required")
                   ^
    opts/env_test.go:57:11: fmt.Errorf can be replaced with errors.New (perfsprint)
                err:   fmt.Errorf("invalid environment variable: =a"),
                       ^
    opts/env_test.go:93:11: fmt.Errorf can be replaced with errors.New (perfsprint)
                err:   fmt.Errorf("invalid environment variable: ="),
                       ^
    cli-plugins/manager/error_test.go:16:11: fmt.Errorf can be replaced with errors.New (perfsprint)
        inner := fmt.Errorf("testing")
                 ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-06-10 21:19:31 +02:00
Paweł Gronowski 296a6f5872
plugins/hooks: Don't show empty hooks
Don't show `Next steps:` with no messages at all when plugin returns an
unitialized value of `HookMessage` (zero-initialization sets its type to
NextSteps and empty template).

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-05-20 11:20:39 +02:00
Alano Terblanche 67aa271410
Merge pull request #5039 from Benehiko/hooks-ctx-wiring
feat: wire ctx into plugin hooks
2024-04-29 14:57:48 +02:00
Alano Terblanche 1d666b4105
feat: wire ctx into plugin hooks
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
2024-04-26 13:03:56 +02:00
Sebastiaan van Stijn 5ccb48459b
cli-plugins: PluginRunCommand: use cmd.Environ instead of os.Environ
Commit 5011759056 implemented a fix that
caused the current environment to be discarded, using `os.Environ()`.
On Windows, `os.Environ()` may produce an incorrect value for `PWD`,
for which a new function was added in go1.19;

- https://tip.golang.org/doc/go1.19#osexecpkgosexec
- https://go-review.googlesource.com/c/go/+/401340

Replace the use of `os.Environ()` with `cmd.Environ()` to address that.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-04-25 19:27:05 +02:00
Laura Brehm 43cb06e1ae
hooks: pass command execution error to plugins
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-04-22 17:12:53 +01:00
Laura Brehm 9d8320de9d
hooks: include full configured command
Before, for plugin commands, only the plugin name (such as `buildx`)
would be both included as `RootCmd` when passed to the hook plugin,
which isn't enough information for a plugin to decide whether to execute
a hook or not since plugins implement multiple varied commands (`buildx
build`, `buildx prune`, etc.).

This commit changes the hook logic to account for this situation, so
that the the entire configured hook is passed, i.e., if a user has a
hook configured for `buildx imagetools inspect` and the command
`docker buildx imagetools inspect alpine` is called, then the plugin
hooks will be passed `buildx imagetools inspect`.

This logic works for aliased commands too, so whether `docker build ...`
or `docker buildx build` is executed (unless Buildx is disabled) the
hook will be invoked with `buildx build`.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>

hooks: include full match when invoking plugins

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-04-22 13:16:26 +01:00
Laura Brehm 5011759056
hooks: set expected environment when executing
During normal plugin execution (from the CLI), the CLI configures the
plugin command it's about to execute in order to pass all environment
variables on, as well as to set the ReExec env var that informs the
plugin about how it was executed, and which plugins rely on to check
whether they are being run standalone or not.

This commit adds the same behavior to hook invocations, which is
necessary for some plugins to know that they are not running standalone
so that they expose their root command at the correct level.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-04-17 16:57:44 +01:00
Laura Brehm 867061b007
plugins/templates: break on newlines when printing hooks
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-04-15 12:59:53 +01:00
Laura Brehm c5016c6d5b
cli-plugins: Introduce support for hooks
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-03-22 17:30:18 +00:00
Tonis Tiigi 5786f20687
plugins: fix encoding for OTEL env var passed to plugin
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2024-02-28 12:43:07 -08:00
Jonathan A. Sternberg 85dcacd78f
plugins: set OTEL_RESOURCE_ATTRIBUTES when invoking a plugin
When a plugin is invoked, the docker cli will now set
`OTEL_RESOURCE_ATTRIBUTES` to pass OTEL resource attribute names to the
plugin as additional resource attributes. At the moment, the only
resource attribute passed is `cobra.command_path`.

All resource attributes passed by the CLI are prepended with the
namespace `docker.cli` to avoid clashing with existing ones the plugin
uses or ones defined by the user.

For aliased commands like the various builder commands, the command path
is overwritten to match with the original name (such as `docker
builder`) instead of the forwarded name (such as `docker buildx build`).

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2024-02-28 12:43:05 -08:00
Laura Brehm 26560ff93c
Revert "plugins: run plugin with new process group ID"
This reverts commit ef5e5fa03f.

Running new plugins under a new pgid isn't a viable solution due to
it causing issues with plugin processes attempting to read from the
TTY (see: https://github.com/moby/moby/issues/47073).

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-01-15 13:30:01 +00:00
Sebastiaan van Stijn 688de6db16
Merge pull request #4769 from laurazard/signal-handling-fix-tty
plugins: run plugin with new process group ID
2024-01-12 22:06:23 +01:00
Laura Brehm ef5e5fa03f
plugins: run plugin with new process group ID
Changes were made in 1554ac3b5f to provide
a mechanism for the CLI to notify running plugin processes that they
should exit, in order to improve the general CLI/plugin UX. The current
implementation boils down to:
1. The CLI creates a socket
2. The CLI executes the plugin
3. The plugin connects to the socket
4. (When) the CLI receives a termination signal, it uses the socket to
   notify the plugin that it should exit
5. The plugin's gets notified via the socket, and cancels it's `cmd.Context`,
   which then gets handled appropriately

This change works in most cases and fixes the issue it sets out to solve
(see: https://github.com/docker/compose/pull/11292) however, in the case
where the user has a TTY attached and the plugin is not already handling
received signals, steps 4+ changes:
4. (When) the CLI receives a termination signal, before it can use the
   socket to notify the plugin that it should exit, the plugin process
   also receives a signal due to sharing the pgid with the CLI

Since we now have a proper "job control" mechanism, we can simplify the
scenarios by executing the plugins with their own process group id,
thereby removing the "double notification" issue and making it so that
plugins can handle the same whether attached to a TTY or not.

In order to make this change "plugin-binary" backwards-compatible, in
the case that a plugin does not connect to the socket, the CLI passes
the signal to the plugin process.

Co-authored-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
Signed-off-by: Bjorn Neergaard <bjorn.neergaard@docker.com>
2024-01-12 13:53:28 -07:00
Sebastiaan van Stijn 4dc2c895b1
cli-plugins/manager: getPluginDirs: take ConfigFile as argument
Update this function to accept a smaller interface, as it doesn't need
all of "CLI".

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-01-11 18:15:30 +01:00
Sebastiaan van Stijn 70216b662d
add //go:build directives to prevent downgrading to go1.16 language
This is a follow-up to 0e73168b7e

This repository is not yet a module (i.e., does not have a `go.mod`). This
is not problematic when building the code in GOPATH or "vendor" mode, but
when using the code as a module-dependency (in module-mode), different semantics
are applied since Go1.21, which switches Go _language versions_ on a per-module,
per-package, or even per-file base.

A condensed summary of that logic [is as follows][1]:

- For modules that have a go.mod containing a go version directive; that
  version is considered a minimum _required_ version (starting with the
  go1.19.13 and go1.20.8 patch releases: before those, it was only a
  recommendation).
- For dependencies that don't have a go.mod (not a module), go language
  version go1.16 is assumed.
- Likewise, for modules that have a go.mod, but the file does not have a
  go version directive, go language version go1.16 is assumed.
- If a go.work file is present, but does not have a go version directive,
  language version go1.17 is assumed.

When switching language versions, Go _downgrades_ the language version,
which means that language features (such as generics, and `any`) are not
available, and compilation fails. For example:

    # github.com/docker/cli/cli/context/store
    /go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/storeconfig.go:6:24: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)
    /go/pkg/mod/github.com/docker/cli@v25.0.0-beta.2+incompatible/cli/context/store/store.go:74:12: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)

Note that these fallbacks are per-module, per-package, and can even be
per-file, so _(indirect) dependencies_ can still use modern language
features, as long as their respective go.mod has a version specified.

Unfortunately, these failures do not occur when building locally (using
vendor / GOPATH mode), but will affect consumers of the module.

Obviously, this situation is not ideal, and the ultimate solution is to
move to go modules (add a go.mod), but this comes with a non-insignificant
risk in other areas (due to our complex dependency tree).

We can revert to using go1.16 language features only, but this may be
limiting, and may still be problematic when (e.g.) matching signatures
of dependencies.

There is an escape hatch: adding a `//go:build` directive to files that
make use of go language features. From the [go toolchain docs][2]:

> The go line for each module sets the language version the compiler enforces
> when compiling packages in that module. The language version can be changed
> on a per-file basis by using a build constraint.
>
> For example, a module containing code that uses the Go 1.21 language version
> should have a `go.mod` file with a go line such as `go 1.21` or `go 1.21.3`.
> If a specific source file should be compiled only when using a newer Go
> toolchain, adding `//go:build go1.22` to that source file both ensures that
> only Go 1.22 and newer toolchains will compile the file and also changes
> the language version in that file to Go 1.22.

This patch adds `//go:build` directives to those files using recent additions
to the language. It's currently using go1.19 as version to match the version
in our "vendor.mod", but we can consider being more permissive ("any" requires
go1.18 or up), or more "optimistic" (force go1.21, which is the version we
currently use to build).

For completeness sake, note that any file _without_ a `//go:build` directive
will continue to use go1.16 language version when used as a module.

[1]: 58c28ba286/src/cmd/go/internal/gover/version.go (L9-L56)
[2]; https://go.dev/doc/toolchain#:~:text=The%20go%20line%20for,file%20to%20Go%201.22

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-12-14 15:03:46 +01:00
Sebastiaan van Stijn 0e73168b7e
golangci-lint: revive: enable use-any
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-11-20 19:52:46 +01:00
Sebastiaan van Stijn a2c9f3c6ce
linting: address else/if/elseif statements found by gocritic
cli/command/formatter/tabwriter/tabwriter.go:579:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
              } else {
                     ^
    cli/connhelper/connhelper.go:43:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic)
    	switch scheme := u.Scheme; scheme {
    	^
    cli/compose/loader/loader.go:666:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
    		} else {
    		       ^
    opts/hosts_test.go:173:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
    		} else {
    		       ^
    cli-plugins/manager/candidate_test.go:78:4: ifElseChain: rewrite if-else to switch statement (gocritic)
    			if tc.err != "" {
    			^
    cli/command/checkpoint/formatter.go:15:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic)
    	switch source {
    	^
    cli/command/image/formatter_history.go:25:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic)
    	switch source {
    	^
    cli/command/service/scale.go:107:2: ifElseChain: rewrite if-else to switch statement (gocritic)
    	if serviceMode.Replicated != nil {
    	^
    cli/command/service/update.go:804:9: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
    	} else {
    	       ^
    cli/command/service/update.go:222:2: ifElseChain: rewrite if-else to switch statement (gocritic)
    	if sendAuth {
    	^
    cli/command/container/formatter_diff.go:17:2: singleCaseSwitch: should rewrite switch statement to if statement (gocritic)
    	switch source {
    	^
    cli/command/container/start.go:79:2: ifElseChain: rewrite if-else to switch statement (gocritic)
    	if opts.Attach || opts.OpenStdin {
    	^
    cli/command/container/utils.go:84:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
    			} else {
    			       ^
    cli/command/container/exec_test.go:200:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
    			} else {
    			       ^
    cli/command/container/logs_test.go:52:11: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
    			} else {
    			       ^
    cli/command/container/opts_test.go:1014:10: elseif: can replace 'else {if cond {}}' with 'else if cond {}' (gocritic)
    		} else {
    		       ^
    cli/command/system/info.go:297:7: singleCaseSwitch: should rewrite switch statement to if statement (gocritic)
    						switch o.Key {
    						^
    cli/command/system/version.go:164:4: singleCaseSwitch: should rewrite switch statement to if statement (gocritic)
    			switch component.Name {
    			^
    cli/command/system/info_test.go:478:4: ifElseChain: rewrite if-else to switch statement (gocritic)
    			if tc.expectedOut != "" {
    			^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-11-20 16:02:16 +01:00
Sebastiaan van Stijn d0dee3cebe
linting: Consider pre-allocating sliceVar (prealloc)
While updating, also addressed some redundant fmt.Sprintf()

    opts/throttledevice.go:86:2: Consider pre-allocating `out` (prealloc)
        var out []string
        ^
    opts/ulimit.go:37:2: Consider pre-allocating `out` (prealloc)
        var out []string
        ^
    opts/ulimit.go:47:2: Consider pre-allocating `ulimits` (prealloc)
        var ulimits []*units.Ulimit
        ^
    opts/weightdevice.go:68:2: Consider pre-allocating `out` (prealloc)
        var out []string
        ^
    cli/context/store/metadatastore.go:96:2: Consider pre-allocating `res` (prealloc)
        var res []Metadata
        ^
    cli/context/store/store.go:127:2: Consider pre-allocating `names` (prealloc)
        var names []string
        ^
    cli/compose/loader/loader.go:223:2: Consider pre-allocating `keys` (prealloc)
        var keys []string
        ^
    cli/compose/loader/loader.go:397:2: Consider pre-allocating `services` (prealloc)
        var services []types.ServiceConfig
        ^
    cli/command/stack/loader/loader.go:63:2: Consider pre-allocating `msgs` (prealloc)
        var msgs []string
        ^
    cli/command/stack/loader/loader.go:118:2: Consider pre-allocating `configFiles` (prealloc)
        var configFiles []composetypes.ConfigFile
        ^
    cli/command/formatter/container.go:245:2: Consider pre-allocating `joinLabels` (prealloc)
        var joinLabels []string
        ^
    cli/command/formatter/container.go:265:2: Consider pre-allocating `mounts` (prealloc)
        var mounts []string
        ^
    cli/command/formatter/container.go:316:2: Consider pre-allocating `result` (prealloc)
        var result []string
        ^
    cli/command/formatter/displayutils.go:43:2: Consider pre-allocating `display` (prealloc)
        var (
        ^
    cli/command/formatter/volume.go:103:2: Consider pre-allocating `joinLabels` (prealloc)
        var joinLabels []string
        ^
    cli-plugins/manager/manager_test.go:49:2: Consider pre-allocating `dirs` (prealloc)
        var dirs []string
        ^
    cli/command/swarm/init.go:69:2: Consider pre-allocating `defaultAddrPool` (prealloc)
        var defaultAddrPool []string
        ^
    cli/command/manifest/push.go:195:2: Consider pre-allocating `blobReqs` (prealloc)
        var blobReqs []manifestBlob
        ^
    cli/command/secret/formatter.go:111:2: Consider pre-allocating `joinLabels` (prealloc)
        var joinLabels []string
        ^
    cli/command/network/formatter.go:104:2: Consider pre-allocating `joinLabels` (prealloc)
        var joinLabels []string
        ^
    cli/command/context/list.go:52:2: Consider pre-allocating `contexts` (prealloc)
        var contexts []*formatter.ClientContext
        ^
    cli/command/config/formatter.go:104:2: Consider pre-allocating `joinLabels` (prealloc)
        var joinLabels []string
        ^
    cli/command/trust/common_test.go:23:2: Consider pre-allocating `targetNames` (prealloc)
        var targetNames []string
        ^
    cli/command/service/generic_resource_opts.go:55:2: Consider pre-allocating `generic` (prealloc)
        var generic []swarm.GenericResource
        ^
    cli/command/service/generic_resource_opts.go:98:2: Consider pre-allocating `l` (prealloc)
        var l []swarm.GenericResource
        ^
    cli/command/service/opts.go:378:2: Consider pre-allocating `netAttach` (prealloc)
        var netAttach []swarm.NetworkAttachmentConfig
        ^
    cli/command/service/update.go:731:2: Consider pre-allocating `limits` (prealloc)
        var limits []*units.Ulimit
        ^
    cli/command/service/update.go:1315:2: Consider pre-allocating `newNetworks` (prealloc)
        var newNetworks []swarm.NetworkAttachmentConfig
        ^
    cli/command/service/update.go:1514:2: Consider pre-allocating `out` (prealloc)
        var out []string
        ^
    cli/compose/convert/service.go:713:2: Consider pre-allocating `ulimits` (prealloc)
        var ulimits []*units.Ulimit
        ^
    cli/compose/convert/volume.go:13:2: Consider pre-allocating `mounts` (prealloc)
        var mounts []mount.Mount
        ^
    cli/command/stack/swarm/list.go:39:2: Consider pre-allocating `stacks` (prealloc)
        var stacks []*formatter.Stack
        ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-11-20 16:02:16 +01:00
Sebastiaan van Stijn 6a50c4f700
cli-plugins: remove deprecated Metadata.Experimental
This field was marked deprecated in 977d3ae046,
which is part of Docker 20.10 and up.

This patch removes the field.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-06-12 14:53:30 +02:00
Sebastiaan van Stijn 4cf04988ae
remove uses of golang.org/x/sys/execabs
the "golang.org/x/sys/execabs" package was introduced to address a security
issue on Windows, and changing the default behavior of os/exec was considered
a breaking change. go1.19 applied the behavior that was previously implemented
in the execabs package;

from the release notes: https://go.dev/doc/go1.19#os-exec-path

> Command and LookPath no longer allow results from a PATH search to be found
> relative to the current directory. This removes a common source of security
> problems but may also break existing programs that depend on using, say,
> exec.Command("prog") to run a binary named prog (or, on Windows, prog.exe)
> in the current directory. See the os/exec package documentation for information
> about how best to update such programs.
>
> On Windows, Command and LookPath now respect the NoDefaultCurrentDirectoryInExePath
> environment variable, making it possible to disable the default implicit search
> of “.” in PATH lookups on Windows systems.

With those changes, we no longer need to use the execabs package, and we can
switch back to os/exec.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-05-26 02:03:45 +02:00
Sebastiaan van Stijn 10bade23e1
Merge pull request #4261 from thaJeztah/remove_old_buildtags
remove pre-go1.17 build-tags
2023-05-16 18:12:50 +01:00
Sebastiaan van Stijn 72e3813ab9
cli-plugins/manager: fix deprecation comment of Metadata.Experimental
This field was marked deprecated in 977d3ae046,
which is part of v20.10 and up, but the comment was missing a newline before
the deprecation message, which may be picked up by IDEs, but is not matching
the correct format, so may not be picked up by linters.

This patch fixes the format, to make sure linters pick up that the field is
deprecated.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-05-09 22:17:27 +02:00