Compare commits

...

23 Commits

Author SHA1 Message Date
Laura Brehm 80a2170864
Merge d6ce04640f into 9861ce90fd 2024-11-15 14:50:49 +01:00
Sebastiaan van Stijn 9861ce90fd
Merge pull request #5621 from thaJeztah/more_go_build_tags
cli/command/container: fix missing go:build tag
2024-11-15 11:24:51 +01:00
Sebastiaan van Stijn d1d5353269
cli/command/container: fix missing go:build tag
make shell
    make -C ./internal/gocompat/

    GO111MODULE=on go test -v
    # github.com/docker/cli/cli/command/container
    ../../cli/command/container/completion.go:37:28: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/command/container/completion.go:82:25: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
    ../../cli/command/container/completion.go:92:27: implicit function instantiation requires go1.18 or later (-lang was set to go1.16; check go.mod)
    FAIL	gocompat [build failed]

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-15 11:06:20 +01:00
Sebastiaan van Stijn 4adbb18f1c
Merge pull request #5580 from albers/container-completions
Improve Cobra completions for `run` and `create`
2024-11-14 16:58:54 +01:00
Sebastiaan van Stijn e00ed82399
Merge pull request #5615 from thaJeztah/bump_compose_buildx
Dockerfile: update buildx to v0.18.0, compose to v2.30.3
2024-11-14 14:42:05 +01:00
Sebastiaan van Stijn 3dd7621240
Dockerfile: update compose to v2.30.3
Update the compose cli plugin used in the dev-container

full diff: https://github.com/docker/compose/compare/v2.29.7...v2.30.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-14 10:35:52 +01:00
Sebastiaan van Stijn 4242cda826
Dockerfile: update buildx to v0.18.0
Update the buildx cli plugin used in the dev-container

full diff: https://github.com/docker/buildx/compare/0.17.1..0.18.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-14 10:35:25 +01:00
Harald Albers 06260e68f3 Handle null completions with a default callback
Credits to thaJeztah

Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 4525fe37b4 Add completion for `--volume-driver`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers db0ed1e216 Add completion for `--cgroupns`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 2915749279 Add completion for `--uts`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 3a2503fa43 Add completion for --log-driver` and --log-opt`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 9a9ae231a9 Add completion for `--security-opt`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 5f7c43e5e6 Add completion for `--detach-keys`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 3292afe6e6 Add completion for `--userns`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 5d709a8d9f Add completion for `--ulimit`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers 2d89339b34 Add completion for `--storage-opt`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers ac7bde6f64 Add completion for `--pid`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:55:59 +00:00
Harald Albers e513454244 Add completion for `--link`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:35:34 +00:00
Harald Albers c555327f0b Add completion for `--ipc`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:35:34 +00:00
Harald Albers b598ec8cdb Add completion for `--attach`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:35:34 +00:00
Harald Albers 761d76750c Share the container completions
Signed-off-by: Harald Albers <github@albersweb.de>
2024-11-08 15:35:34 +00:00
Laura Brehm d6ce04640f
Support plaintext credentials as multi-call binary
The Docker CLI supports storing/managing credentials without a
credential-helper, in which case credentials are fetched from/saved to
the CLI config file (`~/.docker/config.json`). This is all managed
entirely by the CLI itself, without resort to a separate binary.

There are a few issues with this approach – for one, saving the
credentials together with all the configurations make it impossible to
share one without the other, so one can't for example bind mount the
config file into a container without also including all configured
credentials.

