Skip to content

Commit ea47850

Browse files
committed
implement docker trust as plugin
Just a quick experiment to see if we can 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 <[email protected]>
1 parent 73c56e4 commit ea47850

40 files changed

+196
-3
lines changed

cli/command/commands/commands.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"github.com/docker/cli/cli/command/stack"
2121
"github.com/docker/cli/cli/command/swarm"
2222
"github.com/docker/cli/cli/command/system"
23-
"github.com/docker/cli/cli/command/trust"
2423
"github.com/docker/cli/cli/command/volume"
2524
"github.com/spf13/cobra"
2625
)
@@ -53,7 +52,6 @@ func AddCommands(cmd *cobra.Command, dockerCli command.Cli) {
5352
network.NewNetworkCommand(dockerCli),
5453
plugin.NewPluginCommand(dockerCli),
5554
system.NewSystemCommand(dockerCli),
56-
trust.NewTrustCommand(dockerCli),
5755
volume.NewVolumeCommand(dockerCli),
5856

5957
// orchestration (swarm) commands

cmd/docker-trust/main.go

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
"syscall"
10+
11+
cerrdefs "github.com/containerd/errdefs"
12+
"github.com/docker/cli/cli"
13+
"github.com/docker/cli/cli-plugins/metadata"
14+
"github.com/docker/cli/cli-plugins/plugin"
15+
"github.com/docker/cli/cli/command"
16+
"github.com/docker/cli/cli/version"
17+
"github.com/docker/cli/cmd/docker-trust/trust"
18+
"go.opentelemetry.io/otel"
19+
)
20+
21+
func runStandalone(cmd *command.DockerCli) error {
22+
defer flushMetrics(cmd)
23+
executable := os.Args[0]
24+
rootCmd := trust.NewRootCmd(filepath.Base(executable), false, cmd)
25+
return rootCmd.Execute()
26+
}
27+
28+
// flushMetrics will manually flush metrics from the configured
29+
// meter provider. This is needed when running in standalone mode
30+
// because the meter provider is initialized by the cli library,
31+
// but the mechanism for forcing it to report is not presently
32+
// exposed and not invoked when run in standalone mode.
33+
// There are plans to fix that in the next release, but this is
34+
// needed temporarily until the API for this is more thorough.
35+
func flushMetrics(cmd *command.DockerCli) {
36+
if mp, ok := cmd.MeterProvider().(command.MeterProvider); ok {
37+
if err := mp.ForceFlush(context.Background()); err != nil {
38+
otel.Handle(err)
39+
}
40+
}
41+
}
42+
43+
func runPlugin(cmd *command.DockerCli) error {
44+
rootCmd := trust.NewRootCmd("trust", true, cmd)
45+
return plugin.RunPlugin(cmd, rootCmd, metadata.Metadata{
46+
SchemaVersion: "0.1.0",
47+
Vendor: "Docker Inc.",
48+
Version: version.Version,
49+
})
50+
}
51+
52+
func run(cmd *command.DockerCli) error {
53+
if plugin.RunningStandalone() {
54+
return runStandalone(cmd)
55+
}
56+
return runPlugin(cmd)
57+
}
58+
59+
type errCtxSignalTerminated struct {
60+
signal os.Signal
61+
}
62+
63+
func (errCtxSignalTerminated) Error() string {
64+
return ""
65+
}
66+
67+
func main() {
68+
cmd, err := command.NewDockerCli()
69+
if err != nil {
70+
_, _ = fmt.Fprintln(os.Stderr, err)
71+
os.Exit(1)
72+
}
73+
74+
if err = run(cmd); err == nil {
75+
return
76+
}
77+
78+
if errors.As(err, &errCtxSignalTerminated{}) {
79+
os.Exit(getExitCode(err))
80+
}
81+
82+
if !cerrdefs.IsCanceled(err) {
83+
if err.Error() != "" {
84+
_, _ = fmt.Fprintln(cmd.Err(), err)
85+
}
86+
os.Exit(getExitCode(err))
87+
}
88+
}
89+
90+
// getExitCode returns the exit-code to use for the given error.
91+
// If err is a [cli.StatusError] and has a StatusCode set, it uses the
92+
// status-code from it, otherwise it returns "1" for any error.
93+
func getExitCode(err error) int {
94+
if err == nil {
95+
return 0
96+
}
97+
98+
var userTerminatedErr errCtxSignalTerminated
99+
if errors.As(err, &userTerminatedErr) {
100+
s, ok := userTerminatedErr.signal.(syscall.Signal)
101+
if !ok {
102+
return 1
103+
}
104+
return 128 + int(s)
105+
}
106+
107+
var stErr cli.StatusError
108+
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.
109+
return stErr.StatusCode
110+
}
111+
112+
// No status-code provided; all errors should have a non-zero exit code.
113+
return 1
114+
}
File renamed without changes.

