cli/command/plugin: remove DCT

Plugins are not widely used, and there's no known plugins that use
content-trust. We're working on updating the authentication stack
in the CLI, and the trust implementation hinders us in making
changes, so removing parts that are not high-priority (ahead of
full deprecation of DCT).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2025-07-25 23:39:17 +02:00
parent 845870e669
commit 3f5b1bdd32
8 changed files with 33 additions and 141 deletions

View File

@ -8,7 +8,6 @@ import (
"github.com/distribution/reference"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/image"
"github.com/docker/cli/internal/jsonstream"
"github.com/docker/cli/internal/prompt"
"github.com/docker/cli/internal/registry"
@ -17,7 +16,6 @@ import (
"github.com/moby/moby/client"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
type pluginOptions struct {
@ -27,12 +25,6 @@ type pluginOptions struct {
disable bool
args []string
skipRemoteCheck bool
untrusted bool
}
func loadPullFlags(dockerCli command.Cli, opts *pluginOptions, flags *pflag.FlagSet) {
flags.BoolVar(&opts.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin")
command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
}
func newInstallCommand(dockerCli command.Cli) *cobra.Command {
@ -51,13 +43,15 @@ func newInstallCommand(dockerCli command.Cli) *cobra.Command {
}
flags := cmd.Flags()
loadPullFlags(dockerCli, &options, flags)
flags.BoolVar(&options.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin")
flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install")
flags.StringVar(&options.localName, "alias", "", "Local name for plugin")
flags.Bool("disable-content-trust", dockerCli.ContentTrustEnabled(), "Skip image verification (deprecated)")
_ = flags.MarkHidden("disable-content-trust")
return cmd
}
func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOptions) (client.PluginInstallOptions, error) {
func buildPullConfig(dockerCLI command.Cli, opts pluginOptions) (client.PluginInstallOptions, error) {
// Names with both tag and digest will be treated by the daemon
// as a pull by digest with a local name for the tag
// (if no local name is provided).
@ -66,40 +60,21 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti
return client.PluginInstallOptions{}, err
}
indexInfo := registry.NewIndexInfo(ref)
remote := ref.String()
_, isCanonical := ref.(reference.Canonical)
if !opts.untrusted && !isCanonical {
ref = reference.TagNameOnly(ref)
nt, ok := ref.(reference.NamedTagged)
if !ok {
return client.PluginInstallOptions{}, errors.Errorf("invalid name: %s", ref.String())
}
trusted, err := image.TrustedReference(ctx, dockerCli, nt)
if err != nil {
return client.PluginInstallOptions{}, err
}
remote = reference.FamiliarString(trusted)
}
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), indexInfo)
authConfig := command.ResolveAuthConfig(dockerCLI.ConfigFile(), registry.NewIndexInfo(ref))
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
if err != nil {
return client.PluginInstallOptions{}, err
}
options := client.PluginInstallOptions{
return client.PluginInstallOptions{
RegistryAuth: encodedAuth,
RemoteRef: remote,
RemoteRef: ref.String(),
Disabled: opts.disable,
AcceptAllPermissions: opts.grantPerms,
AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.remote),
AcceptPermissionsFunc: acceptPrivileges(dockerCLI, opts.remote),
PrivilegeFunc: nil,
Args: opts.args,
}
return options, nil
}, nil
}
func runInstall(ctx context.Context, dockerCLI command.Cli, opts pluginOptions) error {
@ -115,7 +90,7 @@ func runInstall(ctx context.Context, dockerCLI command.Cli, opts pluginOptions)
localName = reference.FamiliarString(reference.TagNameOnly(aref))
}
options, err := buildPullConfig(ctx, dockerCLI, opts)
options, err := buildPullConfig(dockerCLI, opts)
if err != nil {
return err
}

View File

@ -7,7 +7,6 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
@ -65,50 +64,6 @@ func TestInstallErrors(t *testing.T) {
}
}
func TestInstallContentTrustErrors(t *testing.T) {
testCases := []struct {
description string
args []string
expectedError string
notaryFunc test.NotaryClientFuncType
}{
{
description: "install plugin, offline notary server",
args: []string{"plugin:tag"},
expectedError: "client is offline",
notaryFunc: notary.GetOfflineNotaryRepository,
},
{
description: "install plugin, uninitialized notary server",
args: []string{"plugin:tag"},
expectedError: "remote trust data does not exist",
notaryFunc: notary.GetUninitializedNotaryRepository,
},
{
description: "install plugin, empty notary server",
args: []string{"plugin:tag"},
expectedError: "No valid trust data for tag",
notaryFunc: notary.GetEmptyTargetsNotaryRepository,
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
pluginInstallFunc: func(name string, options client.PluginInstallOptions) (io.ReadCloser, error) {
return nil, errors.New("should not try to install plugin")
},
}, test.EnableContentTrust)
cli.SetNotaryClient(tc.notaryFunc)
cmd := newInstallCommand(cli)
cmd.SetArgs(tc.args)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
})
}
}
func TestInstall(t *testing.T) {
testCases := []struct {
description string

View File

@ -2,55 +2,45 @@ package plugin
import (
"context"
"fmt"
"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/internal/jsonstream"
"github.com/docker/cli/internal/registry"
registrytypes "github.com/moby/moby/api/types/registry"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type pushOptions struct {
name string
untrusted bool
}
func newPushCommand(dockerCli command.Cli) *cobra.Command {
var opts pushOptions
func newPushCommand(dockerCLI command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "push [OPTIONS] PLUGIN[:TAG]",
Short: "Push a plugin to a registry",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
opts.name = args[0]
return runPush(cmd.Context(), dockerCli, opts)
name := args[0]
return runPush(cmd.Context(), dockerCLI, name)
},
}
flags := cmd.Flags()
command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
flags.Bool("disable-content-trust", dockerCLI.ContentTrustEnabled(), "Skip image verification (deprecated)")
_ = flags.MarkHidden("disable-content-trust")
return cmd
}
func runPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error {
named, err := reference.ParseNormalizedNamed(opts.name)
func runPush(ctx context.Context, dockerCli command.Cli, name string) error {
named, err := reference.ParseNormalizedNamed(name)
if err != nil {
return err
}
if _, ok := named.(reference.Canonical); ok {
return errors.Errorf("invalid name: %s", opts.name)
return fmt.Errorf("invalid name: %s", name)
}
named = reference.TagNameOnly(named)
indexInfo := registry.NewIndexInfo(named)
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), indexInfo)
authConfig := command.ResolveAuthConfig(dockerCli.ConfigFile(), registry.NewIndexInfo(named))
encodedAuth, err := registrytypes.EncodeAuthConfig(authConfig)
if err != nil {
return err
@ -63,14 +53,5 @@ func runPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
defer func() {
_ = responseBody.Close()
}()
if !opts.untrusted {
repoInfo := &trust.RepositoryInfo{
Name: reference.TrimNamed(named),
Index: indexInfo,
}
return trust.PushTrustedReference(ctx, dockerCli, repoInfo, named, authConfig, responseBody, command.UserAgent())
}
return jsonstream.Display(ctx, responseBody, dockerCli.Out())
}

View File

@ -31,7 +31,9 @@ func newUpgradeCommand(dockerCli command.Cli) *cobra.Command {
}
flags := cmd.Flags()
loadPullFlags(dockerCli, &options, flags)
flags.BoolVar(&options.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin")
flags.Bool("disable-content-trust", dockerCli.ContentTrustEnabled(), "Skip image verification (deprecated)")
_ = flags.MarkHidden("disable-content-trust")
flags.BoolVar(&options.skipRemoteCheck, "skip-remote-check", false, "Do not check if specified remote plugin matches existing plugin image")
return cmd
}
@ -73,7 +75,7 @@ func runUpgrade(ctx context.Context, dockerCLI command.Cli, opts pluginOptions)
}
}
options, err := buildPullConfig(ctx, dockerCLI, opts)
options, err := buildPullConfig(dockerCLI, opts)
if err != nil {
return err
}

