Compare commits
157 Commits
v23.0.0-be
...
v23.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 569dd73db1 | |||
| f6643207a2 | |||
| f381e08425 | |||
| 18f20a5537 | |||
| d3a36fc38c | |||
| 59bb07f2e4 | |||
| 80f27987f4 | |||
| 6a8406e602 | |||
| c2c122fb65 | |||
| 40a48e4154 | |||
| a43c9f3440 | |||
| 114e17ac4b | |||
| e2c402118c | |||
| d07453890c | |||
| 288b6c79fe | |||
| fbab8cd2be | |||
| b898a46135 | |||
| 90a72a5894 | |||
| 4c63110a92 | |||
| b61b5a9878 | |||
| 84fe451ec7 | |||
| 71615c2df1 | |||
| a1acc9af91 | |||
| 95066ff3a2 | |||
| 0dbf70fad2 | |||
| e0b8e19687 | |||
| 98e874dac7 | |||
| 92164b0306 | |||
| 5af8077eeb | |||
| d352c504a8 | |||
| 28c74b759b | |||
| 57a502772b | |||
| 14ac8db968 | |||
| 1ab7665be8 | |||
| 1810e922ac | |||
| 5051d82a17 | |||
| 7f4e3ead75 | |||
| a5ee5b1dfc | |||
| 27b19a6acf | |||
| ab4ef4aed4 | |||
| 14aac2c232 | |||
| 0cd15abfde | |||
| 168f1b55e2 | |||
| 53ed25d9b6 | |||
| e92dd87c32 | |||
| 9e3d5d1528 | |||
| 3ae101f41e | |||
| 3a118309b8 | |||
| 1e3622c50c | |||
| 4a500f690f | |||
| 9b54d860cd | |||
| 0288f7f724 | |||
| 00070e6e23 | |||
| 67b9617898 | |||
| 285e137aa4 | |||
| 070825bc74 | |||
| 645395cc77 | |||
| 551c4e9ab9 | |||
| 5f9c58ffa0 | |||
| 8672540f8c | |||
| c4fff9da13 | |||
| 526e5e7c95 | |||
| d7f21ea9c8 | |||
| ae43eb0e04 | |||
| caf8b152c6 | |||
| e1152b2418 | |||
| be30cb370e | |||
| f7c322edba | |||
| 5d04b1c49e | |||
| 8627a6df16 | |||
| fe694e8219 | |||
| 1493806509 | |||
| 9bb70217f8 | |||
| 59e74b44ae | |||
| fc6be6ad30 | |||
| d347678cde | |||
| d0a4b6f497 | |||
| e04f3dd0df | |||
| 1af9f22c7e | |||
| f1f12a3332 | |||
| c453cc6873 | |||
| 0d16330dd2 | |||
| 257ff41304 | |||
| b9e1ad3d19 | |||
| f163d2441e | |||
| d1f02a2733 | |||
| 4c0a3c3288 | |||
| bdc7e37b30 | |||
| f3ed6db2b1 | |||
| 35d7fbc818 | |||
| d32f0484eb | |||
| 79c9e527a3 | |||
| 186dcf30b1 | |||
| c49f1ccb49 | |||
| 5a5b7a61d5 | |||
| 4595ce588c | |||
| 9d1ace9aeb | |||
| 81b051298e | |||
| 99023cb372 | |||
| 71e561780a | |||
| 3613fcc864 | |||
| e5b29b8975 | |||
| b811057181 | |||
| 850fb6921c | |||
| 42de5cc7f0 | |||
| 3fa18636ec | |||
| c8bd8932a1 | |||
| 3bed830a27 | |||
| cb19bf9f7d | |||
| acc45f5494 | |||
| 806f9eab68 | |||
| b3557b2840 | |||
| 2b06c0c42c | |||
| f29992c0f1 | |||
| 424401233f | |||
| 6c39bc1f60 | |||
| a473c5b38a | |||
| d84256132d | |||
| 0359f8eeee | |||
| 720a6a8239 | |||
| b1db70ded7 | |||
| 946bb9471b | |||
| ed94b6ee91 | |||
| 112f4ec38d | |||
| cea94069fb | |||
| 784f660143 | |||
| 6fe14e61f2 | |||
| c5982f373c | |||
| 139e924690 | |||
| cc859412c8 | |||
| 70d24e854b | |||
| bab905a442 | |||
| 929f23fcf9 | |||
| 2df9ff91e1 | |||
| 1b75c7c52a | |||
| e3e0b7a6c8 | |||
| 6f2f021b6d | |||
| dedbcec469 | |||
| cd2098c461 | |||
| d7869beade | |||
| 378c92d758 | |||
| 06eba426d7 | |||
| 9a5d5aefb8 | |||
| 895e7a3df8 | |||
| 51f36c6be1 | |||
| 2f733b87f9 | |||
| 990674901b | |||
| 60d62fb729 | |||
| 83ca73f9aa | |||
| 693ae6ca73 | |||
| 0f6023a9c3 | |||
| c567f674c6 | |||
| 40694311b4 | |||
| 016846e950 | |||
| 0e15d73c65 | |||
| e547881e27 | |||
| 35b42efad3 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
2
.github/workflows/e2e.yml
vendored
2
.github/workflows/e2e.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
@ -63,7 +63,7 @@ jobs:
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19.3
|
||||
go-version: 1.19.4
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
|
||||
2
.github/workflows/validate.yml
vendored
2
.github/workflows/validate.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- '[0-9]+.[0-9]{2}'
|
||||
- '[0-9]+.[0-9]+'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG BASE_VARIANT=alpine
|
||||
ARG GO_VERSION=1.19.3
|
||||
ARG GO_VERSION=1.19.7
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG XX_VERSION=1.1.1
|
||||
ARG GOVERSIONINFO_VERSION=v1.3.0
|
||||
ARG GOTESTSUM_VERSION=v1.8.2
|
||||
ARG BUILDX_VERSION=0.9.0
|
||||
ARG BUILDX_VERSION=0.10.4
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
|
||||
4
Makefile
4
Makefile
@ -90,6 +90,10 @@ authors: ## generate AUTHORS file from git history
|
||||
manpages: ## generate man pages from go source and markdown
|
||||
scripts/docs/generate-man.sh
|
||||
|
||||
.PHONY: mddocs
|
||||
mddocs: ## generate markdown files from go source
|
||||
scripts/docs/generate-md.sh
|
||||
|
||||
.PHONY: yamldocs
|
||||
yamldocs: ## generate documentation YAML files consumed by docs repo
|
||||
scripts/docs/generate-yaml.sh
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
# Docker CLI
|
||||
|
||||
[](https://pkg.go.dev/github.com/docker/cli)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Abuild)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Atest)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Abuild)
|
||||
[](https://github.com/docker/cli/actions?query=workflow%3Atest)
|
||||
[](https://goreportcard.com/report/github.com/docker/cli)
|
||||
[](https://codecov.io/gh/docker/cli)
|
||||
[](https://codecov.io/gh/docker/cli)
|
||||
|
||||
## About
|
||||
|
||||
|
||||
@ -133,7 +133,9 @@ func newPluginCommand(dockerCli *command.DockerCli, plugin *cobra.Command, meta
|
||||
}
|
||||
opts, flags := cli.SetupPluginRootCommand(cmd)
|
||||
|
||||
cmd.SetIn(dockerCli.In())
|
||||
cmd.SetOut(dockerCli.Out())
|
||||
cmd.SetErr(dockerCli.Err())
|
||||
|
||||
cmd.AddCommand(
|
||||
plugin,
|
||||
|
||||
14
cli/cobra.go
14
cli/cobra.go
@ -61,7 +61,10 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p
|
||||
rootCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help")
|
||||
rootCmd.PersistentFlags().Lookup("help").Hidden = true
|
||||
|
||||
rootCmd.Annotations = map[string]string{"additionalHelp": "For more help on how to use Docker, head to https://docs.docker.com/go/guides/"}
|
||||
rootCmd.Annotations = map[string]string{
|
||||
"additionalHelp": "For more help on how to use Docker, head to https://docs.docker.com/go/guides/",
|
||||
"docs.code-delimiter": `"`, // https://github.com/docker/cli-docs-tool/blob/77abede22166eaea4af7335096bdcedd043f5b19/annotation/annotation.go#L20-L22
|
||||
}
|
||||
|
||||
// Configure registry.CertsDir() when running in rootless-mode
|
||||
if os.Getenv("ROOTLESSKIT_STATE_DIR") != "" {
|
||||
@ -231,9 +234,13 @@ func isExperimental(cmd *cobra.Command) bool {
|
||||
}
|
||||
|
||||
func additionalHelp(cmd *cobra.Command) string {
|
||||
if additionalHelp, ok := cmd.Annotations["additionalHelp"]; ok {
|
||||
if msg, ok := cmd.Annotations["additionalHelp"]; ok {
|
||||
out := cmd.OutOrStderr()
|
||||
if _, isTerminal := term.GetFdInfo(out); !isTerminal {
|
||||
return msg
|
||||
}
|
||||
style := aec.EmptyBuilder.Bold().ANSI
|
||||
return style.Apply(additionalHelp)
|
||||
return style.Apply(msg)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -504,6 +511,7 @@ Run '{{.CommandPath}} COMMAND --help' for more information on a command.
|
||||
{{- if hasAdditionalHelp .}}
|
||||
|
||||
{{ additionalHelp . }}
|
||||
|
||||
{{- end}}
|
||||
`
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused build cache, not just dangling ones")
|
||||
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=24h')")
|
||||
flags.Var(&options.filter, "filter", `Provide filter values (e.g. "until=24h")`)
|
||||
flags.Var(&options.keepStorage, "keep-storage", "Amount of disk space to keep for cache")
|
||||
|
||||
return cmd
|
||||
|
||||
@ -48,7 +48,7 @@ func NewCommitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags.BoolVarP(&options.pause, "pause", "p", true, "Pause container during commit")
|
||||
flags.StringVarP(&options.comment, "message", "m", "", "Commit message")
|
||||
flags.StringVarP(&options.author, "author", "a", "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")
|
||||
flags.StringVarP(&options.author, "author", "a", "", `Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")`)
|
||||
|
||||
options.changes = opts.NewListOpts(nil)
|
||||
flags.VarP(&options.changes, "change", "c", "Apply Dockerfile instruction to the created image")
|
||||
|
||||
@ -386,13 +386,12 @@ func splitCpArg(arg string) (container, path string) {
|
||||
return "", arg
|
||||
}
|
||||
|
||||
parts := strings.SplitN(arg, ":", 2)
|
||||
|
||||
if len(parts) == 1 || strings.HasPrefix(parts[0], ".") {
|
||||
container, path, ok := strings.Cut(arg, ":")
|
||||
if !ok || strings.HasPrefix(container, ".") {
|
||||
// Either there's no `:` in the arg
|
||||
// OR it's an explicit local relative path like `./file:name.txt`.
|
||||
return "", arg
|
||||
}
|
||||
|
||||
return parts[0], parts[1]
|
||||
return container, path
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.SetInterspersed(false)
|
||||
|
||||
flags.StringVar(&options.name, "name", "", "Assign a name to the container")
|
||||
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before creating ("`+PullImageAlways+`"|"`+PullImageMissing+`"|"`+PullImageNever+`")`)
|
||||
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before creating ("`+PullImageAlways+`", "|`+PullImageMissing+`", "`+PullImageNever+`")`)
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the pull output")
|
||||
|
||||
// Add an explicit help that doesn't have a `-h` to prevent the conflict
|
||||
|
||||
@ -70,7 +70,7 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVarP(&options.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
|
||||
flags.BoolVarP(&options.TTY, "tty", "t", false, "Allocate a pseudo-TTY")
|
||||
flags.BoolVarP(&options.Detach, "detach", "d", false, "Detached mode: run command in the background")
|
||||
flags.StringVarP(&options.User, "user", "u", "", "Username or UID (format: <name|uid>[:<group|gid>])")
|
||||
flags.StringVarP(&options.User, "user", "u", "", `Username or UID (format: "<name|uid>[:<group|gid>]")`)
|
||||
flags.BoolVarP(&options.Privileged, "privileged", "", false, "Give extended privileges to the command")
|
||||
flags.VarP(&options.Env, "env", "e", "Set environment variables")
|
||||
flags.SetAnnotation("env", "version", []string{"1.25"})
|
||||
|
||||
@ -17,14 +17,15 @@ import (
|
||||
)
|
||||
|
||||
type psOptions struct {
|
||||
quiet bool
|
||||
size bool
|
||||
all bool
|
||||
noTrunc bool
|
||||
nLatest bool
|
||||
last int
|
||||
format string
|
||||
filter opts.FilterOpt
|
||||
quiet bool
|
||||
size bool
|
||||
sizeChanged bool
|
||||
all bool
|
||||
noTrunc bool
|
||||
nLatest bool
|
||||
last int
|
||||
format string
|
||||
filter opts.FilterOpt
|
||||
}
|
||||
|
||||
// NewPsCommand creates a new cobra.Command for `docker ps`
|
||||
@ -36,6 +37,7 @@ func NewPsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
Short: "List containers",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.sizeChanged = cmd.Flags().Changed("size")
|
||||
return runPs(dockerCli, &options)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
@ -78,13 +80,8 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
||||
options.Limit = 1
|
||||
}
|
||||
|
||||
if !opts.quiet && !options.Size && len(opts.format) > 0 {
|
||||
// The --size option isn't set, but .Size may be used in the template.
|
||||
// Parse and execute the given template to detect if the .Size field is
|
||||
// used. If it is, then automatically enable the --size option. See #24696
|
||||
//
|
||||
// Only requesting container size information when needed is an optimization,
|
||||
// because calculating the size is a costly operation.
|
||||
// always validate template when `--format` is used, for consistency
|
||||
if len(opts.format) > 0 {
|
||||
tmpl, err := templates.NewParse("", opts.format)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse template")
|
||||
@ -98,8 +95,19 @@ func buildContainerListOptions(opts *psOptions) (*types.ContainerListOptions, er
|
||||
return nil, errors.Wrap(err, "failed to execute template")
|
||||
}
|
||||
|
||||
if _, ok := optionsProcessor.FieldsUsed["Size"]; ok {
|
||||
options.Size = true
|
||||
// if `size` was not explicitly set to false (with `--size=false`)
|
||||
// and `--quiet` is not set, request size if the template requires it
|
||||
if !opts.quiet && !options.Size && !opts.sizeChanged {
|
||||
// The --size option isn't set, but .Size may be used in the template.
|
||||
// Parse and execute the given template to detect if the .Size field is
|
||||
// used. If it is, then automatically enable the --size option. See #24696
|
||||
//
|
||||
// Only requesting container size information when needed is an optimization,
|
||||
// because calculating the size is a costly operation.
|
||||
|
||||
if _, ok := optionsProcessor.FieldsUsed["Size"]; ok {
|
||||
options.Size = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -231,15 +231,56 @@ func TestContainerListFormatTemplateWithArg(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestContainerListFormatSizeSetsOption(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
containerListFunc: func(options types.ContainerListOptions) ([]types.Container, error) {
|
||||
assert.Check(t, options.Size)
|
||||
return []types.Container{}, nil
|
||||
tests := []struct {
|
||||
doc, format, sizeFlag string
|
||||
sizeExpected bool
|
||||
}{
|
||||
{
|
||||
doc: "detect with all fields",
|
||||
format: `{{json .}}`,
|
||||
sizeExpected: true,
|
||||
},
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.Flags().Set("format", `{{.Size}}`)
|
||||
assert.NilError(t, cmd.Execute())
|
||||
{
|
||||
doc: "detect with explicit field",
|
||||
format: `{{.Size}}`,
|
||||
sizeExpected: true,
|
||||
},
|
||||
{
|
||||
doc: "detect no size",
|
||||
format: `{{.Names}}`,
|
||||
sizeExpected: false,
|
||||
},
|
||||
{
|
||||
doc: "override enable",
|
||||
format: `{{.Names}}`,
|
||||
sizeFlag: "true",
|
||||
sizeExpected: true,
|
||||
},
|
||||
{
|
||||
doc: "override disable",
|
||||
format: `{{.Size}}`,
|
||||
sizeFlag: "false",
|
||||
sizeExpected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
tc := tc
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
containerListFunc: func(options types.ContainerListOptions) ([]types.Container, error) {
|
||||
assert.Check(t, is.Equal(options.Size, tc.sizeExpected))
|
||||
return []types.Container{}, nil
|
||||
},
|
||||
})
|
||||
cmd := newListCommand(cli)
|
||||
cmd.Flags().Set("format", tc.format)
|
||||
if tc.sizeFlag != "" {
|
||||
cmd.Flags().Set("size", tc.sizeFlag)
|
||||
}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerListWithConfigFormat(t *testing.T) {
|
||||
|
||||
@ -43,8 +43,8 @@ func NewLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output")
|
||||
flags.StringVar(&opts.since, "since", "", "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)")
|
||||
flags.StringVar(&opts.until, "until", "", "Show logs before a timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)")
|
||||
flags.StringVar(&opts.since, "since", "", `Show logs since timestamp (e.g. "2013-01-02T13:23:37Z") or relative (e.g. "42m" for 42 minutes)`)
|
||||
flags.StringVar(&opts.until, "until", "", `Show logs before a timestamp (e.g. "2013-01-02T13:23:37Z") or relative (e.g. "42m" for 42 minutes)`)
|
||||
flags.SetAnnotation("until", "version", []string{"1.35"})
|
||||
flags.BoolVarP(&opts.timestamps, "timestamps", "t", false, "Show timestamps")
|
||||
flags.BoolVar(&opts.details, "details", false, "Show extra details provided to logs")
|
||||
|
||||
@ -354,14 +354,13 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
toBind := bind
|
||||
|
||||
if parsed.Type == string(mounttypes.TypeBind) {
|
||||
if arr := strings.SplitN(bind, ":", 2); len(arr) == 2 {
|
||||
hostPart := arr[0]
|
||||
if hostPart, targetPath, ok := strings.Cut(bind, ":"); ok {
|
||||
if strings.HasPrefix(hostPart, "."+string(filepath.Separator)) || hostPart == "." {
|
||||
if absHostPart, err := filepath.Abs(hostPart); err == nil {
|
||||
hostPart = absHostPart
|
||||
}
|
||||
}
|
||||
toBind = hostPart + ":" + arr[1]
|
||||
toBind = hostPart + ":" + targetPath
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,11 +376,8 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
// Can't evaluate options passed into --tmpfs until we actually mount
|
||||
tmpfs := make(map[string]string)
|
||||
for _, t := range copts.tmpfs.GetAll() {
|
||||
if arr := strings.SplitN(t, ":", 2); len(arr) > 1 {
|
||||
tmpfs[arr[0]] = arr[1]
|
||||
} else {
|
||||
tmpfs[arr[0]] = ""
|
||||
}
|
||||
k, v, _ := strings.Cut(t, ":")
|
||||
tmpfs[k] = v
|
||||
}
|
||||
|
||||
var (
|
||||
@ -390,7 +386,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
)
|
||||
|
||||
if len(copts.Args) > 0 {
|
||||
runCmd = strslice.StrSlice(copts.Args)
|
||||
runCmd = copts.Args
|
||||
}
|
||||
|
||||
if copts.entrypoint != "" {
|
||||
@ -529,13 +525,11 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
if haveHealthSettings {
|
||||
return nil, errors.Errorf("--no-healthcheck conflicts with --health-* options")
|
||||
}
|
||||
test := strslice.StrSlice{"NONE"}
|
||||
healthConfig = &container.HealthConfig{Test: test}
|
||||
healthConfig = &container.HealthConfig{Test: strslice.StrSlice{"NONE"}}
|
||||
} else if haveHealthSettings {
|
||||
var probe strslice.StrSlice
|
||||
if copts.healthCmd != "" {
|
||||
args := []string{"CMD-SHELL", copts.healthCmd}
|
||||
probe = strslice.StrSlice(args)
|
||||
probe = []string{"CMD-SHELL", copts.healthCmd}
|
||||
}
|
||||
if copts.healthInterval < 0 {
|
||||
return nil, errors.Errorf("--health-interval cannot be negative")
|
||||
@ -598,24 +592,20 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
|
||||
ExposedPorts: ports,
|
||||
User: copts.user,
|
||||
Tty: copts.tty,
|
||||
// TODO: deprecated, it comes from -n, --networking
|
||||
// it's still needed internally to set the network to disabled
|
||||
// if e.g. bridge is none in daemon opts, and in inspect
|
||||
NetworkDisabled: false,
|
||||
OpenStdin: copts.stdin,
|
||||
AttachStdin: attachStdin,
|
||||
AttachStdout: attachStdout,
|
||||
AttachStderr: attachStderr,
|
||||
Env: envVariables,
|
||||
Cmd: runCmd,
|
||||
Image: copts.Image,
|
||||
Volumes: volumes,
|
||||
MacAddress: copts.macAddress,
|
||||
Entrypoint: entrypoint,
|
||||
WorkingDir: copts.workingDir,
|
||||
Labels: opts.ConvertKVStringsToMap(labels),
|
||||
StopSignal: copts.stopSignal,
|
||||
Healthcheck: healthConfig,
|
||||
OpenStdin: copts.stdin,
|
||||
AttachStdin: attachStdin,
|
||||
AttachStdout: attachStdout,
|
||||
AttachStderr: attachStderr,
|
||||
Env: envVariables,
|
||||
Cmd: runCmd,
|
||||
Image: copts.Image,
|
||||
Volumes: volumes,
|
||||
MacAddress: copts.macAddress,
|
||||
Entrypoint: entrypoint,
|
||||
WorkingDir: copts.workingDir,
|
||||
Labels: opts.ConvertKVStringsToMap(labels),
|
||||
StopSignal: copts.stopSignal,
|
||||
Healthcheck: healthConfig,
|
||||
}
|
||||
if flags.Changed("stop-timeout") {
|
||||
config.StopTimeout = &copts.stopTimeout
|
||||
@ -826,12 +816,11 @@ func convertToStandardNotation(ports []string) ([]string, error) {
|
||||
if strings.Contains(publish, "=") {
|
||||
params := map[string]string{"protocol": "tcp"}
|
||||
for _, param := range strings.Split(publish, ",") {
|
||||
opt := strings.Split(param, "=")
|
||||
if len(opt) < 2 {
|
||||
k, v, ok := strings.Cut(param, "=")
|
||||
if !ok || k == "" {
|
||||
return optsList, errors.Errorf("invalid publish opts format (should be name=value but got '%s')", param)
|
||||
}
|
||||
|
||||
params[opt[0]] = opt[1]
|
||||
params[k] = v
|
||||
}
|
||||
optsList = append(optsList, fmt.Sprintf("%s:%s/%s", params["published"], params["target"], params["protocol"]))
|
||||
} else {
|
||||
@ -852,22 +841,22 @@ func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]st
|
||||
// takes a local seccomp daemon, reads the file contents for sending to the daemon
|
||||
func parseSecurityOpts(securityOpts []string) ([]string, error) {
|
||||
for key, opt := range securityOpts {
|
||||
con := strings.SplitN(opt, "=", 2)
|
||||
if len(con) == 1 && con[0] != "no-new-privileges" {
|
||||
if strings.Contains(opt, ":") {
|
||||
con = strings.SplitN(opt, ":", 2)
|
||||
} else {
|
||||
return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt)
|
||||
}
|
||||
k, v, ok := strings.Cut(opt, "=")
|
||||
if !ok && k != "no-new-privileges" {
|
||||
k, v, ok = strings.Cut(opt, ":")
|
||||
}
|
||||
if con[0] == "seccomp" && con[1] != "unconfined" {
|
||||
f, err := os.ReadFile(con[1])
|
||||
if (!ok || v == "") && k != "no-new-privileges" {
|
||||
// "no-new-privileges" is the only option that does not require a value.
|
||||
return securityOpts, errors.Errorf("Invalid --security-opt: %q", opt)
|
||||
}
|
||||
if k == "seccomp" && v != "unconfined" {
|
||||
f, err := os.ReadFile(v)
|
||||
if err != nil {
|
||||
return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
|
||||
return securityOpts, errors.Errorf("opening seccomp profile (%s) failed: %v", v, err)
|
||||
}
|
||||
b := bytes.NewBuffer(nil)
|
||||
if err := json.Compact(b, f); err != nil {
|
||||
return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
|
||||
return securityOpts, errors.Errorf("compacting json for seccomp profile (%s) failed: %v", v, err)
|
||||
}
|
||||
securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
|
||||
}
|
||||
@ -899,12 +888,11 @@ func parseSystemPaths(securityOpts []string) (filtered, maskedPaths, readonlyPat
|
||||
func parseStorageOpts(storageOpts []string) (map[string]string, error) {
|
||||
m := make(map[string]string)
|
||||
for _, option := range storageOpts {
|
||||
if strings.Contains(option, "=") {
|
||||
opt := strings.SplitN(option, "=", 2)
|
||||
m[opt[0]] = opt[1]
|
||||
} else {
|
||||
k, v, ok := strings.Cut(option, "=")
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid storage option")
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@ -925,7 +913,8 @@ func parseDevice(device, serverOS string) (container.DeviceMapping, error) {
|
||||
func parseLinuxDevice(device string) (container.DeviceMapping, error) {
|
||||
var src, dst string
|
||||
permissions := "rwm"
|
||||
arr := strings.Split(device, ":")
|
||||
// We expect 3 parts at maximum; limit to 4 parts to detect invalid options.
|
||||
arr := strings.SplitN(device, ":", 4)
|
||||
switch len(arr) {
|
||||
case 3:
|
||||
permissions = arr[2]
|
||||
|
||||
@ -649,8 +649,8 @@ func TestRunFlagsParseShmSize(t *testing.T) {
|
||||
|
||||
func TestParseRestartPolicy(t *testing.T) {
|
||||
invalids := map[string]string{
|
||||
"always:2:3": "invalid restart policy format",
|
||||
"on-failure:invalid": "maximum retry count must be an integer",
|
||||
"always:2:3": "invalid restart policy format: maximum retry count must be an integer",
|
||||
"on-failure:invalid": "invalid restart policy format: maximum retry count must be an integer",
|
||||
}
|
||||
valids := map[string]container.RestartPolicy{
|
||||
"": {},
|
||||
|
||||
@ -42,7 +42,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=<timestamp>')")
|
||||
flags.Var(&options.filter, "filter", `Provide filter values (e.g. "until=<timestamp>")`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -27,15 +27,16 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
|
||||
var opts rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Short: "Remove one or more containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
Use: "rm [OPTIONS] CONTAINER [CONTAINER...]",
|
||||
Aliases: []string{"remove"},
|
||||
Short: "Remove one or more containers",
|
||||
Args: cli.RequiresMinArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
opts.containers = args
|
||||
return runRm(dockerCli, &opts)
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"aliases": "docker container rm, docker rm",
|
||||
"aliases": "docker container rm, docker container remove, docker rm",
|
||||
},
|
||||
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/cli/internal/test"
|
||||
@ -14,37 +15,46 @@ import (
|
||||
)
|
||||
|
||||
func TestRemoveForce(t *testing.T) {
|
||||
var (
|
||||
removed1 []string
|
||||
removed2 []string
|
||||
)
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
args []string
|
||||
expectedErr string
|
||||
}{
|
||||
{name: "without force", args: []string{"nosuchcontainer", "mycontainer"}, expectedErr: "no such container"},
|
||||
{name: "with force", args: []string{"--force", "nosuchcontainer", "mycontainer"}, expectedErr: ""},
|
||||
} {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var removed []string
|
||||
mutex := new(sync.Mutex)
|
||||
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
containerRemoveFunc: func(ctx context.Context, container string, options types.ContainerRemoveOptions) error {
|
||||
removed1 = append(removed1, container)
|
||||
removed2 = append(removed2, container)
|
||||
if container == "nosuchcontainer" {
|
||||
return errdefs.NotFound(fmt.Errorf("Error: no such container: " + container))
|
||||
cli := test.NewFakeCli(&fakeClient{
|
||||
containerRemoveFunc: func(ctx context.Context, container string, options types.ContainerRemoveOptions) error {
|
||||
// containerRemoveFunc is called in parallel for each container
|
||||
// by the remove command so append must be synchronized.
|
||||
mutex.Lock()
|
||||
removed = append(removed, container)
|
||||
mutex.Unlock()
|
||||
|
||||
if container == "nosuchcontainer" {
|
||||
return errdefs.NotFound(fmt.Errorf("Error: no such container: " + container))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Version: "1.36",
|
||||
})
|
||||
cmd := NewRmCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
|
||||
err := cmd.Execute()
|
||||
if tc.expectedErr != "" {
|
||||
assert.ErrorContains(t, err, tc.expectedErr)
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Version: "1.36",
|
||||
})
|
||||
cmd := NewRmCommand(cli)
|
||||
cmd.SetOut(io.Discard)
|
||||
|
||||
t.Run("without force", func(t *testing.T) {
|
||||
cmd.SetArgs([]string{"nosuchcontainer", "mycontainer"})
|
||||
removed1 = []string{}
|
||||
assert.ErrorContains(t, cmd.Execute(), "no such container")
|
||||
sort.Strings(removed1)
|
||||
assert.DeepEqual(t, removed1, []string{"mycontainer", "nosuchcontainer"})
|
||||
})
|
||||
t.Run("with force", func(t *testing.T) {
|
||||
cmd.SetArgs([]string{"--force", "nosuchcontainer", "mycontainer"})
|
||||
removed2 = []string{}
|
||||
assert.NilError(t, cmd.Execute())
|
||||
sort.Strings(removed2)
|
||||
assert.DeepEqual(t, removed2, []string{"mycontainer", "nosuchcontainer"})
|
||||
})
|
||||
sort.Strings(removed)
|
||||
assert.DeepEqual(t, removed, []string{"mycontainer", "nosuchcontainer"})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVar(&options.sigProxy, "sig-proxy", true, "Proxy received signals to the process")
|
||||
flags.StringVar(&options.name, "name", "", "Assign a name to the container")
|
||||
flags.StringVar(&options.detachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
|
||||
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before running ("`+PullImageAlways+`"|"`+PullImageMissing+`"|"`+PullImageNever+`")`)
|
||||
flags.StringVar(&options.pull, "pull", PullImageMissing, `Pull image before running ("`+PullImageAlways+`", "`+PullImageMissing+`", "`+PullImageNever+`")`)
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the pull output")
|
||||
|
||||
// Add an explicit help that doesn't have a `-h` to prevent the conflict
|
||||
@ -308,7 +308,8 @@ func runStartContainerErr(err error) error {
|
||||
strings.Contains(trimmedErr, "no such file or directory") ||
|
||||
strings.Contains(trimmedErr, "system cannot find the file specified") {
|
||||
statusError = cli.StatusError{StatusCode: 127}
|
||||
} else if strings.Contains(trimmedErr, syscall.EACCES.Error()) {
|
||||
} else if strings.Contains(trimmedErr, syscall.EACCES.Error()) ||
|
||||
strings.Contains(trimmedErr, syscall.EISDIR.Error()) {
|
||||
statusError = cli.StatusError{StatusCode: 126}
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.Uint16Var(&options.blkioWeight, "blkio-weight", 0, "Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)")
|
||||
flags.Uint16Var(&options.blkioWeight, "blkio-weight", 0, `Block IO (relative weight), between 10 and 1000, or 0 to disable (default 0)`)
|
||||
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period")
|
||||
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
|
||||
flags.Int64Var(&options.cpuRealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds")
|
||||
@ -68,7 +68,7 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
||||
flags.VarP(&options.memory, "memory", "m", "Memory limit")
|
||||
flags.Var(&options.memoryReservation, "memory-reservation", "Memory soft limit")
|
||||
flags.Var(&options.memorySwap, "memory-swap", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||
flags.Var(&options.memorySwap, "memory-swap", `Swap limit equal to memory plus swap: -1 to enable unlimited swap`)
|
||||
flags.Var(&options.kernelMemory, "kernel-memory", "Kernel memory limit (deprecated)")
|
||||
// --kernel-memory is deprecated on API v1.42 and up, but our current annotations
|
||||
// do not support only showing on < API-version. This option is no longer supported
|
||||
@ -77,7 +77,7 @@ func NewUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.MarkHidden("kernel-memory")
|
||||
|
||||
flags.StringVar(&options.restartPolicy, "restart", "", "Restart policy to apply when a container exits")
|
||||
flags.Int64Var(&options.pidsLimit, "pids-limit", 0, "Tune container pids limit (set -1 for unlimited)")
|
||||
flags.Int64Var(&options.pidsLimit, "pids-limit", 0, `Tune container pids limit (set -1 for unlimited)`)
|
||||
flags.SetAnnotation("pids-limit", "version", []string{"1.40"})
|
||||
|
||||
flags.Var(&options.cpus, "cpus", "Number of CPUs")
|
||||
|
||||
@ -53,7 +53,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.StringVar(&opts.Description, "description", "", "Description of the context")
|
||||
flags.String(
|
||||
"default-stack-orchestrator", "",
|
||||
"Default orchestrator for stack operations to use with this context (swarm|kubernetes|all)",
|
||||
`Default orchestrator for stack operations to use with this context ("swarm", "kubernetes", "all")`,
|
||||
)
|
||||
flags.SetAnnotation("default-stack-orchestrator", "deprecated", nil)
|
||||
flags.SetAnnotation("default-stack-orchestrator", "deprecated", nil)
|
||||
|
||||
@ -27,6 +27,9 @@ type ImageContext struct {
|
||||
}
|
||||
|
||||
func isDangling(image types.ImageSummary) bool {
|
||||
if len(image.RepoTags) == 0 && len(image.RepoDigests) == 0 {
|
||||
return true
|
||||
}
|
||||
return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
|
||||
}
|
||||
|
||||
|
||||
@ -115,13 +115,13 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags := cmd.Flags()
|
||||
|
||||
flags.VarP(&options.tags, "tag", "t", "Name and optionally a tag in the 'name:tag' format")
|
||||
flags.VarP(&options.tags, "tag", "t", `Name and optionally a tag in the "name:tag" format`)
|
||||
flags.Var(&options.buildArgs, "build-arg", "Set build-time variables")
|
||||
flags.Var(options.ulimits, "ulimit", "Ulimit options")
|
||||
flags.StringVarP(&options.dockerfileName, "file", "f", "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')")
|
||||
flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (Default is "PATH/Dockerfile")`)
|
||||
flags.VarP(&options.memory, "memory", "m", "Memory limit")
|
||||
flags.Var(&options.memorySwap, "memory-swap", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
|
||||
flags.Var(&options.shmSize, "shm-size", "Size of /dev/shm")
|
||||
flags.Var(&options.memorySwap, "memory-swap", `Swap limit equal to memory plus swap: -1 to enable unlimited swap`)
|
||||
flags.Var(&options.shmSize, "shm-size", `Size of "/dev/shm"`)
|
||||
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
|
||||
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
|
||||
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
|
||||
@ -140,7 +140,7 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.StringSliceVar(&options.securityOpt, "security-opt", []string{}, "Security options")
|
||||
flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build")
|
||||
flags.SetAnnotation("network", "version", []string{"1.25"})
|
||||
flags.Var(&options.extraHosts, "add-host", "Add a custom host-to-IP mapping (host:ip)")
|
||||
flags.Var(&options.extraHosts, "add-host", `Add a custom host-to-IP mapping ("host:ip")`)
|
||||
flags.StringVar(&options.target, "target", "", "Set the target build stage to build.")
|
||||
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ func (cli *fakeClient) ImageList(ctx context.Context, options types.ImageListOpt
|
||||
if cli.imageListFunc != nil {
|
||||
return cli.imageListFunc(options)
|
||||
}
|
||||
return []types.ImageSummary{{}}, nil
|
||||
return []types.ImageSummary{}, nil
|
||||
}
|
||||
|
||||
func (cli *fakeClient) ImageInspectWithRaw(_ context.Context, image string) (types.ImageInspect, []byte, error) {
|
||||
|
||||
@ -30,7 +30,7 @@ func TestNewImagesCommandErrors(t *testing.T) {
|
||||
name: "failed-list",
|
||||
expectedError: "something went wrong",
|
||||
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
|
||||
return []types.ImageSummary{{}}, errors.Errorf("something went wrong")
|
||||
return []types.ImageSummary{}, errors.Errorf("something went wrong")
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -66,7 +66,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
|
||||
args: []string{"image"},
|
||||
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
|
||||
assert.Check(t, is.Equal("image", options.Filters.Get("reference")[0]))
|
||||
return []types.ImageSummary{{}}, nil
|
||||
return []types.ImageSummary{}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -74,7 +74,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
|
||||
args: []string{"--filter", "name=value"},
|
||||
imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) {
|
||||
assert.Check(t, is.Equal("value", options.Filters.Get("name")[0]))
|
||||
return []types.ImageSummary{{}}, nil
|
||||
return []types.ImageSummary{}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images, not just dangling ones")
|
||||
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=<timestamp>')")
|
||||
flags.Var(&options.filter, "filter", `Provide filter values (e.g. "until=<timestamp>")`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
registryclient "github.com/docker/cli/cli/registry/client"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/ocischema"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/registry"
|
||||
@ -217,18 +218,53 @@ func buildPutManifestRequest(imageManifest types.ImageManifest, targetRef refere
|
||||
return mountRequest{}, err
|
||||
}
|
||||
|
||||
// This indentation has to be added to ensure sha parity with the registry
|
||||
v2ManifestBytes, err := json.MarshalIndent(imageManifest.SchemaV2Manifest, "", " ")
|
||||
if err != nil {
|
||||
return mountRequest{}, err
|
||||
// Attempt to reconstruct indentation of the manifest to ensure sha parity
|
||||
// with the registry - if we haven't preserved the raw content.
|
||||
//
|
||||
// This is necessary because our previous internal storage format did not
|
||||
// preserve whitespace. If we don't have the newer format present, we can
|
||||
// attempt the reconstruction like before, but explicitly error if the
|
||||
// reconstruction failed!
|
||||
switch {
|
||||
case imageManifest.SchemaV2Manifest != nil:
|
||||
dt := imageManifest.Raw
|
||||
if len(dt) == 0 {
|
||||
dt, err = json.MarshalIndent(imageManifest.SchemaV2Manifest, "", " ")
|
||||
if err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
}
|
||||
|
||||
dig := imageManifest.Descriptor.Digest
|
||||
if dig2 := dig.Algorithm().FromBytes(dt); dig != dig2 {
|
||||
return mountRequest{}, errors.Errorf("internal digest mismatch for %s: expected %s, got %s", imageManifest.Ref, dig, dig2)
|
||||
}
|
||||
|
||||
var manifest schema2.DeserializedManifest
|
||||
if err = manifest.UnmarshalJSON(dt); err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
imageManifest.SchemaV2Manifest = &manifest
|
||||
case imageManifest.OCIManifest != nil:
|
||||
dt := imageManifest.Raw
|
||||
if len(dt) == 0 {
|
||||
dt, err = json.MarshalIndent(imageManifest.OCIManifest, "", " ")
|
||||
if err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
}
|
||||
|
||||
dig := imageManifest.Descriptor.Digest
|
||||
if dig2 := dig.Algorithm().FromBytes(dt); dig != dig2 {
|
||||
return mountRequest{}, errors.Errorf("internal digest mismatch for %s: expected %s, got %s", imageManifest.Ref, dig, dig2)
|
||||
}
|
||||
|
||||
var manifest ocischema.DeserializedManifest
|
||||
if err = manifest.UnmarshalJSON(dt); err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
imageManifest.OCIManifest = &manifest
|
||||
}
|
||||
// indent only the DeserializedManifest portion of this, in order to maintain parity with the registry
|
||||
// and not alter the sha
|
||||
var v2Manifest schema2.DeserializedManifest
|
||||
if err = v2Manifest.UnmarshalJSON(v2ManifestBytes); err != nil {
|
||||
return mountRequest{}, err
|
||||
}
|
||||
imageManifest.SchemaV2Manifest = &v2Manifest
|
||||
|
||||
return mountRequest{ref: mountRef, manifest: imageManifest}, err
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"variant": "v7"
|
||||
}
|
||||
},
|
||||
"Raw": "ewogICAic2NoZW1hVmVyc2lvbiI6IDIsCiAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5kaXN0cmlidXRpb24ubWFuaWZlc3QudjIranNvbiIsCiAgICJjb25maWciOiB7CiAgICAgICJtZWRpYVR5cGUiOiAiYXBwbGljYXRpb24vdm5kLmRvY2tlci5jb250YWluZXIuaW1hZ2UudjEranNvbiIsCiAgICAgICJzaXplIjogMTUyMCwKICAgICAgImRpZ2VzdCI6ICJzaGEyNTY6NzMyOGY2ZjhiNDE4OTA1OTc1NzVjYmFhZGM4ODRlNzM4NmFlMGFjYzUzYjc0NzQwMWViY2U1Y2YwZDYyNDU2MCIKICAgfSwKICAgImxheWVycyI6IFsKICAgICAgewogICAgICAgICAibWVkaWFUeXBlIjogImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuaW1hZ2Uucm9vdGZzLmRpZmYudGFyLmd6aXAiLAogICAgICAgICAic2l6ZSI6IDE5OTA0MDIsCiAgICAgICAgICJkaWdlc3QiOiAic2hhMjU2Ojg4Mjg2ZjQxNTMwZTkzZGZmZDRiOTY0ZTFkYjIyY2U0OTM5ZmZmYTRhNGM2NjVkYWI4NTkxZmJhYjAzZDQ5MjYiCiAgICAgIH0KICAgXQp9",
|
||||
"SchemaV2Manifest": {
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
|
||||
@ -76,6 +76,8 @@ func getManifest(ctx context.Context, dockerCli command.Cli, listRef, namedRef r
|
||||
return dockerCli.RegistryClient(insecure).GetManifest(ctx, namedRef)
|
||||
case err != nil:
|
||||
return types.ImageManifest{}, err
|
||||
case len(data.Raw) == 0:
|
||||
return dockerCli.RegistryClient(insecure).GetManifest(ctx, namedRef)
|
||||
default:
|
||||
return data, nil
|
||||
}
|
||||
|
||||
@ -48,8 +48,8 @@ func newConnectCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.ipaddress, "ip", "", "IPv4 address (e.g., 172.30.100.104)")
|
||||
flags.StringVar(&options.ipv6address, "ip6", "", "IPv6 address (e.g., 2001:db8::33)")
|
||||
flags.StringVar(&options.ipaddress, "ip", "", `IPv4 address (e.g., "172.30.100.104")`)
|
||||
flags.StringVar(&options.ipv6address, "ip6", "", `IPv6 address (e.g., "2001:db8::33")`)
|
||||
flags.Var(&options.links, "link", "Add link to another container")
|
||||
flags.StringSliceVar(&options.aliases, "alias", []string{}, "Add network-scoped alias for the container")
|
||||
flags.StringSliceVar(&options.linklocalips, "link-local-ip", []string{}, "Add a link-local address for the container")
|
||||
@ -81,13 +81,13 @@ func runConnect(dockerCli command.Cli, options connectOptions) error {
|
||||
func convertDriverOpt(opts []string) (map[string]string, error) {
|
||||
driverOpt := make(map[string]string)
|
||||
for _, opt := range opts {
|
||||
parts := strings.SplitN(opt, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
k, v, ok := strings.Cut(opt, "=")
|
||||
// TODO(thaJeztah): we should probably not accept whitespace here (both for key and value).
|
||||
k = strings.TrimSpace(k)
|
||||
if !ok || k == "" {
|
||||
return nil, fmt.Errorf("invalid key/value pair format in driver options")
|
||||
}
|
||||
key := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
driverOpt[key] = value
|
||||
driverOpt[k] = strings.TrimSpace(v)
|
||||
}
|
||||
return driverOpt, nil
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display network IDs")
|
||||
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Do not truncate the output")
|
||||
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
|
||||
flags.VarP(&options.filter, "filter", "f", "Provide filter values (e.g. 'driver=bridge')")
|
||||
flags.VarP(&options.filter, "filter", "f", `Provide filter values (e.g. "driver=bridge")`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=<timestamp>')")
|
||||
flags.Var(&options.filter, "filter", `Provide filter values (e.g. "until=<timestamp>")`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -28,9 +28,9 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.role, flagRole, "", `Role of the node ("worker"|"manager")`)
|
||||
flags.StringVar(&options.availability, flagAvailability, "", `Availability of the node ("active"|"pause"|"drain")`)
|
||||
flags.Var(&options.annotations.labels, flagLabelAdd, "Add or update a node label (key=value)")
|
||||
flags.StringVar(&options.role, flagRole, "", `Role of the node ("worker", "manager")`)
|
||||
flags.StringVar(&options.availability, flagAvailability, "", `Availability of the node ("active", "pause", "drain")`)
|
||||
flags.Var(&options.annotations.labels, flagLabelAdd, `Add or update a node label ("key=value")`)
|
||||
labelKeys := opts.NewListOpts(nil)
|
||||
flags.Var(&labelKeys, flagLabelRemove, "Remove a node label if exists")
|
||||
return cmd
|
||||
|
||||
@ -40,7 +40,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display plugin IDs")
|
||||
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
|
||||
flags.VarP(&options.filter, "filter", "f", "Provide filter values (e.g. 'enabled=true')")
|
||||
flags.VarP(&options.filter, "filter", "f", `Provide filter values (e.g. "enabled=true")`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -21,8 +21,9 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ElectAuthServer returns the default registry to use
|
||||
// Deprecated: use registry.IndexServer instead
|
||||
// ElectAuthServer returns the default registry to use.
|
||||
//
|
||||
// Deprecated: use [registry.IndexServer] instead.
|
||||
func ElectAuthServer(_ context.Context, _ Cli) string {
|
||||
return registry.IndexServer
|
||||
}
|
||||
|
||||
@ -6,16 +6,13 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
|
||||
// Prevents a circular import with "github.com/docker/cli/internal/test"
|
||||
|
||||
. "github.com/docker/cli/cli/command"
|
||||
. "github.com/docker/cli/cli/command" // Prevents a circular import with "github.com/docker/cli/internal/test"
|
||||
configtypes "github.com/docker/cli/cli/config/types"
|
||||
"github.com/docker/cli/internal/test"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"gotest.tools/v3/assert"
|
||||
is "gotest.tools/v3/assert/cmp"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
|
||||
@ -33,7 +33,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&opts.mode, flagMode, "replicated", "Service mode (replicated, global, replicated-job, or global-job)")
|
||||
flags.StringVar(&opts.mode, flagMode, "replicated", `Service mode ("replicated", "global", "replicated-job", "global-job")`)
|
||||
flags.StringVar(&opts.name, flagName, "", "Service name")
|
||||
|
||||
addServiceFlags(flags, opts, buildServiceDefaultFlagMapping())
|
||||
|
||||
@ -62,7 +62,7 @@ func newLogsCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVar(&opts.noTaskIDs, "no-task-ids", false, "Do not include task IDs in output")
|
||||
// options identical to container logs
|
||||
flags.BoolVarP(&opts.follow, "follow", "f", false, "Follow log output")
|
||||
flags.StringVar(&opts.since, "since", "", "Show logs since timestamp (e.g. 2013-01-02T13:23:37Z) or relative (e.g. 42m for 42 minutes)")
|
||||
flags.StringVar(&opts.since, "since", "", `Show logs since timestamp (e.g. "2013-01-02T13:23:37Z") or relative (e.g. "42m" for 42 minutes)`)
|
||||
flags.BoolVarP(&opts.timestamps, "timestamps", "t", false, "Show timestamps")
|
||||
flags.BoolVar(&opts.details, "details", false, "Show extra details provided to logs")
|
||||
flags.SetAnnotation("details", "version", []string{"1.30"})
|
||||
|
||||
@ -93,17 +93,17 @@ func (opts *placementPrefOpts) String() string {
|
||||
// Note: in the future strategies other than "spread", may be supported,
|
||||
// as well as additional comma-separated options.
|
||||
func (opts *placementPrefOpts) Set(value string) error {
|
||||
fields := strings.Split(value, "=")
|
||||
if len(fields) != 2 {
|
||||
strategy, arg, ok := strings.Cut(value, "=")
|
||||
if !ok || strategy == "" {
|
||||
return errors.New(`placement preference must be of the format "<strategy>=<arg>"`)
|
||||
}
|
||||
if fields[0] != "spread" {
|
||||
return errors.Errorf("unsupported placement preference %s (only spread is supported)", fields[0])
|
||||
if strategy != "spread" {
|
||||
return errors.Errorf("unsupported placement preference %s (only spread is supported)", strategy)
|
||||
}
|
||||
|
||||
opts.prefs = append(opts.prefs, swarm.PlacementPreference{
|
||||
Spread: &swarm.SpreadOver{
|
||||
SpreadDescriptor: fields[1],
|
||||
SpreadDescriptor: arg,
|
||||
},
|
||||
})
|
||||
opts.strings = append(opts.strings, value)
|
||||
@ -121,8 +121,11 @@ type ShlexOpt []string
|
||||
// Set the value
|
||||
func (s *ShlexOpt) Set(value string) error {
|
||||
valueSlice, err := shlex.Split(value)
|
||||
*s = ShlexOpt(valueSlice)
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*s = valueSlice
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns the tyep of the value
|
||||
@ -475,10 +478,12 @@ func (opts *healthCheckOptions) toHealthConfig() (*container.HealthConfig, error
|
||||
//
|
||||
// This assumes input value (<host>:<ip>) has already been validated
|
||||
func convertExtraHostsToSwarmHosts(extraHosts []string) []string {
|
||||
hosts := []string{}
|
||||
hosts := make([]string, 0, len(extraHosts))
|
||||
for _, extraHost := range extraHosts {
|
||||
parts := strings.SplitN(extraHost, ":", 2)
|
||||
hosts = append(hosts, fmt.Sprintf("%s %s", parts[1], parts[0]))
|
||||
host, ip, ok := strings.Cut(extraHost, ":")
|
||||
if ok {
|
||||
hosts = append(hosts, ip+" "+host)
|
||||
}
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
@ -628,7 +633,7 @@ func (options *serviceOptions) makeEnv() ([]string, error) {
|
||||
}
|
||||
currentEnv := make([]string, 0, len(envVariables))
|
||||
for _, env := range envVariables { // need to process each var, in order
|
||||
k := strings.SplitN(env, "=", 2)[0]
|
||||
k, _, _ := strings.Cut(env, "=")
|
||||
for i, current := range currentEnv { // remove duplicates
|
||||
if current == env {
|
||||
continue // no update required, may hide this behind flag to preserve order of envVariables
|
||||
@ -852,7 +857,7 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValu
|
||||
flags.Uint64Var(&opts.maxReplicas, flagMaxReplicas, defaultFlagValues.getUint64(flagMaxReplicas), "Maximum number of tasks per node (default 0 = unlimited)")
|
||||
flags.SetAnnotation(flagMaxReplicas, "version", []string{"1.40"})
|
||||
|
||||
flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", flagDesc(flagRestartCondition, `Restart when condition is met ("none"|"on-failure"|"any")`))
|
||||
flags.StringVar(&opts.restartPolicy.condition, flagRestartCondition, "", flagDesc(flagRestartCondition, `Restart when condition is met ("none", "on-failure", "any")`))
|
||||
flags.Var(&opts.restartPolicy.delay, flagRestartDelay, flagDesc(flagRestartDelay, "Delay between restart attempts (ns|us|ms|s|m|h)"))
|
||||
flags.Var(&opts.restartPolicy.maxAttempts, flagRestartMaxAttempts, flagDesc(flagRestartMaxAttempts, "Maximum number of restarts before giving up"))
|
||||
|
||||
@ -862,10 +867,10 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValu
|
||||
flags.DurationVar(&opts.update.delay, flagUpdateDelay, 0, flagDesc(flagUpdateDelay, "Delay between updates (ns|us|ms|s|m|h)"))
|
||||
flags.DurationVar(&opts.update.monitor, flagUpdateMonitor, 0, flagDesc(flagUpdateMonitor, "Duration after each task update to monitor for failure (ns|us|ms|s|m|h)"))
|
||||
flags.SetAnnotation(flagUpdateMonitor, "version", []string{"1.25"})
|
||||
flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "", flagDesc(flagUpdateFailureAction, `Action on update failure ("pause"|"continue"|"rollback")`))
|
||||
flags.StringVar(&opts.update.onFailure, flagUpdateFailureAction, "", flagDesc(flagUpdateFailureAction, `Action on update failure ("pause", "continue", "rollback")`))
|
||||
flags.Var(&opts.update.maxFailureRatio, flagUpdateMaxFailureRatio, flagDesc(flagUpdateMaxFailureRatio, "Failure rate to tolerate during an update"))
|
||||
flags.SetAnnotation(flagUpdateMaxFailureRatio, "version", []string{"1.25"})
|
||||
flags.StringVar(&opts.update.order, flagUpdateOrder, "", flagDesc(flagUpdateOrder, `Update order ("start-first"|"stop-first")`))
|
||||
flags.StringVar(&opts.update.order, flagUpdateOrder, "", flagDesc(flagUpdateOrder, `Update order ("start-first", "stop-first")`))
|
||||
flags.SetAnnotation(flagUpdateOrder, "version", []string{"1.29"})
|
||||
|
||||
flags.Uint64Var(&opts.rollback.parallelism, flagRollbackParallelism, defaultFlagValues.getUint64(flagRollbackParallelism),
|
||||
@ -875,11 +880,11 @@ func addServiceFlags(flags *pflag.FlagSet, opts *serviceOptions, defaultFlagValu
|
||||
flags.SetAnnotation(flagRollbackDelay, "version", []string{"1.28"})
|
||||
flags.DurationVar(&opts.rollback.monitor, flagRollbackMonitor, 0, flagDesc(flagRollbackMonitor, "Duration after each task rollback to monitor for failure (ns|us|ms|s|m|h)"))
|
||||
flags.SetAnnotation(flagRollbackMonitor, "version", []string{"1.28"})
|
||||
flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "", flagDesc(flagRollbackFailureAction, `Action on rollback failure ("pause"|"continue")`))
|
||||
flags.StringVar(&opts.rollback.onFailure, flagRollbackFailureAction, "", flagDesc(flagRollbackFailureAction, `Action on rollback failure ("pause", "continue")`))
|
||||
flags.SetAnnotation(flagRollbackFailureAction, "version", []string{"1.28"})
|
||||
flags.Var(&opts.rollback.maxFailureRatio, flagRollbackMaxFailureRatio, flagDesc(flagRollbackMaxFailureRatio, "Failure rate to tolerate during a rollback"))
|
||||
flags.SetAnnotation(flagRollbackMaxFailureRatio, "version", []string{"1.28"})
|
||||
flags.StringVar(&opts.rollback.order, flagRollbackOrder, "", flagDesc(flagRollbackOrder, `Rollback order ("start-first"|"stop-first")`))
|
||||
flags.StringVar(&opts.rollback.order, flagRollbackOrder, "", flagDesc(flagRollbackOrder, `Rollback order ("start-first", "stop-first")`))
|
||||
flags.SetAnnotation(flagRollbackOrder, "version", []string{"1.29"})
|
||||
|
||||
flags.StringVar(&opts.endpoint.mode, flagEndpointMode, defaultFlagValues.getString(flagEndpointMode), "Endpoint mode (vip or dnsrr)")
|
||||
|
||||
@ -43,7 +43,7 @@ func scaleArgs(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
for _, arg := range args {
|
||||
if parts := strings.SplitN(arg, "=", 2); len(parts) != 2 {
|
||||
if k, v, ok := strings.Cut(arg, "="); !ok || k == "" || v == "" {
|
||||
return errors.Errorf(
|
||||
"Invalid scale specifier '%s'.\nSee '%s --help'.\n\nUsage: %s\n\n%s",
|
||||
arg,
|
||||
@ -62,8 +62,7 @@ func runScale(dockerCli command.Cli, options *scaleOptions, args []string) error
|
||||
ctx := context.Background()
|
||||
|
||||
for _, arg := range args {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
serviceID, scaleStr := parts[0], parts[1]
|
||||
serviceID, scaleStr, _ := strings.Cut(arg, "=")
|
||||
|
||||
// validate input arg scale number
|
||||
scale, err := strconv.ParseUint(scaleStr, 10, 64)
|
||||
|
||||
@ -62,7 +62,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.SetAnnotation(flagDNSOptionRemove, "version", []string{"1.25"})
|
||||
flags.Var(newListOptsVar(), flagDNSSearchRemove, "Remove a DNS search domain")
|
||||
flags.SetAnnotation(flagDNSSearchRemove, "version", []string{"1.25"})
|
||||
flags.Var(newListOptsVar(), flagHostRemove, "Remove a custom host-to-IP mapping (host:ip)")
|
||||
flags.Var(newListOptsVar(), flagHostRemove, `Remove a custom host-to-IP mapping ("host:ip")`)
|
||||
flags.SetAnnotation(flagHostRemove, "version", []string{"1.25"})
|
||||
flags.Var(&options.labels, flagLabelAdd, "Add or update a service label")
|
||||
flags.Var(&options.containerLabels, flagContainerLabelAdd, "Add or update a container label")
|
||||
@ -96,7 +96,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.SetAnnotation(flagDNSOptionAdd, "version", []string{"1.25"})
|
||||
flags.Var(&options.dnsSearch, flagDNSSearchAdd, "Add or update a custom DNS search domain")
|
||||
flags.SetAnnotation(flagDNSSearchAdd, "version", []string{"1.25"})
|
||||
flags.Var(&options.hosts, flagHostAdd, "Add a custom host-to-IP mapping (host:ip)")
|
||||
flags.Var(&options.hosts, flagHostAdd, `Add a custom host-to-IP mapping ("host:ip")`)
|
||||
flags.SetAnnotation(flagHostAdd, "version", []string{"1.25"})
|
||||
flags.BoolVar(&options.init, flagInit, false, "Use an init inside each service container to forward signals and reap processes")
|
||||
flags.SetAnnotation(flagInit, "version", []string{"1.37"})
|
||||
@ -879,8 +879,8 @@ func removeConfigs(flags *pflag.FlagSet, spec *swarm.ContainerSpec, credSpecName
|
||||
}
|
||||
|
||||
func envKey(value string) string {
|
||||
kv := strings.SplitN(value, "=", 2)
|
||||
return kv[0]
|
||||
k, _, _ := strings.Cut(value, "=")
|
||||
return k
|
||||
}
|
||||
|
||||
func buildToRemoveSet(flags *pflag.FlagSet, flag string) map[string]struct{} {
|
||||
@ -1174,12 +1174,8 @@ func updateHosts(flags *pflag.FlagSet, hosts *[]string) error {
|
||||
if flags.Changed(flagHostRemove) {
|
||||
extraHostsToRemove := flags.Lookup(flagHostRemove).Value.(*opts.ListOpts).GetAll()
|
||||
for _, entry := range extraHostsToRemove {
|
||||
v := strings.SplitN(entry, ":", 2)
|
||||
if len(v) > 1 {
|
||||
toRemove = append(toRemove, hostMapping{IPAddr: v[1], Host: v[0]})
|
||||
} else {
|
||||
toRemove = append(toRemove, hostMapping{Host: v[0]})
|
||||
}
|
||||
hostName, ipAddr, _ := strings.Cut(entry, ":")
|
||||
toRemove = append(toRemove, hostMapping{IPAddr: ipAddr, Host: hostName})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVar(&opts.Prune, "prune", false, "Prune services that are no longer referenced")
|
||||
flags.SetAnnotation("prune", "version", []string{"1.27"})
|
||||
flags.StringVar(&opts.ResolveImage, "resolve-image", swarm.ResolveImageAlways,
|
||||
`Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`"|"`+swarm.ResolveImageChanged+`"|"`+swarm.ResolveImageNever+`")`)
|
||||
`Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`", "`+swarm.ResolveImageChanged+`", "`+swarm.ResolveImageNever+`")`)
|
||||
flags.SetAnnotation("resolve-image", "version", []string{"1.30"})
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@ -104,12 +105,25 @@ func GetConfigDetails(composefiles []string, stdin io.Reader) (composetypes.Conf
|
||||
func buildEnvironment(env []string) (map[string]string, error) {
|
||||
result := make(map[string]string, len(env))
|
||||
for _, s := range env {
|
||||
// if value is empty, s is like "K=", not "K".
|
||||
if !strings.Contains(s, "=") {
|
||||
return result, errors.Errorf("unexpected environment %q", s)
|
||||
if runtime.GOOS == "windows" && len(s) > 0 {
|
||||
// cmd.exe can have special environment variables which names start with "=".
|
||||
// They are only there for MS-DOS compatibility and we should ignore them.
|
||||
// See TestBuildEnvironment for examples.
|
||||
//
|
||||
// https://ss64.com/nt/syntax-variables.html
|
||||
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
|
||||
// https://github.com/docker/cli/issues/4078
|
||||
if s[0] == '=' {
|
||||
continue
|
||||
}
|
||||
}
|
||||
kv := strings.SplitN(s, "=", 2)
|
||||
result[kv[0]] = kv[1]
|
||||
|
||||
k, v, ok := strings.Cut(s, "=")
|
||||
if !ok || k == "" {
|
||||
return result, errors.Errorf("unexpected environment variable '%s'", s)
|
||||
}
|
||||
// value may be set, but empty if "s" is like "K=", not "K".
|
||||
result[k] = v
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package loader
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -45,3 +46,34 @@ services:
|
||||
assert.Check(t, is.Equal("3.0", details.ConfigFiles[0].Config["version"]))
|
||||
assert.Check(t, is.Len(details.Environment, len(os.Environ())))
|
||||
}
|
||||
|
||||
func TestBuildEnvironment(t *testing.T) {
|
||||
inputEnv := []string{
|
||||
"LEGIT_VAR=LEGIT_VALUE",
|
||||
"EMPTY_VARIABLE=",
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
inputEnv = []string{
|
||||
"LEGIT_VAR=LEGIT_VALUE",
|
||||
|
||||
// cmd.exe has some special environment variables which start with "=".
|
||||
// These should be ignored as they're only there for MS-DOS compatibility.
|
||||
"=ExitCode=00000041",
|
||||
"=ExitCodeAscii=A",
|
||||
`=C:=C:\some\dir`,
|
||||
`=D:=D:\some\different\dir`,
|
||||
`=X:=X:\`,
|
||||
`=::=::\`,
|
||||
|
||||
"EMPTY_VARIABLE=",
|
||||
}
|
||||
}
|
||||
|
||||
env, err := buildEnvironment(inputEnv)
|
||||
assert.NilError(t, err)
|
||||
|
||||
assert.Check(t, is.Len(env, 2))
|
||||
assert.Check(t, is.Equal("LEGIT_VALUE", env["LEGIT_VAR"]))
|
||||
assert.Check(t, is.Equal("", env["EMPTY_VARIABLE"]))
|
||||
}
|
||||
|
||||
@ -48,15 +48,15 @@ func newInitCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.Var(&opts.listenAddr, flagListenAddr, "Listen address (format: <ip|interface>[:port])")
|
||||
flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", "Advertised address (format: <ip|interface>[:port])")
|
||||
flags.StringVar(&opts.dataPathAddr, flagDataPathAddr, "", "Address or interface to use for data path traffic (format: <ip|interface>)")
|
||||
flags.Var(&opts.listenAddr, flagListenAddr, `Listen address (format: "<ip|interface>[:port]")`)
|
||||
flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", `Advertised address (format: "<ip|interface>[:port]")`)
|
||||
flags.StringVar(&opts.dataPathAddr, flagDataPathAddr, "", `Address or interface to use for data path traffic (format: "<ip|interface>")`)
|
||||
flags.SetAnnotation(flagDataPathAddr, "version", []string{"1.31"})
|
||||
flags.Uint32Var(&opts.dataPathPort, flagDataPathPort, 0, "Port number to use for data path traffic (1024 - 49151). If no value is set or is set to 0, the default port (4789) is used.")
|
||||
flags.SetAnnotation(flagDataPathPort, "version", []string{"1.40"})
|
||||
flags.BoolVar(&opts.forceNewCluster, "force-new-cluster", false, "Force create a new cluster from current state")
|
||||
flags.BoolVar(&opts.autolock, flagAutolock, false, "Enable manager autolocking (requiring an unlock key to start a stopped manager)")
|
||||
flags.StringVar(&opts.availability, flagAvailability, "active", `Availability of the node ("active"|"pause"|"drain")`)
|
||||
flags.StringVar(&opts.availability, flagAvailability, "active", `Availability of the node ("active", "pause", "drain")`)
|
||||
flags.Var(newIPNetSliceValue([]net.IPNet{}, &opts.defaultAddrPools), flagDefaultAddrPool, "default address pool in CIDR format")
|
||||
flags.SetAnnotation(flagDefaultAddrPool, "version", []string{"1.39"})
|
||||
flags.Uint32Var(&opts.DefaultAddrPoolMaskLength, flagDefaultAddrPoolMaskLength, 24, "default address pool subnet mask length")
|
||||
|
||||
@ -43,12 +43,12 @@ func newJoinCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.Var(&opts.listenAddr, flagListenAddr, "Listen address (format: <ip|interface>[:port])")
|
||||
flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", "Advertised address (format: <ip|interface>[:port])")
|
||||
flags.StringVar(&opts.dataPathAddr, flagDataPathAddr, "", "Address or interface to use for data path traffic (format: <ip|interface>)")
|
||||
flags.Var(&opts.listenAddr, flagListenAddr, `Listen address (format: "<ip|interface>[:port]")`)
|
||||
flags.StringVar(&opts.advertiseAddr, flagAdvertiseAddr, "", `Advertised address (format: "<ip|interface>[:port]")`)
|
||||
flags.StringVar(&opts.dataPathAddr, flagDataPathAddr, "", `Address or interface to use for data path traffic (format: "<ip|interface>")`)
|
||||
flags.SetAnnotation(flagDataPathAddr, "version", []string{"1.31"})
|
||||
flags.StringVar(&opts.token, flagToken, "", "Token for entry into the swarm")
|
||||
flags.StringVar(&opts.availability, flagAvailability, "active", `Availability of the node ("active"|"pause"|"drain")`)
|
||||
flags.StringVar(&opts.availability, flagAvailability, "active", `Availability of the node ("active", "pause", "drain")`)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@ -175,14 +175,12 @@ func parseExternalCA(caSpec string) (*swarm.ExternalCA, error) {
|
||||
)
|
||||
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
|
||||
if len(parts) != 2 {
|
||||
key, value, ok := strings.Cut(field, "=")
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field)
|
||||
}
|
||||
|
||||
key, value := parts[0], parts[1]
|
||||
|
||||
// TODO(thaJeztah): these options should not be case-insensitive.
|
||||
switch strings.ToLower(key) {
|
||||
case "protocol":
|
||||
hasProtocol = true
|
||||
|
||||
@ -49,7 +49,7 @@ func newPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images not just dangling ones")
|
||||
flags.BoolVar(&options.pruneVolumes, "volumes", false, "Prune volumes")
|
||||
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'label=<key>=<value>')")
|
||||
flags.Var(&options.filter, "filter", `Provide filter values (e.g. "label=<key>=<value>")`)
|
||||
// "filter" flag is available in 1.28 (docker 17.04) and up
|
||||
flags.SetAnnotation("filter", "version", []string{"1.28"})
|
||||
|
||||
|
||||
@ -96,26 +96,26 @@ func PruneFilters(dockerCli Cli, pruneFilters filters.Args) filters.Args {
|
||||
return pruneFilters
|
||||
}
|
||||
for _, f := range dockerCli.ConfigFile().PruneFilters {
|
||||
parts := strings.SplitN(f, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
k, v, ok := strings.Cut(f, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if parts[0] == "label" {
|
||||
if k == "label" {
|
||||
// CLI label filter supersede config.json.
|
||||
// If CLI label filter conflict with config.json,
|
||||
// skip adding label! filter in config.json.
|
||||
if pruneFilters.Contains("label!") && pruneFilters.ExactMatch("label!", parts[1]) {
|
||||
if pruneFilters.Contains("label!") && pruneFilters.ExactMatch("label!", v) {
|
||||
continue
|
||||
}
|
||||
} else if parts[0] == "label!" {
|
||||
} else if k == "label!" {
|
||||
// CLI label! filter supersede config.json.
|
||||
// If CLI label! filter conflict with config.json,
|
||||
// skip adding label filter in config.json.
|
||||
if pruneFilters.Contains("label") && pruneFilters.ExactMatch("label", parts[1]) {
|
||||
if pruneFilters.Contains("label") && pruneFilters.ExactMatch("label", v) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
pruneFilters.Add(parts[0], parts[1])
|
||||
pruneFilters.Add(k, v)
|
||||
}
|
||||
|
||||
return pruneFilters
|
||||
|
||||
@ -72,16 +72,16 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.StringVar(&options.group, "group", "", "Cluster Volume group (cluster volumes)")
|
||||
flags.SetAnnotation("group", "version", []string{"1.42"})
|
||||
flags.SetAnnotation("group", "swarm", []string{"manager"})
|
||||
flags.StringVar(&options.scope, "scope", "single", `Cluster Volume access scope ("single"|"multi")`)
|
||||
flags.StringVar(&options.scope, "scope", "single", `Cluster Volume access scope ("single", "multi")`)
|
||||
flags.SetAnnotation("scope", "version", []string{"1.42"})
|
||||
flags.SetAnnotation("scope", "swarm", []string{"manager"})
|
||||
flags.StringVar(&options.sharing, "sharing", "none", `Cluster Volume access sharing ("none"|"readonly"|"onewriter"|"all")`)
|
||||
flags.StringVar(&options.sharing, "sharing", "none", `Cluster Volume access sharing ("none", "readonly", "onewriter", "all")`)
|
||||
flags.SetAnnotation("sharing", "version", []string{"1.42"})
|
||||
flags.SetAnnotation("sharing", "swarm", []string{"manager"})
|
||||
flags.StringVar(&options.availability, "availability", "active", `Cluster Volume availability ("active"|"pause"|"drain")`)
|
||||
flags.StringVar(&options.availability, "availability", "active", `Cluster Volume availability ("active", "pause", "drain")`)
|
||||
flags.SetAnnotation("availability", "version", []string{"1.42"})
|
||||
flags.SetAnnotation("availability", "swarm", []string{"manager"})
|
||||
flags.StringVar(&options.accessType, "type", "block", `Cluster Volume access type ("mount"|"block")`)
|
||||
flags.StringVar(&options.accessType, "type", "block", `Cluster Volume access type ("mount", "block")`)
|
||||
flags.SetAnnotation("type", "version", []string{"1.42"})
|
||||
flags.SetAnnotation("type", "swarm", []string{"manager"})
|
||||
flags.Var(&options.secrets, "secret", "Cluster Volume secrets")
|
||||
@ -165,9 +165,9 @@ func runCreate(dockerCli command.Cli, options createOptions) error {
|
||||
// comma-separated list of equal separated maps
|
||||
segments := map[string]string{}
|
||||
for _, segment := range strings.Split(top, ",") {
|
||||
parts := strings.SplitN(segment, "=", 2)
|
||||
// TODO(dperny): validate topology syntax
|
||||
segments[parts[0]] = parts[1]
|
||||
k, v, _ := strings.Cut(segment, "=")
|
||||
segments[k] = v
|
||||
}
|
||||
topology.Requisite = append(
|
||||
topology.Requisite,
|
||||
@ -180,9 +180,9 @@ func runCreate(dockerCli command.Cli, options createOptions) error {
|
||||
// comma-separated list of equal separated maps
|
||||
segments := map[string]string{}
|
||||
for _, segment := range strings.Split(top, ",") {
|
||||
parts := strings.SplitN(segment, "=", 2)
|
||||
// TODO(dperny): validate topology syntax
|
||||
segments[parts[0]] = parts[1]
|
||||
k, v, _ := strings.Cut(segment, "=")
|
||||
segments[k] = v
|
||||
}
|
||||
|
||||
topology.Preferred = append(
|
||||
|
||||
@ -42,7 +42,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display volume names")
|
||||
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
|
||||
flags.VarP(&options.filter, "filter", "f", "Provide filter values (e.g. 'dangling=true')")
|
||||
flags.VarP(&options.filter, "filter", "f", `Provide filter values (e.g. "dangling=true")`)
|
||||
flags.BoolVar(&options.cluster, "cluster", false, "Display only cluster volumes, and use cluster volume list formatting")
|
||||
flags.SetAnnotation("cluster", "version", []string{"1.42"})
|
||||
flags.SetAnnotation("cluster", "swarm", []string{"manager"})
|
||||
|
||||
@ -42,7 +42,7 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'label=<label>')")
|
||||
flags.Var(&options.filter, "filter", `Provide filter values (e.g. "label=<label>")`)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&availability, "availability", "active", `Cluster Volume availability ("active"|"pause"|"drain")`)
|
||||
flags.StringVar(&availability, "availability", "active", `Cluster Volume availability ("active", "pause", "drain")`)
|
||||
flags.SetAnnotation("availability", "version", []string{"1.42"})
|
||||
flags.SetAnnotation("availability", "swarm", []string{"manager"})
|
||||
|
||||
|
||||
@ -427,11 +427,11 @@ func uint32Ptr(value uint32) *uint32 {
|
||||
// convertExtraHosts converts <host>:<ip> mappings to SwarmKit notation:
|
||||
// "IP-address hostname(s)". The original order of mappings is preserved.
|
||||
func convertExtraHosts(extraHosts composetypes.HostsList) []string {
|
||||
hosts := []string{}
|
||||
hosts := make([]string, 0, len(extraHosts))
|
||||
for _, hostIP := range extraHosts {
|
||||
if v := strings.SplitN(hostIP, ":", 2); len(v) == 2 {
|
||||
if hostName, ipAddr, ok := strings.Cut(hostIP, ":"); ok {
|
||||
// Convert to SwarmKit notation: IP-address hostname(s)
|
||||
hosts = append(hosts, fmt.Sprintf("%s %s", v[1], v[0]))
|
||||
hosts = append(hosts, ipAddr+" "+hostName)
|
||||
}
|
||||
}
|
||||
return hosts
|
||||
|
||||
@ -829,21 +829,20 @@ func transformListOrMapping(listOrMapping interface{}, sep string, allowNil bool
|
||||
}
|
||||
|
||||
func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) interface{} {
|
||||
switch value := mappingOrList.(type) {
|
||||
switch values := mappingOrList.(type) {
|
||||
case map[string]interface{}:
|
||||
return toMapStringString(value, allowNil)
|
||||
case ([]interface{}):
|
||||
return toMapStringString(values, allowNil)
|
||||
case []interface{}:
|
||||
result := make(map[string]interface{})
|
||||
for _, value := range value {
|
||||
parts := strings.SplitN(value.(string), sep, 2)
|
||||
key := parts[0]
|
||||
for _, v := range values {
|
||||
key, val, hasValue := strings.Cut(v.(string), sep)
|
||||
switch {
|
||||
case len(parts) == 1 && allowNil:
|
||||
case !hasValue && allowNil:
|
||||
result[key] = nil
|
||||
case len(parts) == 1 && !allowNil:
|
||||
case !hasValue && !allowNil:
|
||||
result[key] = ""
|
||||
default:
|
||||
result[key] = parts[1]
|
||||
result[key] = val
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@ -239,9 +239,9 @@ func matchGroups(matches []string, pattern *regexp.Regexp) map[string]string {
|
||||
//
|
||||
// If the separator is not found, return the string itself, followed by an empty string.
|
||||
func partition(s, sep string) (string, string) {
|
||||
if strings.Contains(s, sep) {
|
||||
parts := strings.SplitN(s, sep, 2)
|
||||
return parts[0], parts[1]
|
||||
k, v, ok := strings.Cut(s, sep)
|
||||
if !ok {
|
||||
return s, ""
|
||||
}
|
||||
return s, ""
|
||||
return k, v
|
||||
}
|
||||
|
||||
@ -241,12 +241,11 @@ func decodeAuth(authStr string) (string, string, error) {
|
||||
if n > decLen {
|
||||
return "", "", errors.Errorf("Something went wrong decoding auth config")
|
||||
}
|
||||
arr := strings.SplitN(string(decoded), ":", 2)
|
||||
if len(arr) != 2 {
|
||||
userName, password, ok := strings.Cut(string(decoded), ":")
|
||||
if !ok || userName == "" {
|
||||
return "", "", errors.Errorf("Invalid auth configuration file")
|
||||
}
|
||||
password := strings.Trim(arr[1], "\x00")
|
||||
return arr[0], password, nil
|
||||
return userName, strings.Trim(password, "\x00"), nil
|
||||
}
|
||||
|
||||
// GetCredentialsStore returns a new credentials store from the settings in the
|
||||
@ -301,7 +300,8 @@ func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig,
|
||||
for registryHostname := range configFile.CredentialHelpers {
|
||||
newAuth, err := configFile.GetAuthConfig(registryHostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
logrus.WithError(err).Warnf("Failed to get credentials for registry: %s", registryHostname)
|
||||
continue
|
||||
}
|
||||
auths[registryHostname] = newAuth
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package configfile
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@ -166,8 +167,9 @@ func TestConfigFile(t *testing.T) {
|
||||
}
|
||||
|
||||
type mockNativeStore struct {
|
||||
GetAllCallCount int
|
||||
authConfigs map[string]types.AuthConfig
|
||||
GetAllCallCount int
|
||||
authConfigs map[string]types.AuthConfig
|
||||
authConfigErrors map[string]error
|
||||
}
|
||||
|
||||
func (c *mockNativeStore) Erase(registryHostname string) error {
|
||||
@ -176,7 +178,7 @@ func (c *mockNativeStore) Erase(registryHostname string) error {
|
||||
}
|
||||
|
||||
func (c *mockNativeStore) Get(registryHostname string) (types.AuthConfig, error) {
|
||||
return c.authConfigs[registryHostname], nil
|
||||
return c.authConfigs[registryHostname], c.authConfigErrors[registryHostname]
|
||||
}
|
||||
|
||||
func (c *mockNativeStore) GetAll() (map[string]types.AuthConfig, error) {
|
||||
@ -191,8 +193,8 @@ func (c *mockNativeStore) Store(authConfig types.AuthConfig) error {
|
||||
// make sure it satisfies the interface
|
||||
var _ credentials.Store = (*mockNativeStore)(nil)
|
||||
|
||||
func NewMockNativeStore(authConfigs map[string]types.AuthConfig) credentials.Store {
|
||||
return &mockNativeStore{authConfigs: authConfigs}
|
||||
func NewMockNativeStore(authConfigs map[string]types.AuthConfig, authConfigErrors map[string]error) credentials.Store {
|
||||
return &mockNativeStore{authConfigs: authConfigs, authConfigErrors: authConfigErrors}
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsFileStoreOnly(t *testing.T) {
|
||||
@ -220,7 +222,7 @@ func TestGetAllCredentialsCredsStore(t *testing.T) {
|
||||
Password: "pass",
|
||||
}
|
||||
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedAuth})
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedAuth}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
@ -237,6 +239,48 @@ func TestGetAllCredentialsCredsStore(t *testing.T) {
|
||||
assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount))
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsCredStoreErrorHandling(t *testing.T) {
|
||||
const (
|
||||
workingHelperRegistryHostname = "working-helper.example.com"
|
||||
brokenHelperRegistryHostname = "broken-helper.example.com"
|
||||
)
|
||||
configFile := New("filename")
|
||||
configFile.CredentialHelpers = map[string]string{
|
||||
workingHelperRegistryHostname: "cred_helper",
|
||||
brokenHelperRegistryHostname: "broken_cred_helper",
|
||||
}
|
||||
expectedAuth := types.AuthConfig{
|
||||
Username: "username",
|
||||
Password: "pass",
|
||||
}
|
||||
// configure the mock store to throw an error
|
||||
// when calling the helper for this registry
|
||||
authErrors := map[string]error{
|
||||
brokenHelperRegistryHostname: errors.New("an error"),
|
||||
}
|
||||
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{
|
||||
workingHelperRegistryHostname: expectedAuth,
|
||||
// configure an auth entry for the "broken" credential
|
||||
// helper that will throw an error
|
||||
brokenHelperRegistryHostname: {},
|
||||
}, authErrors)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
||||
return testCredsStore
|
||||
}
|
||||
|
||||
authConfigs, err := configFile.GetAllCredentials()
|
||||
|
||||
// make sure we're still returning the expected credentials
|
||||
// and skipping the ones throwing an error
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(1, len(authConfigs)))
|
||||
assert.Check(t, is.DeepEqual(expectedAuth, authConfigs[workingHelperRegistryHostname]))
|
||||
}
|
||||
|
||||
func TestGetAllCredentialsCredHelper(t *testing.T) {
|
||||
const (
|
||||
testCredHelperSuffix = "test_cred_helper"
|
||||
@ -261,7 +305,7 @@ func TestGetAllCredentialsCredHelper(t *testing.T) {
|
||||
// Add in an extra auth entry which doesn't appear in CredentialHelpers section of the configFile.
|
||||
// This verifies that only explicitly configured registries are being requested from the cred helpers.
|
||||
testExtraCredHelperRegistryHostname: unexpectedCredHelperAuth,
|
||||
})
|
||||
}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
@ -298,7 +342,7 @@ func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) {
|
||||
configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix}
|
||||
configFile.AuthConfigs[testFileStoreRegistryHostname] = expectedFileStoreAuth
|
||||
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth})
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}, nil)
|
||||
|
||||
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
||||
return testCredHelper
|
||||
@ -337,8 +381,8 @@ func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) {
|
||||
Password: "cred_helper_pass",
|
||||
}
|
||||
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth})
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testCredStoreRegistryHostname: expectedCredStoreAuth})
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}, nil)
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testCredStoreRegistryHostname: expectedCredStoreAuth}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
@ -380,8 +424,8 @@ func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) {
|
||||
Password: "cred_helper_pass",
|
||||
}
|
||||
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedCredHelperAuth})
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: unexpectedCredStoreAuth})
|
||||
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedCredHelperAuth}, nil)
|
||||
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: unexpectedCredStoreAuth}, nil)
|
||||
|
||||
tmpNewNativeStore := newNativeStore
|
||||
defer func() { newNativeStore = tmpNewNativeStore }()
|
||||
|
||||
@ -75,7 +75,6 @@ func ConvertToHostname(url string) string {
|
||||
stripped = strings.TrimPrefix(url, "https://")
|
||||
}
|
||||
|
||||
nameParts := strings.SplitN(stripped, "/", 2)
|
||||
|
||||
return nameParts[0]
|
||||
hostName, _, _ := strings.Cut(stripped, "/")
|
||||
return hostName
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ func New(ctx context.Context, cmd string, args ...string) (net.Conn, error) {
|
||||
c commandConn
|
||||
err error
|
||||
)
|
||||
c.cmd = exec.CommandContext(ctx, cmd, args...)
|
||||
c.cmd = exec.Command(cmd, args...)
|
||||
// we assume that args never contains sensitive information
|
||||
logrus.Debugf("commandconn: starting %s with %v", cmd, args)
|
||||
c.cmd.Env = os.Environ()
|
||||
|
||||
@ -2,12 +2,14 @@ package store
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -35,7 +37,7 @@ func (s *metadataStore) createOrUpdate(meta Metadata) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(contextDir, metaFile), bytes, 0o644)
|
||||
return ioutils.AtomicWriteFile(filepath.Join(contextDir, metaFile), bytes, 0o644)
|
||||
}
|
||||
|
||||
func parseTypedOrMap(payload []byte, getter TypeGetter) (interface{}, error) {
|
||||
@ -65,7 +67,8 @@ func (s *metadataStore) get(name string) (Metadata, error) {
|
||||
}
|
||||
|
||||
func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
|
||||
bytes, err := os.ReadFile(filepath.Join(s.contextDir(id), metaFile))
|
||||
fileName := filepath.Join(s.contextDir(id), metaFile)
|
||||
bytes, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return Metadata{}, errdefs.NotFound(errors.Wrap(err, "context not found"))
|
||||
@ -77,15 +80,15 @@ func (s *metadataStore) getByID(id contextdir) (Metadata, error) {
|
||||
Endpoints: make(map[string]interface{}),
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &untyped); err != nil {
|
||||
return Metadata{}, err
|
||||
return Metadata{}, fmt.Errorf("parsing %s: %v", fileName, err)
|
||||
}
|
||||
r.Name = untyped.Name
|
||||
if r.Metadata, err = parseTypedOrMap(untyped.Metadata, s.config.contextType); err != nil {
|
||||
return Metadata{}, err
|
||||
return Metadata{}, fmt.Errorf("parsing %s: %v", fileName, err)
|
||||
}
|
||||
for k, v := range untyped.Endpoints {
|
||||
if r.Endpoints[k], err = parseTypedOrMap(v, s.config.endpointTypes[k]); err != nil {
|
||||
return Metadata{}, err
|
||||
return Metadata{}, fmt.Errorf("parsing %s: %v", fileName, err)
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
|
||||
@ -7,9 +7,11 @@ import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
@ -230,3 +232,28 @@ func TestImportZipInvalid(t *testing.T) {
|
||||
err = Import("zipInvalid", s, r)
|
||||
assert.ErrorContains(t, err, "unexpected context file")
|
||||
}
|
||||
|
||||
func TestCorruptMetadata(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
s := New(tempDir, testCfg)
|
||||
err := s.CreateOrUpdate(
|
||||
Metadata{
|
||||
Endpoints: map[string]interface{}{
|
||||
"ep1": endpoint{Foo: "bar"},
|
||||
},
|
||||
Metadata: context{Bar: "baz"},
|
||||
Name: "source",
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Simulate the meta.json file getting corrupted
|
||||
// by some external process.
|
||||
contextDir := s.meta.contextDir(contextdirOf("source"))
|
||||
contextFile := filepath.Join(contextDir, metaFile)
|
||||
err = os.WriteFile(contextFile, nil, 0o600)
|
||||
assert.NilError(t, err)
|
||||
|
||||
// Assert that the error message gives the user some clue where to look.
|
||||
_, err = s.GetMetadata("source")
|
||||
assert.ErrorContains(t, err, fmt.Sprintf("parsing %s: unexpected end of JSON input", contextFile))
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -31,7 +32,7 @@ func (s *tlsStore) createOrUpdate(name, endpointName, filename string, data []by
|
||||
if err := os.MkdirAll(endpointDir, 0o700); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join(endpointDir, filename), data, 0o600)
|
||||
return ioutils.AtomicWriteFile(filepath.Join(endpointDir, filename), data, 0o600)
|
||||
}
|
||||
|
||||
func (s *tlsStore) getData(name, endpointName, filename string) ([]byte, error) {
|
||||
|
||||
@ -67,12 +67,10 @@ func (o *ClientOptions) InstallFlags(flags *pflag.FlagSet) {
|
||||
}
|
||||
|
||||
flags.BoolVarP(&o.Debug, "debug", "D", false, "Enable debug mode")
|
||||
flags.StringVarP(&o.LogLevel, "log-level", "l", "info", `Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")`)
|
||||
flags.StringVarP(&o.LogLevel, "log-level", "l", "info", `Set the logging level ("debug", "info", "warn", "error", "fatal")`)
|
||||
flags.BoolVar(&o.TLS, "tls", dockerTLS, "Use TLS; implied by --tlsverify")
|
||||
flags.BoolVar(&o.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote")
|
||||
|
||||
// TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file")
|
||||
|
||||
o.TLSOptions = &tlsconfig.Options{
|
||||
CAFile: filepath.Join(dockerCertPath, DefaultCaFile),
|
||||
CertFile: filepath.Join(dockerCertPath, DefaultCertFile),
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/ocischema"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/opencontainers/go-digest"
|
||||
@ -16,10 +17,12 @@ import (
|
||||
type ImageManifest struct {
|
||||
Ref *SerializableNamed
|
||||
Descriptor ocispec.Descriptor
|
||||
Raw []byte `json:",omitempty"`
|
||||
|
||||
// SchemaV2Manifest is used for inspection
|
||||
// TODO: Deprecate this and store manifest blobs
|
||||
SchemaV2Manifest *schema2.DeserializedManifest `json:",omitempty"`
|
||||
// OCIManifest is used for inspection
|
||||
OCIManifest *ocischema.DeserializedManifest `json:",omitempty"`
|
||||
}
|
||||
|
||||
// OCIPlatform creates an OCI platform from a manifest list platform spec
|
||||
@ -53,8 +56,15 @@ func PlatformSpecFromOCI(p *ocispec.Platform) *manifestlist.PlatformSpec {
|
||||
// Blobs returns the digests for all the blobs referenced by this manifest
|
||||
func (i ImageManifest) Blobs() []digest.Digest {
|
||||
digests := []digest.Digest{}
|
||||
for _, descriptor := range i.SchemaV2Manifest.References() {
|
||||
digests = append(digests, descriptor.Digest)
|
||||
switch {
|
||||
case i.SchemaV2Manifest != nil:
|
||||
for _, descriptor := range i.SchemaV2Manifest.References() {
|
||||
digests = append(digests, descriptor.Digest)
|
||||
}
|
||||
case i.OCIManifest != nil:
|
||||
for _, descriptor := range i.OCIManifest.References() {
|
||||
digests = append(digests, descriptor.Digest)
|
||||
}
|
||||
}
|
||||
return digests
|
||||
}
|
||||
@ -65,6 +75,8 @@ func (i ImageManifest) Payload() (string, []byte, error) {
|
||||
switch {
|
||||
case i.SchemaV2Manifest != nil:
|
||||
return i.SchemaV2Manifest.Payload()
|
||||
case i.OCIManifest != nil:
|
||||
return i.OCIManifest.Payload()
|
||||
default:
|
||||
return "", nil, errors.Errorf("%s has no payload", i.Ref)
|
||||
}
|
||||
@ -76,6 +88,8 @@ func (i ImageManifest) References() []distribution.Descriptor {
|
||||
switch {
|
||||
case i.SchemaV2Manifest != nil:
|
||||
return i.SchemaV2Manifest.References()
|
||||
case i.OCIManifest != nil:
|
||||
return i.OCIManifest.References()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -84,13 +98,35 @@ func (i ImageManifest) References() []distribution.Descriptor {
|
||||
// NewImageManifest returns a new ImageManifest object. The values for Platform
|
||||
// are initialized from those in the image
|
||||
func NewImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *schema2.DeserializedManifest) ImageManifest {
|
||||
raw, err := manifest.MarshalJSON()
|
||||
if err != nil {
|
||||
raw = nil
|
||||
}
|
||||
|
||||
return ImageManifest{
|
||||
Ref: &SerializableNamed{Named: ref},
|
||||
Descriptor: desc,
|
||||
Raw: raw,
|
||||
SchemaV2Manifest: manifest,
|
||||
}
|
||||
}
|
||||
|
||||
// NewOCIImageManifest returns a new ImageManifest object. The values for
|
||||
// Platform are initialized from those in the image
|
||||
func NewOCIImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *ocischema.DeserializedManifest) ImageManifest {
|
||||
raw, err := manifest.MarshalJSON()
|
||||
if err != nil {
|
||||
raw = nil
|
||||
}
|
||||
|
||||
return ImageManifest{
|
||||
Ref: &SerializableNamed{Named: ref},
|
||||
Descriptor: desc,
|
||||
Raw: raw,
|
||||
OCIManifest: manifest,
|
||||
}
|
||||
}
|
||||
|
||||
// SerializableNamed is a reference.Named that can be serialized and deserialized
|
||||
// from JSON
|
||||
type SerializableNamed struct {
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"github.com/docker/cli/cli/manifest/types"
|
||||
"github.com/docker/distribution"
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
"github.com/docker/distribution/manifest/ocischema"
|
||||
"github.com/docker/distribution/manifest/schema2"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
@ -35,6 +36,12 @@ func fetchManifest(ctx context.Context, repo distribution.Repository, ref refere
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
return imageManifest, nil
|
||||
case *ocischema.DeserializedManifest:
|
||||
imageManifest, err := pullManifestOCISchema(ctx, ref, repo, *v)
|
||||
if err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
return imageManifest, nil
|
||||
case *manifestlist.DeserializedManifestList:
|
||||
return types.ImageManifest{}, errors.Errorf("%s is a manifest list", ref)
|
||||
}
|
||||
@ -94,6 +101,28 @@ func pullManifestSchemaV2(ctx context.Context, ref reference.Named, repo distrib
|
||||
return types.NewImageManifest(ref, manifestDesc, &mfst), nil
|
||||
}
|
||||
|
||||
func pullManifestOCISchema(ctx context.Context, ref reference.Named, repo distribution.Repository, mfst ocischema.DeserializedManifest) (types.ImageManifest, error) {
|
||||
manifestDesc, err := validateManifestDigest(ref, mfst)
|
||||
if err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
configJSON, err := pullManifestSchemaV2ImageConfig(ctx, mfst.Target().Digest, repo)
|
||||
if err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
|
||||
if manifestDesc.Platform == nil {
|
||||
manifestDesc.Platform = &ocispec.Platform{}
|
||||
}
|
||||
|
||||
// Fill in os and architecture fields from config JSON
|
||||
if err := json.Unmarshal(configJSON, manifestDesc.Platform); err != nil {
|
||||
return types.ImageManifest{}, err
|
||||
}
|
||||
|
||||
return types.NewOCIImageManifest(ref, manifestDesc, &mfst), nil
|
||||
}
|
||||
|
||||
func pullManifestSchemaV2ImageConfig(ctx context.Context, dgst digest.Digest, repo distribution.Repository) ([]byte, error) {
|
||||
blobs := repo.Blobs(ctx)
|
||||
configJSON, err := blobs.Get(ctx, dgst)
|
||||
@ -153,16 +182,21 @@ func pullManifestList(ctx context.Context, ref reference.Named, repo distributio
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, ok := manifest.(*schema2.DeserializedManifest)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unsupported manifest format: %v", v)
|
||||
}
|
||||
|
||||
manifestRef, err := reference.WithDigest(ref, manifestDescriptor.Digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageManifest, err := pullManifestSchemaV2(ctx, manifestRef, repo, *v)
|
||||
|
||||
var imageManifest types.ImageManifest
|
||||
switch v := manifest.(type) {
|
||||
case *schema2.DeserializedManifest:
|
||||
imageManifest, err = pullManifestSchemaV2(ctx, manifestRef, repo, *v)
|
||||
case *ocischema.DeserializedManifest:
|
||||
imageManifest, err = pullManifestOCISchema(ctx, manifestRef, repo, *v)
|
||||
default:
|
||||
err = errors.Errorf("unsupported manifest type: %T", manifest)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -18,34 +18,30 @@ const (
|
||||
builderDefaultPlugin = "buildx"
|
||||
buildxMissingWarning = `DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
|
||||
Install the buildx component to build images with BuildKit:
|
||||
https://docs.docker.com/go/buildx/
|
||||
`
|
||||
https://docs.docker.com/go/buildx/`
|
||||
|
||||
buildkitDisabledWarning = `DEPRECATED: The legacy builder is deprecated and will be removed in a future release.
|
||||
BuildKit is currently disabled; enable it by removing the DOCKER_BUILDKIT=0
|
||||
environment-variable.`
|
||||
|
||||
buildxMissingError = `ERROR: BuildKit is enabled but the buildx component is missing or broken.
|
||||
Install the buildx component to build images with BuildKit:
|
||||
https://docs.docker.com/go/buildx/
|
||||
`
|
||||
https://docs.docker.com/go/buildx/`
|
||||
)
|
||||
|
||||
func newBuilderError(warn bool, err error) error {
|
||||
var errorMsg string
|
||||
if warn {
|
||||
errorMsg = buildxMissingWarning
|
||||
} else {
|
||||
errorMsg = buildxMissingError
|
||||
}
|
||||
if pluginmanager.IsNotFound(err) {
|
||||
func newBuilderError(errorMsg string, pluginLoadErr error) error {
|
||||
if pluginmanager.IsNotFound(pluginLoadErr) {
|
||||
return errors.New(errorMsg)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w\n\n%s", err, errorMsg)
|
||||
if pluginLoadErr != nil {
|
||||
return fmt.Errorf("%w\n\n%s", pluginLoadErr, errorMsg)
|
||||
}
|
||||
return fmt.Errorf("%s", errorMsg)
|
||||
return errors.New(errorMsg)
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []string) ([]string, []string, []string, error) {
|
||||
var useLegacy, useBuilder, useAlias bool
|
||||
var buildKitDisabled, useBuilder, useAlias bool
|
||||
var envs []string
|
||||
|
||||
// check DOCKER_BUILDKIT env var is present and
|
||||
@ -56,7 +52,7 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
|
||||
return args, osargs, nil, errors.Wrap(err, "DOCKER_BUILDKIT environment variable expects boolean value")
|
||||
}
|
||||
if !enabled {
|
||||
useLegacy = true
|
||||
buildKitDisabled = true
|
||||
} else {
|
||||
useBuilder = true
|
||||
}
|
||||
@ -84,10 +80,10 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
|
||||
return args, osargs, nil, nil
|
||||
}
|
||||
|
||||
if useLegacy {
|
||||
if buildKitDisabled {
|
||||
// display warning if not wcow and continue
|
||||
if dockerCli.ServerInfo().OSType != "windows" {
|
||||
_, _ = fmt.Fprintln(dockerCli.Err(), newBuilderError(true, nil))
|
||||
_, _ = fmt.Fprintf(dockerCli.Err(), "%s\n\n", buildkitDisabledWarning)
|
||||
}
|
||||
return args, osargs, nil, nil
|
||||
}
|
||||
@ -98,12 +94,13 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
|
||||
perr = plugin.Err
|
||||
}
|
||||
if perr != nil {
|
||||
// if builder enforced with DOCKER_BUILDKIT=1, cmd must fail if plugin missing or broken
|
||||
// if builder is enforced with DOCKER_BUILDKIT=1, cmd must fail
|
||||
// if the plugin is missing or broken.
|
||||
if useBuilder {
|
||||
return args, osargs, nil, newBuilderError(false, perr)
|
||||
return args, osargs, nil, newBuilderError(buildxMissingError, perr)
|
||||
}
|
||||
// otherwise, display warning and continue
|
||||
_, _ = fmt.Fprintln(dockerCli.Err(), newBuilderError(true, perr))
|
||||
_, _ = fmt.Fprintf(dockerCli.Err(), "%s\n\n", newBuilderError(buildxMissingWarning, perr))
|
||||
return args, osargs, nil, nil
|
||||
}
|
||||
|
||||
|
||||
@ -155,6 +155,7 @@ func TestBuildkitDisabled(t *testing.T) {
|
||||
|
||||
output.Assert(t, b.String(), map[int]func(string) error{
|
||||
0: output.Suffix("DEPRECATED: The legacy builder is deprecated and will be removed in a future release."),
|
||||
1: output.Suffix("BuildKit is currently disabled; enable it by removing the DOCKER_BUILDKIT=0"),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -51,6 +51,10 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
|
||||
DisableDescriptions: true,
|
||||
},
|
||||
}
|
||||
cmd.SetIn(dockerCli.In())
|
||||
cmd.SetOut(dockerCli.Out())
|
||||
cmd.SetErr(dockerCli.Err())
|
||||
|
||||
opts, flags, helpCmd = cli.SetupRootCommand(cmd)
|
||||
registerCompletionFuncForGlobalFlags(dockerCli, cmd)
|
||||
flags.BoolP("version", "v", false, "Print version information and quit")
|
||||
|
||||
@ -899,7 +899,7 @@ __docker_complete_log_drivers() {
|
||||
}
|
||||
|
||||
__docker_complete_log_options() {
|
||||
# see repository docker/docker.github.io/engine/admin/logging/
|
||||
# see https://docs.docker.com/config/containers/logging/configure/
|
||||
|
||||
# really global options, defined in https://github.com/moby/moby/blob/master/daemon/logger/factory.go
|
||||
local common_options1="max-buffer-size mode"
|
||||
@ -1142,6 +1142,29 @@ __docker_complete_user_group() {
|
||||
fi
|
||||
}
|
||||
|
||||
DOCKER_PLUGINS_PATH=$(docker info --format '{{range .ClientInfo.Plugins}}{{.Path}}:{{end}}')
|
||||
|
||||
__docker_complete_plugin() {
|
||||
local path=$1
|
||||
local completionCommand="__completeNoDesc"
|
||||
local resultArray=($path $completionCommand)
|
||||
for value in "${words[@]:2}"; do
|
||||
if [ -z "$value" ]; then
|
||||
resultArray+=( "''" )
|
||||
else
|
||||
resultArray+=( "$value" )
|
||||
fi
|
||||
done
|
||||
local result=$(eval "${resultArray[*]}" 2> /dev/null | grep -v '^:[0-9]*$')
|
||||
|
||||
# if result empty, just use filename completion as fallback
|
||||
if [ -z "$result" ]; then
|
||||
_filedir
|
||||
else
|
||||
COMPREPLY=( $(compgen -W "${result}" -- "${current-}") )
|
||||
fi
|
||||
}
|
||||
|
||||
_docker_docker() {
|
||||
# global options that may appear after the docker command
|
||||
local boolean_options="
|
||||
@ -5395,23 +5418,6 @@ _docker_wait() {
|
||||
_docker_container_wait
|
||||
}
|
||||
|
||||
COMPOSE_PLUGIN_PATH=$(docker info --format '{{range .ClientInfo.Plugins}}{{if eq .Name "compose"}}{{.Path}}{{end}}{{end}}')
|
||||
|
||||
_docker_compose() {
|
||||
local completionCommand="__completeNoDesc"
|
||||
local resultArray=($COMPOSE_PLUGIN_PATH $completionCommand compose)
|
||||
for value in "${words[@]:2}"; do
|
||||
if [ -z "$value" ]; then
|
||||
resultArray+=( "''" )
|
||||
else
|
||||
resultArray+=( "$value" )
|
||||
fi
|
||||
done
|
||||
local result=$(eval "${resultArray[*]}" 2> /dev/null | grep -v '^:[0-9]*$')
|
||||
|
||||
COMPREPLY=( $(compgen -W "${result}" -- "$current") )
|
||||
}
|
||||
|
||||
_docker() {
|
||||
local previous_extglob_setting=$(shopt -p extglob)
|
||||
shopt -s extglob
|
||||
@ -5481,11 +5487,16 @@ _docker() {
|
||||
wait
|
||||
)
|
||||
|
||||
# Create completion functions for all registered plugins
|
||||
local known_plugin_commands=()
|
||||
|
||||
if [ -f "$COMPOSE_PLUGIN_PATH" ] ; then
|
||||
known_plugin_commands+=("compose")
|
||||
fi
|
||||
local plugin_name=""
|
||||
for plugin_path in ${DOCKER_PLUGINS_PATH//:/ }; do
|
||||
plugin_name=$(basename "$plugin_path" | sed 's/ *$//')
|
||||
plugin_name=${plugin_name#docker-}
|
||||
plugin_name=${plugin_name%%.*}
|
||||
eval "_docker_${plugin_name}() { __docker_complete_plugin \"${plugin_path}\"; }"
|
||||
known_plugin_commands+=(${plugin_name})
|
||||
done
|
||||
|
||||
local experimental_server_commands=(
|
||||
checkpoint
|
||||
|
||||
@ -662,7 +662,7 @@ __docker_container_subcommand() {
|
||||
"($help)*--ulimit=[ulimit options]:ulimit: "
|
||||
"($help)--userns=[Container user namespace]:user namespace:(host)"
|
||||
"($help)--tmpfs[mount tmpfs]"
|
||||
"($help)*-v[Bind mount a volume]:volume: "
|
||||
"($help)*-v[Bind mount a volume]:volume:_directories -W / -P '/' -S '\:' -r '/ '"
|
||||
"($help)--volume-driver=[Optional volume driver for the container]:volume driver:(local)"
|
||||
"($help)*--volumes-from=[Mount volumes from the specified container]:volume: "
|
||||
"($help -w --workdir)"{-w=,--workdir=}"[Working directory inside the container]:directory:_directories"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
variable "GO_VERSION" {
|
||||
default = "1.19.3"
|
||||
default = "1.19.7"
|
||||
}
|
||||
variable "VERSION" {
|
||||
default = ""
|
||||
@ -52,7 +52,7 @@ target "binary" {
|
||||
platforms = ["local"]
|
||||
output = ["build"]
|
||||
args = {
|
||||
BASE_VARIANT = USE_GLIBC != "" ? "bullseye" : "alpine"
|
||||
BASE_VARIANT = USE_GLIBC == "1" ? "bullseye" : "alpine"
|
||||
VERSION = VERSION
|
||||
PACKAGER_NAME = PACKAGER_NAME
|
||||
GO_STRIP = STRIP_TARGET
|
||||
@ -72,7 +72,7 @@ target "plugins" {
|
||||
platforms = ["local"]
|
||||
output = ["build"]
|
||||
args = {
|
||||
BASE_VARIANT = USE_GLIBC != "" ? "bullseye" : "alpine"
|
||||
BASE_VARIANT = USE_GLIBC == "1" ? "bullseye" : "alpine"
|
||||
VERSION = VERSION
|
||||
GO_STRIP = STRIP_TARGET
|
||||
}
|
||||
@ -155,7 +155,13 @@ target "e2e-image" {
|
||||
output = ["type=docker"]
|
||||
tags = ["${IMAGE_NAME}"]
|
||||
args = {
|
||||
BASE_VARIANT = USE_GLIBC != "" ? "bullseye" : "alpine"
|
||||
BASE_VARIANT = USE_GLIBC == "1" ? "bullseye" : "alpine"
|
||||
VERSION = VERSION
|
||||
}
|
||||
}
|
||||
|
||||
target "e2e-gencerts" {
|
||||
inherits = ["_common"]
|
||||
dockerfile = "./e2e/testdata/Dockerfile.gencerts"
|
||||
output = ["./e2e/testdata"]
|
||||
}
|
||||
|
||||
@ -103,6 +103,10 @@ authors: ## generate AUTHORS file from git history
|
||||
manpages: build_docker_image ## generate man pages from go source and markdown
|
||||
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make manpages
|
||||
|
||||
.PHONY: mddocs
|
||||
mddocs: build_docker_image ## generate markdown files from go source
|
||||
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make mddocs
|
||||
|
||||
.PHONY: yamldocs
|
||||
yamldocs: build_docker_image ## generate documentation YAML files consumed by docs repo
|
||||
$(DOCKER_RUN) -it $(DEV_DOCKER_IMAGE_NAME) make yamldocs
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.19.3
|
||||
ARG GO_VERSION=1.19.7
|
||||
ARG ALPINE_VERSION=3.16
|
||||
|
||||
ARG BUILDX_VERSION=0.9.0
|
||||
ARG BUILDX_VERSION=0.10.4
|
||||
FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.19.3
|
||||
ARG GO_VERSION=1.19.7
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG GOLANGCI_LINT_VERSION=v1.49.0
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.19.3
|
||||
ARG GO_VERSION=1.19.7
|
||||
ARG ALPINE_VERSION=3.16
|
||||
ARG MODOUTDATED_VERSION=v0.8.0
|
||||
|
||||
|
||||
@ -50,8 +50,10 @@ The table below provides an overview of the current status of deprecated feature
|
||||
|
||||
| Status | Feature | Deprecated | Remove |
|
||||
|------------|------------------------------------------------------------------------------------------------------------------------------------|------------|---------|
|
||||
| Deprecated | [Buildkit build information](#buildkit-build-information) | v23.0.0 | v23.1.0 |
|
||||
| Deprecated | [Legacy builder for Linux images](#legacy-builder-for-linux-images) | v23.0.0 | - |
|
||||
| Deprecated | [Legacy builder fallback](#legacy-builder-fallback) | v23.0.0 | - |
|
||||
| Removed | [Btrfs storage driver on CentOS 7 and RHEL 7](#btrfs-storage-driver-on-centos-7-and-rhel-7) | v20.10 | v23.0.0 |
|
||||
| Removed | [Support for encrypted TLS private keys](#support-for-encrypted-tls-private-keys) | v20.10 | v23.0.0 |
|
||||
| Removed | [Kubernetes stack and context support](#kubernetes-stack-and-context-support) | v20.10 | v23.0.0 |
|
||||
| Deprecated | [Pulling images from non-compliant image registries](#pulling-images-from-non-compliant-image-registries) | v20.10 | - |
|
||||
@ -103,6 +105,17 @@ The table below provides an overview of the current status of deprecated feature
|
||||
| Removed | [`--run` flag on `docker commit`](#--run-flag-on-docker-commit) | v0.10 | v1.13 |
|
||||
| Removed | [Three arguments form in `docker import`](#three-arguments-form-in-docker-import) | v0.6.7 | v1.12 |
|
||||
|
||||
### Buildkit build information
|
||||
|
||||
**Deprecated in Release: v23.0.0**
|
||||
|
||||
[Build information](https://github.com/moby/buildkit/blob/v0.11/docs/buildinfo.md)
|
||||
structures have been introduced in [BuildKit v0.10.0](https://github.com/moby/buildkit/releases/tag/v0.10.0)
|
||||
and are generated with build metadata that allows you to see all the sources
|
||||
(images, git repositories) that were used by the build with their exact
|
||||
versions and also the configuration that was passed to the build. This
|
||||
information is also embedded into the image configuration if one is generated.
|
||||
|
||||
### Legacy builder for Linux images
|
||||
|
||||
**Deprecated in Release: v23.0.0**
|
||||
@ -123,7 +136,7 @@ This release marks the beginning of the deprecation cycle of the classic ("legac
|
||||
builder for Linux images. No active development will happen on the classic builder
|
||||
(except for bugfixes). BuildKit development started five Years ago, left the
|
||||
"experimental" phase since Docker 18.09, and is already the default builder for
|
||||
[Docker Desktop](https://docs.docker.com/desktop/mac/release-notes/3.x/#docker-desktop-320).
|
||||
[Docker Desktop](https://docs.docker.com/desktop/previous-versions/3.x-mac/#docker-desktop-320).
|
||||
While we're comfortable that BuildKit is stable for general use, there may be
|
||||
some changes in behavior. If you encounter issues with BuildKit, we encourage
|
||||
you to report issues in the [BuildKit issue tracker on GitHub](https://github.com/moby/buildkit/){:target="_blank" rel="noopener" class="_"}
|
||||
@ -180,6 +193,18 @@ Be aware that the [classic builder is deprecated](#legacy-builder-for-linux-imag
|
||||
so both the automatic fallback and opting-out of using BuildKit will no longer
|
||||
be possible in a future release.
|
||||
|
||||
### Btrfs storage driver on CentOS 7 and RHEL 7
|
||||
|
||||
**Removed in Release: v23.0.0**
|
||||
|
||||
The `btrfs` storage driver on CentOS and RHEL was provided as a technology preview
|
||||
by CentOS and RHEL, but has been deprecated since the [Red Hat Enterprise Linux 7.4 release](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/storage_administration_guide/ch-btrfs),
|
||||
and removed in CentOS 8 and RHEL 8. Users of the `btrfs` storage driver on CentOS
|
||||
are recommended to migrate to a different storage driver, such as `overlay2`, which
|
||||
is now the default storage driver. Docker 23.0 continues to provide the `btrfs`
|
||||
storage driver to allow users to migrate to an alternative driver. The next release
|
||||
of Docker will no longer provide this driver.
|
||||
|
||||
### Support for encrypted TLS private keys
|
||||
|
||||
**Deprecated in Release: v20.10**
|
||||
|
||||
@ -10,8 +10,10 @@ import (
|
||||
"os"
|
||||
|
||||
clidocstool "github.com/docker/cli-docs-tool"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/commands"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
@ -19,8 +21,9 @@ import (
|
||||
const defaultSourcePath = "docs/reference/commandline/"
|
||||
|
||||
type options struct {
|
||||
source string
|
||||
target string
|
||||
source string
|
||||
target string
|
||||
formats []string
|
||||
}
|
||||
|
||||
func gen(opts *options) error {
|
||||
@ -34,6 +37,10 @@ func gen(opts *options) error {
|
||||
Use: "docker [OPTIONS] COMMAND [ARG...]",
|
||||
Short: "The base command for the Docker CLI.",
|
||||
}
|
||||
clientOpts, _, _ := cli.SetupRootCommand(cmd)
|
||||
if err := dockerCLI.Initialize(clientOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
commands.AddCommands(cmd, dockerCLI)
|
||||
|
||||
c, err := clidocstool.New(clidocstool.Options{
|
||||
@ -46,7 +53,22 @@ func gen(opts *options) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.GenYamlTree(cmd)
|
||||
for _, format := range opts.formats {
|
||||
switch format {
|
||||
case "md":
|
||||
if err = c.GenMarkdownTree(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
case "yaml":
|
||||
if err = c.GenYamlTree(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.Errorf("unknown format %q", format)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func run() error {
|
||||
@ -54,9 +76,13 @@ func run() error {
|
||||
flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
|
||||
flags.StringVar(&opts.source, "source", defaultSourcePath, "Docs source folder")
|
||||
flags.StringVar(&opts.target, "target", defaultSourcePath, "Docs target folder")
|
||||
flags.StringSliceVar(&opts.formats, "formats", []string{}, "Format (md, yaml)")
|
||||
if err := flags.Parse(os.Args[1:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(opts.formats) == 0 {
|
||||
return errors.New("Docs format required")
|
||||
}
|
||||
return gen(opts)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module github.com/docker/cli/docs
|
||||
module github.com/docker/cli/docs/generate
|
||||
|
||||
// dummy go.mod to avoid dealing with dependencies specific
|
||||
// to docs generation and not really part of the project.
|
||||
@ -10,4 +10,4 @@ go 1.16
|
||||
// github.com/docker/cli-docs-tool v0.5.0
|
||||
//)
|
||||
//
|
||||
//replace github.com/docker/cli v0.0.0+incompatible => ../
|
||||
//replace github.com/docker/cli v0.0.0+incompatible => ../../
|
||||
@ -1,25 +1,22 @@
|
||||
---
|
||||
title: "attach"
|
||||
description: "The attach command description and usage"
|
||||
keywords: "attach, running, container"
|
||||
---
|
||||
|
||||
# attach
|
||||
|
||||
```markdown
|
||||
Usage: docker attach [OPTIONS] CONTAINER
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Attach local standard input, output, and error streams to a running container
|
||||
|
||||
Aliases:
|
||||
docker container attach, docker attach
|
||||
### Aliases
|
||||
|
||||
Options:
|
||||
--detach-keys string Override the key sequence for detaching a container
|
||||
--help Print usage
|
||||
--no-stdin Do not attach STDIN
|
||||
--sig-proxy Proxy all received signals to the process (default true)
|
||||
```
|
||||
`docker container attach`, `docker attach`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:----------------------------------------------------|
|
||||
| `--detach-keys` | `string` | | Override the key sequence for detaching a container |
|
||||
| `--no-stdin` | | | Do not attach STDIN |
|
||||
| `--sig-proxy` | | | Proxy all received signals to the process |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
@ -1,64 +1,49 @@
|
||||
---
|
||||
title: "build"
|
||||
description: "The build command description and usage"
|
||||
keywords: "build, docker, image"
|
||||
---
|
||||
|
||||
# build
|
||||
|
||||
```markdown
|
||||
Usage: docker build [OPTIONS] PATH | URL | -
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Build an image from a Dockerfile
|
||||
|
||||
Aliases:
|
||||
docker image build, docker build, docker buildx build, docker builder build
|
||||
### Aliases
|
||||
|
||||
Options:
|
||||
--add-host value Add a custom host-to-IP mapping (host:ip) (default [])
|
||||
--build-arg value Set build-time variables (default [])
|
||||
--cache-from value Images to consider as cache sources (default [])
|
||||
--cgroup-parent string Optional parent cgroup for the container
|
||||
--compress Compress the build context using gzip
|
||||
--cpu-period int Limit the CPU CFS (Completely Fair Scheduler) period
|
||||
--cpu-quota int Limit the CPU CFS (Completely Fair Scheduler) quota
|
||||
-c, --cpu-shares int CPU shares (relative weight)
|
||||
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
|
||||
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)
|
||||
--disable-content-trust Skip image verification (default true)
|
||||
-f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile')
|
||||
--force-rm Always remove intermediate containers
|
||||
--help Print usage
|
||||
--iidfile string Write the image ID to the file
|
||||
--isolation string Container isolation technology
|
||||
--label value Set metadata for an image (default [])
|
||||
-m, --memory string Memory limit
|
||||
--memory-swap string Swap limit equal to memory plus swap: '-1' to enable unlimited swap
|
||||
--network string Set the networking mode for the RUN instructions during build
|
||||
'bridge': use default Docker bridge
|
||||
'none': no networking
|
||||
'container:<name|id>': reuse another container's network stack
|
||||
'host': use the Docker host network stack
|
||||
'<network-name>|<network-id>': connect to a user-defined network
|
||||
--no-cache Do not use cache when building the image
|
||||
-o, --output Output destination (format: type=local,dest=path)
|
||||
--pull Always attempt to pull a newer version of the image
|
||||
--progress Set type of progress output (only if BuildKit enabled) (auto, plain, tty).
|
||||
Use plain to show container output
|
||||
-q, --quiet Suppress the build output and print image ID on success
|
||||
--rm Remove intermediate containers after a successful build (default true)
|
||||
--secret Secret file to expose to the build (only if BuildKit enabled): id=mysecret,src=/local/secret"
|
||||
--security-opt value Security Options (default [])
|
||||
--shm-size bytes Size of /dev/shm
|
||||
The format is `<number><unit>`. `number` must be greater than `0`.
|
||||
Unit is optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes),
|
||||
or `g` (gigabytes). If you omit the unit, the system uses bytes.
|
||||
--squash Squash newly built layers into a single new layer (**Experimental Only**)
|
||||
--ssh SSH agent socket or keys to expose to the build (only if BuildKit enabled) (format: default|<id>[=<socket>|<key>[,<key>]])
|
||||
-t, --tag value Name and optionally a tag in the 'name:tag' format (default [])
|
||||
--target string Set the target build stage to build.
|
||||
--ulimit value Ulimit options (default [])
|
||||
```
|
||||
`docker image build`, `docker build`, `docker buildx build`, `docker builder build`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------------------------|:--------------|:----------|:------------------------------------------------------------------|
|
||||
| [`--add-host`](#add-host) | `list` | | Add a custom host-to-IP mapping (`host:ip`) |
|
||||
| [`--build-arg`](#build-arg) | `list` | | Set build-time variables |
|
||||
| [`--cache-from`](#cache-from) | `stringSlice` | | Images to consider as cache sources |
|
||||
| [`--cgroup-parent`](#cgroup-parent) | `string` | | Optional parent cgroup for the container |
|
||||
| `--compress` | | | Compress the build context using gzip |
|
||||
| `--cpu-period` | `int64` | `0` | Limit the CPU CFS (Completely Fair Scheduler) period |
|
||||
| `--cpu-quota` | `int64` | `0` | Limit the CPU CFS (Completely Fair Scheduler) quota |
|
||||
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
|
||||
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
|
||||
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
|
||||
| `--disable-content-trust` | | | Skip image verification |
|
||||
| [`-f`](#file), [`--file`](#file) | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) |
|
||||
| `--force-rm` | | | Always remove intermediate containers |
|
||||
| `--iidfile` | `string` | | Write the image ID to the file |
|
||||
| [`--isolation`](#isolation) | `string` | | Container isolation technology |
|
||||
| `--label` | `list` | | Set metadata for an image |
|
||||
| `-m`, `--memory` | `bytes` | `0` | Memory limit |
|
||||
| `--memory-swap` | `bytes` | `0` | Swap limit equal to memory plus swap: -1 to enable unlimited swap |
|
||||
| `--network` | `string` | `default` | Set the networking mode for the RUN instructions during build |
|
||||
| `--no-cache` | | | Do not use cache when building the image |
|
||||
| `--platform` | `string` | | Set platform if server is multi-platform capable |
|
||||
| `--pull` | | | Always attempt to pull a newer version of the image |
|
||||
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
|
||||
| `--rm` | | | Remove intermediate containers after a successful build |
|
||||
| [`--security-opt`](#security-opt) | `stringSlice` | | Security options |
|
||||
| `--shm-size` | `bytes` | `0` | Size of `/dev/shm` |
|
||||
| [`--squash`](#squash) | | | Squash newly built layers into a single new layer |
|
||||
| [`-t`](#tag), [`--tag`](#tag) | `list` | | Name and optionally a tag in the `name:tag` format |
|
||||
| [`--target`](#target) | `string` | | Set the target build stage to build. |
|
||||
| [`--ulimit`](#ulimit) | `ulimit` | | Ulimit options |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
@ -330,7 +315,7 @@ found, the `.dockerignore` file is used if present. Using a Dockerfile based
|
||||
to ignore different sets of files.
|
||||
|
||||
|
||||
### <a name=tag></a> Tag an image (-t, --tag)
|
||||
### <a name="tag"></a> Tag an image (-t, --tag)
|
||||
|
||||
```console
|
||||
$ docker build -t vieux/apache:2.0 .
|
||||
@ -350,7 +335,7 @@ For example, to tag an image both as `whenry/fedora-jboss:latest` and
|
||||
$ docker build -t whenry/fedora-jboss:latest -t whenry/fedora-jboss:v2.1 .
|
||||
```
|
||||
|
||||
### <a name=file></a> Specify a Dockerfile (-f, --file)
|
||||
### <a name="file"></a> Specify a Dockerfile (-f, --file)
|
||||
|
||||
```console
|
||||
$ docker build -f Dockerfile.debug .
|
||||
@ -397,17 +382,17 @@ the command line.
|
||||
> repeatable builds on remote Docker hosts. This is also the reason why
|
||||
> `ADD ../file` does not work.
|
||||
|
||||
### <a name=cgroup-parent></a> Use a custom parent cgroup (--cgroup-parent)
|
||||
### <a name="cgroup-parent"></a> Use a custom parent cgroup (--cgroup-parent)
|
||||
|
||||
When `docker build` is run with the `--cgroup-parent` option the containers
|
||||
used in the build will be run with the [corresponding `docker run` flag](../run.md#specify-custom-cgroups).
|
||||
|
||||
### <a name=ulimit></a> Set ulimits in container (--ulimit)
|
||||
### <a name="ulimit"></a> Set ulimits in container (--ulimit)
|
||||
|
||||
Using the `--ulimit` option with `docker build` will cause each build step's
|
||||
container to be started using those [`--ulimit` flag values](run.md#set-ulimits-in-container---ulimit).
|
||||
container to be started using those [`--ulimit` flag values](run.md#ulimit).
|
||||
|
||||
### <a name=build-arg></a> Set build-time variables (--build-arg)
|
||||
### <a name="build-arg"></a> Set build-time variables (--build-arg)
|
||||
|
||||
You can use `ENV` instructions in a Dockerfile to define variable
|
||||
values. These values persist in the built image. However, often
|
||||
@ -442,16 +427,16 @@ $ export HTTP_PROXY=http://10.20.30.2:1234
|
||||
$ docker build --build-arg HTTP_PROXY .
|
||||
```
|
||||
|
||||
This is similar to how `docker run -e` works. Refer to the [`docker run` documentation](https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file)
|
||||
This is similar to how `docker run -e` works. Refer to the [`docker run` documentation](run.md#env)
|
||||
for more information.
|
||||
|
||||
### <a name=security-opt></a> Optional security options (--security-opt)
|
||||
### <a name="security-opt"></a> Optional security options (--security-opt)
|
||||
|
||||
This flag is only supported on a daemon running on Windows, and only supports
|
||||
the `credentialspec` option. The `credentialspec` must be in the format
|
||||
`file://spec.txt` or `registry://keyname`.
|
||||
|
||||
### <a name=isolation></a> Specify isolation technology for container (--isolation)
|
||||
### <a name="isolation"></a> Specify isolation technology for container (--isolation)
|
||||
|
||||
This option is useful in situations where you are running Docker containers on
|
||||
Windows. The `--isolation=<value>` option sets a container's isolation
|
||||
@ -467,7 +452,7 @@ Linux namespaces. On Microsoft Windows, you can specify these values:
|
||||
|
||||
Specifying the `--isolation` flag without a value is the same as setting `--isolation="default"`.
|
||||
|
||||
### <a name=add-host></a> Add entries to container hosts file (--add-host)
|
||||
### <a name="add-host"></a> Add entries to container hosts file (--add-host)
|
||||
|
||||
You can add other hosts into a container's `/etc/hosts` file by using one or
|
||||
more `--add-host` flags. This example adds a static address for a host named
|
||||
@ -475,7 +460,7 @@ more `--add-host` flags. This example adds a static address for a host named
|
||||
|
||||
$ docker build --add-host=docker:10.180.0.1 .
|
||||
|
||||
### <a name=target></a> Specifying target build stage (--target)
|
||||
### <a name="target"></a> Specifying target build stage (--target)
|
||||
|
||||
When building a Dockerfile with multiple build stages, `--target` can be used to
|
||||
specify an intermediate build stage by name as a final stage for the resulting
|
||||
@ -493,7 +478,7 @@ FROM alpine AS production-env
|
||||
$ docker build -t mybuildimage --target build-env .
|
||||
```
|
||||
|
||||
### <a name=output></a> Custom build outputs (--output)
|
||||
### <a name="output"></a> Custom build outputs (--output)
|
||||
|
||||
> **Note**
|
||||
>
|
||||
@ -587,7 +572,7 @@ $ ls ./out
|
||||
vndr
|
||||
```
|
||||
|
||||
### <a name=cache-from></a> Specifying external cache sources (--cache-from)
|
||||
### <a name="cache-from"></a> Specifying external cache sources (--cache-from)
|
||||
|
||||
> **Note**
|
||||
>
|
||||
@ -630,7 +615,7 @@ On another machine:
|
||||
$ docker build --cache-from myname/myapp .
|
||||
```
|
||||
|
||||
### <a name=squash></a> Squash an image's layers (--squash) (experimental)
|
||||
### <a name="squash"></a> Squash an image's layers (--squash) (experimental)
|
||||
|
||||
#### Overview
|
||||
|
||||
|
||||
16
docs/reference/commandline/builder.md
Normal file
16
docs/reference/commandline/builder.md
Normal file
@ -0,0 +1,16 @@
|
||||
# builder
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Manage builds
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:----------------------------|:---------------------------------|
|
||||
| [`build`](builder_build.md) | Build an image from a Dockerfile |
|
||||
| [`prune`](builder_prune.md) | Remove build cache |
|
||||
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
50
docs/reference/commandline/builder_build.md
Normal file
50
docs/reference/commandline/builder_build.md
Normal file
@ -0,0 +1,50 @@
|
||||
# builder build
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Build an image from a Dockerfile
|
||||
|
||||
### Aliases
|
||||
|
||||
`docker image build`, `docker build`, `docker buildx build`, `docker builder build`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------------|:--------------|:----------|:------------------------------------------------------------------|
|
||||
| `--add-host` | `list` | | Add a custom host-to-IP mapping (`host:ip`) |
|
||||
| `--build-arg` | `list` | | Set build-time variables |
|
||||
| `--cache-from` | `stringSlice` | | Images to consider as cache sources |
|
||||
| `--cgroup-parent` | `string` | | Optional parent cgroup for the container |
|
||||
| `--compress` | | | Compress the build context using gzip |
|
||||
| `--cpu-period` | `int64` | `0` | Limit the CPU CFS (Completely Fair Scheduler) period |
|
||||
| `--cpu-quota` | `int64` | `0` | Limit the CPU CFS (Completely Fair Scheduler) quota |
|
||||
| `-c`, `--cpu-shares` | `int64` | `0` | CPU shares (relative weight) |
|
||||
| `--cpuset-cpus` | `string` | | CPUs in which to allow execution (0-3, 0,1) |
|
||||
| `--cpuset-mems` | `string` | | MEMs in which to allow execution (0-3, 0,1) |
|
||||
| `--disable-content-trust` | | | Skip image verification |
|
||||
| `-f`, `--file` | `string` | | Name of the Dockerfile (Default is `PATH/Dockerfile`) |
|
||||
| `--force-rm` | | | Always remove intermediate containers |
|
||||
| `--iidfile` | `string` | | Write the image ID to the file |
|
||||
| `--isolation` | `string` | | Container isolation technology |
|
||||
| `--label` | `list` | | Set metadata for an image |
|
||||
| `-m`, `--memory` | `bytes` | `0` | Memory limit |
|
||||
| `--memory-swap` | `bytes` | `0` | Swap limit equal to memory plus swap: -1 to enable unlimited swap |
|
||||
| `--network` | `string` | `default` | Set the networking mode for the RUN instructions during build |
|
||||
| `--no-cache` | | | Do not use cache when building the image |
|
||||
| `--platform` | `string` | | Set platform if server is multi-platform capable |
|
||||
| `--pull` | | | Always attempt to pull a newer version of the image |
|
||||
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
|
||||
| `--rm` | | | Remove intermediate containers after a successful build |
|
||||
| `--security-opt` | `stringSlice` | | Security options |
|
||||
| `--shm-size` | `bytes` | `0` | Size of `/dev/shm` |
|
||||
| `--squash` | | | Squash newly built layers into a single new layer |
|
||||
| `-t`, `--tag` | `list` | | Name and optionally a tag in the `name:tag` format |
|
||||
| `--target` | `string` | | Set the target build stage to build. |
|
||||
| `--ulimit` | `ulimit` | | Ulimit options |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
See [docker build](build.md) for more information.
|
||||
16
docs/reference/commandline/builder_prune.md
Normal file
16
docs/reference/commandline/builder_prune.md
Normal file
@ -0,0 +1,16 @@
|
||||
# builder prune
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Remove build cache
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-----------------|:---------|:--------|:------------------------------------------------------|
|
||||
| `-a`, `--all` | | | Remove all unused build cache, not just dangling ones |
|
||||
| `--filter` | `filter` | | Provide filter values (e.g. `until=24h`) |
|
||||
| `-f`, `--force` | | | Do not prompt for confirmation |
|
||||
| `--keep-storage` | `bytes` | `0` | Amount of disk space to keep for cache |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
@ -1,9 +1,19 @@
|
||||
---
|
||||
title: docker checkpoint
|
||||
description: "The checkpoint command description and usage"
|
||||
keywords: experimental, checkpoint, restore, criu
|
||||
experimental: true
|
||||
---
|
||||
# checkpoint
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Manage checkpoints
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:---------------------------------|:---------------------------------------------|
|
||||
| [`create`](checkpoint_create.md) | Create a checkpoint from a running container |
|
||||
| [`ls`](checkpoint_ls.md) | List checkpoints for a container |
|
||||
| [`rm`](checkpoint_rm.md) | Remove a checkpoint |
|
||||
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
15
docs/reference/commandline/checkpoint_create.md
Normal file
15
docs/reference/commandline/checkpoint_create.md
Normal file
@ -0,0 +1,15 @@
|
||||
# checkpoint create
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Create a checkpoint from a running container
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------|:---------|:--------|:---------------------------------------------|
|
||||
| `--checkpoint-dir` | `string` | | Use a custom checkpoint storage directory |
|
||||
| `--leave-running` | | | Leave the container running after checkpoint |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
18
docs/reference/commandline/checkpoint_ls.md
Normal file
18
docs/reference/commandline/checkpoint_ls.md
Normal file
@ -0,0 +1,18 @@
|
||||
# checkpoint ls
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
List checkpoints for a container
|
||||
|
||||
### Aliases
|
||||
|
||||
`docker checkpoint ls`, `docker checkpoint list`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------|:---------|:--------|:------------------------------------------|
|
||||
| `--checkpoint-dir` | `string` | | Use a custom checkpoint storage directory |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
18
docs/reference/commandline/checkpoint_rm.md
Normal file
18
docs/reference/commandline/checkpoint_rm.md
Normal file
@ -0,0 +1,18 @@
|
||||
# checkpoint rm
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Remove a checkpoint
|
||||
|
||||
### Aliases
|
||||
|
||||
`docker checkpoint rm`, `docker checkpoint remove`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:-------------------|:---------|:--------|:------------------------------------------|
|
||||
| `--checkpoint-dir` | `string` | | Use a custom checkpoint storage directory |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
@ -24,31 +24,89 @@ redirect_from:
|
||||
To list available commands, either run `docker` with no parameters
|
||||
or execute `docker help`:
|
||||
|
||||
```console
|
||||
$ docker
|
||||
Usage: docker [OPTIONS] COMMAND [ARG...]
|
||||
docker [ --help | -v | --version ]
|
||||
<!---MARKER_GEN_START-->
|
||||
The base command for the Docker CLI.
|
||||
|
||||
A self-sufficient runtime for containers.
|
||||
### Subcommands
|
||||
|
||||
Options:
|
||||
--config string Location of client config files (default "/root/.docker")
|
||||
-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use")
|
||||
-D, --debug Enable debug mode
|
||||
--help Print usage
|
||||
-H, --host value Daemon socket(s) to connect to (default [])
|
||||
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
|
||||
--tls Use TLS; implied by --tlsverify
|
||||
--tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem")
|
||||
--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")
|
||||
--tlskey string Path to TLS key file (default "/root/.docker/key.pem")
|
||||
--tlsverify Use TLS and verify the remote
|
||||
-v, --version Print version information and quit
|
||||
| Name | Description |
|
||||
|:------------------------------|:------------------------------------------------------------------------------|
|
||||
| [`attach`](attach.md) | Attach local standard input, output, and error streams to a running container |
|
||||
| [`build`](build.md) | Build an image from a Dockerfile |
|
||||
| [`builder`](builder.md) | Manage builds |
|
||||
| [`checkpoint`](checkpoint.md) | Manage checkpoints |
|
||||
| [`commit`](commit.md) | Create a new image from a container's changes |
|
||||
| [`config`](config.md) | Manage Swarm configs |
|
||||
| [`container`](container.md) | Manage containers |
|
||||
| [`context`](context.md) | Manage contexts |
|
||||
| [`cp`](cp.md) | Copy files/folders between a container and the local filesystem |
|
||||
| [`create`](create.md) | Create a new container |
|
||||
| [`diff`](diff.md) | Inspect changes to files or directories on a container's filesystem |
|
||||
| [`events`](events.md) | Get real time events from the server |
|
||||
| [`exec`](exec.md) | Execute a command in a running container |
|
||||
| [`export`](export.md) | Export a container's filesystem as a tar archive |
|
||||
| [`history`](history.md) | Show the history of an image |
|
||||
| [`image`](image.md) | Manage images |
|
||||
| [`images`](images.md) | List images |
|
||||
| [`import`](import.md) | Import the contents from a tarball to create a filesystem image |
|
||||
| [`info`](info.md) | Display system-wide information |
|
||||
| [`inspect`](inspect.md) | Return low-level information on Docker objects |
|
||||
| [`kill`](kill.md) | Kill one or more running containers |
|
||||
| [`load`](load.md) | Load an image from a tar archive or STDIN |
|
||||
| [`login`](login.md) | Log in to a registry |
|
||||
| [`logout`](logout.md) | Log out from a registry |
|
||||
| [`logs`](logs.md) | Fetch the logs of a container |
|
||||
| [`manifest`](manifest.md) | Manage Docker image manifests and manifest lists |
|
||||
| [`network`](network.md) | Manage networks |
|
||||
| [`node`](node.md) | Manage Swarm nodes |
|
||||
| [`pause`](pause.md) | Pause all processes within one or more containers |
|
||||
| [`plugin`](plugin.md) | Manage plugins |
|
||||
| [`port`](port.md) | List port mappings or a specific mapping for the container |
|
||||
| [`ps`](ps.md) | List containers |
|
||||
| [`pull`](pull.md) | Download an image from a registry |
|
||||
| [`push`](push.md) | Upload an image to a registry |
|
||||
| [`rename`](rename.md) | Rename a container |
|
||||
| [`restart`](restart.md) | Restart one or more containers |
|
||||
| [`rm`](rm.md) | Remove one or more containers |
|
||||
| [`rmi`](rmi.md) | Remove one or more images |
|
||||
| [`run`](run.md) | Create and run a new container from an image |
|
||||
| [`save`](save.md) | Save one or more images to a tar archive (streamed to STDOUT by default) |
|
||||
| [`search`](search.md) | Search Docker Hub for images |
|
||||
| [`secret`](secret.md) | Manage Swarm secrets |
|
||||
| [`service`](service.md) | Manage Swarm services |
|
||||
| [`stack`](stack.md) | Manage Swarm stacks |
|
||||
| [`start`](start.md) | Start one or more stopped containers |
|
||||
| [`stats`](stats.md) | Display a live stream of container(s) resource usage statistics |
|
||||
| [`stop`](stop.md) | Stop one or more running containers |
|
||||
| [`swarm`](swarm.md) | Manage Swarm |
|
||||
| [`system`](system.md) | Manage Docker |
|
||||
| [`tag`](tag.md) | Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE |
|
||||
| [`top`](top.md) | Display the running processes of a container |
|
||||
| [`trust`](trust.md) | Manage trust on Docker images |
|
||||
| [`unpause`](unpause.md) | Unpause all processes within one or more containers |
|
||||
| [`update`](update.md) | Update configuration of one or more containers |
|
||||
| [`version`](version.md) | Show the Docker version information |
|
||||
| [`volume`](volume.md) | Manage volumes |
|
||||
| [`wait`](wait.md) | Block until one or more containers stop, then print their exit codes |
|
||||
|
||||
Commands:
|
||||
attach Attach to a running container
|
||||
# […]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------|:---------|:-------------------------|:--------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `--config` | `string` | `/root/.docker` | Location of client config files |
|
||||
| `-c`, `--context` | `string` | | Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with `docker context use`) |
|
||||
| `-D`, `--debug` | | | Enable debug mode |
|
||||
| `-H`, `--host` | `list` | | Daemon socket(s) to connect to |
|
||||
| `-l`, `--log-level` | `string` | `info` | Set the logging level (`debug`, `info`, `warn`, `error`, `fatal`) |
|
||||
| `--tls` | | | Use TLS; implied by --tlsverify |
|
||||
| `--tlscacert` | `string` | `/root/.docker/ca.pem` | Trust certs signed only by this CA |
|
||||
| `--tlscert` | `string` | `/root/.docker/cert.pem` | Path to TLS certificate file |
|
||||
| `--tlskey` | `string` | `/root/.docker/key.pem` | Path to TLS key file |
|
||||
| `--tlsverify` | | | Use TLS and verify the remote |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
@ -153,17 +211,17 @@ different location.
|
||||
These fields allow you to customize the default output format for some commands
|
||||
if no `--format` flag is provided.
|
||||
|
||||
| Property | Description |
|
||||
|:-----------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `configFormat` | Custom default format for `docker config ls` output. Refer to the [**format the output** section in the `docker config ls` documentation](config_ls.md#format-the-output) for a list of supported formatting directives. |
|
||||
| `imagesFormat` | Custom default format for `docker images` / `docker image ls` output. Refer to the [**format the output** section in the `docker images` documentation](images.md#format-the-output) for a list of supported formatting directives. |
|
||||
| `nodesFormat` | Custom default format for `docker node ls` output. Refer to the [**formatting** section in the `docker node ls` documentation](node_ls.md#formatting) for a list of supported formatting directives. |
|
||||
| `pluginsFormat` | Custom default format for `docker plugin ls` output. Refer to the [**formatting** section in the `docker plugin ls` documentation](plugin_ls.md#formatting) for a list of supported formatting directives. |
|
||||
| `psFormat` | Custom default format for `docker ps` / `docker container ps` output. Refer to the [**formatting** section in the `docker ps` documentation](ps.md#formatting) for a list of supported formatting directives. |
|
||||
| `secretFormat` | Custom default format for `docker secret ls` output. Refer to the [**format the output** section in the `docker secret ls` documentation](secret_ls.md#format-the-output) for a list of supported formatting directives. |
|
||||
| `serviceInspectFormat` | Custom default format for `docker service inspect` output. Refer to the [**formatting** section in the `docker service inspect` documentation](service_inspect.md#formatting) for a list of supported formatting directives. |
|
||||
| `servicesFormat` | Custom default format for `docker service ls` output. Refer to the [**formatting** section in the `docker service ls` documentation](service_ls.md#formatting) for a list of supported formatting directives. |
|
||||
| `statsFormat` | Custom default format for `docker stats` output. Refer to the [**formatting** section in the `docker stats` documentation](stats.md#formatting) for a list of supported formatting directives. |
|
||||
| Property | Description |
|
||||
|:-----------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `configFormat` | Custom default format for `docker config ls` output. Refer to the [**format the output** section in the `docker config ls` documentation](config_ls.md#format) for a list of supported formatting directives. |
|
||||
| `imagesFormat` | Custom default format for `docker images` / `docker image ls` output. Refer to the [**format the output** section in the `docker images` documentation](images.md#format) for a list of supported formatting directives. |
|
||||
| `nodesFormat` | Custom default format for `docker node ls` output. Refer to the [**formatting** section in the `docker node ls` documentation](node_ls.md#format) for a list of supported formatting directives. |
|
||||
| `pluginsFormat` | Custom default format for `docker plugin ls` output. Refer to the [**formatting** section in the `docker plugin ls` documentation](plugin_ls.md#format) for a list of supported formatting directives. |
|
||||
| `psFormat` | Custom default format for `docker ps` / `docker container ps` output. Refer to the [**formatting** section in the `docker ps` documentation](ps.md#format) for a list of supported formatting directives. |
|
||||
| `secretFormat` | Custom default format for `docker secret ls` output. Refer to the [**format the output** section in the `docker secret ls` documentation](secret_ls.md#format) for a list of supported formatting directives. |
|
||||
| `serviceInspectFormat` | Custom default format for `docker service inspect` output. Refer to the [**formatting** section in the `docker service inspect` documentation](service_inspect.md#format) for a list of supported formatting directives. |
|
||||
| `servicesFormat` | Custom default format for `docker service ls` output. Refer to the [**formatting** section in the `docker service ls` documentation](service_ls.md#format) for a list of supported formatting directives. |
|
||||
| `statsFormat` | Custom default format for `docker stats` output. Refer to the [**formatting** section in the `docker stats` documentation](stats.md#format) for a list of supported formatting directives. |
|
||||
|
||||
|
||||
### Custom HTTP headers
|
||||
|
||||
@ -1,26 +1,23 @@
|
||||
---
|
||||
title: "commit"
|
||||
description: "The commit command description and usage"
|
||||
keywords: "commit, file, changes"
|
||||
---
|
||||
|
||||
# commit
|
||||
|
||||
```markdown
|
||||
Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Create a new image from a container's changes
|
||||
|
||||
Aliases:
|
||||
docker container commit, docker commit
|
||||
### Aliases
|
||||
|
||||
Options:
|
||||
-a, --author string Author (e.g., "John Hannibal Smith <hannibal@a-team.com>")
|
||||
-c, --change value Apply Dockerfile instruction to the created image (default [])
|
||||
--help Print usage
|
||||
-m, --message string Commit message
|
||||
-p, --pause Pause container during commit (default true)
|
||||
```
|
||||
`docker container commit`, `docker commit`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:---------------------------------------|:---------|:--------|:-----------------------------------------------------------|
|
||||
| `-a`, `--author` | `string` | | Author (e.g., `John Hannibal Smith <hannibal@a-team.com>`) |
|
||||
| [`-c`](#change), [`--change`](#change) | `list` | | Apply Dockerfile instruction to the created image |
|
||||
| `-m`, `--message` | `string` | | Commit message |
|
||||
| `-p`, `--pause` | | | Pause container during commit |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
@ -63,7 +60,7 @@ REPOSITORY TAG ID CREATE
|
||||
svendowideit/testimage version3 f5283438590d 16 seconds ago 335.7 MB
|
||||
```
|
||||
|
||||
### <a name=change></a> Commit a container with new configurations (--change)
|
||||
### <a name="change"></a> Commit a container with new configurations (--change)
|
||||
|
||||
```console
|
||||
$ docker ps
|
||||
|
||||
@ -1,27 +1,20 @@
|
||||
---
|
||||
title: "config"
|
||||
description: "The config command description and usage"
|
||||
keywords: "config"
|
||||
---
|
||||
|
||||
# config
|
||||
|
||||
```markdown
|
||||
Usage: docker config COMMAND
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Manage Swarm configs
|
||||
|
||||
Options:
|
||||
--help Print usage
|
||||
### Subcommands
|
||||
|
||||
Commands:
|
||||
create Create a config from a file or STDIN
|
||||
inspect Display detailed information on one or more configs
|
||||
ls List configs
|
||||
rm Remove one or more configs
|
||||
| Name | Description |
|
||||
|:-------------------------------|:----------------------------------------------------|
|
||||
| [`create`](config_create.md) | Create a config from a file or STDIN |
|
||||
| [`inspect`](config_inspect.md) | Display detailed information on one or more configs |
|
||||
| [`ls`](config_ls.md) | List configs |
|
||||
| [`rm`](config_rm.md) | Remove one or more configs |
|
||||
|
||||
Run 'docker config COMMAND --help' for more information on a command.
|
||||
```
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
@ -1,20 +1,17 @@
|
||||
---
|
||||
title: "config create"
|
||||
description: "The config create command description and usage"
|
||||
keywords: ["config, create"]
|
||||
---
|
||||
|
||||
# config create
|
||||
|
||||
```Markdown
|
||||
Usage: docker config create [OPTIONS] CONFIG [file|-]
|
||||
<!---MARKER_GEN_START-->
|
||||
Create a config from a file or STDIN
|
||||
|
||||
Create a config from a file or STDIN as content
|
||||
### Options
|
||||
|
||||
Options:
|
||||
-l, --label list Config labels
|
||||
--template-driver string Template driver
|
||||
```
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------------------------|:---------|:--------|:----------------|
|
||||
| [`-l`](#label), [`--label`](#label) | `list` | | Config labels |
|
||||
| `--template-driver` | `string` | | Template driver |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
@ -57,7 +54,7 @@ ID NAME CREATED UPDATED
|
||||
dg426haahpi5ezmkkj5kyl3sn my_config 7 seconds ago 7 seconds ago
|
||||
```
|
||||
|
||||
### <a name=label></a> Create a config with labels (-l, --label)
|
||||
### <a name="label"></a> Create a config with labels (-l, --label)
|
||||
|
||||
```console
|
||||
$ docker config create \
|
||||
|
||||
@ -1,24 +1,17 @@
|
||||
---
|
||||
title: "config inspect"
|
||||
description: "The config inspect command description and usage"
|
||||
keywords: ["config, inspect"]
|
||||
---
|
||||
|
||||
# config inspect
|
||||
|
||||
```Markdown
|
||||
Usage: docker config inspect [OPTIONS] CONFIG [CONFIG...]
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Display detailed information on one or more configs
|
||||
|
||||
Options:
|
||||
-f, --format string Format output using a custom template:
|
||||
'json': Print in JSON format
|
||||
'TEMPLATE': Print output using the given Go template.
|
||||
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates
|
||||
--pretty Print the information in a human friendly format
|
||||
--help Print usage
|
||||
```
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:---------------------------------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`-f`](#format), [`--format`](#format) | `string` | | Format output using a custom 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 |
|
||||
| `--pretty` | | | Print the information in a human friendly format |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
@ -81,7 +74,7 @@ The output is in JSON format, for example:
|
||||
]
|
||||
```
|
||||
|
||||
### <a name=format></a> Format the output (--format)
|
||||
### <a name="format"></a> Format the output (--format)
|
||||
|
||||
You can use the --format option to obtain specific information about a
|
||||
config. The following example command outputs the creation time of the
|
||||
|
||||
@ -1,25 +1,22 @@
|
||||
---
|
||||
title: "config ls"
|
||||
description: "The config ls command description and usage"
|
||||
keywords: ["config, ls"]
|
||||
---
|
||||
|
||||
# config ls
|
||||
|
||||
```Markdown
|
||||
Usage: docker config ls [OPTIONS]
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
List configs
|
||||
|
||||
Aliases:
|
||||
ls, list
|
||||
### Aliases
|
||||
|
||||
Options:
|
||||
-f, --filter filter Filter output based on conditions provided
|
||||
--format string Pretty-print configs using a Go template
|
||||
--help Print usage
|
||||
-q, --quiet Only display IDs
|
||||
```
|
||||
`docker config ls`, `docker config list`
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:---------------------------------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`-f`](#filter), [`--filter`](#filter) | `filter` | | Filter output based on conditions provided |
|
||||
| [`--format`](#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 display IDs |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
@ -45,7 +42,7 @@ ID NAME CREATED UPDA
|
||||
mem02h8n73mybpgqjf0kfi1n0 test_config 3 seconds ago 3 seconds ago
|
||||
```
|
||||
|
||||
### <a name=filter></a> Filtering (-f, --filter)
|
||||
### <a name="filter"></a> Filtering (-f, --filter)
|
||||
|
||||
The filtering flag (`-f` or `--filter`) format is a `key=value` pair. If there is more
|
||||
than one filter, then pass multiple flags (e.g., `--filter "foo=bar" --filter "bif=baz"`)
|
||||
@ -105,7 +102,7 @@ ID NAME CREATED UPDA
|
||||
mem02h8n73mybpgqjf0kfi1n0 test_config About an hour ago About an hour ago
|
||||
```
|
||||
|
||||
### <a name=format></a> Format the output (--format)
|
||||
### <a name="format"></a> Format the output (--format)
|
||||
|
||||
The formatting option (`--format`) pretty prints configs output
|
||||
using a Go template.
|
||||
|
||||
@ -1,22 +1,14 @@
|
||||
---
|
||||
title: "config rm"
|
||||
description: "The config rm command description and usage"
|
||||
keywords: ["config, rm"]
|
||||
---
|
||||
|
||||
# config rm
|
||||
|
||||
```Markdown
|
||||
Usage: docker config rm CONFIG [CONFIG...]
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Remove one or more configs
|
||||
|
||||
Aliases:
|
||||
rm, remove
|
||||
### Aliases
|
||||
|
||||
Options:
|
||||
--help Print usage
|
||||
```
|
||||
`docker config rm`, `docker config remove`
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
||||
## Description
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user