cmd/docker-trust/trust/commands.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package trust
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/docker/cli-docs-tool/annotation"
7+
"github.com/docker/cli/cli"
8+
"github.com/docker/cli/cli-plugins/plugin"
9+
"github.com/docker/cli/cli/command"
10+
"github.com/docker/cli/cli/debug"
11+
cliflags "github.com/docker/cli/cli/flags"
12+
"github.com/spf13/cobra"
13+
"github.com/spf13/pflag"
14+
)
15+
16+
func NewRootCmd(name string, isPlugin bool, dockerCLI *command.DockerCli) *cobra.Command {
17+
var opt rootOptions
18+
cmd := &cobra.Command{
19+
Use: name,
20+
Short: "Manage trust on Docker images",
21+
Long: `Extended build capabilities with BuildKit`,
22+
Annotations: map[string]string{
23+
annotation.CodeDelimiter: `"`,
24+
},
25+
CompletionOptions: cobra.CompletionOptions{
26+
HiddenDefaultCmd: true,
27+
},
28+
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
29+
if opt.debug {
30+
debug.Enable()
31+
}
32+
// cmd.SetContext(appcontext.Context())
33+
if !isPlugin {
34+
// InstallFlags and SetDefaultOptions are necessary to match
35+
// the plugin mode behavior to handle env vars such as
36+
// DOCKER_TLS, DOCKER_TLS_VERIFY, ... and we also need to use a
37+
// new flagset to avoid conflict with the global debug flag
38+
// that we already handle in the root command otherwise it
39+
// would panic.
40+
nflags := pflag.NewFlagSet(cmd.DisplayName(), pflag.ContinueOnError)
41+
options := cliflags.NewClientOptions()
42+
options.InstallFlags(nflags)
43+
options.SetDefaultOptions(nflags)
44+
return dockerCLI.Initialize(options)
45+
}
46+
return plugin.PersistentPreRunE(cmd, args)
47+
},
48+
RunE: func(cmd *cobra.Command, args []string) error {
49+
if len(args) == 0 {
50+
return cmd.Help()
51+
}
52+
_ = cmd.Help()
53+
return cli.StatusError{
54+
StatusCode: 1,
55+
Status: fmt.Sprintf("ERROR: unknown command: %q", args[0]),
56+
}
57+
},
58+
}
59+
if !isPlugin {
60+
// match plugin behavior for standalone mode
61+
// https://github.com/docker/cli/blob/6c9eb708fa6d17765d71965f90e1c59cea686ee9/cli-plugins/plugin/plugin.go#L117-L127
62+
cmd.SilenceUsage = true
63+
cmd.SilenceErrors = true
64+
cmd.TraverseChildren = true
65+
cmd.DisableFlagsInUseLine = true
66+
cli.DisableFlagsInUseLine(cmd)
67+
}
68+
69+
cmd.AddCommand(
70+
newRevokeCommand(dockerCLI),
71+
newSignCommand(dockerCLI),
72+
newTrustKeyCommand(dockerCLI),
73+
newTrustSignerCommand(dockerCLI),
74+
newInspectCommand(dockerCLI),
75+
)
76+
77+
return cmd
78+
}
79+
80+
type rootOptions struct {
81+
debug bool
82+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)