mirror of https://github.com/docker/cli.git
Compare commits
39 Commits
ba92f12a7d
...
cc5ea7c176
Author | SHA1 | Date |
---|---|---|
Way2go | cc5ea7c176 | |
Sebastiaan van Stijn | 9861ce90fd | |
Sebastiaan van Stijn | d1d5353269 | |
Sebastiaan van Stijn | 4adbb18f1c | |
Sebastiaan van Stijn | e00ed82399 | |
Sebastiaan van Stijn | 3dd7621240 | |
Sebastiaan van Stijn | 4242cda826 | |
Sebastiaan van Stijn | a4228409d2 | |
Sebastiaan van Stijn | 7c80e4f938 | |
Harald Albers | 06260e68f3 | |
Harald Albers | 4525fe37b4 | |
Harald Albers | db0ed1e216 | |
Harald Albers | 2915749279 | |
Harald Albers | 3a2503fa43 | |
Harald Albers | 9a9ae231a9 | |
Harald Albers | 5f7c43e5e6 | |
Harald Albers | 3292afe6e6 | |
Harald Albers | 5d709a8d9f | |
Harald Albers | 2d89339b34 | |
Harald Albers | ac7bde6f64 | |
Harald Albers | e513454244 | |
Harald Albers | c555327f0b | |
Harald Albers | b598ec8cdb | |
Harald Albers | 761d76750c | |
Sebastiaan van Stijn | 917d2dc837 | |
Sebastiaan van Stijn | f9497b8a46 | |
Paweł Gronowski | 382d4c34a9 | |
aevesdocker | 1440f9f8cf | |
Sebastiaan van Stijn | 9c01d924fb | |
David Karlsson | 172f340112 | |
Wayne Cheng | b039c0e9af | |
Wayne Cheng | 46b474267b | |
Wayne Cheng | c2919580b6 | |
Wayne Cheng | 6fc8567ce6 | |
Wayne Cheng | 3ecd392a8c | |
Wayne Cheng | dbd460a216 | |
Wayne Cheng | 402a3400f0 | |
Wayne Cheng | 72ef456387 | |
Wayne Cheng | 6bff05f02f |
|
@ -62,7 +62,7 @@ jobs:
|
|||
name: Update Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.2
|
||||
go-version: 1.23.3
|
||||
-
|
||||
name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
|
|
|
@ -68,7 +68,7 @@ jobs:
|
|||
name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.2
|
||||
go-version: 1.23.3
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
|
|
@ -43,7 +43,7 @@ linters:
|
|||
run:
|
||||
# prevent golangci-lint from deducting the go version to lint for through go.mod,
|
||||
# which causes it to fallback to go1.17 semantics.
|
||||
go: "1.23.2"
|
||||
go: "1.23.3"
|
||||
timeout: 5m
|
||||
|
||||
linters-settings:
|
||||
|
|
|
@ -4,12 +4,12 @@ ARG BASE_VARIANT=alpine
|
|||
ARG ALPINE_VERSION=3.20
|
||||
ARG BASE_DEBIAN_DISTRO=bookworm
|
||||
|
||||
ARG GO_VERSION=1.23.2
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package manager
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package command
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package config
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package container
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package command
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package context
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package context
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package context
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package context
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package command
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package command
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package command
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package formatter
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package idresolver
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package image
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package image
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package inspect
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ func NewManifestCommand(dockerCli command.Cli) *cobra.Command {
|
|||
newAnnotateCommand(dockerCli),
|
||||
newPushListCommand(dockerCli),
|
||||
newRmManifestListCommand(dockerCli),
|
||||
newListCommand(dockerCli),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package manifest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/cli/cli/manifest/types"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultManifestListQuietFormat = "{{.Name}}"
|
||||
defaultManifestListTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.Platforms}}"
|
||||
|
||||
repositoryHeader = "REPOSITORY"
|
||||
tagHeader = "TAG"
|
||||
platformsHeader = "PLATFORMS"
|
||||
)
|
||||
|
||||
// NewFormat returns a Format for rendering using a manifest list Context
|
||||
func NewFormat(source string, quiet bool) formatter.Format {
|
||||
switch source {
|
||||
case formatter.TableFormatKey:
|
||||
if quiet {
|
||||
return defaultManifestListQuietFormat
|
||||
}
|
||||
return defaultManifestListTableFormat
|
||||
case formatter.RawFormatKey:
|
||||
if quiet {
|
||||
return `name: {{.Name}}`
|
||||
}
|
||||
return `repo: {{.Repository}}\ntag: {{.Tag}}\n`
|
||||
}
|
||||
return formatter.Format(source)
|
||||
}
|
||||
|
||||
// FormatWrite writes formatted manifestLists using the Context
|
||||
func FormatWrite(ctx formatter.Context, manifestLists []reference.Reference, manifests map[string][]types.ImageManifest) error {
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, manifestList := range manifestLists {
|
||||
if n, ok := manifestList.(reference.Named); ok {
|
||||
if nt, ok := n.(reference.NamedTagged); ok {
|
||||
if err := format(&manifestListContext{
|
||||
name: reference.FamiliarString(manifestList),
|
||||
repo: reference.FamiliarName(nt),
|
||||
tag: nt.Tag(),
|
||||
imageManifests: manifests[manifestList.String()],
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return ctx.Write(newManifestListContext(), render)
|
||||
}
|
||||
|
||||
type manifestListContext struct {
|
||||
formatter.HeaderContext
|
||||
name string
|
||||
repo string
|
||||
tag string
|
||||
imageManifests []types.ImageManifest
|
||||
}
|
||||
|
||||
func newManifestListContext() *manifestListContext {
|
||||
manifestListCtx := manifestListContext{}
|
||||
manifestListCtx.Header = formatter.SubHeaderContext{
|
||||
"Name": formatter.NameHeader,
|
||||
"Repository": repositoryHeader,
|
||||
"Tag": tagHeader,
|
||||
"Platforms": platformsHeader,
|
||||
}
|
||||
return &manifestListCtx
|
||||
}
|
||||
|
||||
func (c *manifestListContext) MarshalJSON() ([]byte, error) {
|
||||
return formatter.MarshalJSON(c)
|
||||
}
|
||||
|
||||
func (c *manifestListContext) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
func (c *manifestListContext) Repository() string {
|
||||
return c.repo
|
||||
}
|
||||
|
||||
func (c *manifestListContext) Tag() string {
|
||||
return c.tag
|
||||
}
|
||||
|
||||
func (c *manifestListContext) Platforms() string {
|
||||
platforms := []string{}
|
||||
for _, manifest := range c.imageManifests {
|
||||
os := manifest.Descriptor.Platform.OS
|
||||
arch := manifest.Descriptor.Platform.Architecture
|
||||
platforms = append(platforms, fmt.Sprintf("%s/%s", os, arch))
|
||||
}
|
||||
return strings.Join(platforms, ", ")
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package manifest
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
flagsHelper "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/cli/manifest/types"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listOptions struct {
|
||||
quiet bool
|
||||
format string
|
||||
}
|
||||
|
||||
func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var options listOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls [OPTIONS]",
|
||||
Aliases: []string{"list"},
|
||||
Short: "List local manifest lists",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return runList(dockerCli, options)
|
||||
},
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only show manifest list NAMEs")
|
||||
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runList(dockerCli command.Cli, options listOptions) error {
|
||||
manifestStore := dockerCli.ManifestStore()
|
||||
|
||||
var manifestLists []reference.Reference
|
||||
|
||||
manifestLists, err := manifestStore.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
manifests := map[string][]types.ImageManifest{}
|
||||
for _, manifestList := range manifestLists {
|
||||
if imageManifests, err := manifestStore.GetList(manifestList); err == nil {
|
||||
manifests[manifestList.String()] = imageManifests
|
||||
}
|
||||
}
|
||||
|
||||
format := options.format
|
||||
if len(format) == 0 {
|
||||
if len(dockerCli.ConfigFile().ManifestListsFormat) > 0 && !options.quiet {
|
||||
format = dockerCli.ConfigFile().ManifestListsFormat
|
||||
} else {
|
||||
format = formatter.TableFormatKey
|
||||
}
|
||||
}
|
||||
|
||||
manifestListsCtx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: NewFormat(format, options.quiet),
|
||||
}
|
||||
sort.Slice(manifestLists, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(manifestLists[i].String(), manifestLists[j].String())
|
||||
})
|
||||
return FormatWrite(manifestListsCtx, manifestLists, manifests)
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package manifest
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/cli/manifest/store"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
"gotest.tools/v3/golden"
|
||||
)
|
||||
|
||||
func TestListErrors(t *testing.T) {
|
||||
manifestStore := store.NewStore(t.TempDir())
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
args []string
|
||||
flags map[string]string
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
description: "too many arguments",
|
||||
args: []string{"foo"},
|
||||
expectedError: "accepts no arguments",
|
||||
},
|
||||
{
|
||||
description: "invalid format",
|
||||
args: []string{},
|
||||
flags: map[string]string{
|
||||
"format": "{{invalid format}}",
|
||||
},
|
||||
expectedError: "template parsing error",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(nil)
|
||||
cli.SetManifestStore(manifestStore)
|
||||
cmd := newListCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
cmd.SetOut(io.Discard)
|
||||
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
manifestStore := store.NewStore(t.TempDir())
|
||||
|
||||
list1 := ref(t, "first:1")
|
||||
namedRef := ref(t, "alpine:3.0")
|
||||
err := manifestStore.Save(list1, namedRef, fullImageManifest(t, namedRef))
|
||||
assert.NilError(t, err)
|
||||
namedRef = ref(t, "alpine:3.1")
|
||||
imageManifest := fullImageManifest(t, namedRef)
|
||||
imageManifest.Descriptor.Platform.OS = "linux"
|
||||
imageManifest.Descriptor.Platform.Architecture = "arm64"
|
||||
err = manifestStore.Save(list1, namedRef, imageManifest)
|
||||
assert.NilError(t, err)
|
||||
|
||||
list2 := ref(t, "second:2")
|
||||
namedRef = ref(t, "alpine:3.2")
|
||||
err = manifestStore.Save(list2, namedRef, fullImageManifest(t, namedRef))
|
||||
assert.NilError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
description string
|
||||
args []string
|
||||
flags map[string]string
|
||||
golden string
|
||||
listFunc func(filter filters.Args) (types.PluginsListResponse, error)
|
||||
}{
|
||||
{
|
||||
description: "list with no additional flags",
|
||||
args: []string{},
|
||||
golden: "manifest-list.golden",
|
||||
},
|
||||
{
|
||||
description: "list with quiet option",
|
||||
args: []string{},
|
||||
flags: map[string]string{
|
||||
"quiet": "true",
|
||||
},
|
||||
golden: "manifest-list-with-quiet-option.golden",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
cli := test.NewFakeCli(nil)
|
||||
cli.SetManifestStore(manifestStore)
|
||||
cmd := newListCommand(cli)
|
||||
cmd.SetArgs(tc.args)
|
||||
for key, value := range tc.flags {
|
||||
cmd.Flags().Set(key, value)
|
||||
}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
golden.Assert(t, cli.OutBuffer().String(), tc.golden)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
example.com/first:1
|
||||
example.com/second:2
|
|
@ -0,0 +1,3 @@
|
|||
REPOSITORY TAG PLATFORMS
|
||||
example.com/first 1 linux/amd64, linux/arm64
|
||||
example.com/second 2 linux/amd64
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package network
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package network
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package node
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package node
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package plugin
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package plugin
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package secret
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package service
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package service
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package service
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package service
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package loader
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package system
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(jsternberg): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
// 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
|
||||
|
||||
package command
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package trust
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package command
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package volume
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package interpolation
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package interpolation
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package loader
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package loader
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package loader
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package loader
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package loader
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package loader
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package schema
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package schema
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package template
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package template
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package types
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ type ConfigFile struct {
|
|||
PluginsFormat string `json:"pluginsFormat,omitempty"`
|
||||
VolumesFormat string `json:"volumesFormat,omitempty"`
|
||||
StatsFormat string `json:"statsFormat,omitempty"`
|
||||
ManifestListsFormat string `json:"manifestListsFormat,omitempty"`
|
||||
DetachKeys string `json:"detachKeys,omitempty"`
|
||||
CredentialsStore string `json:"credsStore,omitempty"`
|
||||
CredentialHelpers map[string]string `json:"credHelpers,omitempty"`
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package store
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package api
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ type Store interface {
|
|||
Get(listRef reference.Reference, manifest reference.Reference) (types.ImageManifest, error)
|
||||
GetList(listRef reference.Reference) ([]types.ImageManifest, error)
|
||||
Save(listRef reference.Reference, manifest reference.Reference, image types.ImageManifest) error
|
||||
List() ([]reference.Reference, error)
|
||||
}
|
||||
|
||||
// fsStore manages manifest files stored on the local filesystem
|
||||
|
@ -85,6 +86,26 @@ func (s *fsStore) getFromFilename(ref reference.Reference, filename string) (typ
|
|||
return manifestInfo.ImageManifest, nil
|
||||
}
|
||||
|
||||
// List returns the local manifest lists for a transaction
|
||||
func (s *fsStore) List() ([]reference.Reference, error) {
|
||||
fileInfos, err := os.ReadDir(s.root)
|
||||
switch {
|
||||
case os.IsNotExist(err):
|
||||
return nil, nil
|
||||
case err != nil:
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listRefs := make([]reference.Reference, 0, len(fileInfos))
|
||||
for _, info := range fileInfos {
|
||||
refString := filenameToRefString(info.Name())
|
||||
if listRef, err := reference.Parse(refString); err == nil {
|
||||
listRefs = append(listRefs, listRef)
|
||||
}
|
||||
}
|
||||
return listRefs, nil
|
||||
}
|
||||
|
||||
// GetList returns all the local manifests for a transaction
|
||||
func (s *fsStore) GetList(listRef reference.Reference) ([]types.ImageManifest, error) {
|
||||
filenames, err := s.listManifests(listRef.String())
|
||||
|
@ -148,8 +169,13 @@ func manifestToFilename(root, manifestList, manifest string) string {
|
|||
}
|
||||
|
||||
func makeFilesafeName(ref string) string {
|
||||
fileName := strings.ReplaceAll(ref, ":", "-")
|
||||
return strings.ReplaceAll(fileName, "/", "_")
|
||||
fileName := strings.ReplaceAll(ref, ":", "%")
|
||||
return strings.ReplaceAll(fileName, "/", "#")
|
||||
}
|
||||
|
||||
func filenameToRefString(filename string) string {
|
||||
refString := strings.ReplaceAll(filename, "%", ":")
|
||||
return strings.ReplaceAll(refString, "#", "/")
|
||||
}
|
||||
|
||||
type notFoundError struct {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package main
|
||||
|
||||
|
|
|
@ -4139,10 +4139,15 @@ _docker_manifest() {
|
|||
annotate
|
||||
create
|
||||
inspect
|
||||
ls
|
||||
push
|
||||
rm
|
||||
"
|
||||
__docker_subcommands "$subcommands" && return
|
||||
|
||||
local aliases="
|
||||
list
|
||||
"
|
||||
__docker_subcommands "$subcommands $aliases" && return
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
|
@ -4250,6 +4255,24 @@ _docker_manifest_rm() {
|
|||
esac
|
||||
}
|
||||
|
||||
_docker_manifest_list() {
|
||||
_docker_manifest_ls
|
||||
}
|
||||
|
||||
_docker_manifest_ls() {
|
||||
case "$prev" in
|
||||
--format)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "--format --help --quiet -q" -- "$cur" ) )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_node() {
|
||||
local subcommands="
|
||||
demote
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
variable "GO_VERSION" {
|
||||
default = "1.23.2"
|
||||
default = "1.23.3"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.23.2
|
||||
ARG GO_VERSION=1.23.3
|
||||
ARG ALPINE_VERSION=3.20
|
||||
|
||||
ARG BUILDX_VERSION=0.17.1
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.23.2
|
||||
ARG GO_VERSION=1.23.3
|
||||
ARG ALPINE_VERSION=3.20
|
||||
ARG GOLANGCI_LINT_VERSION=v1.61.0
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.23.2
|
||||
ARG GO_VERSION=1.23.3
|
||||
ARG ALPINE_VERSION=3.20
|
||||
ARG MODOUTDATED_VERSION=v0.8.0
|
||||
|
||||
|
|
|
@ -1283,7 +1283,7 @@ connect to services running on the host machine.
|
|||
|
||||
It's conventional to use `host.docker.internal` as the hostname referring to
|
||||
`host-gateway`. Docker Desktop automatically resolves this hostname, see
|
||||
[Explore networking features](https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host).
|
||||
[Explore networking features](https://docs.docker.com/desktop/features/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host).
|
||||
|
||||
The following example shows how the special `host-gateway` value works. The
|
||||
example runs an HTTP server that serves a file from host to container over the
|
||||
|
|
|
@ -21,6 +21,7 @@ For full details on using docker manifest lists, see the registry v2 specificati
|
|||
| [`annotate`](manifest_annotate.md) | Add additional information to a local image manifest |
|
||||
| [`create`](manifest_create.md) | Create a local manifest list for annotating and pushing to a registry |
|
||||
| [`inspect`](manifest_inspect.md) | Display an image manifest, or manifest list |
|
||||
| [`ls`](manifest_ls.md) | List local manifest lists |
|
||||
| [`push`](manifest_push.md) | Push a manifest list to a repository |
|
||||
| [`rm`](manifest_rm.md) | Delete one or more manifest lists from local storage |
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# docker manifest ls
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
List local manifest lists
|
||||
|
||||
### Aliases
|
||||
|
||||
`docker manifest ls`, `docker manifest list`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--format` | `string` | | Format output using a custom template:<br>'table': Print output in table format with column headers (default)<br>'table TEMPLATE': Print output in table format using the given Go template<br>'json': Print in JSON format<br>'TEMPLATE': Print output using the given Go template.<br>Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
|
||||
| `-q`, `--quiet` | | | Only show manifest list NAMEs |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
|
@ -34,11 +34,11 @@ information about available filter options.
|
|||
$ docker node ps swarm-manager1
|
||||
|
||||
NAME IMAGE NODE DESIRED STATE CURRENT STATE
|
||||
redis.1.7q92v0nr1hcgts2amcjyqg3pq redis:3.0.6 swarm-manager1 Running Running 5 hours
|
||||
redis.6.b465edgho06e318egmgjbqo4o redis:3.0.6 swarm-manager1 Running Running 29 seconds
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:3.0.6 swarm-manager1 Running Running 5 seconds
|
||||
redis.9.dkkual96p4bb3s6b10r7coxxt redis:3.0.6 swarm-manager1 Running Running 5 seconds
|
||||
redis.10.0tgctg8h8cech4w0k0gwrmr23 redis:3.0.6 swarm-manager1 Running Running 5 seconds
|
||||
redis.1.7q92v0nr1hcgts2amcjyqg3pq redis:7.4.1 swarm-manager1 Running Running 5 hours
|
||||
redis.6.b465edgho06e318egmgjbqo4o redis:7.4.1 swarm-manager1 Running Running 29 seconds
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:7.4.1 swarm-manager1 Running Running 5 seconds
|
||||
redis.9.dkkual96p4bb3s6b10r7coxxt redis:7.4.1 swarm-manager1 Running Running 5 seconds
|
||||
redis.10.0tgctg8h8cech4w0k0gwrmr23 redis:7.4.1 swarm-manager1 Running Running 5 seconds
|
||||
```
|
||||
|
||||
### <a name="filter"></a> Filtering (--filter)
|
||||
|
@ -64,11 +64,11 @@ The following filter matches all tasks with a name containing the `redis` string
|
|||
$ docker node ps -f name=redis swarm-manager1
|
||||
|
||||
NAME IMAGE NODE DESIRED STATE CURRENT STATE
|
||||
redis.1.7q92v0nr1hcgts2amcjyqg3pq redis:3.0.6 swarm-manager1 Running Running 5 hours
|
||||
redis.6.b465edgho06e318egmgjbqo4o redis:3.0.6 swarm-manager1 Running Running 29 seconds
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:3.0.6 swarm-manager1 Running Running 5 seconds
|
||||
redis.9.dkkual96p4bb3s6b10r7coxxt redis:3.0.6 swarm-manager1 Running Running 5 seconds
|
||||
redis.10.0tgctg8h8cech4w0k0gwrmr23 redis:3.0.6 swarm-manager1 Running Running 5 seconds
|
||||
redis.1.7q92v0nr1hcgts2amcjyqg3pq redis:7.4.1 swarm-manager1 Running Running 5 hours
|
||||
redis.6.b465edgho06e318egmgjbqo4o redis:7.4.1 swarm-manager1 Running Running 29 seconds
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:7.4.1 swarm-manager1 Running Running 5 seconds
|
||||
redis.9.dkkual96p4bb3s6b10r7coxxt redis:7.4.1 swarm-manager1 Running Running 5 seconds
|
||||
redis.10.0tgctg8h8cech4w0k0gwrmr23 redis:7.4.1 swarm-manager1 Running Running 5 seconds
|
||||
```
|
||||
|
||||
#### id
|
||||
|
@ -79,7 +79,7 @@ The `id` filter matches a task's id.
|
|||
$ docker node ps -f id=bg8c07zzg87di2mufeq51a2qp swarm-manager1
|
||||
|
||||
NAME IMAGE NODE DESIRED STATE CURRENT STATE
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:3.0.6 swarm-manager1 Running Running 5 seconds
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:7.4.1 swarm-manager1 Running Running 5 seconds
|
||||
```
|
||||
|
||||
#### label
|
||||
|
@ -93,8 +93,8 @@ The following filter matches tasks with the `usage` label regardless of its valu
|
|||
$ docker node ps -f "label=usage"
|
||||
|
||||
NAME IMAGE NODE DESIRED STATE CURRENT STATE
|
||||
redis.6.b465edgho06e318egmgjbqo4o redis:3.0.6 swarm-manager1 Running Running 10 minutes
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:3.0.6 swarm-manager1 Running Running 9 minutes
|
||||
redis.6.b465edgho06e318egmgjbqo4o redis:7.4.1 swarm-manager1 Running Running 10 minutes
|
||||
redis.7.bg8c07zzg87di2mufeq51a2qp redis:7.4.1 swarm-manager1 Running Running 9 minutes
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -99,19 +99,19 @@ Creates a service as described by the specified parameters.
|
|||
### Create a service
|
||||
|
||||
```console
|
||||
$ docker service create --name redis redis:3.0.6
|
||||
$ docker service create --name redis redis:7.4.1
|
||||
|
||||
dmu1ept4cxcfe8k8lhtux3ro3
|
||||
|
||||
$ docker service create --mode global --name redis2 redis:3.0.6
|
||||
$ docker service create --mode global --name redis2 redis:7.4.1
|
||||
|
||||
a8q9dasaafudfs8q8w32udass
|
||||
|
||||
$ docker service ls
|
||||
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
dmu1ept4cxcf redis replicated 1/1 redis:3.0.6
|
||||
a8q9dasaafud redis2 global 1/1 redis:3.0.6
|
||||
dmu1ept4cxcf redis replicated 1/1 redis:7.4.1
|
||||
a8q9dasaafud redis2 global 1/1 redis:7.4.1
|
||||
```
|
||||
|
||||
#### <a name="with-registry-auth"></a> Create a service using an image on a private registry (--with-registry-auth)
|
||||
|
@ -140,7 +140,7 @@ Use the `--replicas` flag to set the number of replica tasks for a replicated
|
|||
service. The following command creates a `redis` service with `5` replica tasks:
|
||||
|
||||
```console
|
||||
$ docker service create --name redis --replicas=5 redis:3.0.6
|
||||
$ docker service create --name redis --replicas=5 redis:7.4.1
|
||||
|
||||
4cdgfyky7ozwh3htjfw0d12qv
|
||||
```
|
||||
|
@ -157,7 +157,7 @@ number of `RUNNING` tasks is `3`:
|
|||
$ docker service ls
|
||||
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
4cdgfyky7ozw redis replicated 3/5 redis:3.0.7
|
||||
4cdgfyky7ozw redis replicated 3/5 redis:7.4.1
|
||||
```
|
||||
|
||||
Once all the tasks are created and `RUNNING`, the actual number of tasks is
|
||||
|
@ -167,7 +167,7 @@ equal to the desired number:
|
|||
$ docker service ls
|
||||
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
4cdgfyky7ozw redis replicated 5/5 redis:3.0.7
|
||||
4cdgfyky7ozw redis replicated 5/5 redis:7.4.1
|
||||
```
|
||||
|
||||
### <a name="secret"></a> Create a service with secrets (--secret)
|
||||
|
@ -178,7 +178,7 @@ Use the `--secret` flag to give a container access to a
|
|||
Create a service specifying a secret:
|
||||
|
||||
```console
|
||||
$ docker service create --name redis --secret secret.json redis:3.0.6
|
||||
$ docker service create --name redis --secret secret.json redis:7.4.1
|
||||
|
||||
4cdgfyky7ozwh3htjfw0d12qv
|
||||
```
|
||||
|
@ -189,7 +189,7 @@ Create a service specifying the secret, target, user/group ID, and mode:
|
|||
$ docker service create --name redis \
|
||||
--secret source=ssh-key,target=ssh \
|
||||
--secret source=app-key,target=app,uid=1000,gid=1001,mode=0400 \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
|
||||
4cdgfyky7ozwh3htjfw0d12qv
|
||||
```
|
||||
|
@ -215,14 +215,14 @@ pre-exist in the container. The `mode` is specified as a 4-number sequence such
|
|||
as `0755`.
|
||||
|
||||
```console
|
||||
$ docker service create --name=redis --config redis-conf redis:3.0.6
|
||||
$ docker service create --name=redis --config redis-conf redis:7.4.1
|
||||
```
|
||||
|
||||
Create a service with a config and specify the target location and file mode:
|
||||
|
||||
```console
|
||||
$ docker service create --name redis \
|
||||
--config source=redis-conf,target=/etc/redis/redis.conf,mode=0400 redis:3.0.6
|
||||
--config source=redis-conf,target=/etc/redis/redis.conf,mode=0400 redis:7.4.1
|
||||
```
|
||||
|
||||
To grant a service access to multiple configs, use multiple `--config` flags.
|
||||
|
@ -239,7 +239,7 @@ $ docker service create \
|
|||
--name redis \
|
||||
--update-delay 10s \
|
||||
--update-parallelism 2 \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
When you run a [service update](service_update.md), the scheduler updates a
|
||||
|
@ -256,7 +256,7 @@ $ docker service create \
|
|||
--name redis_2 \
|
||||
--replicas 5 \
|
||||
--env MYVAR=foo \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
To specify multiple environment variables, specify multiple `--env` flags, each
|
||||
|
@ -268,7 +268,7 @@ $ docker service create \
|
|||
--replicas 5 \
|
||||
--env MYVAR=foo \
|
||||
--env MYVAR2=bar \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
### <a name="hostname"></a> Create a service with specific hostname (--hostname)
|
||||
|
@ -277,7 +277,7 @@ This option sets the docker service containers hostname to a specific string.
|
|||
For example:
|
||||
|
||||
```console
|
||||
$ docker service create --name redis --hostname myredis redis:3.0.6
|
||||
$ docker service create --name redis --hostname myredis redis:7.4.1
|
||||
```
|
||||
|
||||
### <a name="label"></a> Set metadata on a service (-l, --label)
|
||||
|
@ -290,7 +290,7 @@ $ docker service create \
|
|||
--name redis_2 \
|
||||
--label com.example.foo="bar" \
|
||||
--label bar=baz \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
For more information about labels, refer to [apply custom
|
||||
|
@ -679,7 +679,7 @@ The following command creates a global service:
|
|||
$ docker service create \
|
||||
--name redis_2 \
|
||||
--mode global \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
### <a name="constraint"></a> Specify service constraints (--constraint)
|
||||
|
@ -712,7 +712,7 @@ $ docker service create \
|
|||
--name redis_2 \
|
||||
--constraint node.platform.os==linux \
|
||||
--constraint node.labels.type==queue \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
If the service constraints exclude all nodes in the cluster, a message is printed
|
||||
|
@ -760,7 +760,7 @@ $ docker service create \
|
|||
--replicas 9 \
|
||||
--name redis_2 \
|
||||
--placement-pref spread=node.labels.datacenter \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
This uses `--placement-pref` with a `spread` strategy (currently the only
|
||||
|
@ -812,7 +812,7 @@ $ docker service create \
|
|||
--name redis_2 \
|
||||
--placement-pref 'spread=node.labels.datacenter' \
|
||||
--placement-pref 'spread=node.labels.rack' \
|
||||
redis:3.0.6
|
||||
redis:7.4.1
|
||||
```
|
||||
|
||||
When updating a service with `docker service update`, `--placement-pref-add`
|
||||
|
|
|
@ -40,7 +40,7 @@ For example, given the following service;
|
|||
```console
|
||||
$ docker service ls
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
dmu1ept4cxcf redis replicated 3/3 redis:3.0.6
|
||||
dmu1ept4cxcf redis replicated 3/3 redis:7.4.1
|
||||
```
|
||||
|
||||
Both `docker service inspect redis`, and `docker service inspect dmu1ept4cxcf`
|
||||
|
@ -65,7 +65,7 @@ The output is in JSON format, for example:
|
|||
"Name": "redis",
|
||||
"TaskTemplate": {
|
||||
"ContainerSpec": {
|
||||
"Image": "redis:3.0.6"
|
||||
"Image": "redis:7.4.1"
|
||||
},
|
||||
"Resources": {
|
||||
"Limits": {},
|
||||
|
|
|
@ -37,7 +37,7 @@ $ docker service ls
|
|||
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
c8wgl7q4ndfd frontend replicated 5/5 nginx:alpine
|
||||
dmu1ept4cxcf redis replicated 3/3 redis:3.0.6
|
||||
dmu1ept4cxcf redis replicated 3/3 redis:7.4.1
|
||||
iwe3278osahj mongo global 7/7 mongo:3.3
|
||||
hh08h9uu8uwr job replicated-job 1/1 (3/5 completed) nginx:latest
|
||||
```
|
||||
|
@ -68,7 +68,7 @@ The following filter matches services with an ID starting with `0bcjw`:
|
|||
```console
|
||||
$ docker service ls -f "id=0bcjw"
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
0bcjwfh8ychr redis replicated 1/1 redis:3.0.6
|
||||
0bcjwfh8ychr redis replicated 1/1 redis:7.4.1
|
||||
```
|
||||
|
||||
#### label
|
||||
|
@ -84,7 +84,7 @@ $ docker service ls --filter label=project
|
|||
ID NAME MODE REPLICAS IMAGE
|
||||
01sl1rp6nj5u frontend2 replicated 1/1 nginx:alpine
|
||||
36xvvwwauej0 frontend replicated 5/5 nginx:alpine
|
||||
74nzcxxjv6fq backend replicated 3/3 redis:3.0.6
|
||||
74nzcxxjv6fq backend replicated 3/3 redis:7.4.1
|
||||
```
|
||||
|
||||
The following filter matches only services with the `project` label with the
|
||||
|
@ -94,7 +94,7 @@ The following filter matches only services with the `project` label with the
|
|||
$ docker service ls --filter label=project=project-a
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
36xvvwwauej0 frontend replicated 5/5 nginx:alpine
|
||||
74nzcxxjv6fq backend replicated 3/3 redis:3.0.6
|
||||
74nzcxxjv6fq backend replicated 3/3 redis:7.4.1
|
||||
```
|
||||
|
||||
#### mode
|
||||
|
@ -118,7 +118,7 @@ The following filter matches services with a name starting with `redis`.
|
|||
```console
|
||||
$ docker service ls --filter name=redis
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
0bcjwfh8ychr redis replicated 1/1 redis:3.0.6
|
||||
0bcjwfh8ychr redis replicated 1/1 redis:7.4.1
|
||||
```
|
||||
|
||||
### <a name="format"></a> Format the output (--format)
|
||||
|
|
|
@ -36,35 +36,35 @@ The following command shows all the tasks that are part of the `redis` service:
|
|||
$ docker service ps redis
|
||||
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
0qihejybwf1x redis.1 redis:3.0.5 manager1 Running Running 8 seconds
|
||||
bk658fpbex0d redis.2 redis:3.0.5 worker2 Running Running 9 seconds
|
||||
5ls5s5fldaqg redis.3 redis:3.0.5 worker1 Running Running 9 seconds
|
||||
8ryt076polmc redis.4 redis:3.0.5 worker1 Running Running 9 seconds
|
||||
1x0v8yomsncd redis.5 redis:3.0.5 manager1 Running Running 8 seconds
|
||||
71v7je3el7rr redis.6 redis:3.0.5 worker2 Running Running 9 seconds
|
||||
4l3zm9b7tfr7 redis.7 redis:3.0.5 worker2 Running Running 9 seconds
|
||||
9tfpyixiy2i7 redis.8 redis:3.0.5 worker1 Running Running 9 seconds
|
||||
3w1wu13yupln redis.9 redis:3.0.5 manager1 Running Running 8 seconds
|
||||
8eaxrb2fqpbn redis.10 redis:3.0.5 manager1 Running Running 8 seconds
|
||||
0qihejybwf1x redis.1 redis:7.4.0 manager1 Running Running 8 seconds
|
||||
bk658fpbex0d redis.2 redis:7.4.0 worker2 Running Running 9 seconds
|
||||
5ls5s5fldaqg redis.3 redis:7.4.0 worker1 Running Running 9 seconds
|
||||
8ryt076polmc redis.4 redis:7.4.0 worker1 Running Running 9 seconds
|
||||
1x0v8yomsncd redis.5 redis:7.4.0 manager1 Running Running 8 seconds
|
||||
71v7je3el7rr redis.6 redis:7.4.0 worker2 Running Running 9 seconds
|
||||
4l3zm9b7tfr7 redis.7 redis:7.4.0 worker2 Running Running 9 seconds
|
||||
9tfpyixiy2i7 redis.8 redis:7.4.0 worker1 Running Running 9 seconds
|
||||
3w1wu13yupln redis.9 redis:7.4.0 manager1 Running Running 8 seconds
|
||||
8eaxrb2fqpbn redis.10 redis:7.4.0 manager1 Running Running 8 seconds
|
||||
```
|
||||
|
||||
In addition to running tasks, the output also shows the task history. For
|
||||
example, after updating the service to use the `redis:3.0.6` image, the output
|
||||
example, after updating the service to use the `redis:7.4.1` image, the output
|
||||
may look like this:
|
||||
|
||||
```console
|
||||
$ docker service ps redis
|
||||
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
50qe8lfnxaxk redis.1 redis:3.0.6 manager1 Running Running 6 seconds ago
|
||||
ky2re9oz86r9 \_ redis.1 redis:3.0.5 manager1 Shutdown Shutdown 8 seconds ago
|
||||
3s46te2nzl4i redis.2 redis:3.0.6 worker2 Running Running less than a second ago
|
||||
nvjljf7rmor4 \_ redis.2 redis:3.0.6 worker2 Shutdown Rejected 23 seconds ago "No such image: redis@sha256:6…"
|
||||
vtiuz2fpc0yb \_ redis.2 redis:3.0.5 worker2 Shutdown Shutdown 1 second ago
|
||||
jnarweeha8x4 redis.3 redis:3.0.6 worker1 Running Running 3 seconds ago
|
||||
vs448yca2nz4 \_ redis.3 redis:3.0.5 worker1 Shutdown Shutdown 4 seconds ago
|
||||
jf1i992619ir redis.4 redis:3.0.6 worker1 Running Running 10 seconds ago
|
||||
blkttv7zs8ee \_ redis.4 redis:3.0.5 worker1 Shutdown Shutdown 11 seconds ago
|
||||
50qe8lfnxaxk redis.1 redis:7.4.1 manager1 Running Running 6 seconds ago
|
||||
ky2re9oz86r9 \_ redis.1 redis:7.4.0 manager1 Shutdown Shutdown 8 seconds ago
|
||||
3s46te2nzl4i redis.2 redis:7.4.1 worker2 Running Running less than a second ago
|
||||
nvjljf7rmor4 \_ redis.2 redis:7.4.1 worker2 Shutdown Rejected 23 seconds ago "No such image: redis@sha256:6…"
|
||||
vtiuz2fpc0yb \_ redis.2 redis:7.4.0 worker2 Shutdown Shutdown 1 second ago
|
||||
jnarweeha8x4 redis.3 redis:7.4.1 worker1 Running Running 3 seconds ago
|
||||
vs448yca2nz4 \_ redis.3 redis:7.4.0 worker1 Shutdown Shutdown 4 seconds ago
|
||||
jf1i992619ir redis.4 redis:7.4.1 worker1 Running Running 10 seconds ago
|
||||
blkttv7zs8ee \_ redis.4 redis:7.4.0 worker1 Shutdown Shutdown 11 seconds ago
|
||||
```
|
||||
|
||||
The number of items in the task history is determined by the
|
||||
|
@ -82,10 +82,10 @@ example:
|
|||
$ docker service ps --no-trunc redis
|
||||
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
50qe8lfnxaxksi9w2a704wkp7 redis.1 redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842 manager1 Running Running 5 minutes ago
|
||||
ky2re9oz86r9556i2szb8a8af \_ redis.1 redis:3.0.5@sha256:f8829e00d95672c48c60f468329d6693c4bdd28d1f057e755f8ba8b40008682e worker2 Shutdown Shutdown 5 minutes ago
|
||||
bk658fpbex0d57cqcwoe3jthu redis.2 redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842 worker2 Running Running 5 seconds
|
||||
nvjljf7rmor4htv7l8rwcx7i7 \_ redis.2 redis:3.0.6@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842 worker2 Shutdown Rejected 5 minutes ago "No such image: redis@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842"
|
||||
50qe8lfnxaxksi9w2a704wkp7 redis.1 redis:7.4.1@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842 manager1 Running Running 5 minutes ago
|
||||
ky2re9oz86r9556i2szb8a8af \_ redis.1 redis:7.4.0@sha256:f8829e00d95672c48c60f468329d6693c4bdd28d1f057e755f8ba8b40008682e worker2 Shutdown Shutdown 5 minutes ago
|
||||
bk658fpbex0d57cqcwoe3jthu redis.2 redis:7.4.1@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842 worker2 Running Running 5 seconds
|
||||
nvjljf7rmor4htv7l8rwcx7i7 \_ redis.2 redis:7.4.1@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842 worker2 Shutdown Rejected 5 minutes ago "No such image: redis@sha256:6a692a76c2081888b589e26e6ec835743119fe453d67ecf03df7de5b73d69842"
|
||||
```
|
||||
|
||||
### <a name="filter"></a> Filtering (--filter)
|
||||
|
@ -111,8 +111,8 @@ The `id` filter matches on all or a prefix of a task's ID.
|
|||
$ docker service ps -f "id=8" redis
|
||||
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
8ryt076polmc redis.4 redis:3.0.6 worker1 Running Running 9 seconds
|
||||
8eaxrb2fqpbn redis.10 redis:3.0.6 manager1 Running Running 8 seconds
|
||||
8ryt076polmc redis.4 redis:7.4.1 worker1 Running Running 9 seconds
|
||||
8eaxrb2fqpbn redis.10 redis:7.4.1 manager1 Running Running 8 seconds
|
||||
```
|
||||
|
||||
#### name
|
||||
|
@ -123,7 +123,7 @@ The `name` filter matches on task names.
|
|||
$ docker service ps -f "name=redis.1" redis
|
||||
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
qihejybwf1x5 redis.1 redis:3.0.6 manager1 Running Running 8 seconds
|
||||
qihejybwf1x5 redis.1 redis:7.4.1 manager1 Running Running 8 seconds
|
||||
```
|
||||
|
||||
|
||||
|
@ -135,10 +135,10 @@ The `node` filter matches on a node name or a node ID.
|
|||
$ docker service ps -f "node=manager1" redis
|
||||
|
||||
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
|
||||
0qihejybwf1x redis.1 redis:3.0.6 manager1 Running Running 8 seconds
|
||||
1x0v8yomsncd redis.5 redis:3.0.6 manager1 Running Running 8 seconds
|
||||
3w1wu13yupln redis.9 redis:3.0.6 manager1 Running Running 8 seconds
|
||||
8eaxrb2fqpbn redis.10 redis:3.0.6 manager1 Running Running 8 seconds
|
||||
0qihejybwf1x redis.1 redis:7.4.1 manager1 Running Running 8 seconds
|
||||
1x0v8yomsncd redis.5 redis:7.4.1 manager1 Running Running 8 seconds
|
||||
3w1wu13yupln redis.9 redis:7.4.1 manager1 Running Running 8 seconds
|
||||
8eaxrb2fqpbn redis.10 redis:7.4.1 manager1 Running Running 8 seconds
|
||||
```
|
||||
|
||||
#### desired-state
|
||||
|
|
|
@ -84,7 +84,7 @@ $ docker service ls
|
|||
|
||||
ID NAME MODE REPLICAS IMAGE
|
||||
3pr5mlvu3fh9 frontend replicated 5/5 nginx:alpine
|
||||
74nzcxxjv6fq backend replicated 3/3 redis:3.0.6
|
||||
74nzcxxjv6fq backend replicated 3/3 redis:7.4.1
|
||||
```
|
||||
|
||||
## Related commands
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.23.2
|
||||
ARG GO_VERSION=1.23.3
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS generated
|
||||
ENV GOTOOLCHAIN=local
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
||||
//go:build go1.21
|
||||
//go:build go1.22
|
||||
|
||||
package templates
|
||||
|
||||
|
|
Loading…
Reference in New Issue