Compare commits

...

169 Commits

Author SHA1 Message Date
Sebastiaan van Stijn ef1405f869
Merge e2986f4467 into 917d2dc837 2024-11-09 01:45:13 +01:00
Sebastiaan van Stijn 917d2dc837
Merge pull request #5601 from vvoland/update-go
update to go1.23.3
2024-11-07 14:42:18 +01:00
Sebastiaan van Stijn f9497b8a46
Merge pull request #5600 from aevesdocker/desktop-link-fix
docs: change link to desktop docs
2024-11-07 13:56:47 +01:00
Paweł Gronowski 382d4c34a9
update to go1.23.3
- https://github.com/golang/go/issues?q=milestone%3AGo1.23.3+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.23.2...go1.23.3

go1.23.3 (released 2024-11-06) includes fixes to the linker, the
runtime, and the net/http, os, and syscall packages. See the
[Go 1.23.3 milestone](https://github.com/golang/go/issues?q=milestone%3AGo1.23.3+label%3ACherryPickApproved)
for details.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-11-07 13:24:16 +01:00
aevesdocker 1440f9f8cf docs: change link to desktop docs
Signed-off-by: aevesdocker <allie.sadler@docker.com>
2024-11-07 09:38:19 +00:00
Sebastiaan van Stijn 9c01d924fb
Merge pull request #5595 from dvdksn/docs-redis-example-tags
docs: update example redis tags from 3.0.x to 7.4.x
2024-11-06 15:52:12 +01:00
Sebastiaan van Stijn 8c22315e31
Merge pull request #5596 from laurazard/update-jwt-dep-v4
deps: update `go-jose/go-jose` to `v4`
2024-11-05 15:29:00 +01:00
Laura Brehm 13754f6776
deps: update `go-jose/go-jose` to `v4`
See: https://github.com/go-jose/go-jose

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
2024-11-05 14:12:16 +00:00
Sebastiaan van Stijn 9eb7b52189
Merge pull request #5594 from thaJeztah/fix_golangci_go_version
golangci-lint: set go version to prevent fallback to go1.17, and fix copyloopvar linting issues
2024-11-05 11:24:44 +01:00
David Karlsson 172f340112 docs: update example redis tags from 3.0.x to 7.4.x
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-11-05 11:20:17 +01:00
Sebastiaan van Stijn 750b8ebcdc
Merge pull request #5590 from thaJeztah/bump_engine_28
vendor: github.com/docker/docker 6ac445c42bad (master, v28.0-dev)
2024-11-05 10:21:26 +01:00
Sebastiaan van Stijn 4a7b04d412
golangci-lint: set go version to prevent fallback to go1.17
GolangCI-lint attempts to deduct the Go version to lint for through the
go version specified in go.mod, which we don't have, and therefore it
falls back to go1.17 semantics:

    level=warning msg="[linters_context] copyloopvar: this linter is disabled because the Go version (1.17) of your project is lower than Go 1.22

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:15:36 +01:00
Sebastiaan van Stijn d77760fe53
cli-plugins/manager: remove redundant capturing of loop vars (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    cli-plugins/manager/cobra.go:55:4: The copy of the 'for' variable "p" can be deleted (Go 1.22+) (copyloopvar)
                p := p
                ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:32 +01:00
Sebastiaan van Stijn 32b40deb46
cli/command/service: remove redundant capturing of loop vars (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    cli/command/service/update.go:1061:3: The copy of the 'for' variable "entry" can be deleted (Go 1.22+) (copyloopvar)
            entry := entry
            ^
    cli/command/service/update.go:1089:4: The copy of the 'for' variable "port" can be deleted (Go 1.22+) (copyloopvar)
                port := port
                ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:32 +01:00
Sebastiaan van Stijn 40833fd296
cli/compose/loader: remove redundant capturing of loop vars (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    cli/compose/loader/merge.go:71:3: The copy of the 'for' variable "overrideService" can be deleted (Go 1.22+) (copyloopvar)
            overrideService := overrideService
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:32 +01:00
Sebastiaan van Stijn 78a7e15032
cli/command/container: remove redundant capturing of loop vars (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    cli/command/container/opts.go:765:3: The copy of the 'for' variable "n" can be deleted (Go 1.22+) (copyloopvar)
            n := n
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:32 +01:00
Sebastiaan van Stijn 4a71ce02e6
cli/command/image: remove redundant capturing of loop vars (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    cli/command/image/tree.go:59:4: The copy of the 'for' variable "im" can be deleted (Go 1.22+) (copyloopvar)
                im := im
                ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:31 +01:00
Sebastiaan van Stijn 7d9ea25564
templates: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:31 +01:00
Sebastiaan van Stijn 046ac9714c
service: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:31 +01:00
Sebastiaan van Stijn 762b5a8df3
opts: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:31 +01:00
Sebastiaan van Stijn 417974cdc3
cmd/docker: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:31 +01:00
Sebastiaan van Stijn bf37e26b33
cli/manifest: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:30 +01:00
Sebastiaan van Stijn 6489a777e5
e2e: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:30 +01:00
Sebastiaan van Stijn 20de861134
cli/config: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:30 +01:00
Sebastiaan van Stijn 1448cecba1
cli/compose: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:30 +01:00
Sebastiaan van Stijn 67458f710d
cli/command: remove redundant capturing of loop vars in tests (copyloopvar)
go1.22 and up now produce a unique variable in loops, tehrefore no longer
requiring to capture the variable manually;

    service/logs/parse_logs_test.go:50:3: The copy of the 'for' variable "tc" can be deleted (Go 1.22+) (copyloopvar)
            tc := tc
            ^

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-05 10:14:30 +01:00
Sebastiaan van Stijn b45477bffa
Merge pull request #5591 from noahsilas/docs-run-exit-code
docs: Correct `run` exit code 126 description
2024-11-04 17:27:25 +01:00
Noah Silas 0c999fe95b docs: Correct `run` exit code 126 description
The command to run inside the container is `/etc`. The semicolon is a
statement terminator, which ends the command `docker run busybox /etc`,
while `echo $?` prints the exit code of that full docker command.

Having this mistake could confuse someone who thinks that `/etc; echo
$?` is all run inside the container, which wouldn't help the reader
understand the exit code of the `docker run` command itself.

Signed-off-by: Noah Silas <noah@hustle.com>
2024-11-04 00:21:33 +00:00
Sebastiaan van Stijn 5f1311ae8d
vendor: github.com/docker/docker 6ac445c42bad (master, v28.0-dev)
full diff: 36a3bd0904...6ac445c42b

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 14:10:15 +01:00
Sebastiaan van Stijn 10c5a57927
vendor: go.opentelemetry.io/contrib/instrumentation/xxx v0.53.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 14:10:09 +01:00
Sebastiaan van Stijn 5e40d288c7
vendor: go.opentelemetry.io/otel v1.28.0
aligning all related packages to v1.28.0 as well

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 14:08:45 +01:00
Sebastiaan van Stijn 9ba73a1a05
vendor: github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0
full diff: https://github.com/grpc-ecosystem/grpc-gateway/compare/v2.16.0...v2.20.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 14:04:41 +01:00
Sebastiaan van Stijn f3cf1b4213
vendor: go.etcd.io/etcd/raft/v3 v3.5.16
no changes in vendored code

full diff: https://github.com/etcd-io/etcd/compare/v3.5.6...v3.5.16

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:54:25 +01:00
Sebastiaan van Stijn cae19e3928
vendor: github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6
no changes in vendored code

full diff: ced1acdcaa...e8a1dd7889

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:46:38 +01:00
Sebastiaan van Stijn 074d1028b5
vendor: update prometheus dependencies
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:44:05 +01:00
Sebastiaan van Stijn 1dbcce2057
vendor: google.golang.org/grpc v1.67.1
full diff:

- https://github.com/grpc/grpc-go/compare/v1.66.2...v1.67.1
- ef581f9131...5fefd90f89

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:39:15 +01:00
Sebastiaan van Stijn 1bba009944
vendor: google.golang.org/protobuf v1.35.1
full diff: https://github.com/protocolbuffers/protobuf-go/compare/v1.34.1...v1.35.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:31:29 +01:00
Sebastiaan van Stijn e3942d46a0
vendor: github.com/klauspost/compress v1.17.11
full diff: https://github.com/klauspost/compress/compare/v1.17.9...v1.17.11

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:28:47 +01:00
Sebastiaan van Stijn 97ff1b7c0a
vendor: github.com/go-logr/logr v1.4.2
full diff: https://github.com/go-logr/logr/compare/v1.4.1...v1.4.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:25:17 +01:00
Sebastiaan van Stijn 4c85feb4dd
vendor: github.com/cenkalti/backoff/v4 v4.3.0
full diff: https://github.com/cenkalti/backoff/compare/v4.2.1...v4.3.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:23:39 +01:00
Sebastiaan van Stijn 3b48a57b04
vendor: github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161
documentation changes only, no changes in vendored code

full diff: d185dfc1b5...306776ec81

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:20:48 +01:00
Sebastiaan van Stijn 36e6c42977
vendor: golang.org/x/net v0.30.0
full diff: https://github.com/golang/net/compare/v0.29.0...v0.30.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:17:57 +01:00
Sebastiaan van Stijn 84bfa52a6c
vendor: golang.org/x/crypto v0.28.0
no changes in vendored code

full diff: https://github.com/golang/crypto/compare/v0.27.0...v0.28.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:16:26 +01:00
Sebastiaan van Stijn 7a94f592ed
vendor: golang.org/x/text v0.19.0
no changes in vendored code

full diff: https://github.com/golang/text/compare/v0.18.0...v0.19.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:14:25 +01:00
Sebastiaan van Stijn ef197f7314
vendor: golang.org/x/term v0.25.0
no changes in vendored code

full diff: https://github.com/golang/term/compare/v0.24.0...v0.25.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:12:59 +01:00
Sebastiaan van Stijn 02b92c699d
vendor: golang.org/x/sys v0.26.0
full diff: https://github.com/golang/sys/compare/v0.25.0...v0.26.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-01 13:11:15 +01:00
Laura Brehm 2995631498
Merge pull request #5586 from Giedriusj1/master 2024-10-31 13:29:09 +00:00
Paweł Gronowski fb103cb982
Merge pull request #5583 from thaJeztah/bump_golang_1.23.2
update to go1.23.2
2024-10-31 13:10:28 +00:00
Sebastiaan van Stijn 42cda38840
update to go1.23.2
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-31 13:04:02 +01:00
Sebastiaan van Stijn 5e51513a8b
Merge pull request #5585 from thaJeztah/bump_golangci_lint
bump golangci-lint to v1.61.0 and cleanup config
2024-10-31 13:03:00 +01:00
Giedrius Jonikas 0b16070ae6 Buffer 'docker stats' text to avoid terminal flickering
This change reduces the flickering of the terminal when
running `docker stats` by buffering the formatted stats
text and printing it in one write.

Should also consume less CPU as we now only have to issue
a single syscall to write the stats text to the terminal.

Signed-off-by: Giedrius Jonikas <giedriusj1@gmail.com>
2024-10-31 11:23:57 +00:00
Sebastiaan van Stijn 9af049c618
bump golangci-lint to v1.61.0
Also updating a linter that was deprecated;

    The linter 'exportloopref' is deprecated (since v1.60.2) due to: Since Go1.22 (loopvar) this linter is no longer relevant. Replaced by copyloopvar.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-30 15:54:47 +01:00
Sebastiaan van Stijn 745629bd55
golangci-lint: update comment, and disable "exclude-dirs-use-default"
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-30 15:52:18 +01:00
Sebastiaan van Stijn 7451339ab0
golangci-lint: move gosec excludes to linters-settings
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-30 15:52:18 +01:00
Sebastiaan van Stijn 020f3a7ad9
golangci-lint: enable G204, add #nosec comments instead
There's only 3 locations where it's hit, so putting #gosec ignore comments
in those locations.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-30 15:52:18 +01:00
Sebastiaan van Stijn aa331e94cc
Merge pull request #5579 from thaJeztah/remove_warning
cli/command/container: parse: remove client-side warning
2024-10-30 10:50:34 +01:00
Sebastiaan van Stijn 1875d9fdcb
Merge pull request #5538 from albers/completion-events--filter
Completion for `events --filter`
2024-10-29 17:58:16 +01:00
Harald Albers e1c5180dba Add tests for completions that call the API
Signed-off-by: Harald Albers <github@albersweb.de>
2024-10-29 15:58:17 +00:00
Harald Albers d4f4cf1418 Add completion for `events --filter`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-10-29 15:58:17 +00:00
Sebastiaan van Stijn 59b90305f7
cli/command/container: parse: remove client-side warning
remove a client-side warning about volume drivers combined with "mounts"
in favor of producing the warning on the daemon side.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-28 23:13:31 +01:00
Sebastiaan van Stijn 32ff200fe6
Merge pull request #5573 from thaJeztah/inspect_add_configs
docker inspect: add support for swarm configs
2024-10-25 17:38:47 +02:00
Sebastiaan van Stijn e9ae9f788b
docker inspect: add support for swarm configs
The docker inspect command did not inspect configs. This patch adds support for
it, and while at it, also sorts the list of objects in runInspect.

Before this patch:

    docker config create myconfig ./codecov.yml
    danpeyh8qzb30vgdj9fr665l1

    docker inspect --format='{{.ID}}' myconfig
    []
    Error: No such object: myconfig

    docker inspect --format='{{.ID}}' --type=config myconfig
    "config" is not a valid value for --type

With this patch:

    docker inspect --format='{{.ID}}' myconfig
    danpeyh8qzb30vgdj9fr665l1

    docker inspect --format='{{.ID}}' --type=config myconfig
    danpeyh8qzb30vgdj9fr665l1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-24 18:32:32 +02:00
Sebastiaan van Stijn 61baf2a3d9
Merge pull request #5570 from thaJeztah/credentials_coverage
cli/config/credentials: add test for save being idempotent
2024-10-22 16:36:58 +02:00
Sebastiaan van Stijn c34b80bc65
Merge pull request #5567 from thaJeztah/config_fix_err
cli/config: improve error when failing to parse config file
2024-10-22 14:05:45 +02:00
Sebastiaan van Stijn ea511f0de2
Merge pull request #5549 from thaJeztah/remove_cli_errors
cli: remove deprecated Errors type
2024-10-22 12:35:42 +02:00
Sebastiaan van Stijn 3c78069240
cli/config/credentials: add test for save being idempotent
Test case for d3f6867e4d

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-22 12:22:54 +02:00
Sebastiaan van Stijn 0dd6f7f1b3
cil/config/credentials: remove newStore() test-utility
This function was names slightly confusing, as it returns a fakeStore,
and it didn't do any constructing, so didn't provide value above just
constructing the type.

I'm planning to add more functionality to the fakeStore, but don't want
to maintain a full-fledged constructor for all of that, so let's remove
this utility.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-22 10:58:29 +02:00
Sebastiaan van Stijn 5f4b14950e
cli: remove deprecated Errors type
The Errors type was deprecated in d3bafa5f3e,
which has been included in the 27.4.0 release.

This patch removes the type, as there are no external consumers.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-22 10:43:24 +02:00
Sebastiaan van Stijn 1aab64dd90
Merge pull request #5547 from thaJeztah/plugin_better_error
cli/command/plugins: use errors.Join instead of custom cli.Errors, and deprecate cli.Errors
2024-10-22 10:42:26 +02:00
Sebastiaan van Stijn 0ab0eca8bd
Merge pull request #5550 from thaJeztah/login_minor_refactor
cli/command: PromptUserForCredentials: assorted minor improvements and (linting) fixes
2024-10-21 23:23:06 +02:00
Sebastiaan van Stijn d96f8b7f91
cli/config: improve error when failing to parse config file
The format had a stray colon and space included. While fixing that, also
updating the error message to clarify the error happened while parsing
the file (not so much "loading" it).

Before:

    WARNING: Error loading config file: /root/.docker/config.json: : json: cannot unmarshal bool into Go struct field ConfigFile.features of type string

After:

    WARNING: Error parsing config file (/root/.docker/config.json): json: cannot unmarshal bool into Go struct field ConfigFile.features of type string

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-21 18:26:02 +02:00
Sebastiaan van Stijn abb8e9b78a
Merge pull request #5546 from thaJeztah/hints_coverage
cli/hints: add tests
2024-10-21 18:08:28 +02:00
Laura Brehm 7029147458
Merge pull request #5557 from thaJeztah/minor_linting_issues 2024-10-21 17:00:40 +01:00
Paweł Gronowski d2b87a0a3b
Merge pull request #5553 from thaJeztah/login_idempotent
cli/config/credentials: skip saving config-file if credentials didn't change
2024-10-21 15:23:26 +02:00
Sebastiaan van Stijn 24ee5f228a
Merge pull request #5551 from thaJeztah/fix_ConfigureAuth_deprecation
cli/command: ConfigureAuth: fix deprecation comment
2024-10-21 14:28:43 +02:00
Sebastiaan van Stijn 8b6133a2b7
Merge pull request #5544 from thaJeztah/bump_engine_28
vendor: github.com/docker/docker 36a3bd090489 (master, v28.0-dev)
2024-10-21 13:28:35 +02:00
Sebastiaan van Stijn d3f6867e4d
cli/config/credentials: skip saving config-file if credentials didn't change
Before this change, the config-file was always updated, even if there
were no changes to save. This could cause issues when the config-file
already had credentials set and was read-only for the current user.

For example, on NixOS, this poses a problem because `config.json` is a
symlink to a write-protected file;

    $ readlink ~/.docker/config.json
    /home/username/.config/sops-nix/secrets/ghcr_auth

    $ readlink -f ~/.docker/config.json
    /run/user/1000/secrets.d/28/ghcr_auth

Which causes `docker login` to fail, even if no changes were to be made;

    Error saving credentials: rename /home/derek/.docker/config.json2180380217 /home/username/.config/sops-nix/secrets/ghcr_auth: invalid cross-device link

This patch updates the code to only update the config file if changes
were detected. It there's nothing to save, it skips updating the file,
as well as skips printing the warning about credentials being stored
insecurely.

With this patch applied:

    $ docker login -u yourname
    Password:

    WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
    Configure a credential helper to remove this warning. See
    https://docs.docker.com/go/credential-store/

    Login Succeeded

    $ docker login -u yourname
    Password:
    Login Succeeded

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-21 00:19:52 +02:00
Sebastiaan van Stijn 6b9083776f
cli/command: AddPlatformFlag: suppress unhandled error
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-20 17:51:36 +02:00
Sebastiaan van Stijn fb61156b05
cli/command/registry: fix minor linting issues
- fix camelCase naming of verifyLoginOptions
- suppress unhandled errors that can be ignored

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-20 17:51:12 +02:00
Sebastiaan van Stijn 062eecf14a
Merge pull request #5554 from albers/fix-completion-events-filter-daemon
Fix bash completion for `events --filter daemon=`
2024-10-19 17:55:12 +02:00
Harald Albers 3f7b156c85 Fix bash completion for `events --filter daemon=`
Signed-off-by: Harald Albers <github@albersweb.de>
2024-10-19 15:40:07 +00:00
Sebastiaan van Stijn 4b7a1e4613
cli/command: PromptUserForCredentials: suppress unhandled errors
Keep the linters (and my IDE) happy; these errors should be safe to ignore.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 13:24:19 +02:00
Sebastiaan van Stijn 378a3d7d36
cli/command: PromptUserForCredentials: use consts for all hints
This message resulted in code-lines that were too long; move it to a
const together with the other hint. While at it, also suppress unhandled
error, and touch-up the code-comment.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 13:23:29 +02:00
Sebastiaan van Stijn 54e3685bcd
cli/command: ConfigureAuth: fix deprecation comment
Deprecation comments must have an empty line before them, otherwise tools
and linters may not recognise them. While fixing this, also updated the
reference to PromptUserForCredentials to be a docs-link to make it clickable.

Updates 6e4818e7d6.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 13:05:31 +02:00
Sebastiaan van Stijn 3d8b49523d
cli/command: PromptUserForCredentials: print error on terminal restore fail
If restoring the terminal state fails, "echo" no longer works, which means
that anything the user types is no longer shown. The login itself may already
have succeeded, so we should not fail the command, but it's good to inform
the user that this happened, which may give them a clue why things no longer
work as they expect them to work.

With this patch:

    docker login -u yourname
    Password:
    Error: failed to restore terminal state to echo input: something bad happened

    Login Succeeded

We should consider printing instructions how  to restore this manually (other
than restarting the shell). e.g., 'run stty echo' when in a Linux or macOS shell,
but PowerShell and CMD.exe may need different instructions.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 12:49:44 +02:00
Sebastiaan van Stijn a21a5f4243
cli/command: PromptUserForCredentials: always trim password
we don't support empty passwords; when prompting the user for a password,
we already trim the result, but we didn't do the same for a password that's
passed through stdin or through the `-p` / `--password` flag.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 12:10:46 +02:00
Sebastiaan van Stijn eda78e9cdc
cli/command: PromptUserForCredentials: move trimming where it's used
- move trimming defaultUsername inside the if-branch, as it's the only
  location where the result of the trimmed username is use.
- do the reverse for trimming argUser, because the result of trimming
  argUser is used outside of the if-branch (not just for the condition).
  putting it inside the condition makes it easy to assume the result is
  only used locally.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 12:07:51 +02:00
Sebastiaan van Stijn 581cf36bd4
cli/command: PromptUserForCredentials: move "post" check for empty name
move the "post" check for username being empty inside the branch
that's handling the username, as it's the only branch where username
is mutated after checking if it's empty.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 12:06:49 +02:00
Sebastiaan van Stijn a55cfe5f82
cli/command: PromptUserForCredentials: inline isDefaultRegistry
remove isDefaultRegistry and inline it where it's used; the code-comment
already outlines what we're looking for, so the intermediate var didn't
add much currently.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 11:58:42 +02:00
Sebastiaan van Stijn 3a8485085d
cli/command: PromptUserForCredentials: remove named output variables
This function has multiple conditional branches, which makes it harder
to see at a glance whether authConfig may be partially populated. This
patch instead returns a fresh instance for error returns to prevent any
confusion.

It also removes the named output variables, as they're now no longer used,
and the returned types should already be descriptive enough to understand
what's returned.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 11:46:21 +02:00
Sebastiaan van Stijn d3bafa5f3e
cli: deprecate Errors type
The Errors type is no longer used by the CLI itself, and this custom
"multi-error" implementation had both limitations (empty list not being
`nil`), as well as formatting not being great. All of this making it not
something to recommend, and better handled with Go's stdlib.

As far as I could find, there's no external consumers of this, but let's
deprecate first, and remove in the next release.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 10:32:55 +02:00
Sebastiaan van Stijn 71ebbb81ae
cli/command/plugins: use errors.Join instead of custom cli.Errors
This command was using a custom "multi-error" implementation, but it
had some limitations, and the formatting wasn't great.

This patch replaces it with Go's errors.Join.

Before:

    docker plugin remove one two three
    Error response from daemon: plugin "one" not found, Error response from daemon: plugin "two" not found, Error response from daemon: plugin "three" not found

After:

    docker plugin remove one two three
    Error response from daemon: plugin "one" not found
    Error response from daemon: plugin "two" not found
    Error response from daemon: plugin "three" not found

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 10:32:54 +02:00
Sebastiaan van Stijn 87acf77aef
cli/hints: add tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 00:48:16 +02:00
Sebastiaan van Stijn 9b525bc9d1
vendor: github.com/docker/docker 36a3bd090489 (master, v28.0-dev)
full diff: 164cae56ed...36a3bd0904

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-18 17:48:05 +02:00
Sebastiaan van Stijn 8a7c5ae68f
Merge pull request #5542 from thaJeztah/base_completion_tests
cmd/docker: add tests for flag-completions, and refactor
2024-10-18 12:07:02 +02:00
Sebastiaan van Stijn da9e984231
Merge pull request #5541 from thaJeztah/template_coverage
templates: add test for HeaderFunctions
2024-10-18 11:42:00 +02:00
Sebastiaan van Stijn 670f81803f
cmd/docker: add tests for flag-completions, and refactor
Remove the registerCompletionFuncForGlobalFlags for now, as
the error it returned was ignored, so it didn't add much
benefit, other than abstracting things.

Split the underlying completion-functions to separate
functions, and add some basic tests for them.

Remove the completions helper, as it now didn't add much,
and it saved having the dependency on the package.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-18 11:23:55 +02:00
Paweł Gronowski 38653277af
Merge pull request #5539 from thaJeztah/bump_swarmkit
vendor: github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e
2024-10-18 10:44:41 +02:00
Sebastiaan van Stijn 12dcc6e25c
templates: add test for HeaderFunctions
Before:

    go test -test.coverprofile -
    PASS
    coverage: 65.2% of statements
    ok  	github.com/docker/cli/templates	0.607s

After:

    go test -test.coverprofile -
    PASS
    coverage: 95.7% of statements
    ok  	github.com/docker/cli/templates	0.259s

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-18 10:07:33 +02:00
Sebastiaan van Stijn cbbb917323
vendor: github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e
- add Unwrap error to custom error types
- removes dependency on github.com/rexray/gocsi
- fix CSI plugin load issue

full diff: ea1a7cec35...e8ecf83ee0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-17 23:01:19 +02:00
Paweł Gronowski 3590f946a3
Merge pull request #5535 from dvdksn/fix-image-tag-spec
docs: update prose about image tag/name format
2024-10-17 12:46:46 +02:00
David Karlsson 2c6b80491b docs: update prose about image tag/name format
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-10-17 12:36:32 +02:00
Paweł Gronowski 09e16fc9c6
Merge pull request #5537 from dvdksn/correct_events_limit
docs: corrected the max events returned
2024-10-17 11:26:09 +02:00
Laura Brehm dba4b15d6b
Merge pull request #5534 from thaJeztah/container_testfixes 2024-10-16 22:08:22 +01:00
David Karlsson 50ef0c58c2 docs: corrected the max events returned
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-10-16 12:21:51 +02:00
Sebastiaan van Stijn 35d7b1a7a6
cli/command/container: TestWaitExitOrRemoved use subtests
=== RUN   TestWaitExitOrRemoved
    === RUN   TestWaitExitOrRemoved/normal-container
    === RUN   TestWaitExitOrRemoved/give-me-exit-code-42
    === RUN   TestWaitExitOrRemoved/i-want-a-wait-error
    time="2024-10-13T18:48:14+02:00" level=error msg="Error waiting for container: removal failed"
    === RUN   TestWaitExitOrRemoved/non-existent-container-id
    time="2024-10-13T18:48:14+02:00" level=error msg="error waiting for container: no such container: non-existent-container-id"
    --- PASS: TestWaitExitOrRemoved (0.00s)
        --- PASS: TestWaitExitOrRemoved/normal-container (0.00s)
        --- PASS: TestWaitExitOrRemoved/give-me-exit-code-42 (0.00s)
        --- PASS: TestWaitExitOrRemoved/i-want-a-wait-error (0.00s)
        --- PASS: TestWaitExitOrRemoved/non-existent-container-id (0.00s)
    PASS

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-16 12:03:17 +02:00
Sebastiaan van Stijn 3b38dc67be
cli/command/container: set empty args in tests and discard output
Prevent some tests from failing when running from a pre-compiled
testbinary, and discard output to make the output less noisy.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-16 12:01:25 +02:00
Sebastiaan van Stijn 31eeed7ca4
Merge pull request #5533 from thaJeztah/completions_coverage
cli/command/completion: add more unit-tests
2024-10-14 17:38:02 +02:00
Sebastiaan van Stijn 089448ba6d
Merge pull request #5532 from thaJeztah/update_badges
README: update pkg.go.dev badge, add OpenSSF scorecard
2024-10-14 17:18:23 +02:00
Sebastiaan van Stijn 6ed137f7dd
Merge pull request #5529 from thaJeztah/bump_deps
vendor assorted dependencies in preparation of engine update
2024-10-14 13:29:16 +02:00
Sebastiaan van Stijn e1c472a436
completion: add test for VolumeNames
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 19:08:56 +02:00
Sebastiaan van Stijn 302d73f990
completion: add test for NetworkNames
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 19:08:52 +02:00
Sebastiaan van Stijn ab418a38d8
completion: add test for ImageNames
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 19:08:26 +02:00
Sebastiaan van Stijn f3b4094eb0
completion: add test for ContainerNames
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 19:07:52 +02:00
Sebastiaan van Stijn be197da6b8
completion: add test for NoComplete
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 17:54:40 +02:00
Sebastiaan van Stijn 51713196c9
completion: add test for FromList
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 17:53:19 +02:00
Sebastiaan van Stijn a5ca5b33f1
completion: add test for FileNames
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 17:52:50 +02:00
Sebastiaan van Stijn 8f2e5662e7
completion: add test for EnvVarNames
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 17:48:49 +02:00
Sebastiaan van Stijn b8cddc63ad
completion: ContainerNames: don't panic on nil filter
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-13 17:47:07 +02:00
Sebastiaan van Stijn a58faf7971
README: update pkg.go.dev badge, add OpenSSF scorecard
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 22:12:38 +02:00
Sebastiaan van Stijn b6d27ff60e
vendor: google.golang.org/grpc v1.66.2
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:52:41 +02:00
Sebastiaan van Stijn 200225f530
vendor: google.golang.org/protobuf v1.34.1
full diff: https://github.com/protocolbuffers/protobuf-go/compare/v1.33.0...v1.34.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:49:59 +02:00
Sebastiaan van Stijn 9599251d07
vendor: github.com/cespare/xxhash/v2 v2.3.0
full diff: https://github.com/cespare/xxhash/compare/v2.2.0...v2.3.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:48:45 +02:00
Sebastiaan van Stijn ea8aa2a419
vendor: golang.org/x/net v0.29.0
no changes in vendored code

full diff: https://github.com/golang/net/compare/v0.28.0...v0.29.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:47:34 +02:00
Sebastiaan van Stijn 61867feecf
vendor: golang.org/x/crypto v0.27.0
no changes in vendored code

full diff: https://github.com/golang/crypto/compare/v0.26.0...v0.27.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:46:59 +02:00
Sebastiaan van Stijn 843ae6d7e2
vendor: golang.org/x/term v0.24.0
full diff: https://github.com/golang/term/compare/v0.23.0...v0.24.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:46:04 +02:00
Sebastiaan van Stijn bea4ee6588
vendor: golang.org/x/text v0.18.0
no changes in vendored code

full diff: https://github.com/golang/text/compare/v0.17.0...v0.18.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:43:36 +02:00
Sebastiaan van Stijn a88ee33f71
vendor: golang.org/x/sys v0.25.0
full diff: https://github.com/golang/sys/compare/v0.24.0...v0.25.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-12 21:42:42 +02:00
Sebastiaan van Stijn 21eea1e003
Merge pull request #5527 from albers/completion-container-rm
Improve completion of containers for `docker rm`
2024-10-11 22:35:25 +02:00
Harald Albers 147630a309 Only complete removable containers if --force is not given
Signed-off-by: Harald Albers <github@albersweb.de>
2024-10-10 21:34:38 +00:00
Sebastiaan van Stijn 88f1e99e8e
Merge pull request #5507 from p-rogalski/link-supported-go-duration-strings
docs: Link supported Go duration strings
2024-10-10 18:04:19 +02:00
Sebastiaan van Stijn f483aacd6b
Merge pull request #5331 from vvoland/c8d-saveload-platform
c8d: Add `--platform` flag to history, save and load
2024-10-10 17:33:08 +02:00
Paweł Gronowski d085e2445c
image/history: Add `--platform` flag
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-10 17:00:43 +02:00
Paweł Gronowski b0bb4ba7f2
image/load: Add `--platform`
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-10 16:35:07 +02:00
Paweł Gronowski a20eb45b26
image/save: Add `--platform`
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-10 16:35:03 +02:00
Sebastiaan van Stijn 6a78e9231a
Merge pull request #5524 from Stavrospanakakis/compose-ports-validation
cli/compose: implement the ports validation method
2024-10-10 14:34:09 +02:00
Stavros Panakakis 0319795d4f cli/compose: implement the ports validation method
This commit implements a validation
method for the port mappings.

Also, it removes the ports validation
method from the expose property
since they do not accept the
same type of values.

Signed-off-by: Stavros Panakakis <stavrospanakakis@gmail.com>
2024-10-10 11:50:11 +03:00
Sebastiaan van Stijn 305985c1b1
Merge pull request #5417 from DilepDev/5416-brokenLinks
Update broken links in CONTRIBUTING.md
2024-10-09 00:34:17 +02:00
Sebastiaan van Stijn 2d47c9b222
Merge pull request #5521 from thaJeztah/fix_test_args
cli/command/images: set cmd.Args to prevent test-failures
2024-10-08 20:49:28 +02:00
Sebastiaan van Stijn 839dbbcf27
cli/command/images: set cmd.Args to prevent test-failures
When running tests from my IDE, it compiles the tests before running,
then executes the compiled binary to run the tests. Cobra doesn't like that,
because in that situation os.Args is taken as argument for the command that's
executed. The command that's tested now sees the `test-` flags as arguments
(`-test.v -test.run ..`), which causes various tests to fail ("Command XYZ
does not accept arguments").

    # compile the tests:
    go test -c -o foo.test

    # execute the test:
    ./foo.test -test.v -test.run TestFoo
    === RUN   TestFoo
    Error: "foo" accepts no arguments.

Set arguments to an empty slice to make sure it doesn't inherit arguments
from the test-binary.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-08 20:26:15 +02:00
Sebastiaan van Stijn b64f265d0f
Merge pull request #5520 from thaJeztah/fix_TestNewSaveCommandSuccess
cli/command/image: fix TestNewSaveCommandSuccess to actually test
2024-10-08 19:57:11 +02:00
Sebastiaan van Stijn 19eeb10155
cli/command/image: fix TestNewSaveCommandSuccess to actually test
This test was added in [moby@b2551c6] as part of a larger PR that implemented
unit tests in various packages. In this specific test, it looks like the
`imageSaveFunc` that's defined in the test-table was forgotten to be wired
up, causing all tests to effectively be skipped.

This patch wires up the function so that it's used in the test.

[moby@b2551c6]: b2551c619d

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-08 19:41:05 +02:00
Sebastiaan van Stijn 185622986e
Merge pull request #5506 from Stavrospanakakis/cli-container-testing
command: add tests for container kill, commit, and pause
2024-10-08 16:15:26 +02:00
Sebastiaan van Stijn eb53046ad2
Merge pull request #5516 from thaJeztah/complete_platforms
add shell-completion for --platform flags
2024-10-08 15:26:49 +02:00
Sebastiaan van Stijn d42cf96e15
cli/command/image: add shell completion for --platform flags
With this patch, completion is provided for `--platform` flags:

    docker pull --platform<TAB>
    linux           linux/amd64     linux/arm/v5    linux/arm/v7    linux/arm64/v8  linux/riscv64   wasip1          windows
    linux/386       linux/arm       linux/arm/v6    linux/arm64     linux/ppc64le   linux/s390x     wasip1/wasm     windows/amd64

Note that `docker buildx build` (with BuildKit) does not yet provide completion;
it's provided through buildx, and uses a different format (accepting multiple
comma-separated platforms). Interestingly, tab-completion for `docker build`
currently uses completion for non-buildkit, and has some other issues that may
have to be looked into.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-08 13:15:28 +02:00
Sebastiaan van Stijn 8c7f713db6
cli/command/container: add shell completion for --platform flags
With this patch, completion is provided for `--platform` flags:

    docker run --platform<TAB>
    linux           linux/amd64     linux/arm/v5    linux/arm/v7    linux/arm64/v8  linux/riscv64   wasip1          windows
    linux/386       linux/arm       linux/arm/v6    linux/arm64     linux/ppc64le   linux/s390x     wasip1/wasm     windows/amd64

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-08 13:14:45 +02:00
Sebastiaan van Stijn ce1aebcc30
cli/command/completion: add Platforms
Add a utility for completing platform strings.

Platforms offers completion for platform-strings. It provides a non-exhaustive
list of platforms to be used for completion. Platform-strings are based on
[runtime.GOOS] and [runtime.GOARCH], but with (optional) variants added. A
list of recognised os/arch combinations from the Go runtime can be obtained
through "go tool dist list".

Some noteworthy exclusions from this list:

  - arm64 images ("windows/arm64", "windows/arm64/v8") do not yet exist for windows.
  - we don't (yet) include `os-variant` for completion (as can be used for Windows images)
  - we don't (yet) include platforms for which we don't build binaries, such as
    BSD platforms (freebsd, netbsd, openbsd), android, macOS (darwin).
  - we currently exclude architectures that may have unofficial builds,
    but don't have wide adoption (and no support), such as loong64, mipsXXX,
    ppc64 (non-le) to prevent confusion.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-08 13:12:31 +02:00
Sebastiaan van Stijn 55ae7d7113
Merge pull request #5504 from austinvazquez/update-golang-1.22.8
ci: update to go1.22.8
2024-10-07 20:27:44 +02:00
Sebastiaan van Stijn fc7e779c57
Merge pull request #5508 from dvdksn/fix-unless-stopped-desc
docs: fix inaccurate description of --restart=unless-stopped
2024-10-07 16:34:22 +02:00
David Karlsson 2f2b16a966 docs: fix inaccurate description of --restart=unless-stopped
`unless-stopped` containers are not restart after a daemon restart

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-10-07 13:07:33 +02:00
Paul Rogalski c70b2165a9 docs: Link supported Go duration strings
Signed-off-by: Paul Rogalski <mail@paul-rogalski.de>
2024-10-07 10:37:00 +02:00
Stavros Panakakis 442c38636f command: add tests for container kill, commit, and pause
This commit adds tests for the commands
docker kill, docker commit, and docker
pause. Also, it creates the mock methods
of the docker client ContainerCommit and
ContainerPause so they can
be used in the tests.

For docker kill, it covers the
cases that:
 - the command runs successfully
 - the client returns an error

For docker commit, it covers
the cases that:
 - the command runs successfully
 - the client returns an error

For docker pause, it covers
the cases that:
 - the command runs successfully
 - the client returns an error

Signed-off-by: Stavros Panakakis <stavrospanakakis@gmail.com>
2024-10-06 20:00:49 +03:00
Austin Vazquez a6ab65948e
ci: update to go1.22.8
Signed-off-by: Austin Vazquez <macedonv@amazon.com>
2024-10-04 20:20:16 +00:00
Sebastiaan van Stijn 7908982543
Merge pull request #5467 from Stavrospanakakis/cli-container-tests
command: add tests for container diff and rename
2024-10-04 13:49:32 +02:00
Sebastiaan van Stijn ff3f94a542
Merge pull request #5502 from thaJeztah/separate_kvfile
move parsing key-value files to a separate package
2024-10-04 13:46:08 +02:00
Sebastiaan van Stijn 9025c932b9
Merge pull request #5469 from dvdksn/docs-use-important-callout-legacy-build
docs: use important callout for buildkit vs legacy builder
2024-10-04 13:05:52 +02:00
Nicolas De Loof 9ecfe4f5a7
move parsing key-value files to a separate package
Move the code for parsing key-value files, such as used for
env-files and label-files to a separate package. This allows
other projects (such as compose) to use the same parsing
logic, but provide custom lookup functions for their situation
(which is slightly different).

The new package provides utilities for parsing key-value files
for either a file or an io.Reader. Most tests for EnvFile were
now testing functionality that's already tested in the new package,
so were (re)moved.

Co-authored-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-04 12:27:10 +02:00
Sebastiaan van Stijn 9dee12ff69
Merge pull request #5496 from thaJeztah/parseKeyValueFile_refactor
opts: parseKeyValueFile: cleanup and remove redundant trimming
2024-10-04 12:12:09 +02:00
Sebastiaan van Stijn 76196dbb01
opts: parseKeyValueFile: cleanup and remove redundant trimming
- the function already trimmed leading whitespace from each line before
  parsing. keys with trailing whitespace would be invalidated, and values
  have whitespace preserved, so there's no need to trim whitespace for the
  key.
- if a line is validated (key is valid), we don't need to reconstruct the
  key=value by concatenating, and we can add the line as-is.
- check if the key is empty before checking if it contains whitespace
- touch-up comments
- rename some variables for readability
- slight cleanup to use early returns / early continues to reduce nesting

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-04 11:56:15 +02:00
Sebastiaan van Stijn dac7319f10
Merge pull request #5495 from thaJeztah/opts_remove_ErrBadKey
opts: remove ErrBadKey as it's not used as a sentinel error
2024-10-03 17:52:56 +02:00
Sebastiaan van Stijn 95e221ef4d
opts: remove ErrBadKey as it's not used as a sentinel error
This error was originally introduced `ErrBadEnvVariable` in [moby/moby@500c8ba],
but merely for convenience, and not used as a sentinel error. After the code
was moved from the daemon to the cli repository, it was renamed to be more
generic `ErrBadKey` in commit 2b17f4c8a8.

A search on GitHub shows that there's no consumers using this error as
sentinel error, and it's not used in our own code as such, so it should
be safe to remove this error.

This patch removes the `ErrBadKey` error-type; it also removes the prefix
(`poorly formatted environment:`) to make the error more generic, because
the same function was used both for env-files and label-files.

[moby/moby@500c8ba]: 500c8ba4b6

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-03 17:22:22 +02:00
Sebastiaan van Stijn 7c85db6e1a
Merge pull request #5494 from thaJeztah/opts_test_cleanup
opts: cleanup ParseEnvFile tests
2024-10-03 15:36:50 +02:00
Sebastiaan van Stijn b129660dd3
opts: cleanup ParseEnvFile tests
- Use gotest.tools for assertions
- Check for expected error messages
- Don't check for ErrBadKey errors, as it's not used
  as a sentinel error anywhere.
- Use t.SetEnv() instead of depending on `HOME` being set
- Use t.TempDir() for writing temporary files

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-03 14:08:22 +02:00
Laura Brehm 30e9abbd3f
Merge pull request #5492 from thaJeztah/completion_unit_tests
cli/command/container: add unit tests for completion helpers
2024-10-02 15:52:14 +01:00
Sebastiaan van Stijn d49e72c0ac
cli/command/container: add unit tests for completion helpers
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-02 10:38:25 +02:00
Stavros Panakakis 46b360b059 command: add tests for container diff and rename
This commit adds tests for the commands
docker diff and docker rename. Also,
it creates the mock methods of the
docker client ContainerDiff and
ContainerRename so they can
be used in the tests.

For docker diff, it covers the
cases that:
 - the command runs successfully
 - the client returns an error
 - the container id is empty

For docker rename, it covers
the cases that:
 - the command runs successfully
 - the container old name is empty
 - the container new name is empty
 - the client returns an error

Co-authored-by: Laura Brehm <laurabrehm@hey.com>
Signed-off-by: Stavros Panakakis <stavrospanakakis@gmail.com>
2024-09-23 16:17:36 +03:00
David Karlsson 8a3d838a19 docs: use important callout for buildkit vs legacy builder
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-09-23 13:38:33 +02:00
Dilep Dev 45d55961d0
Fix broken links in CONTRIBUTING.md
Signed-off-by: Dilep Dev <34891655+DilepDev@users.noreply.github.com>
2024-09-08 23:08:14 +05:30
Sebastiaan van Stijn e2986f4467
build: print error if BuildKit/non-BuildKit-specific flags are used
With this patch, the `--progress`, `--secret`, `--ssh`, and `--output` flags
trigger an error when trying to use without BuildKit enabled;

    DOCKER_BUILDKIT=0 docker build --progress=plain .
    --progress is only supported with BuildKit enabled. Enable BuildKit with DOCKER_BUILDKIT=1

    DOCKER_BUILDKIT=0 docker build --output=foo .
    --output is only supported with BuildKit enabled. Enable BuildKit with DOCKER_BUILDKIT=1

Likewise, options that are not supported yet by BuildKit, now trigger an error:

    DOCKER_BUILDKIT=1 docker build --memory=500M .
    --memory is not supported with BuildKit enabled. Disable BuildKit with DOCKER_BUILDKIT=0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-11-17 11:06:29 +01:00
871 changed files with 43397 additions and 14541 deletions

View File

@ -62,7 +62,7 @@ jobs:
name: Update Go
uses: actions/setup-go@v5
with:
go-version: 1.22.7
go-version: 1.23.3
-
name: Initialize CodeQL
uses: github/codeql-action/init@v3

View File

@ -68,7 +68,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.22.7
go-version: 1.23.3
-
name: Test
run: |

View File

@ -1,12 +1,12 @@
linters:
enable:
- bodyclose
- copyloopvar # Detects places where loop variables are copied.
- depguard
- dogsled
- dupword # Detects duplicate words.
- durationcheck
- errchkjson
- exportloopref # Detects pointers to enclosing loop variables.
- gocritic # Metalinter; detects bugs, performance, and styling issues.
- gocyclo
- gofumpt # Detects whether code was gofumpt-ed.
@ -41,6 +41,9 @@ linters:
- errcheck
run:
# prevent golangci-lint from deducting the go version to lint for through go.mod,
# which causes it to fallback to go1.17 semantics.
go: "1.23.3"
timeout: 5m
linters-settings:
@ -52,6 +55,13 @@ linters-settings:
desc: The io/ioutil package has been deprecated, see https://go.dev/doc/go1.16#ioutil
gocyclo:
min-complexity: 16
gosec:
excludes:
- G104 # G104: Errors unhandled; (TODO: reduce unhandled errors, or explicitly ignore)
- G113 # G113: Potential uncontrolled memory consumption in Rat.SetString (CVE-2022-23772); (only affects go < 1.16.14. and go < 1.17.7)
- G115 # G115: integer overflow conversion; (TODO: verify these: https://github.com/docker/cli/issues/5584)
- G306 # G306: Expect WriteFile permissions to be 0600 or less (too restrictive; also flags "0o644" permissions)
- G307 # G307: Deferring unsafe method "*os.File" on type "Close" (also EXC0008); (TODO: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close")
govet:
enable:
- shadow
@ -87,6 +97,10 @@ issues:
# The default exclusion rules are a bit too permissive, so copying the relevant ones below
exclude-use-default: false
# This option has been defined when Go modules was not existed and when the
# golangci-lint core was different, this is not something we still recommend.
exclude-dirs-use-default: false
exclude:
- parameter .* always receives
@ -104,6 +118,9 @@ issues:
#
# These exclusion patterns are copied from the default excluses at:
# https://github.com/golangci/golangci-lint/blob/v1.44.0/pkg/config/issues.go#L10-L104
#
# The default list of exclusions can be found at:
# https://golangci-lint.run/usage/false-positives/#default-exclusions
# EXC0001
- text: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked"
@ -121,11 +138,6 @@ issues:
- text: "Subprocess launch(ed with variable|ing should be audited)"
linters:
- gosec
# EXC0008
# TODO: evaluate these and fix where needed: G307: Deferring unsafe method "*os.File" on type "Close" (gosec)
- text: "G307"
linters:
- gosec
# EXC0009
- text: "(Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)"
linters:
@ -135,26 +147,6 @@ issues:
linters:
- gosec
# G113 Potential uncontrolled memory consumption in Rat.SetString (CVE-2022-23772)
# only affects gp < 1.16.14. and go < 1.17.7
- text: "G113"
linters:
- gosec
# TODO: G104: Errors unhandled. (gosec)
- text: "G104"
linters:
- gosec
# Looks like the match in "EXC0007" above doesn't catch this one
# TODO: consider upstreaming this to golangci-lint's default exclusion rules
- text: "G204: Subprocess launched with a potential tainted input or cmd arguments"
linters:
- gosec
# Looks like the match in "EXC0009" above doesn't catch this one
# TODO: consider upstreaming this to golangci-lint's default exclusion rules
- text: "G306: Expect WriteFile permissions to be 0600 or less"
linters:
- gosec
# TODO: make sure all packages have a description. Currently, there's 67 packages without.
- text: "package-comments: should have a package comment"
linters:

View File

@ -66,7 +66,7 @@ anybody starts working on it.
We are always thrilled to receive pull requests. We do our best to process them
quickly. If your pull request is not accepted on the first try,
don't get discouraged! Our contributor's guide explains [the review process we
use for simple changes](https://docs.docker.com/opensource/workflow/make-a-contribution/).
use for simple changes](https://github.com/docker/docker/blob/master/project/REVIEWING.md).
### Talking to other Docker users and contributors
@ -124,8 +124,8 @@ submitting a pull request.
Update the documentation when creating or modifying features. Test your
documentation changes for clarity, concision, and correctness, as well as a
clean documentation build. See our contributors guide for [our style
guide](https://docs.docker.com/opensource/doc-style) and instructions on [building
the documentation](https://docs.docker.com/opensource/project/test-and-docs/#build-and-test-the-documentation).
guide](https://docs.docker.com/contribute/style/grammar/) and instructions on [building
the documentation](https://docs.docker.com/contribute/).
Write clean code. Universally formatted code promotes ease of writing, reading,
and maintenance. Always run `gofmt -s -w file.go` on each changed file before

View File

@ -4,7 +4,7 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.20
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.22.7
ARG GO_VERSION=1.23.3
ARG XX_VERSION=1.5.0
ARG GOVERSIONINFO_VERSION=v1.3.0
ARG GOTESTSUM_VERSION=v1.10.0

View File

@ -1,9 +1,10 @@
# Docker CLI
[![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/cli)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/docker/cli)](https://pkg.go.dev/github.com/docker/cli)
[![Build Status](https://img.shields.io/github/actions/workflow/status/docker/cli/build.yml?branch=master&label=build&logo=github)](https://github.com/docker/cli/actions?query=workflow%3Abuild)
[![Test Status](https://img.shields.io/github/actions/workflow/status/docker/cli/test.yml?branch=master&label=test&logo=github)](https://github.com/docker/cli/actions?query=workflow%3Atest)
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/cli)](https://goreportcard.com/report/github.com/docker/cli)
[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/docker/cli/badge)](https://scorecard.dev/viewer/?uri=github.com/docker/cli)
[![Codecov](https://img.shields.io/codecov/c/github/docker/cli?logo=codecov)](https://codecov.io/gh/docker/cli)
## About

View File

@ -17,5 +17,5 @@ func (c *candidate) Path() string {
}
func (c *candidate) Metadata() ([]byte, error) {
return exec.Command(c.path, MetadataSubcommandName).Output()
return exec.Command(c.path, MetadataSubcommandName).Output() // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
}

View File

@ -52,7 +52,6 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e
return
}
for _, p := range plugins {
p := p
vendor := p.Vendor
if vendor == "" {
vendor = "unknown"

View File

@ -240,7 +240,8 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
// TODO: why are we not returning plugin.Err?
return nil, errPluginNotFound(name)
}
cmd := exec.Command(plugin.Path, args...)
cmd := exec.Command(plugin.Path, args...) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
// Using dockerCli.{In,Out,Err}() here results in a hang until something is input.
// See: - https://github.com/golang/go/issues/10338
// - https://github.com/golang/go/commit/d000e8742a173aa0659584aa01b7ba2834ba28ab

View File

@ -112,7 +112,7 @@ func (p *Plugin) RunHook(ctx context.Context, hookData HookPluginData) ([]byte,
return nil, wrapAsPluginError(err, "failed to marshall hook data")
}
pCmd := exec.CommandContext(ctx, p.Path, p.Name, HookSubcommandName, string(hDataBytes))
pCmd := exec.CommandContext(ctx, p.Path, p.Name, HookSubcommandName, string(hDataBytes)) // #nosec G204 -- ignore "Subprocess launched with a potential tainted input or cmd arguments"
pCmd.Env = os.Environ()
pCmd.Env = append(pCmd.Env, ReexecEnvvar+"="+os.Args[0])
hookCmdOutput, err := pCmd.Output()

View File

@ -187,19 +187,18 @@ func TestInitializeFromClient(t *testing.T) {
},
}
for _, testcase := range testcases {
testcase := testcase
t.Run(testcase.doc, func(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.doc, func(t *testing.T) {
apiclient := &fakeClient{
pingFunc: testcase.pingFunc,
pingFunc: tc.pingFunc,
version: defaultVersion,
}
cli := &DockerCli{client: apiclient}
err := cli.Initialize(flags.NewClientOptions())
assert.NilError(t, err)
assert.DeepEqual(t, cli.ServerInfo(), testcase.expectedServer)
assert.Equal(t, apiclient.negotiated, testcase.negotiated)
assert.DeepEqual(t, cli.ServerInfo(), tc.expectedServer)
assert.Equal(t, apiclient.negotiated, tc.negotiated)
})
}
}
@ -277,10 +276,9 @@ func TestExperimentalCLI(t *testing.T) {
},
}
for _, testcase := range testcases {
testcase := testcase
t.Run(testcase.doc, func(t *testing.T) {
dir := fs.NewDir(t, testcase.doc, fs.WithFile("config.json", testcase.configfile))
for _, tc := range testcases {
t.Run(tc.doc, func(t *testing.T) {
dir := fs.NewDir(t, tc.doc, fs.WithFile("config.json", tc.configfile))
defer dir.Remove()
apiclient := &fakeClient{
version: defaultVersion,

View File

@ -59,7 +59,7 @@ func ContainerNames(dockerCLI APIClientProvider, all bool, filters ...func(conta
for _, ctr := range list {
skip := false
for _, fn := range filters {
if !fn(ctr) {
if fn != nil && !fn(ctr) {
skip = true
break
}
@ -145,3 +145,47 @@ func FileNames(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCom
func NoComplete(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveNoFileComp
}
var commonPlatforms = []string{
"linux",
"linux/386",
"linux/amd64",
"linux/arm",
"linux/arm/v5",
"linux/arm/v6",
"linux/arm/v7",
"linux/arm64",
"linux/arm64/v8",
// IBM power and z platforms
"linux/ppc64le",
"linux/s390x",
// Not yet supported
"linux/riscv64",
"windows",
"windows/amd64",
"wasip1",
"wasip1/wasm",
}
// Platforms offers completion for platform-strings. It provides a non-exhaustive
// list of platforms to be used for completion. Platform-strings are based on
// [runtime.GOOS] and [runtime.GOARCH], but with (optional) variants added. A
// list of recognised os/arch combinations from the Go runtime can be obtained
// through "go tool dist list".
//
// Some noteworthy exclusions from this list:
//
// - arm64 images ("windows/arm64", "windows/arm64/v8") do not yet exist for windows.
// - we don't (yet) include `os-variant` for completion (as can be used for Windows images)
// - we don't (yet) include platforms for which we don't build binaries, such as
// BSD platforms (freebsd, netbsd, openbsd), android, macOS (darwin).
// - we currently exclude architectures that may have unofficial builds,
// but don't have wide adoption (and no support), such as loong64, mipsXXX,
// ppc64 (non-le) to prevent confusion.
func Platforms(_ *cobra.Command, _ []string, _ string) (platforms []string, _ cobra.ShellCompDirective) {
return commonPlatforms, cobra.ShellCompDirectiveNoFileComp
}

View File

@ -0,0 +1,346 @@
package completion
import (
"context"
"errors"
"sort"
"testing"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/env"
)
type fakeCLI struct {
*fakeClient
}
// Client implements [APIClientProvider].
func (c fakeCLI) Client() client.APIClient {
return c.fakeClient
}
type fakeClient struct {
client.Client
containerListFunc func(options container.ListOptions) ([]container.Summary, error)
imageListFunc func(options image.ListOptions) ([]image.Summary, error)
networkListFunc func(ctx context.Context, options network.ListOptions) ([]network.Summary, error)
volumeListFunc func(filter filters.Args) (volume.ListResponse, error)
}
func (c *fakeClient) ContainerList(_ context.Context, options container.ListOptions) ([]container.Summary, error) {
if c.containerListFunc != nil {
return c.containerListFunc(options)
}
return []container.Summary{}, nil
}
func (c *fakeClient) ImageList(_ context.Context, options image.ListOptions) ([]image.Summary, error) {
if c.imageListFunc != nil {
return c.imageListFunc(options)
}
return []image.Summary{}, nil
}
func (c *fakeClient) NetworkList(ctx context.Context, options network.ListOptions) ([]network.Summary, error) {
if c.networkListFunc != nil {
return c.networkListFunc(ctx, options)
}
return []network.Inspect{}, nil
}
func (c *fakeClient) VolumeList(_ context.Context, options volume.ListOptions) (volume.ListResponse, error) {
if c.volumeListFunc != nil {
return c.volumeListFunc(options.Filters)
}
return volume.ListResponse{}, nil
}
func TestCompleteContainerNames(t *testing.T) {
tests := []struct {
doc string
showAll, showIDs bool
filters []func(container.Summary) bool
containers []container.Summary
expOut []string
expOpts container.ListOptions
expDirective cobra.ShellCompDirective
}{
{
doc: "no results",
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "all containers",
showAll: true,
containers: []container.Summary{
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
{ID: "id-a", State: "exited", Names: []string{"/container-a"}},
},
expOut: []string{"container-c", "container-c/link-b", "container-b", "container-a"},
expOpts: container.ListOptions{All: true},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "all containers with ids",
showAll: true,
showIDs: true,
containers: []container.Summary{
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
{ID: "id-a", State: "exited", Names: []string{"/container-a"}},
},
expOut: []string{"id-c", "container-c", "container-c/link-b", "id-b", "container-b", "id-a", "container-a"},
expOpts: container.ListOptions{All: true},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "only running containers",
showAll: false,
containers: []container.Summary{
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
},
expOut: []string{"container-c", "container-c/link-b"},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with filter",
showAll: true,
filters: []func(container.Summary) bool{
func(container container.Summary) bool { return container.State == "created" },
},
containers: []container.Summary{
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
{ID: "id-a", State: "exited", Names: []string{"/container-a"}},
},
expOut: []string{"container-b"},
expOpts: container.ListOptions{All: true},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "multiple filters",
showAll: true,
filters: []func(container.Summary) bool{
func(container container.Summary) bool { return container.ID == "id-a" },
func(container container.Summary) bool { return container.State == "created" },
},
containers: []container.Summary{
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
{ID: "id-a", State: "created", Names: []string{"/container-a"}},
},
expOut: []string{"container-a"},
expOpts: container.ListOptions{All: true},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with error",
expDirective: cobra.ShellCompDirectiveError,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
if tc.showIDs {
t.Setenv("DOCKER_COMPLETION_SHOW_CONTAINER_IDS", "yes")
}
comp := ContainerNames(fakeCLI{&fakeClient{
containerListFunc: func(opts container.ListOptions) ([]container.Summary, error) {
assert.Check(t, is.DeepEqual(opts, tc.expOpts, cmpopts.IgnoreUnexported(container.ListOptions{}, filters.Args{})))
if tc.expDirective == cobra.ShellCompDirectiveError {
return nil, errors.New("some error occurred")
}
return tc.containers, nil
},
}}, tc.showAll, tc.filters...)
containers, directives := comp(&cobra.Command{}, nil, "")
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
assert.Check(t, is.DeepEqual(containers, tc.expOut))
})
}
}
func TestCompleteEnvVarNames(t *testing.T) {
env.PatchAll(t, map[string]string{
"ENV_A": "hello-a",
"ENV_B": "hello-b",
})
values, directives := EnvVarNames(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
sort.Strings(values)
expected := []string{"ENV_A", "ENV_B"}
assert.Check(t, is.DeepEqual(values, expected))
}
func TestCompleteFileNames(t *testing.T) {
values, directives := FileNames(nil, nil, "")
assert.Check(t, is.Equal(directives, cobra.ShellCompDirectiveDefault))
assert.Check(t, is.Len(values, 0))
}
func TestCompleteFromList(t *testing.T) {
expected := []string{"one", "two", "three"}
values, directives := FromList(expected...)(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
assert.Check(t, is.DeepEqual(values, expected))
}
func TestCompleteImageNames(t *testing.T) {
tests := []struct {
doc string
images []image.Summary
expOut []string
expDirective cobra.ShellCompDirective
}{
{
doc: "no results",
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with results",
images: []image.Summary{
{RepoTags: []string{"image-c:latest", "image-c:other"}},
{RepoTags: []string{"image-b:latest", "image-b:other"}},
{RepoTags: []string{"image-a:latest", "image-a:other"}},
},
expOut: []string{"image-c:latest", "image-c:other", "image-b:latest", "image-b:other", "image-a:latest", "image-a:other"},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with error",
expDirective: cobra.ShellCompDirectiveError,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
comp := ImageNames(fakeCLI{&fakeClient{
imageListFunc: func(options image.ListOptions) ([]image.Summary, error) {
if tc.expDirective == cobra.ShellCompDirectiveError {
return nil, errors.New("some error occurred")
}
return tc.images, nil
},
}})
volumes, directives := comp(&cobra.Command{}, nil, "")
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
assert.Check(t, is.DeepEqual(volumes, tc.expOut))
})
}
}
func TestCompleteNetworkNames(t *testing.T) {
tests := []struct {
doc string
networks []network.Summary
expOut []string
expDirective cobra.ShellCompDirective
}{
{
doc: "no results",
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with results",
networks: []network.Summary{
{ID: "nw-c", Name: "network-c"},
{ID: "nw-b", Name: "network-b"},
{ID: "nw-a", Name: "network-a"},
},
expOut: []string{"network-c", "network-b", "network-a"},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with error",
expDirective: cobra.ShellCompDirectiveError,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
comp := NetworkNames(fakeCLI{&fakeClient{
networkListFunc: func(ctx context.Context, options network.ListOptions) ([]network.Summary, error) {
if tc.expDirective == cobra.ShellCompDirectiveError {
return nil, errors.New("some error occurred")
}
return tc.networks, nil
},
}})
volumes, directives := comp(&cobra.Command{}, nil, "")
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
assert.Check(t, is.DeepEqual(volumes, tc.expOut))
})
}
}
func TestCompleteNoComplete(t *testing.T) {
values, directives := NoComplete(nil, nil, "")
assert.Check(t, is.Equal(directives, cobra.ShellCompDirectiveNoFileComp))
assert.Check(t, is.Len(values, 0))
}
func TestCompletePlatforms(t *testing.T) {
values, directives := Platforms(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
assert.Check(t, is.DeepEqual(values, commonPlatforms))
}
func TestCompleteVolumeNames(t *testing.T) {
tests := []struct {
doc string
volumes []*volume.Volume
expOut []string
expDirective cobra.ShellCompDirective
}{
{
doc: "no results",
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with results",
volumes: []*volume.Volume{
{Name: "volume-c"},
{Name: "volume-b"},
{Name: "volume-a"},
},
expOut: []string{"volume-c", "volume-b", "volume-a"},
expDirective: cobra.ShellCompDirectiveNoFileComp,
},
{
doc: "with error",
expDirective: cobra.ShellCompDirectiveError,
},
}
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
comp := VolumeNames(fakeCLI{&fakeClient{
volumeListFunc: func(filter filters.Args) (volume.ListResponse, error) {
if tc.expDirective == cobra.ShellCompDirectiveError {
return volume.ListResponse{}, errors.New("some error occurred")
}
return volume.ListResponse{Volumes: tc.volumes}, nil
},
}})
volumes, directives := comp(&cobra.Command{}, nil, "")
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
assert.Check(t, is.DeepEqual(volumes, tc.expOut))
})
}
}

View File

@ -43,7 +43,6 @@ func TestConfigCreateErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.expectedError, func(t *testing.T) {
cmd := newConfigCreateCommand(
test.NewFakeCli(&fakeClient{

View File

@ -61,7 +61,6 @@ id_rsa
},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -73,7 +73,6 @@ func TestNewAttachCommandErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewAttachCommand(test.NewFakeCli(&fakeClient{inspectFunc: tc.containerInspectFunc}))
cmd.SetOut(io.Discard)

View File

@ -40,6 +40,10 @@ type fakeClient struct {
containerKillFunc func(ctx context.Context, containerID, signal string) error
containerPruneFunc func(ctx context.Context, pruneFilters filters.Args) (container.PruneReport, error)
containerAttachFunc func(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error)
containerDiffFunc func(ctx context.Context, containerID string) ([]container.FilesystemChange, error)
containerRenameFunc func(ctx context.Context, oldName, newName string) error
containerCommitFunc func(ctx context.Context, container string, options container.CommitOptions) (types.IDResponse, error)
containerPauseFunc func(ctx context.Context, container string) error
Version string
}
@ -197,3 +201,34 @@ func (f *fakeClient) ContainerAttach(ctx context.Context, containerID string, op
}
return types.HijackedResponse{}, nil
}
func (f *fakeClient) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
if f.containerDiffFunc != nil {
return f.containerDiffFunc(ctx, containerID)
}
return []container.FilesystemChange{}, nil
}
func (f *fakeClient) ContainerRename(ctx context.Context, oldName, newName string) error {
if f.containerRenameFunc != nil {
return f.containerRenameFunc(ctx, oldName, newName)
}
return nil
}
func (f *fakeClient) ContainerCommit(ctx context.Context, containerID string, options container.CommitOptions) (types.IDResponse, error) {
if f.containerCommitFunc != nil {
return f.containerCommitFunc(ctx, containerID, options)
}
return types.IDResponse{}, nil
}
func (f *fakeClient) ContainerPause(ctx context.Context, containerID string) error {
if f.containerPauseFunc != nil {
return f.containerPauseFunc(ctx, containerID)
}
return nil
}

View File

@ -0,0 +1,71 @@
package container
import (
"context"
"io"
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunCommit(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerCommitFunc: func(
ctx context.Context,
container string,
options container.CommitOptions,
) (types.IDResponse, error) {
assert.Check(t, is.Equal(options.Author, "Author Name <author@name.com>"))
assert.Check(t, is.DeepEqual(options.Changes, []string{"EXPOSE 80"}))
assert.Check(t, is.Equal(options.Comment, "commit message"))
assert.Check(t, is.Equal(options.Pause, false))
assert.Check(t, is.Equal(container, "container-id"))
return types.IDResponse{ID: "image-id"}, nil
},
})
cmd := NewCommitCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetArgs(
[]string{
"--author", "Author Name <author@name.com>",
"--change", "EXPOSE 80",
"--message", "commit message",
"--pause=false",
"container-id",
},
)
err := cmd.Execute()
assert.NilError(t, err)
assert.Assert(t, is.Equal(cli.OutBuffer().String(), "image-id\n"))
}
func TestRunCommitClientError(t *testing.T) {
clientError := errors.New("client error")
cli := test.NewFakeCli(&fakeClient{
containerCommitFunc: func(
ctx context.Context,
container string,
options container.CommitOptions,
) (types.IDResponse, error) {
return types.IDResponse{}, clientError
},
})
cmd := NewCommitCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{"container-id"})
err := cmd.Execute()
assert.ErrorIs(t, err, clientError)
}

View File

@ -4,6 +4,7 @@ import (
"strings"
"testing"
"github.com/moby/sys/signal"
"github.com/spf13/cobra"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@ -19,3 +20,17 @@ func TestCompleteLinuxCapabilityNames(t *testing.T) {
assert.Check(t, is.Equal(name, strings.ToUpper(name)), "Should be formatted uppercase")
}
}
func TestCompleteRestartPolicies(t *testing.T) {
values, directives := completeRestartPolicies(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
expected := restartPolicies
assert.Check(t, is.DeepEqual(values, expected))
}
func TestCompleteSignals(t *testing.T) {
values, directives := completeSignals(nil, nil, "")
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
assert.Check(t, len(values) > 1)
assert.Check(t, is.Len(values, len(signal.SignalMap)))
}

View File

@ -178,7 +178,6 @@ func TestSplitCpArg(t *testing.T) {
},
}
for _, tc := range testcases {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
if tc.os == "windows" && runtime.GOOS != "windows" {
t.Skip("skipping windows test on non-windows platform")

View File

@ -83,6 +83,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command {
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames)
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames)
_ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli))
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
_ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever))
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
_ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals)

View File

@ -113,7 +113,6 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.PullPolicy, func(t *testing.T) {
pullCounter := 0
@ -176,7 +175,6 @@ func TestCreateContainerImagePullPolicyInvalid(t *testing.T) {
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.PullPolicy, func(t *testing.T) {
dockerCli := test.NewFakeCli(&fakeClient{})
err := runCreate(
@ -207,7 +205,6 @@ func TestCreateContainerValidateFlags(t *testing.T) {
expectedErr: `invalid argument "STDINFO" for "-a, --attach" flag: valid streams are STDIN, STDOUT and STDERR`,
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewCreateCommand(test.NewFakeCli(&fakeClient{}))
cmd.SetOut(io.Discard)
@ -251,7 +248,6 @@ func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig,
@ -312,7 +308,6 @@ func TestNewCreateCommandWithWarnings(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,

View File

@ -0,0 +1,93 @@
package container
import (
"context"
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types/container"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunDiff(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerDiffFunc: func(
ctx context.Context,
containerID string,
) ([]container.FilesystemChange, error) {
return []container.FilesystemChange{
{
Kind: container.ChangeModify,
Path: "/path/to/file0",
},
{
Kind: container.ChangeAdd,
Path: "/path/to/file1",
},
{
Kind: container.ChangeDelete,
Path: "/path/to/file2",
},
}, nil
},
})
cmd := NewDiffCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetArgs([]string{"container-id"})
err := cmd.Execute()
assert.NilError(t, err)
diff := strings.SplitN(cli.OutBuffer().String(), "\n", 3)
assert.Assert(t, is.Len(diff, 3))
file0 := strings.TrimSpace(diff[0])
file1 := strings.TrimSpace(diff[1])
file2 := strings.TrimSpace(diff[2])
assert.Check(t, is.Equal(file0, "C /path/to/file0"))
assert.Check(t, is.Equal(file1, "A /path/to/file1"))
assert.Check(t, is.Equal(file2, "D /path/to/file2"))
}
func TestRunDiffClientError(t *testing.T) {
clientError := errors.New("client error")
cli := test.NewFakeCli(&fakeClient{
containerDiffFunc: func(
ctx context.Context,
containerID string,
) ([]container.FilesystemChange, error) {
return nil, clientError
},
})
cmd := NewDiffCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{"container-id"})
err := cmd.Execute()
assert.ErrorIs(t, err, clientError)
}
func TestRunDiffEmptyContainerError(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{})
cmd := NewDiffCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
containerID := ""
cmd.SetArgs([]string{containerID})
err := cmd.Execute()
assert.Error(t, err, "Container name cannot be empty")
}

View File

@ -47,7 +47,6 @@ D: /usr/app/old_app.js
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
out := bytes.NewBufferString("")
tc.context.Output = out

View File

@ -178,7 +178,6 @@ container2 -- --
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
@ -223,7 +222,6 @@ func TestContainerStatsContextWriteWithNoStats(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := statsFormatWrite(tc.context, []StatsEntry{}, "linux", false)
assert.NilError(t, err)
@ -265,7 +263,6 @@ func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := statsFormatWrite(tc.context, []StatsEntry{}, "windows", false)
assert.NilError(t, err)

View File

@ -0,0 +1,74 @@
package container
import (
"context"
"fmt"
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunKill(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerKillFunc: func(
ctx context.Context,
container string,
signal string,
) error {
assert.Assert(t, is.Equal(signal, "STOP"))
return nil
},
})
cmd := NewKillCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetArgs([]string{
"--signal", "STOP",
"container-id-1",
"container-id-2",
})
err := cmd.Execute()
assert.NilError(t, err)
containerIDs := strings.SplitN(cli.OutBuffer().String(), "\n", 2)
assert.Assert(t, is.Len(containerIDs, 2))
containerID1 := strings.TrimSpace(containerIDs[0])
containerID2 := strings.TrimSpace(containerIDs[1])
assert.Check(t, is.Equal(containerID1, "container-id-1"))
assert.Check(t, is.Equal(containerID2, "container-id-2"))
}
func TestRunKillClientError(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerKillFunc: func(
ctx context.Context,
container string,
signal string,
) error {
return fmt.Errorf("client error for container %s", container)
},
})
cmd := NewKillCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{"container-id-1", "container-id-2"})
err := cmd.Execute()
errs := strings.SplitN(err.Error(), "\n", 2)
assert.Assert(t, is.Len(errs, 2))
errContainerID1 := errs[0]
errContainerID2 := errs[1]
assert.Assert(t, is.Equal(errContainerID1, "client error for container container-id-1"))
assert.Assert(t, is.Equal(errContainerID2, "client error for container container-id-2"))
}

View File

@ -127,7 +127,6 @@ func TestContainerListBuildContainerListOptions(t *testing.T) {
func TestContainerListErrors(t *testing.T) {
testCases := []struct {
args []string
flags map[string]string
containerListFunc func(container.ListOptions) ([]container.Summary, error)
expectedError string
@ -157,10 +156,10 @@ func TestContainerListErrors(t *testing.T) {
containerListFunc: tc.containerListFunc,
}),
)
cmd.SetArgs(tc.args)
for key, value := range tc.flags {
assert.Check(t, cmd.Flags().Set(key, value))
}
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
@ -180,6 +179,9 @@ func TestContainerListWithoutFormat(t *testing.T) {
},
})
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-without-format.golden")
}
@ -194,6 +196,9 @@ func TestContainerListNoTrunc(t *testing.T) {
},
})
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.Check(t, cmd.Flags().Set("no-trunc", "true"))
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-without-format-no-trunc.golden")
@ -210,6 +215,9 @@ func TestContainerListNamesMultipleTime(t *testing.T) {
},
})
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.Check(t, cmd.Flags().Set("format", "{{.Names}} {{.Names}}"))
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-format-name-name.golden")
@ -226,6 +234,9 @@ func TestContainerListFormatTemplateWithArg(t *testing.T) {
},
})
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.Check(t, cmd.Flags().Set("format", `{{.Names}} {{.Label "some.label"}}`))
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-format-with-arg.golden")
@ -266,7 +277,6 @@ func TestContainerListFormatSizeSetsOption(t *testing.T) {
}
for _, tc := range tests {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(options container.ListOptions) ([]container.Summary, error) {
@ -275,6 +285,9 @@ func TestContainerListFormatSizeSetsOption(t *testing.T) {
},
})
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.Check(t, cmd.Flags().Set("format", tc.format))
if tc.sizeFlag != "" {
assert.Check(t, cmd.Flags().Set("size", tc.sizeFlag))
@ -297,6 +310,9 @@ func TestContainerListWithConfigFormat(t *testing.T) {
PsFormat: "{{ .Names }} {{ .Image }} {{ .Labels }} {{ .Size}}",
})
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-with-config-format.golden")
}
@ -314,6 +330,9 @@ func TestContainerListWithFormat(t *testing.T) {
t.Run("with format", func(t *testing.T) {
cli.OutBuffer().Reset()
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.Check(t, cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}"))
assert.NilError(t, cmd.Execute())
golden.Assert(t, cli.OutBuffer().String(), "container-list-with-format.golden")
@ -322,6 +341,9 @@ func TestContainerListWithFormat(t *testing.T) {
t.Run("with format and quiet", func(t *testing.T) {
cli.OutBuffer().Reset()
cmd := newListCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
assert.Check(t, cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}"))
assert.Check(t, cmd.Flags().Set("quiet", "true"))
assert.NilError(t, cmd.Execute())

View File

@ -23,7 +23,6 @@ import (
"github.com/docker/docker/errdefs"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/pflag"
cdi "tags.cncf.io/container-device-interface/pkg/parser"
)
@ -364,10 +363,6 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
return nil, errors.Errorf("invalid value: %d. Valid memory swappiness range is 0-100", swappiness)
}
mounts := copts.mounts.Value()
if len(mounts) > 0 && copts.volumeDriver != "" {
logrus.Warn("`--volume-driver` is ignored for volumes specified via `--mount`. Use `--mount type=volume,volume-driver=...` instead.")
}
var binds []string
volumes := copts.volumes.GetMap()
// add any bind targets to the list of container volumes
@ -697,7 +692,7 @@ func parse(flags *pflag.FlagSet, copts *containerOptions, serverOS string) (*con
Tmpfs: tmpfs,
Sysctls: copts.sysctls.GetAll(),
Runtime: copts.runtime,
Mounts: mounts,
Mounts: copts.mounts.Value(),
MaskedPaths: maskedPaths,
ReadonlyPaths: readonlyPaths,
Annotations: copts.annotations.GetAll(),
@ -767,7 +762,6 @@ func parseNetworkOpts(copts *containerOptions) (map[string]*networktypes.Endpoin
}
for i, n := range copts.netMode.Value() {
n := n
if container.NetworkMode(n.Target).IsUserDefined() {
hasUserDefined = true
} else {

View File

@ -126,7 +126,6 @@ func TestParseRunAttach(t *testing.T) {
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.input, func(t *testing.T) {
config, _, _ := mustParse(t, tc.input)
assert.Equal(t, config.AttachStdin, tc.expected.AttachStdin)
@ -802,7 +801,6 @@ func TestParseRestartPolicy(t *testing.T) {
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.input, func(t *testing.T) {
_, hostConfig, _, err := parseRun([]string{"--restart=" + tc.input, "img", "cmd"})
if tc.expectedErr != "" {
@ -919,7 +917,7 @@ func TestParseEnvfileVariablesWithBOMUnicode(t *testing.T) {
}
// UTF16 with BOM
e := "contains invalid utf8 bytes at line"
e := "invalid utf8 bytes at line"
if _, _, _, err := parseRun([]string{"--env-file=testdata/utf16.env", "img", "cmd"}); err == nil || !strings.Contains(err.Error(), e) {
t.Fatalf("Expected an error with message '%s', got %v", e, err)
}

View File

@ -0,0 +1,65 @@
package container
import (
"context"
"fmt"
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunPause(t *testing.T) {
cli := test.NewFakeCli(
&fakeClient{
containerPauseFunc: func(ctx context.Context, container string) error {
return nil
},
},
)
cmd := NewPauseCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetArgs([]string{"container-id-1", "container-id-2"})
err := cmd.Execute()
assert.NilError(t, err)
containerIDs := strings.SplitN(cli.OutBuffer().String(), "\n", 2)
assert.Assert(t, is.Len(containerIDs, 2))
containerID1 := strings.TrimSpace(containerIDs[0])
containerID2 := strings.TrimSpace(containerIDs[1])
assert.Check(t, is.Equal(containerID1, "container-id-1"))
assert.Check(t, is.Equal(containerID2, "container-id-2"))
}
func TestRunPauseClientError(t *testing.T) {
cli := test.NewFakeCli(
&fakeClient{
containerPauseFunc: func(ctx context.Context, container string) error {
return fmt.Errorf("client error for container %s", container)
},
},
)
cmd := NewPauseCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{"container-id-1", "container-id-2"})
err := cmd.Execute()
errs := strings.SplitN(err.Error(), "\n", 2)
assert.Assert(t, is.Len(errs, 2))
errContainerID1 := errs[0]
errContainerID2 := errs[1]
assert.Assert(t, is.Equal(errContainerID1, "client error for container container-id-1"))
assert.Assert(t, is.Equal(errContainerID2, "client error for container container-id-2"))
}

View File

@ -43,7 +43,6 @@ func TestNewPortCommandOutput(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
inspectFunc: func(string) (container.InspectResponse, error) {

View File

@ -21,6 +21,7 @@ func TestContainerPrunePromptTermination(t *testing.T) {
},
})
cmd := NewPruneCommand(cli)
cmd.SetArgs([]string{})
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
test.TerminatePrompt(ctx, t, cmd, cli)

View File

@ -0,0 +1,77 @@
package container
import (
"context"
"io"
"testing"
"github.com/docker/cli/internal/test"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunRename(t *testing.T) {
testcases := []struct {
doc, oldName, newName, expectedErr string
}{
{
doc: "success",
oldName: "oldName",
newName: "newName",
expectedErr: "",
},
{
doc: "empty old name",
oldName: "",
newName: "newName",
expectedErr: "Error: Neither old nor new names may be empty",
},
{
doc: "empty new name",
oldName: "oldName",
newName: "",
expectedErr: "Error: Neither old nor new names may be empty",
},
}
for _, tc := range testcases {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerRenameFunc: func(ctx context.Context, oldName, newName string) error {
return nil
},
})
cmd := NewRenameCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{tc.oldName, tc.newName})
err := cmd.Execute()
if tc.expectedErr != "" {
assert.ErrorContains(t, err, tc.expectedErr)
} else {
assert.NilError(t, err)
}
})
}
}
func TestRunRenameClientError(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerRenameFunc: func(ctx context.Context, oldName, newName string) error {
return errors.New("client error")
},
})
cmd := NewRenameCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{"oldName", "newName"})
err := cmd.Execute()
assert.Check(t, is.Error(err, "Error: failed to rename container named oldName"))
}

View File

@ -58,7 +58,6 @@ func TestRestart(t *testing.T) {
expectedErr: "conflicting options: cannot specify both --timeout and --time",
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
var restarted []string
mutex := new(sync.Mutex)

View File

@ -38,7 +38,9 @@ func NewRmCommand(dockerCli command.Cli) *cobra.Command {
Annotations: map[string]string{
"aliases": "docker container rm, docker container remove, docker rm",
},
ValidArgsFunction: completion.ContainerNames(dockerCli, true),
ValidArgsFunction: completion.ContainerNames(dockerCli, true, func(ctr container.Summary) bool {
return opts.force || ctr.State == "exited" || ctr.State == "created"
}),
}
flags := cmd.Flags()

View File

@ -23,7 +23,6 @@ func TestRemoveForce(t *testing.T) {
{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)

View File

@ -74,6 +74,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command {
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames)
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames)
_ = cmd.RegisterFlagCompletionFunc("network", completion.NetworkNames(dockerCli))
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
_ = cmd.RegisterFlagCompletionFunc("pull", completion.FromList(PullImageAlways, PullImageMissing, PullImageNever))
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
_ = cmd.RegisterFlagCompletionFunc("stop-signal", completeSignals)

View File

@ -35,7 +35,6 @@ func TestRunValidateFlags(t *testing.T) {
expectedErr: "conflicting options: cannot specify both --attach and --detach",
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewRunCommand(test.NewFakeCli(&fakeClient{}))
cmd.SetOut(io.Discard)
@ -245,7 +244,6 @@ func TestRunCommandWithContentTrustErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,
@ -286,7 +284,6 @@ func TestRunContainerImagePullPolicyInvalid(t *testing.T) {
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.PullPolicy, func(t *testing.T) {
dockerCli := test.NewFakeCli(&fakeClient{})
err := runRun(

View File

@ -1,6 +1,7 @@
package container
import (
"bytes"
"context"
"fmt"
"io"
@ -264,31 +265,40 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
// so we unlikely hit this code in practice.
daemonOSType = dockerCLI.ServerInfo().OSType
}
// Buffer to store formatted stats text.
// Once formatted, it will be printed in one write to avoid screen flickering.
var statsTextBuffer bytes.Buffer
statsCtx := formatter.Context{
Output: dockerCLI.Out(),
Output: &statsTextBuffer,
Format: NewStatsFormat(format, daemonOSType),
}
cleanScreen := func() {
if !options.NoStream {
_, _ = fmt.Fprint(dockerCLI.Out(), "\033[2J")
_, _ = fmt.Fprint(dockerCLI.Out(), "\033[H")
}
}
var err error
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for range ticker.C {
cleanScreen()
var ccStats []StatsEntry
cStats.mu.RLock()
for _, c := range cStats.cs {
ccStats = append(ccStats, c.GetStatistics())
}
cStats.mu.RUnlock()
if !options.NoStream {
// Start by clearing the screen and moving the cursor to the top-left
_, _ = fmt.Fprint(&statsTextBuffer, "\033[2J\033[H")
}
if err = statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
break
}
_, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String())
statsTextBuffer.Reset()
if len(cStats.cs) == 0 && !showAll {
break
}

View File

@ -58,7 +58,6 @@ func TestStop(t *testing.T) {
expectedErr: "conflicting options: cannot specify both --timeout and --time",
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
var stopped []string
mutex := new(sync.Mutex)

View File

@ -38,7 +38,7 @@ func waitFn(cid string) (<-chan container.WaitResponse, <-chan error) {
}
func TestWaitExitOrRemoved(t *testing.T) {
testcases := []struct {
tests := []struct {
cid string
exitCode int
}{
@ -61,9 +61,11 @@ func TestWaitExitOrRemoved(t *testing.T) {
}
client := &fakeClient{waitFunc: waitFn, Version: api.DefaultVersion}
for _, testcase := range testcases {
statusC := waitExitOrRemoved(context.Background(), client, testcase.cid, true)
exitCode := <-statusC
assert.Check(t, is.Equal(testcase.exitCode, exitCode))
for _, tc := range tests {
t.Run(tc.cid, func(t *testing.T) {
statusC := waitExitOrRemoved(context.Background(), client, tc.cid, true)
exitCode := <-statusC
assert.Check(t, is.Equal(tc.exitCode, exitCode))
})
}
}

View File

@ -94,7 +94,6 @@ func TestCreate(t *testing.T) {
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.options.Name, func(t *testing.T) {
err := RunCreate(cli, &tc.options)
if tc.expecterErr == "" {
@ -164,25 +163,24 @@ func TestCreateFromContext(t *testing.T) {
cli.SetCurrentContext("dummy")
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{
From: "original",
Name: c.name,
Description: c.description,
Docker: c.docker,
Name: tc.name,
Description: tc.description,
Docker: tc.docker,
})
assert.NilError(t, err)
assertContextCreateLogging(t, cli, c.name)
newContext, err := cli.ContextStore().GetMetadata(c.name)
assertContextCreateLogging(t, cli, tc.name)
newContext, err := cli.ContextStore().GetMetadata(tc.name)
assert.NilError(t, err)
newContextTyped, err := command.GetDockerContext(newContext)
assert.NilError(t, err)
dockerEndpoint, err := docker.EndpointFromContext(newContext)
assert.NilError(t, err)
assert.Equal(t, newContextTyped.Description, c.expectedDescription)
assert.Equal(t, newContextTyped.Description, tc.expectedDescription)
assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
})
}
@ -219,23 +217,22 @@ func TestCreateFromCurrent(t *testing.T) {
cli.SetCurrentContext("original")
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
cli.ResetOutputBuffers()
err := RunCreate(cli, &CreateOptions{
Name: c.name,
Description: c.description,
Name: tc.name,
Description: tc.description,
})
assert.NilError(t, err)
assertContextCreateLogging(t, cli, c.name)
newContext, err := cli.ContextStore().GetMetadata(c.name)
assertContextCreateLogging(t, cli, tc.name)
newContext, err := cli.ContextStore().GetMetadata(tc.name)
assert.NilError(t, err)
newContextTyped, err := command.GetDockerContext(newContext)
assert.NilError(t, err)
dockerEndpoint, err := docker.EndpointFromContext(newContext)
assert.NilError(t, err)
assert.Equal(t, newContextTyped.Description, c.expectedDescription)
assert.Equal(t, newContextTyped.Description, tc.expectedDescription)
assert.Equal(t, dockerEndpoint.Host, "tcp://42.42.42.42:2375")
})
}

View File

@ -346,7 +346,6 @@ size: 0B
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
@ -411,7 +410,6 @@ func TestContainerContextWriteWithNoContainers(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := ContainerWrite(tc.context, containers)
assert.NilError(t, err)

View File

@ -106,7 +106,6 @@ Build Cache 0 0 0B 0B
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -304,7 +304,6 @@ image_id: imageID3
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
@ -365,7 +364,6 @@ func TestImageContextWriteWithNoImage(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := ImageWrite(tc.context, images)
assert.NilError(t, err)

View File

@ -131,7 +131,6 @@ foobar_bar
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -18,6 +18,7 @@ import (
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/image/build"
"github.com/docker/cli/opts"
"github.com/docker/docker/api"
@ -159,6 +160,8 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command {
flags.SetAnnotation("squash", "experimental", nil)
flags.SetAnnotation("squash", "version", []string{"1.25"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}

View File

@ -255,7 +255,6 @@ imageID6 17 years ago /bin/bash echo 183MB
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := HistoryWrite(tc.context, true, histories)
assert.NilError(t, err)

View File

@ -3,17 +3,20 @@ package image
import (
"context"
"github.com/containerd/platforms"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/formatter"
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/docker/api/types/image"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type historyOptions struct {
image string
image string
platform string
human bool
quiet bool
@ -45,12 +48,24 @@ func NewHistoryCommand(dockerCli command.Cli) *cobra.Command {
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Only show image IDs")
flags.BoolVar(&opts.noTrunc, "no-trunc", false, "Don't truncate output")
flags.StringVar(&opts.format, "format", "", flagsHelper.FormatHelp)
flags.StringVar(&opts.platform, "platform", "", `Show history for the given platform. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`)
_ = flags.SetAnnotation("platform", "version", []string{"1.48"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}
func runHistory(ctx context.Context, dockerCli command.Cli, opts historyOptions) error {
history, err := dockerCli.Client().ImageHistory(ctx, opts.image, image.HistoryOptions{})
var options image.HistoryOptions
if opts.platform != "" {
p, err := platforms.Parse(opts.platform)
if err != nil {
return errors.Wrap(err, "invalid platform")
}
options.Platform = &p
}
history, err := dockerCli.Client().ImageHistory(ctx, opts.image, options)
if err != nil {
return err
}

View File

@ -8,8 +8,10 @@ import (
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
)
@ -33,9 +35,13 @@ func TestNewHistoryCommandErrors(t *testing.T) {
return []image.HistoryResponseItem{{}}, errors.Errorf("something went wrong")
},
},
{
name: "invalid platform",
args: []string{"--platform", "<invalid>", "arg1"},
expectedError: `invalid platform`,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc}))
cmd.SetOut(io.Discard)
@ -89,9 +95,19 @@ func TestNewHistoryCommandSuccess(t *testing.T) {
}}, nil
},
},
{
name: "platform",
args: []string{"--platform", "linux/amd64", "image:tag"},
imageHistoryFunc: func(img string, options image.HistoryOptions) ([]image.HistoryResponseItem, error) {
assert.Check(t, is.DeepEqual(ocispec.Platform{OS: "linux", Architecture: "amd64"}, *options.Platform))
return []image.HistoryResponseItem{{
ID: "1234567890123456789",
Created: time.Now().Unix(),
}}, nil
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
// Set to UTC timezone as timestamps in output are
// printed in the current timezone

View File

@ -7,6 +7,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
dockeropts "github.com/docker/cli/opts"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/pkg/jsonmessage"
@ -47,6 +48,7 @@ func NewImportCommand(dockerCli command.Cli) *cobra.Command {
flags.VarP(&options.changes, "change", "c", "Apply Dockerfile instruction to the created image")
flags.StringVarP(&options.message, "message", "m", "", "Set commit message for imported image")
command.AddPlatformFlag(flags, &options.platform)
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}

View File

@ -98,7 +98,6 @@ func TestNewImportCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc}))
cmd.SetOut(io.Discard)

View File

@ -25,7 +25,6 @@ func TestNewInspectCommandErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := newInspectCommand(test.NewFakeCli(&fakeClient{}))
cmd.SetOut(io.Discard)
@ -79,7 +78,6 @@ func TestNewInspectCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
imageInspectInvocationCount = 0
cli := test.NewFakeCli(&fakeClient{imageInspectFunc: tc.imageInspectFunc})

View File

@ -35,7 +35,6 @@ func TestNewImagesCommandErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewImagesCommand(test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc}))
cmd.SetOut(io.Discard)
@ -83,7 +82,6 @@ func TestNewImagesCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc})
cli.SetConfigFile(&configfile.ConfigFile{ImagesFormat: tc.imageFormat})

View File

@ -4,6 +4,7 @@ import (
"context"
"io"
"github.com/containerd/platforms"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
@ -15,8 +16,9 @@ import (
)
type loadOptions struct {
input string
quiet bool
input string
quiet bool
platform string
}
// NewLoadCommand creates a new `docker load` command
@ -40,7 +42,10 @@ func NewLoadCommand(dockerCli command.Cli) *cobra.Command {
flags.StringVarP(&opts.input, "input", "i", "", "Read from tar archive file, instead of STDIN")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress the load output")
flags.StringVar(&opts.platform, "platform", "", `Load only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`)
_ = flags.SetAnnotation("platform", "version", []string{"1.48"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}
@ -63,12 +68,20 @@ func runLoad(ctx context.Context, dockerCli command.Cli, opts loadOptions) error
return errors.Errorf("requested load from stdin, but stdin is empty")
}
var loadOpts image.LoadOptions
var options image.LoadOptions
if opts.quiet || !dockerCli.Out().IsTerminal() {
loadOpts.Quiet = true
options.Quiet = true
}
response, err := dockerCli.Client().ImageLoad(ctx, input, loadOpts)
if opts.platform != "" {
p, err := platforms.Parse(opts.platform)
if err != nil {
return errors.Wrap(err, "invalid platform")
}
options.Platform = &p
}
response, err := dockerCli.Client().ImageLoad(ctx, input, options)
if err != nil {
return err
}

View File

@ -8,8 +8,10 @@ import (
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
)
@ -28,19 +30,28 @@ func TestNewLoadCommandErrors(t *testing.T) {
},
{
name: "input-to-terminal",
args: []string{},
isTerminalIn: true,
expectedError: "requested load from stdin, but stdin is empty",
},
{
name: "pull-error",
args: []string{},
expectedError: "something went wrong",
imageLoadFunc: func(input io.Reader, options image.LoadOptions) (image.LoadResponse, error) {
return image.LoadResponse{}, errors.Errorf("something went wrong")
},
},
{
name: "invalid platform",
args: []string{"--platform", "<invalid>"},
expectedError: `invalid platform`,
imageLoadFunc: func(input io.Reader, options image.LoadOptions) (image.LoadResponse, error) {
return image.LoadResponse{}, nil
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc})
cli.In().SetIsTerminal(tc.isTerminalIn)
@ -71,12 +82,14 @@ func TestNewLoadCommandSuccess(t *testing.T) {
}{
{
name: "simple",
args: []string{},
imageLoadFunc: func(input io.Reader, options image.LoadOptions) (image.LoadResponse, error) {
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
},
},
{
name: "json",
args: []string{},
imageLoadFunc: func(input io.Reader, options image.LoadOptions) (image.LoadResponse, error) {
json := "{\"ID\": \"1\"}"
return image.LoadResponse{
@ -92,9 +105,16 @@ func TestNewLoadCommandSuccess(t *testing.T) {
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
},
},
{
name: "with platform",
args: []string{"--platform", "linux/amd64"},
imageLoadFunc: func(input io.Reader, options image.LoadOptions) (image.LoadResponse, error) {
assert.Check(t, is.DeepEqual(ocispec.Platform{OS: "linux", Architecture: "amd64"}, *options.Platform))
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imageLoadFunc: tc.imageLoadFunc})
cmd := NewLoadCommand(cli)

View File

@ -39,7 +39,6 @@ func TestNewPruneCommandErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewPruneCommand(test.NewFakeCli(&fakeClient{
imagesPruneFunc: tc.imagesPruneFunc,
@ -98,7 +97,6 @@ func TestNewPruneCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imagesPruneFunc: tc.imagesPruneFunc})
// when prompted, answer "Y" to confirm the prune.

View File

@ -50,6 +50,8 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command {
command.AddPlatformFlag(flags, &opts.platform)
command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled())
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}

View File

@ -38,7 +38,6 @@ func TestNewPullCommandErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{})
cmd := NewPullCommand(cli)
@ -73,7 +72,6 @@ func TestNewPullCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
imagePullFunc: func(ref string, options image.PullOptions) (io.ReadCloser, error) {
@ -119,7 +117,6 @@ func TestNewPullCommandWithContentTrustErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
imagePullFunc: func(ref string, options image.PullOptions) (io.ReadCloser, error) {

View File

@ -68,6 +68,8 @@ Image index won't be pushed, meaning that other manifests, including attestation
'os[/arch[/variant]]': Explicit platform (eg. linux/amd64)`)
flags.SetAnnotation("platform", "version", []string{"1.46"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}

View File

@ -38,7 +38,6 @@ func TestNewPushCommandErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc})
cmd := NewPushCommand(cli)
@ -68,7 +67,6 @@ func TestNewPushCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
imagePushFunc: func(ref string, options image.PushOptions) (io.ReadCloser, error) {

View File

@ -62,7 +62,6 @@ func TestNewRemoveCommandErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := NewRemoveCommand(test.NewFakeCli(&fakeClient{
imageRemoveFunc: tc.imageRemoveFunc,
@ -121,7 +120,6 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imageRemoveFunc: tc.imageRemoveFunc})
cmd := NewRemoveCommand(cli)

View File

@ -4,6 +4,7 @@ import (
"context"
"io"
"github.com/containerd/platforms"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
@ -13,8 +14,9 @@ import (
)
type saveOptions struct {
images []string
output string
images []string
output string
platform string
}
// NewSaveCommand creates a new `docker save` command
@ -38,7 +40,10 @@ func NewSaveCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.StringVarP(&opts.output, "output", "o", "", "Write to a file, instead of STDOUT")
flags.StringVar(&opts.platform, "platform", "", `Save only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`)
_ = flags.SetAnnotation("platform", "version", []string{"1.48"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}
@ -52,7 +57,16 @@ func RunSave(ctx context.Context, dockerCli command.Cli, opts saveOptions) error
return errors.Wrap(err, "failed to save image")
}
responseBody, err := dockerCli.Client().ImageSave(ctx, opts.images, image.SaveOptions{})
var options image.SaveOptions
if opts.platform != "" {
p, err := platforms.Parse(opts.platform)
if err != nil {
return errors.Wrap(err, "invalid platform")
}
options.Platform = &p
}
responseBody, err := dockerCli.Client().ImageSave(ctx, opts.images, options)
if err != nil {
return err
}

View File

@ -8,6 +8,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types/image"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@ -51,9 +52,13 @@ func TestNewSaveCommandErrors(t *testing.T) {
args: []string{"-o", "/dev/null", "arg1"},
expectedError: "failed to save image: invalid output path: \"/dev/null\" must be a directory or a regular file",
},
{
name: "invalid platform",
args: []string{"--platform", "<invalid>", "arg1"},
expectedError: `invalid platform`,
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{imageSaveFunc: tc.imageSaveFunc})
cli.Out().SetIsTerminal(tc.isTerminal)
@ -70,13 +75,13 @@ func TestNewSaveCommandSuccess(t *testing.T) {
testCases := []struct {
args []string
isTerminal bool
imageSaveFunc func(images []string) (io.ReadCloser, error)
imageSaveFunc func(images []string, options image.SaveOptions) (io.ReadCloser, error)
deferredFunc func()
}{
{
args: []string{"-o", "save_tmp_file", "arg1"},
isTerminal: true,
imageSaveFunc: func(images []string) (io.ReadCloser, error) {
imageSaveFunc: func(images []string, _ image.SaveOptions) (io.ReadCloser, error) {
assert.Assert(t, is.Len(images, 1))
assert.Check(t, is.Equal("arg1", images[0]))
return io.NopCloser(strings.NewReader("")), nil
@ -88,21 +93,28 @@ func TestNewSaveCommandSuccess(t *testing.T) {
{
args: []string{"arg1", "arg2"},
isTerminal: false,
imageSaveFunc: func(images []string) (io.ReadCloser, error) {
imageSaveFunc: func(images []string, _ image.SaveOptions) (io.ReadCloser, error) {
assert.Assert(t, is.Len(images, 2))
assert.Check(t, is.Equal("arg1", images[0]))
assert.Check(t, is.Equal("arg2", images[1]))
return io.NopCloser(strings.NewReader("")), nil
},
},
{
args: []string{"--platform", "linux/amd64", "arg1"},
isTerminal: false,
imageSaveFunc: func(images []string, options image.SaveOptions) (io.ReadCloser, error) {
assert.Assert(t, is.Len(images, 1))
assert.Check(t, is.Equal("arg1", images[0]))
assert.Check(t, is.DeepEqual(ocispec.Platform{OS: "linux", Architecture: "amd64"}, *options.Platform))
return io.NopCloser(strings.NewReader("")), nil
},
},
}
for _, tc := range testCases {
tc := tc
t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
cmd := NewSaveCommand(test.NewFakeCli(&fakeClient{
imageSaveFunc: func(images []string, options image.SaveOptions) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("")), nil
},
imageSaveFunc: tc.imageSaveFunc,
}))
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)

View File

@ -0,0 +1,2 @@
IMAGE CREATED CREATED BY SIZE COMMENT
123456789012 Less than a second ago 0B

View File

@ -0,0 +1 @@
Success

View File

@ -56,7 +56,6 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) error
continue
}
im := im
sub := subImage{
Platform: platforms.Format(im.ImageData.Platform),
Available: im.Available,

View File

@ -31,7 +31,6 @@ func TestManifestCreateErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.expectedError, func(t *testing.T) {
cli := test.NewFakeCli(nil)
cmd := newCreateListCommand(cli)

View File

@ -218,7 +218,6 @@ func TestNetworkCreateIPv6(t *testing.T) {
}
for _, tc := range tests {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
networkCreateFunc: func(ctx context.Context, name string, createBody network.CreateOptions) (network.CreateResponse, error) {

View File

@ -161,7 +161,6 @@ foobar_bar 2017-01-01 00:00:00 +0000 UTC
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -83,7 +83,6 @@ func TestNetworkList(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{networkListFunc: tc.networkListFunc})
cmd := newListCommand(cli)

View File

@ -63,7 +63,6 @@ func TestNetworkRemoveForce(t *testing.T) {
}
for _, tc := range tests {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
fakeCli := test.NewFakeCli(&fakeClient{
networkRemoveFunc: func(ctx context.Context, networkID string) error {

View File

@ -202,7 +202,6 @@ foobar_boo Unknown
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -106,7 +106,6 @@ func TestNodeInspectPretty(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
nodeInspectFunc: tc.nodeInspectFunc,

View File

@ -134,7 +134,6 @@ func TestNodePs(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
infoFunc: tc.infoFunc,

View File

@ -131,7 +131,6 @@ foobar_bar
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -66,7 +66,6 @@ func TestInspectErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{pluginInspectFunc: tc.inspectFunc})
cmd := newInspectCommand(cli)
@ -138,7 +137,6 @@ func TestInspect(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{pluginInspectFunc: tc.inspectFunc})
cmd := newInspectCommand(cli)

View File

@ -54,7 +54,6 @@ func TestInstallErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{pluginInstallFunc: tc.installFunc})
cmd := newInstallCommand(cli)
@ -94,7 +93,6 @@ func TestInstallContentTrustErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
pluginInstallFunc: func(name string, options types.PluginInstallOptions) (io.ReadCloser, error) {
@ -138,7 +136,6 @@ func TestInstall(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{pluginInstallFunc: tc.installFunc})
cmd := newInstallCommand(cli)

View File

@ -46,7 +46,6 @@ func TestListErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{pluginListFunc: tc.listFunc})
cmd := newListCommand(cli)
@ -166,7 +165,6 @@ func TestList(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.description, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{pluginListFunc: tc.listFunc})
cmd := newListCommand(cli)

View File

@ -2,6 +2,7 @@ package plugin
import (
"context"
"errors"
"fmt"
"github.com/docker/cli/cli"
@ -36,17 +37,13 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
}
func runRemove(ctx context.Context, dockerCli command.Cli, opts *rmOptions) error {
var errs cli.Errors
var errs error
for _, name := range opts.plugins {
if err := dockerCli.Client().PluginRemove(ctx, name, types.PluginRemoveOptions{Force: opts.force}); err != nil {
errs = append(errs, err)
errs = errors.Join(errs, err)
continue
}
fmt.Fprintln(dockerCli.Out(), name)
_, _ = fmt.Fprintln(dockerCli.Out(), name)
}
// Do not simplify to `return errs` because even if errs == nil, it is not a nil-error interface value.
if errs != nil {
return errs
}
return nil
return errs
}

View File

@ -18,20 +18,24 @@ import (
"github.com/pkg/errors"
)
const patSuggest = "You can log in with your password or a Personal Access " +
"Token (PAT). Using a limited-scope PAT grants better security and is required " +
"for organizations using SSO. Learn more at https://docs.docker.com/go/access-tokens/"
const (
registerSuggest = "Log in with your Docker ID or email address to push and pull images from Docker Hub. " +
"If you don't have a Docker ID, head over to https://hub.docker.com/ to create one."
patSuggest = "You can log in with your password or a Personal Access " +
"Token (PAT). Using a limited-scope PAT grants better security and is required " +
"for organizations using SSO. Learn more at https://docs.docker.com/go/access-tokens/"
)
// RegistryAuthenticationPrivilegedFunc returns a RequestPrivilegeFunc from the specified registry index info
// for the given command.
func RegistryAuthenticationPrivilegedFunc(cli Cli, index *registrytypes.IndexInfo, cmdName string) registrytypes.RequestAuthConfig {
return func(ctx context.Context) (string, error) {
fmt.Fprintf(cli.Out(), "\nLogin prior to %s:\n", cmdName)
_, _ = fmt.Fprintf(cli.Out(), "\nLogin prior to %s:\n", cmdName)
indexServer := registry.GetAuthConfigKey(index)
isDefaultRegistry := indexServer == registry.IndexServer
authConfig, err := GetDefaultAuthConfig(cli.ConfigFile(), true, indexServer, isDefaultRegistry)
if err != nil {
fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
_, _ = fmt.Fprintf(cli.Err(), "Unable to retrieve stored credentials for %s, error: %s.\n", indexServer, err)
}
select {
@ -86,7 +90,8 @@ func GetDefaultAuthConfig(cfg *configfile.ConfigFile, checkCredStore bool, serve
}
// ConfigureAuth handles prompting of user's username and password if needed.
// Deprecated: use PromptUserForCredentials instead.
//
// Deprecated: use [PromptUserForCredentials] instead.
func ConfigureAuth(ctx context.Context, cli Cli, flUser, flPassword string, authConfig *registrytypes.AuthConfig, _ bool) error {
defaultUsername := authConfig.Username
serverAddress := authConfig.ServerAddress
@ -110,7 +115,7 @@ func ConfigureAuth(ctx context.Context, cli Cli, flUser, flPassword string, auth
// If defaultUsername is not empty, the username prompt includes that username
// and the user can hit enter without inputting a username to use that default
// username.
func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword, defaultUsername, serverAddress string) (authConfig registrytypes.AuthConfig, err error) {
func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword, defaultUsername, serverAddress string) (registrytypes.AuthConfig, error) {
// On Windows, force the use of the regular OS stdin stream.
//
// See:
@ -123,57 +128,71 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
cli.SetIn(streams.NewIn(os.Stdin))
}
isDefaultRegistry := serverAddress == registry.IndexServer
defaultUsername = strings.TrimSpace(defaultUsername)
if argUser = strings.TrimSpace(argUser); argUser == "" {
if isDefaultRegistry {
// if this is a default registry (docker hub), then display the following message.
fmt.Fprintln(cli.Out(), "Log in with your Docker ID or email address to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com/ to create one.")
argUser = strings.TrimSpace(argUser)
if argUser == "" {
if serverAddress == registry.IndexServer {
// When signing in to the default (Docker Hub) registry, we display
// hints for creating an account, and (if hints are enabled), using
// a token instead of a password.
_, _ = fmt.Fprintln(cli.Out(), registerSuggest)
if hints.Enabled() {
fmt.Fprintln(cli.Out(), patSuggest)
fmt.Fprintln(cli.Out())
_, _ = fmt.Fprintln(cli.Out(), patSuggest)
_, _ = fmt.Fprintln(cli.Out())
}
}
var prompt string
defaultUsername = strings.TrimSpace(defaultUsername)
if defaultUsername == "" {
prompt = "Username: "
} else {
prompt = fmt.Sprintf("Username (%s): ", defaultUsername)
}
var err error
argUser, err = PromptForInput(ctx, cli.In(), cli.Out(), prompt)
if err != nil {
return authConfig, err
return registrytypes.AuthConfig{}, err
}
if argUser == "" {
argUser = defaultUsername
}
if argUser == "" {
return registrytypes.AuthConfig{}, errors.Errorf("Error: Non-null Username Required")
}
}
if argUser == "" {
return authConfig, errors.Errorf("Error: Non-null Username Required")
}
argPassword = strings.TrimSpace(argPassword)
if argPassword == "" {
restoreInput, err := DisableInputEcho(cli.In())
if err != nil {
return authConfig, err
return registrytypes.AuthConfig{}, err
}
defer restoreInput()
defer func() {
if err := restoreInput(); err != nil {
// TODO(thaJeztah): we should consider printing instructions how
// to restore this manually (other than restarting the shell).
// e.g., 'run stty echo' when in a Linux or macOS shell, but
// PowerShell and CMD.exe may need different instructions.
_, _ = fmt.Fprintln(cli.Err(), "Error: failed to restore terminal state to echo input:", err)
}
}()
argPassword, err = PromptForInput(ctx, cli.In(), cli.Out(), "Password: ")
if err != nil {
return authConfig, err
return registrytypes.AuthConfig{}, err
}
fmt.Fprint(cli.Out(), "\n")
_, _ = fmt.Fprintln(cli.Out())
if argPassword == "" {
return authConfig, errors.Errorf("Error: Password Required")
return registrytypes.AuthConfig{}, errors.Errorf("Error: Password Required")
}
}
authConfig.Username = argUser
authConfig.Password = argPassword
authConfig.ServerAddress = serverAddress
return authConfig, nil
return registrytypes.AuthConfig{
Username: argUser,
Password: argPassword,
ServerAddress: serverAddress,
}, nil
}
// RetrieveAuthTokenFromImage retrieves an encoded auth token given a complete

View File

@ -203,7 +203,6 @@ result2 5
}
for _, tc := range cases {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
var out bytes.Buffer
err := SearchWrite(formatter.Context{Format: tc.format, Output: &out}, results)

View File

@ -58,9 +58,9 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
return cmd
}
func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
func verifyLoginOptions(dockerCli command.Cli, opts *loginOptions) error {
if opts.password != "" {
fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
_, _ = fmt.Fprintln(dockerCli.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
if opts.passwordStdin {
return errors.New("--password and --password-stdin are mutually exclusive")
}
@ -83,7 +83,7 @@ func verifyloginOptions(dockerCli command.Cli, opts *loginOptions) error {
}
func runLogin(ctx context.Context, dockerCli command.Cli, opts loginOptions) error {
if err := verifyloginOptions(dockerCli, &opts); err != nil {
if err := verifyLoginOptions(dockerCli, &opts); err != nil {
return err
}
var (
@ -174,7 +174,7 @@ func loginUser(ctx context.Context, dockerCli command.Cli, opts loginOptions, de
if !errors.Is(err, manager.ErrDeviceLoginStartFail) {
return response, err
}
fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
_, _ = fmt.Fprint(dockerCli.Err(), "Failed to start web-based login - falling back to command line login...\n\n")
}
return loginWithUsernameAndPassword(ctx, dockerCli, opts, defaultUsername, serverAddress)

View File

@ -61,7 +61,6 @@ id_rsa
},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -93,7 +93,6 @@ func TestSecretInspectWithoutFormat(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
secretInspectFunc: tc.secretInspectFunc,
@ -132,7 +131,6 @@ func TestSecretInspectWithFormat(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
secretInspectFunc: tc.secretInspectFunc,

View File

@ -223,7 +223,6 @@ zarp2
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -168,7 +168,6 @@ func TestServiceListServiceStatus(t *testing.T) {
}
for _, tc := range matrix {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
if tc.cluster == nil {
tc.cluster = generateCluster(t, tc.opts)

View File

@ -50,7 +50,6 @@ func TestCredentialSpecOpt(t *testing.T) {
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
var cs credentialSpecOpt

View File

@ -91,7 +91,6 @@ func TestRollbackWithErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := newRollbackCommand(
test.NewFakeCli(&fakeClient{

View File

@ -1058,7 +1058,6 @@ func updatePorts(flags *pflag.FlagSet, portConfig *[]swarm.PortConfig) error {
// Build the current list of portConfig
for _, entry := range *portConfig {
entry := entry
if _, ok := portSet[portConfigToString(&entry)]; !ok {
portSet[portConfigToString(&entry)] = entry
}
@ -1086,7 +1085,6 @@ portLoop:
ports := flags.Lookup(flagPublishAdd).Value.(*opts.PortOpt).Value()
for _, port := range ports {
port := port
if _, ok := portSet[portConfigToString(&port)]; ok {
continue
}

View File

@ -1690,7 +1690,6 @@ func TestUpdateUlimits(t *testing.T) {
}
for _, tc := range tests {
tc := tc
t.Run(tc.name, func(t *testing.T) {
svc := swarm.ServiceSpec{
TaskTemplate: swarm.TaskSpec{

View File

@ -51,7 +51,6 @@ bar
{Name: "bar", Services: 1},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out

View File

@ -48,7 +48,6 @@ func TestListErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.expectedError, func(t *testing.T) {
cmd := newListCommand(test.NewFakeCli(&fakeClient{
serviceListFunc: tc.serviceListFunc,
@ -104,7 +103,6 @@ func TestStackList(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
var services []swarm.Service
for _, name := range tc.serviceNames {

View File

@ -40,7 +40,6 @@ func TestStackPsErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.expectedError, func(t *testing.T) {
cmd := newPsCommand(test.NewFakeCli(&fakeClient{
taskListFunc: tc.taskListFunc,
@ -160,7 +159,6 @@ func TestStackPs(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
taskListFunc: tc.taskListFunc,

View File

@ -67,7 +67,6 @@ func TestStackServicesErrors(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.expectedError, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
serviceListFunc: tc.serviceListFunc,

View File

@ -88,7 +88,6 @@ func TestServiceUpdateResolveImageChanged(t *testing.T) {
ctx := context.Background()
for _, tc := range testcases {
tc := tc
t.Run(tc.image, func(t *testing.T) {
spec := map[string]swarm.ServiceSpec{
"myservice": {

View File

@ -63,7 +63,6 @@ func TestSwarmInitErrorOnAPIFailure(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := newInitCommand(
test.NewFakeCli(&fakeClient{

View File

@ -48,7 +48,6 @@ func TestSwarmJoinErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := newJoinCommand(
test.NewFakeCli(&fakeClient{
@ -93,7 +92,6 @@ func TestSwarmJoin(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
infoFunc: tc.infoFunc,

View File

@ -87,7 +87,6 @@ func TestSwarmJoinTokenErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
swarmInspectFunc: tc.swarmInspectFunc,
@ -198,7 +197,6 @@ func TestSwarmJoinToken(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
swarmInspectFunc: tc.swarmInspectFunc,

View File

@ -32,7 +32,6 @@ func TestSwarmLeaveErrors(t *testing.T) {
},
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cmd := newLeaveCommand(
test.NewFakeCli(&fakeClient{

Some files were not shown because too many files have changed in this diff Show More