Another issue is that this has made it so that any other clients
accessing registry credentials (such as
https://github.com/google/go-containerregistry) all have to both:
- read/parse the CLI `config.json`, to check for credentials there,
  which also means they're dependent on this type and might break if the
  type changes/we need to be careful not to break other codebases parsing
  this file, and can't change the location where plaintext credentials
  are stored.
- support the credential helper protocol, so that they can access
  credentials when users do have configured credential helpers.

This means that if we want to do something like support oauth
credentials by having credential-helpers refresh oauth tokens before
returning them, we have to both implement that in each credential-helper
and in the CLI itself, and any client directly reading `config.json`
will also need to implement this logic.

This commit turns the Docker CLI binary into a multicall binary, acting
as a standalone credentials helper when invoked as
`docker-credential-file`, while still storing/fetching credentials from
the configuration file (`~/.docker/config.json`), and without any
further changes.

This represents a first step into aligning the "no credhelper"/plaintext
flow with the "credhelper" flow, meaning that instead of this being an
exception where credentials must be read directly from the config file,
credentials can now be accessed in the exact same way as with other
credential helpers – by invoking `docker-credential-[credhelper name]`,
such as `docker-credential-pass`, `docker-credential-osxkeychain` or
`docker-credential-wincred`.

This would also make it possible for any other clients accessing
credentials to untangle themselves from things like the location of the
credentials, parsing credentials from `config.json`, etc. and instead
simply support the credential-helper protocol, and call the
`docker-credential-file` binary as they do others.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-10-15 15:19:01 +01:00
12 changed files with 580 additions and 22 deletions

View File

@ -8,8 +8,8 @@ ARG GO_VERSION=1.23.3
ARG XX_VERSION=1.5.0
ARG GOVERSIONINFO_VERSION=v1.3.0
ARG GOTESTSUM_VERSION=v1.10.0
ARG BUILDX_VERSION=0.17.1
ARG COMPOSE_VERSION=v2.29.7
ARG BUILDX_VERSION=0.18.0
ARG COMPOSE_VERSION=v2.30.3
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx

View File

@ -1,3 +1,7 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.22
// +build go1.22
package container
import (
@ -44,6 +48,65 @@ var allLinuxCapabilities = sync.OnceValue(func() []string {
return out
})
// logDriverOptions provides the options for each built-in logging driver.
var logDriverOptions = map[string][]string{
"awslogs": {
"max-buffer-size", "mode", "awslogs-create-group", "awslogs-credentials-endpoint", "awslogs-datetime-format",
"awslogs-group", "awslogs-multiline-pattern", "awslogs-region", "awslogs-stream", "tag",
},
"fluentd": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "fluentd-address", "fluentd-async",
"fluentd-buffer-limit", "fluentd-request-ack", "fluentd-retry-wait", "fluentd-max-retries",
"fluentd-sub-second-precision", "tag",
},
"gcplogs": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "gcp-log-cmd", "gcp-meta-id", "gcp-meta-name",
"gcp-meta-zone", "gcp-project",
},
"gelf": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "gelf-address", "gelf-compression-level",
"gelf-compression-type", "gelf-tcp-max-reconnect", "gelf-tcp-reconnect-delay", "tag",
},
"journald": {"max-buffer-size", "mode", "env", "env-regex", "labels", "tag"},
"json-file": {"max-buffer-size", "mode", "env", "env-regex", "labels", "compress", "max-file", "max-size"},
"local": {"max-buffer-size", "mode", "compress", "max-file", "max-size"},
"none": {},
"splunk": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "splunk-caname", "splunk-capath", "splunk-format",
"splunk-gzip", "splunk-gzip-level", "splunk-index", "splunk-insecureskipverify", "splunk-source",
"splunk-sourcetype", "splunk-token", "splunk-url", "splunk-verify-connection", "tag",
},
"syslog": {
"max-buffer-size", "mode", "env", "env-regex", "labels", "syslog-address", "syslog-facility", "syslog-format",
"syslog-tls-ca-cert", "syslog-tls-cert", "syslog-tls-key", "syslog-tls-skip-verify", "tag",
},
}
// builtInLogDrivers provides a list of the built-in logging drivers.
var builtInLogDrivers = sync.OnceValue(func() []string {
drivers := make([]string, 0, len(logDriverOptions))
for driver := range logDriverOptions {
drivers = append(drivers, driver)
}
return drivers
})
// allLogDriverOptions provides all options of the built-in logging drivers.
// The list does not contain duplicates.
var allLogDriverOptions = sync.OnceValue(func() []string {
var result []string
seen := make(map[string]bool)
for driver := range logDriverOptions {
for _, opt := range logDriverOptions[driver] {
if !seen[opt] {
seen[opt] = true
result = append(result, opt)
}
}
}
return result
})
// restartPolicies is a list of all valid restart-policies..
//
// TODO(thaJeztah): add descriptions, and enable descriptions for our completion scripts (cobra.CompletionOptions.DisableDescriptions is currently set to "true")
@ -54,6 +117,207 @@ var restartPolicies = []string{
string(container.RestartPolicyUnlessStopped),
}
// addCompletions adds the completions that `run` and `create` have in common.
func addCompletions(cmd *cobra.Command, dockerCLI completion.APIClientProvider) {
_ = cmd.RegisterFlagCompletionFunc("attach", completion.FromList("stderr", "stdin", "stdout"))
_ = cmd.RegisterFlagCompletionFunc("cap-add", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("cap-drop", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("cgroupns", completeCgroupns())
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames)
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames)
_ = cmd.RegisterFlagCompletionFunc("ipc", completeIpc(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("link", completeLink(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("log-driver", completeLogDriver(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("log-opt", completeLogOpt)
_ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("pid", completePid(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
_ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever))
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
_ = cmd.RegisterFlagCompletionFunc("security-opt", completeSecurityOpt)
_ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals)
_ = cmd.RegisterFlagCompletionFunc("storage-opt", completeStorageOpt)
_ = cmd.RegisterFlagCompletionFunc("ulimit", completeUlimit)
_ = cmd.RegisterFlagCompletionFunc("userns", completion.FromList("host"))
_ = cmd.RegisterFlagCompletionFunc("uts", completion.FromList("host"))
_ = cmd.RegisterFlagCompletionFunc("volume-driver", completeVolumeDriver(dockerCLI))
_ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCLI, true))
}
// completeCgroupns implements shell completion for the `--cgroupns` option of `run` and `create`.
func completeCgroupns() completion.ValidArgsFn {
return completion.FromList(string(container.CgroupnsModeHost), string(container.CgroupnsModePrivate))
}
// completeDetachKeys implements shell completion for the `--detach-keys` option of `run` and `create`.
func completeDetachKeys(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"ctrl-"}, cobra.ShellCompDirectiveNoSpace
}
// completeIpc implements shell completion for the `--ipc` option of `run` and `create`.
// The completion is partly composite.
func completeIpc(dockerCLI completion.APIClientProvider) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(toComplete) > 0 && strings.HasPrefix("container", toComplete) { //nolint:gocritic // not swapped, matches partly typed "container"
return []string{"container:"}, cobra.ShellCompDirectiveNoSpace
}
if strings.HasPrefix(toComplete, "container:") {
names, _ := completion.ContainerNames(dockerCLI, true)(cmd, args, toComplete)
return prefixWith("container:", names), cobra.ShellCompDirectiveNoFileComp
}
return []string{
string(container.IPCModeContainer + ":"),
string(container.IPCModeHost),
string(container.IPCModeNone),
string(container.IPCModePrivate),
string(container.IPCModeShareable),
}, cobra.ShellCompDirectiveNoFileComp
}
}
// completeLink implements shell completion for the `--link` option of `run` and `create`.
func completeLink(dockerCLI completion.APIClientProvider) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return postfixWith(":", containerNames(dockerCLI, cmd, args, toComplete)), cobra.ShellCompDirectiveNoSpace
}
}
// completeLogDriver implements shell completion for the `--log-driver` option of `run` and `create`.
// The log drivers are collected from a call to the Info endpoint with a fallback to a hard-coded list
// of the build-in log drivers.
func completeLogDriver(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
if err != nil {
return builtInLogDrivers(), cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Log
return drivers, cobra.ShellCompDirectiveNoFileComp
}
}
// completeLogOpt implements shell completion for the `--log-opt` option of `run` and `create`.
// If the user supplied a log-driver, only options for that driver are returned.
func completeLogOpt(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
driver, _ := cmd.Flags().GetString("log-driver")
if options, exists := logDriverOptions[driver]; exists {
return postfixWith("=", options), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
return postfixWith("=", allLogDriverOptions()), cobra.ShellCompDirectiveNoSpace
}
// completePid implements shell completion for the `--pid` option of `run` and `create`.
func completePid(dockerCLI completion.APIClientProvider) func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(toComplete) > 0 && strings.HasPrefix("container", toComplete) { //nolint:gocritic // not swapped, matches partly typed "container"
return []string{"container:"}, cobra.ShellCompDirectiveNoSpace
}
if strings.HasPrefix(toComplete, "container:") {
names, _ := completion.ContainerNames(dockerCLI, true)(cmd, args, toComplete)
return prefixWith("container:", names), cobra.ShellCompDirectiveNoFileComp
}
return []string{"container:", "host"}, cobra.ShellCompDirectiveNoFileComp
}
}
// completeSecurityOpt implements shell completion for the `--security-opt` option of `run` and `create`.
// The completion is partly composite.
func completeSecurityOpt(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(toComplete) > 0 && strings.HasPrefix("apparmor=", toComplete) { //nolint:gocritic // not swapped, matches partly typed "apparmor="
return []string{"apparmor="}, cobra.ShellCompDirectiveNoSpace
}
if len(toComplete) > 0 && strings.HasPrefix("label", toComplete) { //nolint:gocritic // not swapped, matches partly typed "label"
return []string{"label="}, cobra.ShellCompDirectiveNoSpace
}
if strings.HasPrefix(toComplete, "label=") {
if strings.HasPrefix(toComplete, "label=d") {
return []string{"label=disable"}, cobra.ShellCompDirectiveNoFileComp
}
labels := []string{"disable", "level:", "role:", "type:", "user:"}
return prefixWith("label=", labels), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
}
// length must be > 1 here so that completion of "s" falls through.
if len(toComplete) > 1 && strings.HasPrefix("seccomp", toComplete) { //nolint:gocritic // not swapped, matches partly typed "seccomp"
return []string{"seccomp="}, cobra.ShellCompDirectiveNoSpace
}
if strings.HasPrefix(toComplete, "seccomp=") {
return []string{"seccomp=unconfined"}, cobra.ShellCompDirectiveNoFileComp
}
return []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"}, cobra.ShellCompDirectiveNoFileComp
}
// completeStorageOpt implements shell completion for the `--storage-opt` option of `run` and `create`.
func completeStorageOpt(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"size="}, cobra.ShellCompDirectiveNoSpace
}
// completeUlimit implements shell completion for the `--ulimit` option of `run` and `create`.
func completeUlimit(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
limits := []string{
"as",
"chroot",
"core",
"cpu",
"data",
"fsize",
"locks",
"maxlogins",
"maxsyslogins",
"memlock",
"msgqueue",
"nice",
"nofile",
"nproc",
"priority",
"rss",
"rtprio",
"sigpending",
"stack",
}
return postfixWith("=", limits), cobra.ShellCompDirectiveNoSpace
}
// completeVolumeDriver contacts the API to get the built-in and installed volume drivers.
func completeVolumeDriver(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
return func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
if err != nil {
// fallback: the built-in drivers
return []string{"local"}, cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Volume
return drivers, cobra.ShellCompDirectiveNoFileComp
}
}
// containerNames contacts the API to get names and optionally IDs of containers.
// In case of an error, an empty list is returned.
func containerNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command, args []string, toComplete string) []string {
names, _ := completion.ContainerNames(dockerCLI, true)(cmd, args, toComplete)
if names == nil {
return []string{}
}
return names
}
// prefixWith prefixes every element in the slice with the given prefix.
func prefixWith(prefix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = prefix + v
}
return result
}
// postfixWith appends postfix to every element in the slice.
func postfixWith(postfix string, values []string) []string {
result := make([]string, len(values))
for i, v := range values {
result[i] = v + postfix
}
return result
}
func completeLinuxCapabilityNames(cmd *cobra.Command, args []string, toComplete string) (names []string, _ cobra.ShellCompDirective) {
return completion.FromList(allLinuxCapabilities()...)(cmd, args, toComplete)
}

