From c9bb29115455f56fde4600ff676b7e0a795d5a2e Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 2 Jun 2025 17:28:41 +0200 Subject: [PATCH] implement `docker trust` as plugin move the `trust` subcommands to a plugin, so that the subcommands can be installed separate from the `docker trust` integration in push/pull (for situations where trust verification happens on the daemon side). make binary go build -o /usr/libexec/docker/cli-plugins/docker-trust ./cmd/docker-trust docker info Client: Version: 28.2.0-dev Context: default Debug Mode: false Plugins: buildx: Docker Buildx (Docker Inc.) Version: v0.24.0 Path: /usr/libexec/docker/cli-plugins/docker-buildx trust: Manage trust on Docker images (Docker Inc.) Version: unknown-version Path: /usr/libexec/docker/cli-plugins/docker-trust docker trust --help Usage: docker trust [OPTIONS] COMMAND Extended build capabilities with BuildKit Options: -D, --debug Enable debug logging Management Commands: key Manage keys for signing Docker images signer Manage entities who can sign Docker images Commands: inspect Return low-level information about keys and signatures revoke Remove trust for an image sign Sign an image Run 'docker trust COMMAND --help' for more information on a command. Signed-off-by: Sebastiaan van Stijn --- Makefile | 9 ++ cli/command/commands/commands.go | 1 - .../docker-trust/docs/reference}/trust.md | 0 .../docs/reference}/trust_inspect.md | 0 .../docker-trust/docs/reference}/trust_key.md | 0 .../docs/reference}/trust_key_generate.md | 0 .../docs/reference}/trust_key_load.md | 0 .../docs/reference}/trust_revoke.md | 0 .../docs/reference}/trust_sign.md | 0 .../docs/reference}/trust_signer.md | 0 .../docs/reference}/trust_signer_add.md | 0 .../docs/reference}/trust_signer_remove.md | 0 cmd/docker-trust/internal/test/cli.go | 128 ++++++++++++++++++ .../internal}/test/notary/client.go | 0 cmd/docker-trust/internal/test/randomid.go | 15 ++ .../docker-trust/internal}/trust/trust.go | 0 .../internal}/trust/trust_push.go | 0 .../docker-trust/internal}/trust/trust_tag.go | 0 .../internal}/trust/trust_test.go | 0 cmd/docker-trust/main.go | 114 ++++++++++++++++ .../command => cmd/docker-trust}/trust/cmd.go | 0 cmd/docker-trust/trust/commands.go | 81 +++++++++++ .../docker-trust}/trust/common.go | 2 +- .../docker-trust}/trust/common_test.go | 2 +- .../docker-trust}/trust/formatter.go | 0 .../docker-trust}/trust/formatter_test.go | 2 +- .../docker-trust}/trust/helpers.go | 2 +- .../docker-trust}/trust/inspect.go | 0 .../docker-trust}/trust/inspect_pretty.go | 0 .../trust/inspect_pretty_test.go | 6 +- .../docker-trust}/trust/inspect_test.go | 4 +- .../command => cmd/docker-trust}/trust/key.go | 0 .../docker-trust}/trust/key_generate.go | 2 +- .../docker-trust}/trust/key_generate_test.go | 2 +- .../docker-trust}/trust/key_load.go | 2 +- .../docker-trust}/trust/key_load_test.go | 2 +- .../docker-trust}/trust/revoke.go | 2 +- .../docker-trust}/trust/revoke_test.go | 4 +- .../docker-trust}/trust/sign.go | 2 +- .../docker-trust}/trust/sign_test.go | 6 +- .../docker-trust}/trust/signer.go | 0 .../docker-trust}/trust/signer_add.go | 2 +- .../docker-trust}/trust/signer_add_test.go | 4 +- .../docker-trust}/trust/signer_remove.go | 2 +- .../docker-trust}/trust/signer_remove_test.go | 4 +- .../testdata/trust-inspect-empty-repo.golden | 0 .../trust-inspect-full-repo-no-signers.golden | 0 ...rust-inspect-full-repo-with-signers.golden | 0 ...inspect-multiple-repos-with-signers.golden | 0 .../trust-inspect-one-tag-no-signers.golden | 0 ...inspect-pretty-full-repo-no-signers.golden | 0 ...spect-pretty-full-repo-with-signers.golden | 0 ...t-inspect-pretty-one-tag-no-signers.golden | 0 ...ct-pretty-unsigned-tag-with-signers.golden | 0 .../trust-inspect-uninitialized.golden | 0 ...t-inspect-unsigned-tag-with-signers.golden | 0 .../trust-revoke-prompt-termination.golden | 0 docs/reference/commandline/docker.md | 1 - internal/test/cli.go | 45 ++---- scripts/build/trust-plugin | 22 +++ 60 files changed, 408 insertions(+), 60 deletions(-) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_inspect.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_key.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_key_generate.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_key_load.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_revoke.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_sign.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_signer.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_signer_add.md (100%) rename {docs/reference/commandline => cmd/docker-trust/docs/reference}/trust_signer_remove.md (100%) create mode 100644 cmd/docker-trust/internal/test/cli.go rename {internal => cmd/docker-trust/internal}/test/notary/client.go (100%) create mode 100644 cmd/docker-trust/internal/test/randomid.go rename {cli => cmd/docker-trust/internal}/trust/trust.go (100%) rename {cli => cmd/docker-trust/internal}/trust/trust_push.go (100%) rename {cli => cmd/docker-trust/internal}/trust/trust_tag.go (100%) rename {cli => cmd/docker-trust/internal}/trust/trust_test.go (100%) create mode 100644 cmd/docker-trust/main.go rename {cli/command => cmd/docker-trust}/trust/cmd.go (100%) create mode 100644 cmd/docker-trust/trust/commands.go rename {cli/command => cmd/docker-trust}/trust/common.go (99%) rename {cli/command => cmd/docker-trust}/trust/common_test.go (93%) rename {cli/command => cmd/docker-trust}/trust/formatter.go (100%) rename {cli/command => cmd/docker-trust}/trust/formatter_test.go (98%) rename {cli/command => cmd/docker-trust}/trust/helpers.go (96%) rename {cli/command => cmd/docker-trust}/trust/inspect.go (100%) rename {cli/command => cmd/docker-trust}/trust/inspect_pretty.go (100%) rename {cli/command => cmd/docker-trust}/trust/inspect_pretty_test.go (99%) rename {cli/command => cmd/docker-trust}/trust/inspect_test.go (97%) rename {cli/command => cmd/docker-trust}/trust/key.go (100%) rename {cli/command => cmd/docker-trust}/trust/key_generate.go (98%) rename {cli/command => cmd/docker-trust}/trust/key_generate_test.go (98%) rename {cli/command => cmd/docker-trust}/trust/key_load.go (98%) rename {cli/command => cmd/docker-trust}/trust/key_load_test.go (99%) rename {cli/command => cmd/docker-trust}/trust/revoke.go (98%) rename {cli/command => cmd/docker-trust}/trust/revoke_test.go (97%) rename {cli/command => cmd/docker-trust}/trust/sign.go (99%) rename {cli/command => cmd/docker-trust}/trust/sign_test.go (98%) rename {cli/command => cmd/docker-trust}/trust/signer.go (100%) rename {cli/command => cmd/docker-trust}/trust/signer_add.go (98%) rename {cli/command => cmd/docker-trust}/trust/signer_add_test.go (97%) rename {cli/command => cmd/docker-trust}/trust/signer_remove.go (98%) rename {cli/command => cmd/docker-trust}/trust/signer_remove_test.go (97%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-empty-repo.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-full-repo-no-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-full-repo-with-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-multiple-repos-with-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-one-tag-no-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-uninitialized.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden (100%) rename {cli/command => cmd/docker-trust}/trust/testdata/trust-revoke-prompt-termination.golden (100%) create mode 100755 scripts/build/trust-plugin diff --git a/Makefile b/Makefile index 221a88f8e..74df05638 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,15 @@ dynbinary: ## build dynamically linked binary plugins: ## build example CLI plugins scripts/build/plugins +.PHONY: trust-plugin +trust-plugin: ## build docker-trust CLI plugins + scripts/build/trust-plugin + +.PHONY: install-trust-plugin +install-trust-plugin: trust-plugin +install-trust-plugin: ## install docker-trust CLI plugins + install -D -m 0755 "$$(readlink -f build/docker-trust)" /usr/libexec/docker/cli-plugins/docker-trust + .PHONY: vendor vendor: ## update vendor with go modules rm -rf vendor diff --git a/cli/command/commands/commands.go b/cli/command/commands/commands.go index bac0f4389..2b375938c 100644 --- a/cli/command/commands/commands.go +++ b/cli/command/commands/commands.go @@ -18,7 +18,6 @@ import ( _ "github.com/docker/cli/cli/command/stack" _ "github.com/docker/cli/cli/command/swarm" _ "github.com/docker/cli/cli/command/system" - _ "github.com/docker/cli/cli/command/trust" _ "github.com/docker/cli/cli/command/volume" "github.com/docker/cli/internal/commands" "github.com/spf13/cobra" diff --git a/docs/reference/commandline/trust.md b/cmd/docker-trust/docs/reference/trust.md similarity index 100% rename from docs/reference/commandline/trust.md rename to cmd/docker-trust/docs/reference/trust.md diff --git a/docs/reference/commandline/trust_inspect.md b/cmd/docker-trust/docs/reference/trust_inspect.md similarity index 100% rename from docs/reference/commandline/trust_inspect.md rename to cmd/docker-trust/docs/reference/trust_inspect.md diff --git a/docs/reference/commandline/trust_key.md b/cmd/docker-trust/docs/reference/trust_key.md similarity index 100% rename from docs/reference/commandline/trust_key.md rename to cmd/docker-trust/docs/reference/trust_key.md diff --git a/docs/reference/commandline/trust_key_generate.md b/cmd/docker-trust/docs/reference/trust_key_generate.md similarity index 100% rename from docs/reference/commandline/trust_key_generate.md rename to cmd/docker-trust/docs/reference/trust_key_generate.md diff --git a/docs/reference/commandline/trust_key_load.md b/cmd/docker-trust/docs/reference/trust_key_load.md similarity index 100% rename from docs/reference/commandline/trust_key_load.md rename to cmd/docker-trust/docs/reference/trust_key_load.md diff --git a/docs/reference/commandline/trust_revoke.md b/cmd/docker-trust/docs/reference/trust_revoke.md similarity index 100% rename from docs/reference/commandline/trust_revoke.md rename to cmd/docker-trust/docs/reference/trust_revoke.md diff --git a/docs/reference/commandline/trust_sign.md b/cmd/docker-trust/docs/reference/trust_sign.md similarity index 100% rename from docs/reference/commandline/trust_sign.md rename to cmd/docker-trust/docs/reference/trust_sign.md diff --git a/docs/reference/commandline/trust_signer.md b/cmd/docker-trust/docs/reference/trust_signer.md similarity index 100% rename from docs/reference/commandline/trust_signer.md rename to cmd/docker-trust/docs/reference/trust_signer.md diff --git a/docs/reference/commandline/trust_signer_add.md b/cmd/docker-trust/docs/reference/trust_signer_add.md similarity index 100% rename from docs/reference/commandline/trust_signer_add.md rename to cmd/docker-trust/docs/reference/trust_signer_add.md diff --git a/docs/reference/commandline/trust_signer_remove.md b/cmd/docker-trust/docs/reference/trust_signer_remove.md similarity index 100% rename from docs/reference/commandline/trust_signer_remove.md rename to cmd/docker-trust/docs/reference/trust_signer_remove.md diff --git a/cmd/docker-trust/internal/test/cli.go b/cmd/docker-trust/internal/test/cli.go new file mode 100644 index 000000000..20047cb0f --- /dev/null +++ b/cmd/docker-trust/internal/test/cli.go @@ -0,0 +1,128 @@ +package test + +import ( + "bytes" + "errors" + "io" + "strings" + + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/streams" + "github.com/moby/moby/client" + notaryclient "github.com/theupdateframework/notary/client" +) + +// NotaryClientFuncType defines a function that returns a fake notary client +type NotaryClientFuncType func() (notaryclient.Repository, error) + +// FakeCli emulates the default DockerCli +type FakeCli struct { + command.DockerCli + client client.APIClient + configfile *configfile.ConfigFile + out *streams.Out + outBuffer *bytes.Buffer + err *streams.Out + errBuffer *bytes.Buffer + in *streams.In + server command.ServerInfo + notaryClientFunc NotaryClientFuncType + currentContext string +} + +// NewFakeCli returns a fake for the command.Cli interface +func NewFakeCli(apiClient client.APIClient, opts ...func(*FakeCli)) *FakeCli { + outBuffer := new(bytes.Buffer) + errBuffer := new(bytes.Buffer) + c := &FakeCli{ + client: apiClient, + out: streams.NewOut(outBuffer), + outBuffer: outBuffer, + err: streams.NewOut(errBuffer), + errBuffer: errBuffer, + in: streams.NewIn(io.NopCloser(strings.NewReader(""))), + // Use an empty string for filename so that tests don't create configfiles + // Set cli.ConfigFile().Filename to a tempfile to support Save. + configfile: configfile.New(""), + currentContext: command.DefaultContextName, + } + for _, opt := range opts { + opt(c) + } + return c +} + +// SetIn sets the input of the cli to the specified ReadCloser +func (c *FakeCli) SetIn(in *streams.In) { + c.in = in +} + +// SetErr sets the stderr stream for the cli to the specified io.Writer +func (c *FakeCli) SetErr(err *streams.Out) { + c.err = err +} + +// SetOut sets the stdout stream for the cli to the specified io.Writer +func (c *FakeCli) SetOut(out *streams.Out) { + c.out = out +} + +// Client returns a docker API client +func (c *FakeCli) Client() client.APIClient { + return c.client +} + +// CurrentVersion returns the API version used by FakeCli. +// func (*FakeCli) CurrentVersion() string { +// return client.MaxAPIVersion +// } + +// Out returns the output stream (stdout) the cli should write on +func (c *FakeCli) Out() *streams.Out { + return c.out +} + +// Err returns the output stream (stderr) the cli should write on +func (c *FakeCli) Err() *streams.Out { + return c.err +} + +// In returns the input stream the cli will use +func (c *FakeCli) In() *streams.In { + return c.in +} + +// ConfigFile returns the cli configfile object (to get client configuration) +func (c *FakeCli) ConfigFile() *configfile.ConfigFile { + return c.configfile +} + +// OutBuffer returns the stdout buffer +func (c *FakeCli) OutBuffer() *bytes.Buffer { + return c.outBuffer +} + +// ErrBuffer Buffer returns the stderr buffer +func (c *FakeCli) ErrBuffer() *bytes.Buffer { + return c.errBuffer +} + +// ResetOutputBuffers resets the .OutBuffer() and.ErrBuffer() back to empty +func (c *FakeCli) ResetOutputBuffers() { + c.outBuffer.Reset() + c.errBuffer.Reset() +} + +// SetNotaryClient sets the internal getter for retrieving a NotaryClient +func (c *FakeCli) SetNotaryClient(notaryClientFunc NotaryClientFuncType) { + c.notaryClientFunc = notaryClientFunc +} + +// NotaryClient returns an err for testing unless defined +func (c *FakeCli) NotaryClient() (notaryclient.Repository, error) { + if c.notaryClientFunc != nil { + return c.notaryClientFunc() + } + return nil, errors.New("no notary client available unless defined") +} diff --git a/internal/test/notary/client.go b/cmd/docker-trust/internal/test/notary/client.go similarity index 100% rename from internal/test/notary/client.go rename to cmd/docker-trust/internal/test/notary/client.go diff --git a/cmd/docker-trust/internal/test/randomid.go b/cmd/docker-trust/internal/test/randomid.go new file mode 100644 index 000000000..fd5ed2ca7 --- /dev/null +++ b/cmd/docker-trust/internal/test/randomid.go @@ -0,0 +1,15 @@ +package test + +import ( + "crypto/rand" + "encoding/hex" +) + +// RandomID returns a unique, 64-character ID consisting of a-z, 0-9. +func RandomID() string { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + panic(err) // This shouldn't happen + } + return hex.EncodeToString(b) +} diff --git a/cli/trust/trust.go b/cmd/docker-trust/internal/trust/trust.go similarity index 100% rename from cli/trust/trust.go rename to cmd/docker-trust/internal/trust/trust.go diff --git a/cli/trust/trust_push.go b/cmd/docker-trust/internal/trust/trust_push.go similarity index 100% rename from cli/trust/trust_push.go rename to cmd/docker-trust/internal/trust/trust_push.go diff --git a/cli/trust/trust_tag.go b/cmd/docker-trust/internal/trust/trust_tag.go similarity index 100% rename from cli/trust/trust_tag.go rename to cmd/docker-trust/internal/trust/trust_tag.go diff --git a/cli/trust/trust_test.go b/cmd/docker-trust/internal/trust/trust_test.go similarity index 100% rename from cli/trust/trust_test.go rename to cmd/docker-trust/internal/trust/trust_test.go diff --git a/cmd/docker-trust/main.go b/cmd/docker-trust/main.go new file mode 100644 index 000000000..bf6bc2e01 --- /dev/null +++ b/cmd/docker-trust/main.go @@ -0,0 +1,114 @@ +package main + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "syscall" + + cerrdefs "github.com/containerd/errdefs" + "github.com/docker/cli/cli" + "github.com/docker/cli/cli-plugins/metadata" + "github.com/docker/cli/cli-plugins/plugin" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/version" + "github.com/docker/cli/cmd/docker-trust/trust" + "go.opentelemetry.io/otel" +) + +func runStandalone(cmd *command.DockerCli) error { + defer flushMetrics(cmd) + executable := os.Args[0] + rootCmd := trust.NewRootCmd(filepath.Base(executable), false, cmd) + return rootCmd.Execute() +} + +// flushMetrics will manually flush metrics from the configured +// meter provider. This is needed when running in standalone mode +// because the meter provider is initialized by the cli library, +// but the mechanism for forcing it to report is not presently +// exposed and not invoked when run in standalone mode. +// There are plans to fix that in the next release, but this is +// needed temporarily until the API for this is more thorough. +func flushMetrics(cmd *command.DockerCli) { + if mp, ok := cmd.MeterProvider().(command.MeterProvider); ok { + if err := mp.ForceFlush(context.Background()); err != nil { + otel.Handle(err) + } + } +} + +func runPlugin(cmd *command.DockerCli) error { + rootCmd := trust.NewRootCmd("trust", true, cmd) + return plugin.RunPlugin(cmd, rootCmd, metadata.Metadata{ + SchemaVersion: "0.1.0", + Vendor: "Docker Inc.", + Version: version.Version, + }) +} + +func run(cmd *command.DockerCli) error { + if plugin.RunningStandalone() { + return runStandalone(cmd) + } + return runPlugin(cmd) +} + +type errCtxSignalTerminated struct { + signal os.Signal +} + +func (errCtxSignalTerminated) Error() string { + return "" +} + +func main() { + cmd, err := command.NewDockerCli() + if err != nil { + _, _ = fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + + if err = run(cmd); err == nil { + return + } + + if errors.As(err, &errCtxSignalTerminated{}) { + os.Exit(getExitCode(err)) + } + + if !cerrdefs.IsCanceled(err) { + if err.Error() != "" { + _, _ = fmt.Fprintln(cmd.Err(), err) + } + os.Exit(getExitCode(err)) + } +} + +// getExitCode returns the exit-code to use for the given error. +// If err is a [cli.StatusError] and has a StatusCode set, it uses the +// status-code from it, otherwise it returns "1" for any error. +func getExitCode(err error) int { + if err == nil { + return 0 + } + + var userTerminatedErr errCtxSignalTerminated + if errors.As(err, &userTerminatedErr) { + s, ok := userTerminatedErr.signal.(syscall.Signal) + if !ok { + return 1 + } + return 128 + int(s) + } + + var stErr cli.StatusError + if errors.As(err, &stErr) && stErr.StatusCode != 0 { // FIXME(thaJeztah): StatusCode should never be used with a zero status-code. Check if we do this anywhere. + return stErr.StatusCode + } + + // No status-code provided; all errors should have a non-zero exit code. + return 1 +} diff --git a/cli/command/trust/cmd.go b/cmd/docker-trust/trust/cmd.go similarity index 100% rename from cli/command/trust/cmd.go rename to cmd/docker-trust/trust/cmd.go diff --git a/cmd/docker-trust/trust/commands.go b/cmd/docker-trust/trust/commands.go new file mode 100644 index 000000000..cb0fc6fc4 --- /dev/null +++ b/cmd/docker-trust/trust/commands.go @@ -0,0 +1,81 @@ +package trust + +import ( + "fmt" + + "github.com/docker/cli-docs-tool/annotation" + "github.com/docker/cli/cli" + "github.com/docker/cli/cli-plugins/plugin" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/debug" + cliflags "github.com/docker/cli/cli/flags" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func NewRootCmd(name string, isPlugin bool, dockerCLI *command.DockerCli) *cobra.Command { + var opt rootOptions + cmd := &cobra.Command{ + Use: name, + Short: "Manage trust on Docker images", + Long: `Extended build capabilities with BuildKit`, + Annotations: map[string]string{ + annotation.CodeDelimiter: `"`, + }, + CompletionOptions: cobra.CompletionOptions{ + HiddenDefaultCmd: true, + }, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + if opt.debug { + debug.Enable() + } + // cmd.SetContext(appcontext.Context()) + if !isPlugin { + // InstallFlags and SetDefaultOptions are necessary to match + // the plugin mode behavior to handle env vars such as + // DOCKER_TLS, DOCKER_TLS_VERIFY, ... and we also need to use a + // new flagset to avoid conflict with the global debug flag + // that we already handle in the root command otherwise it + // would panic. + nflags := pflag.NewFlagSet(cmd.DisplayName(), pflag.ContinueOnError) + options := cliflags.NewClientOptions() + options.InstallFlags(nflags) + options.SetDefaultOptions(nflags) + return dockerCLI.Initialize(options) + } + return plugin.PersistentPreRunE(cmd, args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return cmd.Help() + } + _ = cmd.Help() + return cli.StatusError{ + StatusCode: 1, + Status: fmt.Sprintf("ERROR: unknown command: %q", args[0]), + } + }, + } + if !isPlugin { + // match plugin behavior for standalone mode + // https://github.com/docker/cli/blob/6c9eb708fa6d17765d71965f90e1c59cea686ee9/cli-plugins/plugin/plugin.go#L117-L127 + cmd.SilenceUsage = true + cmd.SilenceErrors = true + cmd.TraverseChildren = true + cmd.DisableFlagsInUseLine = true + } + + cmd.AddCommand( + newRevokeCommand(dockerCLI), + newSignCommand(dockerCLI), + newTrustKeyCommand(dockerCLI), + newTrustSignerCommand(dockerCLI), + newInspectCommand(dockerCLI), + ) + + return cmd +} + +type rootOptions struct { + debug bool +} diff --git a/cli/command/trust/common.go b/cmd/docker-trust/trust/common.go similarity index 99% rename from cli/command/trust/common.go rename to cmd/docker-trust/trust/common.go index 2b8ad9dae..f05e57727 100644 --- a/cli/command/trust/common.go +++ b/cmd/docker-trust/trust/common.go @@ -10,7 +10,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/fvbommel/sortorder" registrytypes "github.com/moby/moby/api/types/registry" "github.com/sirupsen/logrus" diff --git a/cli/command/trust/common_test.go b/cmd/docker-trust/trust/common_test.go similarity index 93% rename from cli/command/trust/common_test.go rename to cmd/docker-trust/trust/common_test.go index 13f24f502..a2e5f3b5f 100644 --- a/cli/command/trust/common_test.go +++ b/cmd/docker-trust/trust/common_test.go @@ -3,7 +3,7 @@ package trust import ( "testing" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/tuf/data" "gotest.tools/v3/assert" diff --git a/cli/command/trust/formatter.go b/cmd/docker-trust/trust/formatter.go similarity index 100% rename from cli/command/trust/formatter.go rename to cmd/docker-trust/trust/formatter.go diff --git a/cli/command/trust/formatter_test.go b/cmd/docker-trust/trust/formatter_test.go similarity index 98% rename from cli/command/trust/formatter_test.go rename to cmd/docker-trust/trust/formatter_test.go index 413a2605a..49c8ba24c 100644 --- a/cli/command/trust/formatter_test.go +++ b/cmd/docker-trust/trust/formatter_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/docker/cli/cli/command/formatter" - "github.com/docker/cli/internal/test" + "github.com/docker/cli/cmd/docker-trust/internal/test" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" ) diff --git a/cli/command/trust/helpers.go b/cmd/docker-trust/trust/helpers.go similarity index 96% rename from cli/command/trust/helpers.go rename to cmd/docker-trust/trust/helpers.go index 9f4e58b8b..ad7fa7f5b 100644 --- a/cli/command/trust/helpers.go +++ b/cmd/docker-trust/trust/helpers.go @@ -3,7 +3,7 @@ package trust import ( "strings" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/tuf/data" ) diff --git a/cli/command/trust/inspect.go b/cmd/docker-trust/trust/inspect.go similarity index 100% rename from cli/command/trust/inspect.go rename to cmd/docker-trust/trust/inspect.go diff --git a/cli/command/trust/inspect_pretty.go b/cmd/docker-trust/trust/inspect_pretty.go similarity index 100% rename from cli/command/trust/inspect_pretty.go rename to cmd/docker-trust/trust/inspect_pretty.go diff --git a/cli/command/trust/inspect_pretty_test.go b/cmd/docker-trust/trust/inspect_pretty_test.go similarity index 99% rename from cli/command/trust/inspect_pretty_test.go rename to cmd/docker-trust/trust/inspect_pretty_test.go index d23f0aaeb..e083985d4 100644 --- a/cli/command/trust/inspect_pretty_test.go +++ b/cmd/docker-trust/trust/inspect_pretty_test.go @@ -8,9 +8,9 @@ import ( "net/http" "testing" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/test" + notaryfake "github.com/docker/cli/cmd/docker-trust/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/moby/moby/client" "github.com/theupdateframework/notary" notaryclient "github.com/theupdateframework/notary/client" diff --git a/cli/command/trust/inspect_test.go b/cmd/docker-trust/trust/inspect_test.go similarity index 97% rename from cli/command/trust/inspect_test.go rename to cmd/docker-trust/trust/inspect_test.go index 1f0333461..60e771a5a 100644 --- a/cli/command/trust/inspect_test.go +++ b/cmd/docker-trust/trust/inspect_test.go @@ -4,8 +4,8 @@ import ( "io" "testing" - "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/test" + "github.com/docker/cli/cmd/docker-trust/internal/test/notary" "github.com/theupdateframework/notary/client" "gotest.tools/v3/assert" "gotest.tools/v3/golden" diff --git a/cli/command/trust/key.go b/cmd/docker-trust/trust/key.go similarity index 100% rename from cli/command/trust/key.go rename to cmd/docker-trust/trust/key.go diff --git a/cli/command/trust/key_generate.go b/cmd/docker-trust/trust/key_generate.go similarity index 98% rename from cli/command/trust/key_generate.go rename to cmd/docker-trust/trust/key_generate.go index 553a7e7b2..cf42ffc9b 100644 --- a/cli/command/trust/key_generate.go +++ b/cmd/docker-trust/trust/key_generate.go @@ -9,7 +9,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/docker/cli/internal/lazyregexp" "github.com/spf13/cobra" "github.com/theupdateframework/notary" diff --git a/cli/command/trust/key_generate_test.go b/cmd/docker-trust/trust/key_generate_test.go similarity index 98% rename from cli/command/trust/key_generate_test.go rename to cmd/docker-trust/trust/key_generate_test.go index 1efd1d312..f971dc238 100644 --- a/cli/command/trust/key_generate_test.go +++ b/cmd/docker-trust/trust/key_generate_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/docker/cli/cli/config" - "github.com/docker/cli/internal/test" + "github.com/docker/cli/cmd/docker-trust/internal/test" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/trustmanager" tufutils "github.com/theupdateframework/notary/tuf/utils" diff --git a/cli/command/trust/key_load.go b/cmd/docker-trust/trust/key_load.go similarity index 98% rename from cli/command/trust/key_load.go rename to cmd/docker-trust/trust/key_load.go index 382f82ceb..7234dbc1a 100644 --- a/cli/command/trust/key_load.go +++ b/cmd/docker-trust/trust/key_load.go @@ -11,7 +11,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/spf13/cobra" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/storage" diff --git a/cli/command/trust/key_load_test.go b/cmd/docker-trust/trust/key_load_test.go similarity index 99% rename from cli/command/trust/key_load_test.go rename to cmd/docker-trust/trust/key_load_test.go index 2c004d388..3b3ec08e1 100644 --- a/cli/command/trust/key_load_test.go +++ b/cmd/docker-trust/trust/key_load_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/docker/cli/cli/config" - "github.com/docker/cli/internal/test" + "github.com/docker/cli/cmd/docker-trust/internal/test" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/storage" "github.com/theupdateframework/notary/trustmanager" diff --git a/cli/command/trust/revoke.go b/cmd/docker-trust/trust/revoke.go similarity index 98% rename from cli/command/trust/revoke.go rename to cmd/docker-trust/trust/revoke.go index 1db6acca5..b005d341b 100644 --- a/cli/command/trust/revoke.go +++ b/cmd/docker-trust/trust/revoke.go @@ -7,7 +7,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/docker/cli/internal/prompt" "github.com/spf13/cobra" "github.com/theupdateframework/notary/client" diff --git a/cli/command/trust/revoke_test.go b/cmd/docker-trust/trust/revoke_test.go similarity index 97% rename from cli/command/trust/revoke_test.go rename to cmd/docker-trust/trust/revoke_test.go index 8ec078c8e..7b3e887e6 100644 --- a/cli/command/trust/revoke_test.go +++ b/cmd/docker-trust/trust/revoke_test.go @@ -5,8 +5,8 @@ import ( "io" "testing" - "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/test" + "github.com/docker/cli/cmd/docker-trust/internal/test/notary" "github.com/theupdateframework/notary/client" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" diff --git a/cli/command/trust/sign.go b/cmd/docker-trust/trust/sign.go similarity index 99% rename from cli/command/trust/sign.go rename to cmd/docker-trust/trust/sign.go index 8d3489ec9..6b4b933f7 100644 --- a/cli/command/trust/sign.go +++ b/cmd/docker-trust/trust/sign.go @@ -12,7 +12,7 @@ import ( "github.com/distribution/reference" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/moby/moby/api/pkg/authconfig" "github.com/moby/moby/client" "github.com/spf13/cobra" diff --git a/cli/command/trust/sign_test.go b/cmd/docker-trust/trust/sign_test.go similarity index 98% rename from cli/command/trust/sign_test.go rename to cmd/docker-trust/trust/sign_test.go index 635dc30f2..e7604c958 100644 --- a/cli/command/trust/sign_test.go +++ b/cmd/docker-trust/trust/sign_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/docker/cli/cli/config" - "github.com/docker/cli/cli/trust" - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/test" + notaryfake "github.com/docker/cli/cmd/docker-trust/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/client/changelist" diff --git a/cli/command/trust/signer.go b/cmd/docker-trust/trust/signer.go similarity index 100% rename from cli/command/trust/signer.go rename to cmd/docker-trust/trust/signer.go diff --git a/cli/command/trust/signer_add.go b/cmd/docker-trust/trust/signer_add.go similarity index 98% rename from cli/command/trust/signer_add.go rename to cmd/docker-trust/trust/signer_add.go index 853250a63..0d94b9c46 100644 --- a/cli/command/trust/signer_add.go +++ b/cmd/docker-trust/trust/signer_add.go @@ -11,7 +11,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/docker/cli/internal/lazyregexp" "github.com/docker/cli/opts" "github.com/spf13/cobra" diff --git a/cli/command/trust/signer_add_test.go b/cmd/docker-trust/trust/signer_add_test.go similarity index 97% rename from cli/command/trust/signer_add_test.go rename to cmd/docker-trust/trust/signer_add_test.go index 31fd98361..02858e2dd 100644 --- a/cli/command/trust/signer_add_test.go +++ b/cmd/docker-trust/trust/signer_add_test.go @@ -9,8 +9,8 @@ import ( "testing" "github.com/docker/cli/cli/config" - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/test" + notaryfake "github.com/docker/cli/cmd/docker-trust/internal/test/notary" "github.com/theupdateframework/notary" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" diff --git a/cli/command/trust/signer_remove.go b/cmd/docker-trust/trust/signer_remove.go similarity index 98% rename from cli/command/trust/signer_remove.go rename to cmd/docker-trust/trust/signer_remove.go index 27f180a9a..e920ec645 100644 --- a/cli/command/trust/signer_remove.go +++ b/cmd/docker-trust/trust/signer_remove.go @@ -8,7 +8,7 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/trust" + "github.com/docker/cli/cmd/docker-trust/internal/trust" "github.com/docker/cli/internal/prompt" "github.com/spf13/cobra" "github.com/theupdateframework/notary/client" diff --git a/cli/command/trust/signer_remove_test.go b/cmd/docker-trust/trust/signer_remove_test.go similarity index 97% rename from cli/command/trust/signer_remove_test.go rename to cmd/docker-trust/trust/signer_remove_test.go index 402ddda99..64cede214 100644 --- a/cli/command/trust/signer_remove_test.go +++ b/cmd/docker-trust/trust/signer_remove_test.go @@ -5,8 +5,8 @@ import ( "io" "testing" - "github.com/docker/cli/internal/test" - notaryfake "github.com/docker/cli/internal/test/notary" + "github.com/docker/cli/cmd/docker-trust/internal/test" + notaryfake "github.com/docker/cli/cmd/docker-trust/internal/test/notary" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/tuf/data" "gotest.tools/v3/assert" diff --git a/cli/command/trust/testdata/trust-inspect-empty-repo.golden b/cmd/docker-trust/trust/testdata/trust-inspect-empty-repo.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-empty-repo.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-empty-repo.golden diff --git a/cli/command/trust/testdata/trust-inspect-full-repo-no-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-full-repo-no-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-full-repo-no-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-full-repo-no-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-full-repo-with-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-full-repo-with-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-full-repo-with-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-full-repo-with-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-multiple-repos-with-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-multiple-repos-with-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-multiple-repos-with-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-multiple-repos-with-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-one-tag-no-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-one-tag-no-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-one-tag-no-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-one-tag-no-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-pretty-full-repo-no-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-pretty-full-repo-with-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-pretty-one-tag-no-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-pretty-unsigned-tag-with-signers.golden diff --git a/cli/command/trust/testdata/trust-inspect-uninitialized.golden b/cmd/docker-trust/trust/testdata/trust-inspect-uninitialized.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-uninitialized.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-uninitialized.golden diff --git a/cli/command/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden b/cmd/docker-trust/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden similarity index 100% rename from cli/command/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden rename to cmd/docker-trust/trust/testdata/trust-inspect-unsigned-tag-with-signers.golden diff --git a/cli/command/trust/testdata/trust-revoke-prompt-termination.golden b/cmd/docker-trust/trust/testdata/trust-revoke-prompt-termination.golden similarity index 100% rename from cli/command/trust/testdata/trust-revoke-prompt-termination.golden rename to cmd/docker-trust/trust/testdata/trust-revoke-prompt-termination.golden diff --git a/docs/reference/commandline/docker.md b/docs/reference/commandline/docker.md index 92be79281..6ee6c2b76 100644 --- a/docs/reference/commandline/docker.md +++ b/docs/reference/commandline/docker.md @@ -59,7 +59,6 @@ The base command for the Docker CLI. | [`system`](system.md) | Manage Docker | | [`tag`](tag.md) | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE | | [`top`](top.md) | Display the running processes of a container | -| [`trust`](trust.md) | Manage trust on Docker images | | [`unpause`](unpause.md) | Unpause all processes within one or more containers | | [`update`](update.md) | Update configuration of one or more containers | | [`version`](version.md) | Show the Docker version information | diff --git a/internal/test/cli.go b/internal/test/cli.go index 175a10d17..4a5b0bf9a 100644 --- a/internal/test/cli.go +++ b/internal/test/cli.go @@ -2,7 +2,6 @@ package test import ( "bytes" - "errors" "io" "strings" @@ -14,29 +13,24 @@ import ( "github.com/docker/cli/cli/streams" "github.com/docker/cli/internal/registryclient" "github.com/moby/moby/client" - notaryclient "github.com/theupdateframework/notary/client" ) -// NotaryClientFuncType defines a function that returns a fake notary client -type NotaryClientFuncType func() (notaryclient.Repository, error) - // FakeCli emulates the default DockerCli type FakeCli struct { command.DockerCli - client client.APIClient - configfile *configfile.ConfigFile - out *streams.Out - outBuffer *bytes.Buffer - err *streams.Out - errBuffer *bytes.Buffer - in *streams.In - server command.ServerInfo - notaryClientFunc NotaryClientFuncType - manifestStore manifeststore.Store - registryClient registryclient.RegistryClient - contextStore store.Store - currentContext string - dockerEndpoint docker.Endpoint + client client.APIClient + configfile *configfile.ConfigFile + out *streams.Out + outBuffer *bytes.Buffer + err *streams.Out + errBuffer *bytes.Buffer + in *streams.In + server command.ServerInfo + manifestStore manifeststore.Store + registryClient registryclient.RegistryClient + contextStore store.Store + currentContext string + dockerEndpoint docker.Endpoint } // NewFakeCli returns a fake for the command.Cli interface @@ -162,19 +156,6 @@ func (c *FakeCli) ResetOutputBuffers() { c.errBuffer.Reset() } -// SetNotaryClient sets the internal getter for retrieving a NotaryClient -func (c *FakeCli) SetNotaryClient(notaryClientFunc NotaryClientFuncType) { - c.notaryClientFunc = notaryClientFunc -} - -// NotaryClient returns an err for testing unless defined -func (c *FakeCli) NotaryClient() (notaryclient.Repository, error) { - if c.notaryClientFunc != nil { - return c.notaryClientFunc() - } - return nil, errors.New("no notary client available unless defined") -} - // ManifestStore returns a fake store used for testing func (c *FakeCli) ManifestStore() manifeststore.Store { return c.manifestStore diff --git a/scripts/build/trust-plugin b/scripts/build/trust-plugin new file mode 100755 index 000000000..beb8487b2 --- /dev/null +++ b/scripts/build/trust-plugin @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# +# Build plugins examples for the host OS/ARCH +# + +set -eu -o pipefail + +# Disable CGO - we don't need it for these plugins. +# +# Important: this must be done before sourcing "./scripts/build/.variables", +# because some other variables are conditionally set whether CGO is enabled. +export CGO_ENABLED=0 + +source ./scripts/build/.variables + +TARGET_PLUGIN="$(dirname "${TARGET}")/docker-trust-${GOOS}-${GOARCH}" +mkdir -p "$(dirname "${TARGET_PLUGIN}")" + +echo "Building $GO_LINKMODE $(basename "${TARGET_PLUGIN}")" +(set -x ; GO111MODULE=auto go build -o "${TARGET_PLUGIN}" -tags "${GO_BUILDTAGS}" -ldflags "${GO_LDFLAGS}" ${GO_BUILDMODE} "github.com/docker/cli/cmd/docker-trust") + +ln -sf "$(basename "${TARGET_PLUGIN}")" "$(dirname "${TARGET}")/docker-trust"