View File

@ -9,7 +9,6 @@ Install a plugin
|:--------------------------|:---------|:--------|:--------------------------------------------------|
| `--alias` | `string` | | Local name for plugin |
| `--disable` | `bool` | | Do not enable the plugin on install |
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
| `--grant-all-permissions` | `bool` | | Grant all permissions necessary to run the plugin |

View File

@ -3,12 +3,6 @@
<!---MARKER_GEN_START-->
Push a plugin to a registry
### Options
| Name | Type | Default | Description |
|:--------------------------|:-------|:--------|:-------------------|
| `--disable-content-trust` | `bool` | `true` | Skip image signing |
<!---MARKER_GEN_END-->

View File

@ -7,7 +7,6 @@ Upgrade an existing plugin
| Name | Type | Default | Description |
|:--------------------------|:-------|:--------|:----------------------------------------------------------------------|
| `--disable-content-trust` | `bool` | `true` | Skip image verification |
| `--grant-all-permissions` | `bool` | | Grant all permissions necessary to run the plugin |
| `--skip-remote-check` | `bool` | | Do not check if specified remote plugin matches existing plugin image |

View File

@ -2,26 +2,24 @@ package plugin
import (
"context"
"fmt"
"testing"
"github.com/docker/cli/e2e/internal/fixtures"
"github.com/docker/cli/e2e/testutils"
"github.com/docker/cli/internal/test/environment"
"github.com/moby/moby/api/types/versions"
"gotest.tools/v3/icmd"
"gotest.tools/v3/skip"
)
const registryPrefix = "registry:5000"
func TestInstallWithContentTrust(t *testing.T) {
// TODO(krissetto): remove this skip once the fix (see https://github.com/moby/moby/pull/47299) is deployed to moby versions < 25
skip.If(t, versions.LessThan(environment.DaemonAPIVersion(t), "1.44"))
func TestCreatePushPull(t *testing.T) {
skip.If(t, environment.SkipPluginTests())
t.Skip("flaky")
const pluginName = registryPrefix + "/plugin-content-trust"
const pluginName = registryPrefix + "/my-plugin"
// TODO(thaJeztah): probably should use a config without the content trust bits.
dir := fixtures.SetupConfigFile(t)
defer dir.Remove()
@ -33,39 +31,28 @@ func TestInstallWithContentTrust(t *testing.T) {
icmd.RunCommand("docker", "plugin", "create", pluginName, pluginDir).Assert(t, icmd.Success)
result := icmd.RunCmd(icmd.Command("docker", "plugin", "push", pluginName),
fixtures.WithConfig(dir.Path()),
fixtures.WithTrust,
fixtures.WithNotary,
fixtures.WithPassphrase("foo", "bar"),
)
result.Assert(t, icmd.Expected{
Out: "Signing and pushing trust metadata",
Out: fmt.Sprintf("The push refers to repository [%s]", pluginName),
})
icmd.RunCommand("docker", "plugin", "rm", "-f", pluginName).Assert(t, icmd.Success)
result = icmd.RunCmd(icmd.Command("docker", "plugin", "install", "--grant-all-permissions", pluginName),
fixtures.WithConfig(dir.Path()),
fixtures.WithTrust,
fixtures.WithNotary,
)
result.Assert(t, icmd.Expected{
Out: "Installed plugin " + pluginName,
})
}
func TestInstallWithContentTrustUntrusted(t *testing.T) {
func TestInstall(t *testing.T) {
skip.If(t, environment.SkipPluginTests())
dir := fixtures.SetupConfigFile(t)
defer dir.Remove()
result := icmd.RunCmd(icmd.Command("docker", "plugin", "install", "--grant-all-permissions", "tiborvass/sample-volume-plugin:latest"),
fixtures.WithConfig(dir.Path()),
fixtures.WithTrust,
fixtures.WithNotary,
)
const pluginName = "tiborvass/sample-volume-plugin:latest"
result := icmd.RunCmd(icmd.Command("docker", "plugin", "install", "--grant-all-permissions", pluginName))
result.Assert(t, icmd.Expected{
ExitCode: 1,
Err: "Error: remote trust data does not exist",
Out: "Installed plugin " + pluginName,
})
}