View File

@ -4,6 +4,9 @@ import (
"strings"
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/builders"
"github.com/docker/docker/api/types/container"
"github.com/moby/sys/signal"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
@ -21,6 +24,48 @@ func TestCompleteLinuxCapabilityNames(t *testing.T) {
}
}
func TestCompletePid(t *testing.T) {
tests := []struct {
containerListFunc func(container.ListOptions) ([]container.Summary, error)
toComplete string
expectedCompletions []string
expectedDirective cobra.ShellCompDirective
}{
{
toComplete: "",
expectedCompletions: []string{"container:", "host"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "c",
expectedCompletions: []string{"container:"},
expectedDirective: cobra.ShellCompDirectiveNoSpace,
},
{
containerListFunc: func(container.ListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2"),
}, nil
},
toComplete: "container:",
expectedCompletions: []string{"container:c1", "container:c2"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.toComplete, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: tc.containerListFunc,
})
completions, directive := completePid(cli)(NewRunCommand(cli), nil, tc.toComplete)
assert.Check(t, is.DeepEqual(completions, tc.expectedCompletions))
assert.Check(t, is.Equal(directive, tc.expectedDirective))
})
}
}
func TestCompleteRestartPolicies(t *testing.T) {
values, directives := completeRestartPolicies(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
@ -28,6 +73,59 @@ func TestCompleteRestartPolicies(t *testing.T) {
assert.Check(t, is.DeepEqual(values, expected))
}
func TestCompleteSecurityOpt(t *testing.T) {
tests := []struct {
toComplete string
expectedCompletions []string
expectedDirective cobra.ShellCompDirective
}{
{
toComplete: "",
expectedCompletions: []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "apparmor=",
expectedCompletions: []string{"apparmor="},
expectedDirective: cobra.ShellCompDirectiveNoSpace,
},
{
toComplete: "label=",
expectedCompletions: []string{"label=disable", "label=level:", "label=role:", "label=type:", "label=user:"},
expectedDirective: cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "s",
// We do not filter matching completions but delegate this task to the shell script.
expectedCompletions: []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "se",
expectedCompletions: []string{"seccomp="},
expectedDirective: cobra.ShellCompDirectiveNoSpace,
},
{
toComplete: "seccomp=",
expectedCompletions: []string{"seccomp=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
toComplete: "sy",
expectedCompletions: []string{"apparmor=", "label=", "no-new-privileges", "seccomp=", "systempaths=unconfined"},
expectedDirective: cobra.ShellCompDirectiveNoFileComp,
},
}
for _, tc := range tests {
t.Run(tc.toComplete, func(t *testing.T) {
completions, directive := completeSecurityOpt(nil, nil, tc.toComplete)
assert.Check(t, is.DeepEqual(completions, tc.expectedCompletions))
assert.Check(t, is.Equal(directive, tc.expectedDirective))
})
}
}
func TestCompleteSignals(t *testing.T) {
values, directives := completeSignals(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")

View File

@ -78,16 +78,15 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
copts = addFlags(flags)
_ = cmd.RegisterFlagCompletionFunc("cap-add", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("cap-drop", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames)
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames)
_ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli))
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
_ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever))
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
_ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals)
_ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCli, true))
addCompletions(cmd, dockerCli)
flags.VisitAll(func(flag *pflag.Flag) {
// Set a default completion function if none was set. We don't look
// up if it does already have one set, because Cobra does this for
// us, and returns an error (which we ignore for this reason).
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
})
return cmd
}

View File

@ -69,16 +69,16 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled())
copts = addFlags(flags)
_ = cmd.RegisterFlagCompletionFunc("cap-add", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("cap-drop", completeLinuxCapabilityNames)
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames)
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames)
_ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli))
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
_ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever))
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
_ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals)
_ = cmd.RegisterFlagCompletionFunc("volumes-from", completion.ContainerNames(dockerCli, true))
_ = cmd.RegisterFlagCompletionFunc("detach-keys", completeDetachKeys)
addCompletions(cmd, dockerCli)
flags.VisitAll(func(flag *pflag.Flag) {
// Set a default completion function if none was set. We don't look
// up if it does already have one set, because Cobra does this for
// us, and returns an error (which we ignore for this reason).
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
})
return cmd
}

View File

@ -19,8 +19,10 @@ import (
cliflags "github.com/docker/cli/cli/flags"
"github.com/docker/cli/cli/version"
platformsignals "github.com/docker/cli/cmd/docker/internal/signals"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/reexec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
@ -29,6 +31,10 @@ import (
)
func main() {
if reexec.Init() {
return
}
err := dockerMain(context.Background())
if err != nil && !errdefs.IsCancelled(err) {
_, _ = fmt.Fprintln(os.Stderr, err)

69
cmd/docker/file_helper.go Normal file
View File

@ -0,0 +1,69 @@
package main
import (
"os"
credhelpers "github.com/docker/docker-credential-helpers/credentials"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/credentials"
"github.com/docker/cli/cli/config/types"
)
//nolint:gosec // ignore G101: Potential hardcoded credentials
const fileCredsHelperBinary = "docker-credential-file"
func init() {
reexec.Register(fileCredsHelperBinary, serveFileCredHelper)
}
func serveFileCredHelper() {
configfile := config.LoadDefaultConfigFile(os.Stderr)
store := credentials.NewFileStore(configfile)
credhelpers.Serve(&FileHelper{
fileStore: store,
})
}
var _ credhelpers.Helper = &FileHelper{}
type FileHelper struct {
fileStore credentials.Store
}
func (f *FileHelper) Add(creds *credhelpers.Credentials) error {
return f.fileStore.Store(types.AuthConfig{
Username: creds.Username,
Password: creds.Secret,
ServerAddress: creds.ServerURL,
})
}
func (f *FileHelper) Delete(serverAddress string) error {
return f.fileStore.Erase(serverAddress)
}
func (f *FileHelper) Get(serverAddress string) (string, string, error) {
authConfig, err := f.fileStore.Get(serverAddress)
if err != nil {
return "", "", err
}
return authConfig.Username, authConfig.Password, nil
}
func (f *FileHelper) List() (map[string]string, error) {
creds := make(map[string]string)
authConfig, err := f.fileStore.GetAll()
if err != nil {
return nil, err
}
for k, v := range authConfig {
creds[k] = v.Username
}
return creds, nil
}

View File

@ -0,0 +1,26 @@
package reexec
import (
"os/exec"
"syscall"
)
// Command returns an [*exec.Cmd] which has Path as current binary which,
// on Linux, is set to the in-memory version (/proc/self/exe) of the current
// binary, it is thus safe to delete or replace the on-disk binary (os.Args[0]).
//
// On Linux, the Pdeathsig of [*exec.Cmd.SysProcAttr] is set to SIGTERM.
// This signal will be sent to the process when the OS thread which created
// the process dies.
//
// It is the caller's responsibility to ensure that the creating thread is
// not terminated prematurely. See https://go.dev/issue/27505 for more details.
func Command(args ...string) *exec.Cmd {
return &exec.Cmd{
Path: Self(),
Args: args,
SysProcAttr: &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
},
}
}

View File

@ -0,0 +1,19 @@
//go:build freebsd || darwin || windows
package reexec
import (
"os/exec"
)
// Command returns *exec.Cmd with its Path set to the path of the current
// binary using the result of [Self]. For example if current binary is
// "my-binary" at "/usr/bin/" (or "my-binary.exe" at "C:\" on Windows),
// then cmd.Path is set to "/usr/bin/my-binary" and "C:\my-binary.exe"
// respectively.
func Command(args ...string) *exec.Cmd {
return &exec.Cmd{
Path: Self(),
Args: args,
}
}

View File

@ -0,0 +1,12 @@
//go:build !linux && !windows && !freebsd && !darwin
package reexec
import (
"os/exec"
)
// Command is unsupported on operating systems apart from Linux, Windows, and Darwin.
func Command(args ...string) *exec.Cmd {
return nil
}

64
vendor/github.com/docker/docker/pkg/reexec/reexec.go generated vendored Normal file
View File

@ -0,0 +1,64 @@
// Package reexec facilitates the busybox style reexec of a binary.
//
// Handlers can be registered with a name and the argv 0 of the exec of
// the binary will be used to find and execute custom init paths.
//
// It is used in dockerd to work around forking limitations when using Go.
package reexec
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
)
var registeredInitializers = make(map[string]func())
// Register adds an initialization func under the specified name. It panics
// if the given name is already registered.
func Register(name string, initializer func()) {
if _, exists := registeredInitializers[name]; exists {
panic(fmt.Sprintf("reexec func already registered under name %q", name))
}
registeredInitializers[name] = initializer
}
// Init is called as the first part of the exec process and returns true if an
// initialization function was called.
func Init() bool {
if initializer, ok := registeredInitializers[os.Args[0]]; ok {
initializer()
return true
}
return false
}
// Self returns the path to the current process's binary. On Linux, it
// returns "/proc/self/exe", which provides the in-memory version of the
// current binary, whereas on other platforms it attempts to looks up the
// absolute path for os.Args[0], or otherwise returns os.Args[0] as-is.
func Self() string {
if runtime.GOOS == "linux" {
return "/proc/self/exe"
}
return naiveSelf()
}
func naiveSelf() string {
name := os.Args[0]
if filepath.Base(name) == name {
if lp, err := exec.LookPath(name); err == nil {
return lp
}
}
// handle conversion of relative paths to absolute
if absName, err := filepath.Abs(name); err == nil {
return absName
}
// if we couldn't get absolute name, return original
// (NOTE: Go only errors on Abs() if os.Getwd fails)
return name
}

1
vendor/modules.txt vendored
View File

@ -91,6 +91,7 @@ github.com/docker/docker/pkg/longpath
github.com/docker/docker/pkg/pools
github.com/docker/docker/pkg/process
github.com/docker/docker/pkg/progress
github.com/docker/docker/pkg/reexec
github.com/docker/docker/pkg/stdcopy
github.com/docker/docker/pkg/streamformatter
github.com/docker/docker/pkg/stringid