Compare commits

..

59 Commits

Author SHA1 Message Date
5dc9bcc5b7 Merge pull request #4951 from vvoland/vendor-docker-25.0.5-dev
Some checks failed
build / prepare-plugins (push) Has been cancelled
build / plugins (push) Has been cancelled
codeql / codeql (push) Has been cancelled
e2e / e2e (alpine, 23, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 23, experimental) (push) Has been cancelled
e2e / e2e (alpine, 23, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, 24, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 24, experimental) (push) Has been cancelled
e2e / e2e (alpine, 24, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 25, experimental) (push) Has been cancelled
e2e / e2e (alpine, 25, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 23, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 23, experimental) (push) Has been cancelled
e2e / e2e (debian, 23, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 24, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 24, experimental) (push) Has been cancelled
e2e / e2e (debian, 24, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 25, experimental) (push) Has been cancelled
e2e / e2e (debian, 25, non-experimental) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-12) (push) Has been cancelled
validate / validate (lint) (push) Has been cancelled
validate / validate (shellcheck) (push) Has been cancelled
validate / validate (update-authors) (push) Has been cancelled
validate / validate (validate-vendor) (push) Has been cancelled
validate / validate-md (push) Has been cancelled
validate / validate-make (manpages) (push) Has been cancelled
validate / validate-make (yamldocs) (push) Has been cancelled
vendor: github.com/docker/docker e63daec8672d (v25.0.5-dev)
2024-03-19 15:51:10 +01:00
c2be159764 vendor: github.com/docker/docker e63daec8672d (v25.0.5-dev)
full diff: 061aa95809...e63daec867

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-19 15:43:45 +01:00
1a576c50a9 Merge pull request #4924 from vvoland/v25.0-4923
Some checks failed
build / prepare-plugins (push) Has been cancelled
build / plugins (push) Has been cancelled
codeql / codeql (push) Has been cancelled
e2e / e2e (alpine, 23, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 23, experimental) (push) Has been cancelled
e2e / e2e (alpine, 23, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, 24, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 24, experimental) (push) Has been cancelled
e2e / e2e (alpine, 24, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, 25, experimental) (push) Has been cancelled
e2e / e2e (alpine, 25, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 23, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 23, experimental) (push) Has been cancelled
e2e / e2e (debian, 23, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 24, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 24, experimental) (push) Has been cancelled
e2e / e2e (debian, 24, non-experimental) (push) Has been cancelled
e2e / e2e (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, 25, experimental) (push) Has been cancelled
e2e / e2e (debian, 25, non-experimental) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-12) (push) Has been cancelled
validate / validate (lint) (push) Has been cancelled
validate / validate (shellcheck) (push) Has been cancelled
validate / validate (update-authors) (push) Has been cancelled
validate / validate (validate-vendor) (push) Has been cancelled
validate / validate-md (push) Has been cancelled
validate / validate-make (manpages) (push) Has been cancelled
validate / validate-make (yamldocs) (push) Has been cancelled
[25.0 backport] bake: Add `windows/arm64` target to bin-image-cross
2024-03-06 09:08:42 -07:00
690b1565fb bake: Add windows/arm64 target to bin-image-cross
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit ab9d560570)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-06 16:48:25 +01:00
03114ec2ca Merge pull request #4921 from vvoland/vendor-docker
vendor: github.com/docker/docker 061aa95809be396a6
2024-03-06 15:33:35 +01:00
833128bce5 vendor: github.com/docker/docker 061aa95809be396a6
no change in vendored files

full diff: 9e526bc394...061aa95809

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-06 15:08:04 +01:00
fd4d39aa88 Merge pull request #4920 from vvoland/vendor-docker
[25.0] vendor: github.com/docker/docker 9e526bc3943c
2024-03-05 22:25:25 +01:00
b4b35dedc6 Merge pull request #4919 from vvoland/v25.0-4918
[25.0 backport] update to go1.21.8
2024-03-05 22:24:46 +01:00
ce113a74af vendor: github.com/docker/docker 9e526bc3943c
no change in vendored files

full diff: 51e876cd96...9e526bc394

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-05 22:15:37 +01:00
a3b6c9ea7e update to go1.21.8
go1.21.8 (released 2024-03-05) includes 5 security fixes:

- crypto/x509: Verify panics on certificates with an unknown public key algorithm (CVE-2024-24783, https://go.dev/issue/65390)
- net/http: memory exhaustion in Request.ParseMultipartForm (CVE-2023-45290, https://go.dev/issue/65383)
- net/http, net/http/cookiejar: incorrect forwarding of sensitive headers and cookies on HTTP redirect (CVE-2023-45289, https://go.dev/issue/65065)
- html/template: errors returned from MarshalJSON methods may break template escaping (CVE-2024-24785, https://go.dev/issue/65697)
- net/mail: comments in display names are incorrectly handled (CVE-2024-24784, https://go.dev/issue/65083)

View the release notes for more information:
https://go.dev/doc/devel/release#go1.21.8

- https://github.com/golang/go/issues?q=milestone%3AGo1.21.8+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.21.6...go1.21.8

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 3b77477943)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-05 22:09:26 +01:00
2bf4225ad2 Merge pull request #4908 from vvoland/vendor-docker
[25.0] vendor: github.com/docker/docker 25.0.4-51e876cd964c4bb1f0a7c1bc24ecab9321b3ff1c
2024-03-05 17:13:10 +01:00
f783e8d58a Merge pull request #4915 from vvoland/v25.0-4839
[25.0 backport] update CI
2024-03-05 13:01:56 +01:00
956d15c723 Cleanup of dockerfiles, compose files and env vars
Signed-off-by: Christopher Petito <chrisjpetito@gmail.com>
(cherry picked from commit 69ed6588a8)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-05 09:56:21 +01:00
5a942fadcf Update gha runners and engines used in e2e tests
- gha runners updated to ubuntu 22.04
- e2e now runs against moby 23.0, 24.0 and 25.0
- temporarily skip broken test for moby < 25

Signed-off-by: Christopher Petito <chrisjpetito@gmail.com>
(cherry picked from commit 6b67b95493)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-05 09:56:19 +01:00
592c146cca testenv: Add DaemonAPIVersion helper
Allow tests to check the negotiated API version used by the client.

Can be used to skip tests based on API versions, for example:
```go
    skip.If(t, versions.LessThan(environment.DaemonAPIVersion(t), "1.44"))
```

will skip the test if the API version is older than 1.44

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
(cherry picked from commit 9831fea4db)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-05 09:56:16 +01:00
0735e78cc9 vendor: github.com/docker/docker 25.0.4-51e876cd96
full diff: https://github.com/docker/docker/compare/v25.0.3...51e876cd964c4bb1f0a7c1bc24ecab9321b3ff1c

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-04 12:58:21 +01:00
63a3db4b31 Merge pull request #4914 from vvoland/v25.0-4831
[25.0 backport] Dockerfile: update docker compose to v2.24.3
2024-03-04 12:50:24 +01:00
0b9bf6a6f4 Merge pull request #4913 from vvoland/v25-4867
[25.0 backport] Test fixes needed for upgrading ci runners and engine
2024-03-04 12:49:50 +01:00
e0dab5ce1e Dockerfile: update docker compose to v2.24.3
Update the version of compose used in CI to the latest version.

- full diff: https://github.com/docker/compose/compare/v2.24.2...v2.24.3
- release notes: https://github.com/docker/compose/releases/tag/v2.24.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 53e2e54c29)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-04 12:32:16 +01:00
b59204cc43 Merge pull request #4912 from vvoland/v25-4881
[25.0 backport] update to go1.21.7
2024-03-04 12:20:28 +01:00
b8459ce351 Merge pull request #4911 from vvoland/v25-4876
[25.0 backport] Fixed typo in bash completion functions
2024-03-04 12:19:52 +01:00
a25a9100f3 Minor test fixes necessary for eventually upgrading ci runners and engine version
Signed-off-by: Christopher Petito <chrisjpetito@gmail.com>
(cherry picked from commit 30dd7c1319)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-04 12:19:36 +01:00
eb223e7eaf Merge pull request #4910 from vvoland/v25-4860
[25.0 backport] docker stack: allow '=' separator in extra_hosts
2024-03-04 12:19:04 +01:00
c87c4c96ec update to go1.21.7
go1.21.7 (released 2024-02-06) includes fixes to the compiler, the go command,
the runtime, and the crypto/x509 package. See the Go 1.21.7 milestone on our
issue tracker for details:

- https://github.com/golang/go/issues?q=milestone%3AGo1.21.7+label%3ACherryPickApproved
- full diff: https://github.com/golang/go/compare/go1.21.6...go1.21.7

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 20b9d489e0)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-04 12:15:36 +01:00
c270556d44 Fixed typo in bash completion functions
Signed-off-by: David le Blanc <david-le-blanc@users.noreply.github.com>
(cherry picked from commit c514003e69)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-04 12:12:55 +01:00
98f603bdd1 Merge pull request #4909 from vvoland/v25-4862
[25.0 backport] Avoid keeping @docker_cli_[UUID] files
2024-03-04 11:55:42 +01:00
1cddb2b03d docker stack: allow '=' separator in extra_hosts
extra_hosts in the compose file format allows '=' as a separator, and brackets
around IP addresses, the engine API doesn't.

So, transform the values when reading a compose file for 'docker stack'.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit c986d09bca)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-04 11:55:30 +01:00
8715d9a33a Avoid keeping @docker_cli_[UUID] files
Seems that OpenBSD behaves like darwin and requires to unlink all
socket, after it was used.

Tested on OpenBSD 7.4

Signed-off-by: Kirill A. Korinsky <kirill@korins.ky>
(cherry picked from commit 2c214241fa)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2024-03-04 11:34:38 +01:00
a5937c6043 Merge pull request #4885 from thaJeztah/25.0_backport_regenerate_mdddocs
[25.0 backport] docs: regenerate markdown
2024-02-21 11:58:13 +01:00
9142b58351 docs: regenerate markdown
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit f2e98f9a93)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-21 11:34:20 +01:00
f67e569a8f Merge pull request #4883 from dvdksn/backport_docs_cli_reference_urlscheme
[25.0 backport] docs: update link targets
2024-02-21 09:32:16 +01:00
08eba2246c docs: update url scheme for reference docs
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit caf72655fb)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-02-21 09:22:08 +01:00
4fd2cf5f2d deps: update cli-docs-tool version (v0.7.0)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit e244044944)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-02-21 09:22:04 +01:00
bdfe1645f5 Merge pull request #4858 from thaJeztah/25.0_vendor_docker_25.0.3
[25.0] vendor: github.com/docker/docker v25.0.3
2024-02-07 10:47:40 +01:00
e456704864 vendor: github.com/docker/docker v25.0.3
full diff: https://github.com/docker/docker/compare/v25.0.2...v25.0.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-07 02:20:23 +01:00
4debf411d1 Merge pull request #4857 from thaJeztah/25.0_backport_codecov-action-4
Some checks failed
build / prepare (push) Has been cancelled
build / build (push) Has been cancelled
build / bin-image (push) Has been cancelled
build / prepare-plugins (push) Has been cancelled
build / plugins (push) Has been cancelled
codeql / codeql (push) Has been cancelled
e2e / e2e (19.03-dind, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, experimental) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, non-experimental) (push) Has been cancelled
e2e / e2e (debian, stable-dind, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, stable-dind, experimental) (push) Has been cancelled
e2e / e2e (debian, stable-dind, non-experimental) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-12) (push) Has been cancelled
validate / validate (lint) (push) Has been cancelled
validate / validate (shellcheck) (push) Has been cancelled
validate / validate (update-authors) (push) Has been cancelled
validate / validate (validate-vendor) (push) Has been cancelled
validate / validate-md (push) Has been cancelled
validate / validate-make (manpages) (push) Has been cancelled
validate / validate-make (yamldocs) (push) Has been cancelled
[25.0 backport] build(deps): Bump codecov/codecov-action from 3 to 4
2024-02-06 21:42:40 +01:00
5e6ce1bde1 Merge pull request #4856 from thaJeztah/25.0_backport_plugin-socket-tests
[25.0 backport] Add tests for CLI/plugin communication
2024-02-06 21:42:20 +01:00
5428301e3f build(deps): Bump codecov/codecov-action from 3 to 4
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit b123ce6526)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-06 21:35:08 +01:00
1cbc218c05 tests: add plugin-socket-compatibility tests
Adds a new plugin to the e2e plugins that simulates an older
plugin binary and a test suite to ensure older plugin binaries
keep behaving the same with newer CLI versions.

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
(cherry picked from commit cfa9fef77d)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-06 21:31:55 +01:00
2f6b5ada71 scripts: don't hardcode architecture in e2e script
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
(cherry picked from commit 1c4d6d85dd)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-06 21:31:55 +01:00
d8e07c9c47 tests: add tests for cli-plugins/socket
Signed-off-by: Laura Brehm <laurabrehm@hey.com>
(cherry picked from commit 469bfc05ed)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-06 21:31:52 +01:00
5f1b610fc3 Merge pull request #4841 from thaJeztah/25.0_vendor_docker_25.0.2
[25.0] vendor: github.com/docker/docker v25.0.2
2024-02-01 16:32:18 +01:00
c105cd3ac2 Merge pull request #4837 from dvdksn/25.0_docs_backport_linode_volume_plugin
[25.0 Backport] docs: Add Linode docker volume plugin #4396
2024-02-01 04:15:42 +01:00
62b2963b80 vendor: github.com/docker/docker v25.0.2
no changes in vendored code

full diff: https://github.com/docker/docker/compare/v25.0.1...v25.0.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-01 04:12:31 +01:00
71f2b0d109 vendor: github.com/docker/docker v25.0.1
relevant changes:

- Fix isGitURL regular expression
- pkg/system: return even richer xattr errors

full diff: https://github.com/moby/moby/compare/v25.0.0...v25.0.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 4b1ed1f442)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-02-01 04:11:47 +01:00
617bc98c8d Add Linode docker volume plugin
Signed-off-by: Zhiwei Liang <zliang@akamai.com>
(cherry picked from commit 1f9573bb05)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-01-31 13:06:53 +01:00
29cf629222 Merge pull request #4819 from dvdksn/25.0_backport_docs_host-gateway-ip_daemonjson
Some checks failed
build / prepare (push) Has been cancelled
build / build (push) Has been cancelled
build / bin-image (push) Has been cancelled
build / prepare-plugins (push) Has been cancelled
build / plugins (push) Has been cancelled
codeql / codeql (push) Has been cancelled
e2e / e2e (19.03-dind, non-experimental) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, connhelper-ssh) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, experimental) (push) Has been cancelled
e2e / e2e (alpine, stable-dind, non-experimental) (push) Has been cancelled
e2e / e2e (debian, stable-dind, connhelper-ssh) (push) Has been cancelled
e2e / e2e (debian, stable-dind, experimental) (push) Has been cancelled
e2e / e2e (debian, stable-dind, non-experimental) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-12) (push) Has been cancelled
validate / validate (lint) (push) Has been cancelled
validate / validate (shellcheck) (push) Has been cancelled
validate / validate (update-authors) (push) Has been cancelled
validate / validate (validate-vendor) (push) Has been cancelled
validate / validate-md (push) Has been cancelled
validate / validate-make (manpages) (push) Has been cancelled
validate / validate-make (yamldocs) (push) Has been cancelled
[25.0 backport] docs: update host-gateway-ip to use daemon.json instead of cli flag
2024-01-23 16:11:10 +01:00
4caf4de039 docs: update host-gateway-ip to use daemon.json instead of cli flag
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit ec0a62436e)
2024-01-23 16:04:58 +01:00
950ecd42fd Merge pull request #4815 from thaJeztah/25.0_backport_update_compose
[25.0 backport] Dockerfile: update docker compose to v2.24.2
2024-01-23 11:04:43 +01:00
6ab4781bd0 Dockerfile: update docker compose to v2.24.2
Update the version of compose used in CI to the latest version.

- full diff: docker/compose@v2.24.0...v2.24.2
- release notes: https://github.com/docker/compose/releases/tag/v2.24.1
- release notes: https://github.com/docker/compose/releases/tag/v2.24.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 091af560ca)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-01-23 10:34:14 +01:00
e8852e8ed2 Merge pull request #4806 from thaJeztah/25.0_backport_socket-eof-return
[25.0 backport] socket: return from loop after EOF
2024-01-22 14:10:02 +01:00
4e097c643d socket: return from loop after EOF
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 8cd3b00420)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-01-20 13:45:39 +01:00
01f9332618 Merge pull request #4803 from thaJeztah/25.0_backport_update_engine
[25.0 backport] vendor: github.com/docker/docker v25.0.0
2024-01-19 15:31:35 +01:00
4cd8d5cf47 Merge pull request #4804 from dvdksn/backport_25.0_docs_fix_cli_broken_alias_links
[25.0 backport] docs: fix broken links to alias pages
2024-01-19 15:18:55 +01:00
21c12847bf docs: move base command to docker.md
CLI reference for the base command was generated to cli.md

Changed it to docker.md to handle broken links.

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit d633890f91)
2024-01-19 15:09:15 +01:00
22e1f2cbfa docs: fix broken links to alias pages
Alias pages redirect to the canonical names, but these pages still
linked to the aliases, causing broken links when building the docs site.

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit aff4649cb7)
2024-01-19 15:09:07 +01:00
68abf14c15 vendor: github.com/docker/docker v25.0.0
full diff: https://github.com/docker/docker/compare/v25.0.0-rc.3...v25.0.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 337dd82d8b)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-01-19 15:07:25 +01:00
85a5ee4cb0 Merge pull request #4801 from dvdksn/backport_25.0_docs_cdi
[25.0 backport] docs: add documentation for CDI
2024-01-19 15:05:59 +01:00
9e1e07657a docs: add documentation for CDI
- Add section about cdi-spec-dirs daemon configuration
- Add subsection about cdi in --device section for docker run
- Update `docker info` output example

Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit 852d198bb5)
2024-01-19 14:58:18 +01:00
377 changed files with 3480 additions and 10106 deletions

19
.circleci/config.yml Normal file
View File

@ -0,0 +1,19 @@
# This is a dummy CircleCI config file to avoid GitHub status failures reported
# on branches that don't use CircleCI. This file should be deleted when all
# branches are no longer dependent on CircleCI.
version: 2
jobs:
dummy:
docker:
- image: busybox
steps:
- run:
name: "dummy"
command: echo "dummy job"
workflows:
version: 2
ci:
jobs:
- dummy

View File

@ -26,8 +26,6 @@ jobs:
codeql:
runs-on: 'ubuntu-latest'
timeout-minutes: 360
env:
DISABLE_WARN_OUTSIDE_CONTAINER: '1'
permissions:
actions: read
contents: read
@ -54,16 +52,6 @@ jobs:
uses: github/codeql-action/init@v3
with:
languages: go
# CodeQL 2.16.4's auto-build added support for multi-module repositories,
# and is trying to be smart by searching for modules in every directory,
# including vendor directories. If no module is found, it's creating one
# which is ... not what we want, so let's give it a "go.mod".
# see: https://github.com/docker/cli/pull/4944#issuecomment-2002034698
-
name: Create go.mod
run: |
ln -s vendor.mod go.mod
ln -s vendor.sum go.sum
-
name: Autobuild
uses: github/codeql-action/autobuild@v3

View File

@ -62,4 +62,3 @@ jobs:
uses: codecov/codecov-action@v4
with:
file: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -34,7 +34,6 @@ jobs:
uses: codecov/codecov-action@v4
with:
file: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
host:
runs-on: ${{ matrix.os }}
@ -64,7 +63,7 @@ jobs:
name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.21.9
go-version: 1.21.8
-
name: Test
run: |
@ -78,4 +77,3 @@ jobs:
with:
file: /tmp/coverage.txt
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -4,8 +4,8 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.18
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.21.9
ARG XX_VERSION=1.4.0
ARG GO_VERSION=1.21.8
ARG XX_VERSION=1.2.1
ARG GOVERSIONINFO_VERSION=v1.3.0
ARG GOTESTSUM_VERSION=v1.10.0
ARG BUILDX_VERSION=0.12.1
@ -123,14 +123,8 @@ COPY --link . .
FROM scratch AS plugins
COPY --from=build-plugins /out .
FROM scratch AS bin-image-linux
FROM scratch AS bin-image
COPY --from=build /out/docker /docker
FROM scratch AS bin-image-darwin
COPY --from=build /out/docker /docker
FROM scratch AS bin-image-windows
COPY --from=build /out/docker /docker.exe
FROM bin-image-${TARGETOS} AS bin-image
FROM scratch AS binary
COPY --from=build /out .

View File

@ -52,7 +52,7 @@ shellcheck: ## run shellcheck validation
.PHONY: fmt
fmt: ## run gofumpt (if present) or gofmt
@if command -v gofumpt > /dev/null; then \
gofumpt -w -d -lang=1.21 . ; \
gofumpt -w -d -lang=1.19 . ; \
else \
go list -f {{.Dir}} ./... | xargs gofmt -w -s -d ; \
fi

View File

@ -2,14 +2,11 @@ package manager
import (
"fmt"
"net/url"
"os"
"strings"
"sync"
"github.com/docker/cli/cli/command"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
)
const (
@ -33,10 +30,6 @@ const (
// is, one which failed it's candidate test) and contains the
// reason for the failure.
CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid"
// CommandAnnotationPluginCommandPath is added to overwrite the
// command path for a plugin invocation.
CommandAnnotationPluginCommandPath = "com.docker.cli.plugin.command_path"
)
var pluginCommandStubsOnce sync.Once
@ -105,44 +98,3 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e
})
return err
}
const (
dockerCliAttributePrefix = attribute.Key("docker.cli")
cobraCommandPath = attribute.Key("cobra.command_path")
)
func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Set {
commandPath := cmd.Annotations[CommandAnnotationPluginCommandPath]
if commandPath == "" {
commandPath = fmt.Sprintf("%s %s", cmd.CommandPath(), plugin.Name)
}
attrSet := attribute.NewSet(
cobraCommandPath.String(commandPath),
)
kvs := make([]attribute.KeyValue, 0, attrSet.Len())
for iter := attrSet.Iter(); iter.Next(); {
attr := iter.Attribute()
kvs = append(kvs, attribute.KeyValue{
Key: dockerCliAttributePrefix + "." + attr.Key,
Value: attr.Value,
})
}
return attribute.NewSet(kvs...)
}
func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string {
if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 {
// values in environment variables need to be in baggage format
// otel/baggage package can be used after update to v1.22, currently it encodes incorrectly
attrsSlice := make([]string, attrs.Len())
for iter := attrs.Iter(); iter.Next(); {
i, v := iter.IndexedAttribute()
attrsSlice[i] = string(v.Key) + "=" + url.PathEscape(v.Value.AsString())
}
env = append(env, ResourceAttributesEnvvar+"="+strings.Join(attrsSlice, ","))
}
return env
}

View File

@ -17,17 +17,11 @@ import (
"golang.org/x/sync/errgroup"
)
const (
// ReexecEnvvar is the name of an ennvar which is set to the command
// used to originally invoke the docker CLI when executing a
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
// the plugin to re-execute the original CLI.
ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
// ResourceAttributesEnvvar is the name of the envvar that includes additional
// resource attributes for OTEL.
ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES"
)
// ReexecEnvvar is the name of an ennvar which is set to the command
// used to originally invoke the docker CLI when executing a
// plugin. Assuming $PATH and $CWD remain unchanged this should allow
// the plugin to re-execute the original CLI.
const ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"
// errPluginNotFound is the error returned when a plugin could not be found.
type errPluginNotFound string
@ -242,7 +236,6 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, ReexecEnvvar+"="+os.Args[0])
cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin)
return cmd, nil
}

View File

@ -1,12 +1,12 @@
package socket
import (
"crypto/rand"
"encoding/hex"
"errors"
"io"
"net"
"os"
"github.com/docker/distribution/uuid"
)
// EnvKey represents the well-known environment variable used to pass the plugin being
@ -17,7 +17,7 @@ const EnvKey = "DOCKER_CLI_PLUGIN_SOCKET"
// and update the conn pointer, and returns the listener for the socket (which the caller
// is responsible for closing when it's no longer needed).
func SetupConn(conn **net.UnixConn) (*net.UnixListener, error) {
listener, err := listen("docker_cli_" + randomID())
listener, err := listen("docker_cli_" + uuid.Generate().String())
if err != nil {
return nil, err
}
@ -27,14 +27,6 @@ func SetupConn(conn **net.UnixConn) (*net.UnixListener, error) {
return listener, nil
}
func randomID() string {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
panic(err) // This shouldn't happen
}
return hex.EncodeToString(b)
}
func accept(listener *net.UnixListener, conn **net.UnixConn) {
go func() {
for {

View File

@ -470,7 +470,7 @@ Common Commands:
Management Commands:
{{- range managementSubCommands . }}
{{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}
{{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}{{ if isPlugin .}} {{vendorAndVersion .}}{{ end}}
{{- end}}
{{- end}}
@ -479,7 +479,7 @@ Management Commands:
Swarm Commands:
{{- range orchestratorSubCommands . }}
{{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}
{{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}{{ if isPlugin .}} {{vendorAndVersion .}}{{ end}}
{{- end}}
{{- end}}

View File

@ -1,20 +0,0 @@
package builder
import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)
type fakeClient struct {
client.Client
builderPruneFunc func(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error)
}
func (c *fakeClient) BuildCachePrune(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
if c.builderPruneFunc != nil {
return c.builderPruneFunc(ctx, opts)
}
return nil, nil
}

View File

@ -66,10 +66,8 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
if options.all {
warning = allCacheWarning
}
if !options.force {
if r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning); !r || err != nil {
return 0, "", err
}
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return 0, "", nil
}
report, err := dockerCli.Client().BuildCachePrune(ctx, types.BuildCachePruneOptions{

View File

@ -1,28 +0,0 @@
package builder
import (
"context"
"errors"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"gotest.tools/v3/assert"
)
func TestBuilderPromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
builderPruneFunc: func(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
return nil, errors.New("fakeClient builderPruneFunc should not be called")
},
})
cmd := NewPruneCommand(cli)
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
}

View File

@ -35,7 +35,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.BoolVar(&opts.leaveRunning, "leave-running", false, "Leave the container running after checkpoint")
flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory")
flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "Use a custom checkpoint storage directory")
return cmd
}

View File

@ -30,7 +30,7 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
}
flags := cmd.Flags()
flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory")
flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "Use a custom checkpoint storage directory")
return cmd
}

View File

@ -27,7 +27,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
}
flags := cmd.Flags()
flags.StringVar(&opts.checkpointDir, "checkpoint-dir", "", "Use a custom checkpoint storage directory")
flags.StringVarP(&opts.checkpointDir, "checkpoint-dir", "", "", "Use a custom checkpoint storage directory")
return cmd
}

View File

@ -11,11 +11,19 @@ import (
"github.com/moby/term"
)
// CLIOption is a functional argument to apply options to a [DockerCli]. These
// options can be passed to [NewDockerCli] to initialize a new CLI, or
// applied with [DockerCli.Initialize] or [DockerCli.Apply].
// CLIOption applies a modification on a DockerCli.
type CLIOption func(cli *DockerCli) error
// DockerCliOption applies a modification on a DockerCli.
//
// Deprecated: use [CLIOption] instead.
type DockerCliOption = CLIOption
// InitializeOpt is the type of the functional options passed to DockerCli.Initialize
//
// Deprecated: use [CLIOption] instead.
type InitializeOpt = CLIOption
// WithStandardStreams sets a cli in, out and err streams with the standard streams.
func WithStandardStreams() CLIOption {
return func(cli *DockerCli) error {

View File

@ -7,7 +7,6 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/volume"
"github.com/spf13/cobra"
)
@ -18,7 +17,7 @@ type ValidArgsFn func(cmd *cobra.Command, args []string, toComplete string) ([]s
// ImageNames offers completion for images present within the local store
func ImageNames(dockerCli command.Cli) ValidArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
list, err := dockerCli.Client().ImageList(cmd.Context(), image.ListOptions{})
list, err := dockerCli.Client().ImageList(cmd.Context(), types.ImageListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}

View File

@ -38,7 +38,7 @@ func newConfigListCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.BoolVarP(&listOpts.Quiet, "quiet", "q", false, "Only display IDs")
flags.StringVar(&listOpts.Format, "format", "", flagsHelper.FormatHelp)
flags.StringVarP(&listOpts.Format, "format", "", "", flagsHelper.FormatHelp)
flags.VarP(&listOpts.Filter, "filter", "f", "Filter output based on conditions provided")
return cmd

View File

@ -43,21 +43,22 @@ func inspectContainerAndCheckState(ctx context.Context, apiClient client.APIClie
}
// NewAttachCommand creates a new cobra.Command for `docker attach`
func NewAttachCommand(dockerCLI command.Cli) *cobra.Command {
func NewAttachCommand(dockerCli command.Cli) *cobra.Command {
var opts AttachOptions
var ctr string
cmd := &cobra.Command{
Use: "attach [OPTIONS] CONTAINER",
Short: "Attach local standard input, output, and error streams to a running container",
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
containerID := args[0]
return RunAttach(cmd.Context(), dockerCLI, containerID, &opts)
ctr = args[0]
return RunAttach(cmd.Context(), dockerCli, ctr, &opts)
},
Annotations: map[string]string{
"aliases": "docker container attach, docker attach",
},
ValidArgsFunction: completion.ContainerNames(dockerCLI, false, func(ctr types.Container) bool {
ValidArgsFunction: completion.ContainerNames(dockerCli, false, func(ctr types.Container) bool {
return ctr.State != "paused"
}),
}
@ -70,13 +71,13 @@ func NewAttachCommand(dockerCLI command.Cli) *cobra.Command {
}
// RunAttach executes an `attach` command
func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, opts *AttachOptions) error {
func RunAttach(ctx context.Context, dockerCLI command.Cli, target string, opts *AttachOptions) error {
apiClient := dockerCLI.Client()
// request channel to wait for client
resultC, errC := apiClient.ContainerWait(ctx, containerID, "")
resultC, errC := apiClient.ContainerWait(ctx, target, "")
c, err := inspectContainerAndCheckState(ctx, apiClient, containerID)
c, err := inspectContainerAndCheckState(ctx, apiClient, target)
if err != nil {
return err
}
@ -105,11 +106,11 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
if opts.Proxy && !c.Config.Tty {
sigc := notifyAllSignals()
go ForwardAllSignals(ctx, apiClient, containerID, sigc)
go ForwardAllSignals(ctx, apiClient, target, sigc)
defer signal.StopCatch(sigc)
}
resp, errAttach := apiClient.ContainerAttach(ctx, containerID, options)
resp, errAttach := apiClient.ContainerAttach(ctx, target, options)
if errAttach != nil {
return errAttach
}
@ -123,13 +124,13 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
// the container and not exit.
//
// Recheck the container's state to avoid attach block.
_, err = inspectContainerAndCheckState(ctx, apiClient, containerID)
_, err = inspectContainerAndCheckState(ctx, apiClient, target)
if err != nil {
return err
}
if c.Config.Tty && dockerCLI.Out().IsTerminal() {
resizeTTY(ctx, dockerCLI, containerID)
resizeTTY(ctx, dockerCLI, target)
}
streamer := hijackedIOStreamer{

View File

@ -6,8 +6,6 @@ import (
"github.com/docker/docker/api/types"
"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/system"
"github.com/docker/docker/client"
@ -25,7 +23,7 @@ type fakeClient struct {
platform *specs.Platform,
containerName string) (container.CreateResponse, error)
containerStartFunc func(containerID string, options container.StartOptions) error
imageCreateFunc func(parentReference string, options image.CreateOptions) (io.ReadCloser, error)
imageCreateFunc func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error)
infoFunc func() (system.Info, error)
containerStatPathFunc func(containerID, path string) (types.ContainerPathStat, error)
containerCopyFromFunc func(containerID, srcPath string) (io.ReadCloser, types.ContainerPathStat, error)
@ -36,7 +34,6 @@ type fakeClient struct {
containerExecResizeFunc func(id string, options container.ResizeOptions) error
containerRemoveFunc func(ctx context.Context, containerID string, options container.RemoveOptions) error
containerKillFunc func(ctx context.Context, containerID, signal string) error
containerPruneFunc func(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error)
Version string
}
@ -93,7 +90,7 @@ func (f *fakeClient) ContainerRemove(ctx context.Context, containerID string, op
return nil
}
func (f *fakeClient) ImageCreate(_ context.Context, parentReference string, options image.CreateOptions) (io.ReadCloser, error) {
func (f *fakeClient) ImageCreate(_ context.Context, parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
if f.imageCreateFunc != nil {
return f.imageCreateFunc(parentReference, options)
}
@ -166,10 +163,3 @@ func (f *fakeClient) ContainerKill(ctx context.Context, containerID, signal stri
}
return nil
}
func (f *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) {
if f.containerPruneFunc != nil {
return f.containerPruneFunc(ctx, pruneFilters)
}
return types.ContainersPruneReport{}, nil
}

View File

@ -15,8 +15,8 @@ import (
"github.com/docker/cli/cli/command/image"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
imagetypes "github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/jsonmessage"
@ -119,7 +119,7 @@ func pullImage(ctx context.Context, dockerCli command.Cli, img string, options *
return err
}
responseBody, err := dockerCli.Client().ImageCreate(ctx, img, imagetypes.CreateOptions{
responseBody, err := dockerCli.Client().ImageCreate(ctx, img, types.ImageCreateOptions{
RegistryAuth: encodedAuth,
Platform: options.platform,
})

View File

@ -15,8 +15,8 @@ import (
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/system"
"github.com/google/go-cmp/cmp"
@ -134,7 +134,7 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
return container.CreateResponse{ID: containerID}, nil
}
},
imageCreateFunc: func(parentReference string, options image.CreateOptions) (io.ReadCloser, error) {
imageCreateFunc: func(parentReference string, options types.ImageCreateOptions) (io.ReadCloser, error) {
defer func() { pullCounter++ }()
return io.NopCloser(strings.NewReader("")), nil
},

View File

@ -66,12 +66,12 @@ func NewExecCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.SetInterspersed(false)
flags.StringVar(&options.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container")
flags.StringVarP(&options.DetachKeys, "detach-keys", "", "", "Override the key sequence for detaching a container")
flags.BoolVarP(&options.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached")
flags.BoolVarP(&options.TTY, "tty", "t", false, "Allocate a pseudo-TTY")
flags.BoolVarP(&options.Detach, "detach", "d", false, "Detached mode: run command in the background")
flags.StringVarP(&options.User, "user", "u", "", `Username or UID (format: "<name|uid>[:<group|gid>]")`)
flags.BoolVar(&options.Privileged, "privileged", false, "Give extended privileges to the command")
flags.BoolVarP(&options.Privileged, "privileged", "", false, "Give extended privileges to the command")
flags.VarP(&options.Env, "env", "e", "Set environment variables")
flags.SetAnnotation("env", "version", []string{"1.25"})
flags.Var(&options.EnvFile, "env-file", "Read in a file of environment variables")

View File

@ -55,7 +55,7 @@ func NewPsCommand(dockerCLI command.Cli) *cobra.Command {
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
flags.BoolVarP(&options.nLatest, "latest", "l", false, "Show the latest created container (includes all states)")
flags.IntVarP(&options.last, "last", "n", -1, "Show n last created containers (includes all states)")
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
flags.StringVarP(&options.format, "format", "", "", flagsHelper.FormatHelp)
flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
return cmd

View File

@ -53,10 +53,8 @@ Are you sure you want to continue?`
func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
if !options.force {
if r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning); !r || err != nil {
return 0, "", err
}
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return 0, "", nil
}
report, err := dockerCli.Client().ContainersPrune(ctx, pruneFilters)

View File

@ -1,29 +0,0 @@
package container
import (
"context"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
)
func TestContainerPrunePromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
containerPruneFunc: func(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) {
return types.ContainersPruneReport{}, errors.New("fakeClient containerPruneFunc should not be called")
},
})
cmd := NewPruneCommand(cli)
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
}

View File

@ -28,6 +28,13 @@ type StartOptions struct {
Containers []string
}
// NewStartOptions creates a new StartOptions.
//
// Deprecated: create a new [StartOptions] directly.
func NewStartOptions() StartOptions {
return StartOptions{}
}
// NewStartCommand creates a new cobra.Command for `docker start`
func NewStartCommand(dockerCli command.Cli) *cobra.Command {
var opts StartOptions

View File

@ -18,7 +18,6 @@ import (
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@ -107,6 +106,15 @@ var acceptedStatsFilters = map[string]bool{
func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions) error {
apiClient := dockerCLI.Client()
// Get the daemonOSType if not set already
if daemonOSType == "" {
sv, err := apiClient.ServerVersion(ctx)
if err != nil {
return err
}
daemonOSType = sv.Os
}
// waitFirst is a WaitGroup to wait first stat data's reach for each container
waitFirst := &sync.WaitGroup{}
// closeChan is a non-buffered channel used to collect errors from goroutines.
@ -130,9 +138,9 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
return err
}
eh := newEventHandler()
eh := command.InitEventHandler()
if options.All {
eh.setHandler(events.ActionCreate, func(e events.Message) {
eh.Handle(events.ActionCreate, func(e events.Message) {
s := NewStats(e.Actor.ID[:12])
if cStats.add(s) {
waitFirst.Add(1)
@ -141,7 +149,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
})
}
eh.setHandler(events.ActionStart, func(e events.Message) {
eh.Handle(events.ActionStart, func(e events.Message) {
s := NewStats(e.Actor.ID[:12])
if cStats.add(s) {
waitFirst.Add(1)
@ -150,7 +158,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
})
if !options.All {
eh.setHandler(events.ActionDie, func(e events.Message) {
eh.Handle(events.ActionDie, func(e events.Message) {
cStats.remove(e.Actor.ID[:12])
})
}
@ -187,7 +195,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
}
eventChan := make(chan events.Message)
go eh.watch(eventChan)
go eh.Watch(eventChan)
stopped := make(chan struct{})
go monitorContainerEvents(started, eventChan, stopped)
defer close(stopped)
@ -259,12 +267,6 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
format = formatter.TableFormatKey
}
}
if daemonOSType == "" {
// Get the daemonOSType if not set already. The daemonOSType variable
// should already be set when collecting stats as part of "collect()",
// so we unlikely hit this code in practice.
daemonOSType = dockerCLI.ServerInfo().OSType
}
statsCtx := formatter.Context{
Output: dockerCLI.Out(),
Format: NewStatsFormat(format, daemonOSType),
@ -314,31 +316,3 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
}
return err
}
// newEventHandler initializes and returns an eventHandler
func newEventHandler() *eventHandler {
return &eventHandler{handlers: make(map[events.Action]func(events.Message))}
}
// eventHandler allows for registering specific events to setHandler.
type eventHandler struct {
handlers map[events.Action]func(events.Message)
}
func (eh *eventHandler) setHandler(action events.Action, handler func(events.Message)) {
eh.handlers[action] = handler
}
// watch ranges over the passed in event chan and processes the events based on the
// handlers created for a given action.
// To stop watching, close the event chan.
func (eh *eventHandler) watch(c <-chan events.Message) {
for e := range c {
h, exists := eh.handlers[e.Action]
if !exists {
continue
}
logrus.Debugf("event handler: received event: %v", e)
go h(e)
}
}

View File

@ -9,16 +9,12 @@ import (
// EventHandler is abstract interface for user to customize
// own handle functions of each type of events
//
// Deprecated: EventHandler is no longer used, and will be removed in the next release.
type EventHandler interface {
Handle(action events.Action, h func(events.Message))
Watch(c <-chan events.Message)
}
// InitEventHandler initializes and returns an EventHandler
//
// Deprecated: InitEventHandler is no longer used, and will be removed in the next release.
func InitEventHandler() EventHandler {
return &eventHandler{handlers: make(map[events.Action]func(events.Message))}
}

View File

@ -129,6 +129,7 @@ func TestGetContextFromReaderString(t *testing.T) {
tarReader := tar.NewReader(tarArchive)
_, err = tarReader.Next()
if err != nil {
t.Fatalf("Error when reading tar archive: %s", err)
}

View File

@ -17,15 +17,15 @@ type fakeClient struct {
client.Client
imageTagFunc func(string, string) error
imageSaveFunc func(images []string) (io.ReadCloser, error)
imageRemoveFunc func(image string, options image.RemoveOptions) ([]image.DeleteResponse, error)
imagePushFunc func(ref string, options image.PushOptions) (io.ReadCloser, error)
imageRemoveFunc func(image string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error)
imagePushFunc func(ref string, options types.ImagePushOptions) (io.ReadCloser, error)
infoFunc func() (system.Info, error)
imagePullFunc func(ref string, options image.PullOptions) (io.ReadCloser, error)
imagePullFunc func(ref string, options types.ImagePullOptions) (io.ReadCloser, error)
imagesPruneFunc func(pruneFilter filters.Args) (types.ImagesPruneReport, error)
imageLoadFunc func(input io.Reader, quiet bool) (types.ImageLoadResponse, error)
imageListFunc func(options image.ListOptions) ([]image.Summary, error)
imageListFunc func(options types.ImageListOptions) ([]image.Summary, error)
imageInspectFunc func(image string) (types.ImageInspect, []byte, error)
imageImportFunc func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error)
imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
imageHistoryFunc func(image string) ([]image.HistoryResponseItem, error)
imageBuildFunc func(context.Context, io.Reader, types.ImageBuildOptions) (types.ImageBuildResponse, error)
}
@ -45,7 +45,7 @@ func (cli *fakeClient) ImageSave(_ context.Context, images []string) (io.ReadClo
}
func (cli *fakeClient) ImageRemove(_ context.Context, img string,
options image.RemoveOptions,
options types.ImageRemoveOptions,
) ([]image.DeleteResponse, error) {
if cli.imageRemoveFunc != nil {
return cli.imageRemoveFunc(img, options)
@ -53,7 +53,7 @@ func (cli *fakeClient) ImageRemove(_ context.Context, img string,
return []image.DeleteResponse{}, nil
}
func (cli *fakeClient) ImagePush(_ context.Context, ref string, options image.PushOptions) (io.ReadCloser, error) {
func (cli *fakeClient) ImagePush(_ context.Context, ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
if cli.imagePushFunc != nil {
return cli.imagePushFunc(ref, options)
}
@ -67,7 +67,7 @@ func (cli *fakeClient) Info(_ context.Context) (system.Info, error) {
return system.Info{}, nil
}
func (cli *fakeClient) ImagePull(_ context.Context, ref string, options image.PullOptions) (io.ReadCloser, error) {
func (cli *fakeClient) ImagePull(_ context.Context, ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
if cli.imagePullFunc != nil {
cli.imagePullFunc(ref, options)
}
@ -88,7 +88,7 @@ func (cli *fakeClient) ImageLoad(_ context.Context, input io.Reader, quiet bool)
return types.ImageLoadResponse{}, nil
}
func (cli *fakeClient) ImageList(_ context.Context, options image.ListOptions) ([]image.Summary, error) {
func (cli *fakeClient) ImageList(_ context.Context, options types.ImageListOptions) ([]image.Summary, error) {
if cli.imageListFunc != nil {
return cli.imageListFunc(options)
}
@ -103,7 +103,7 @@ func (cli *fakeClient) ImageInspectWithRaw(_ context.Context, img string) (types
}
func (cli *fakeClient) ImageImport(_ context.Context, source types.ImageImportSource, ref string,
options image.ImportOptions,
options types.ImageImportOptions,
) (io.ReadCloser, error) {
if cli.imageImportFunc != nil {
return cli.imageImportFunc(source, ref, options)

View File

@ -9,7 +9,6 @@ import (
"github.com/docker/cli/cli/command"
dockeropts "github.com/docker/cli/opts"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/spf13/cobra"
)
@ -79,7 +78,7 @@ func runImport(ctx context.Context, dockerCli command.Cli, options importOptions
}
}
responseBody, err := dockerCli.Client().ImageImport(ctx, source, options.reference, image.ImportOptions{
responseBody, err := dockerCli.Client().ImageImport(ctx, source, options.reference, types.ImageImportOptions{
Message: options.message,
Changes: options.changes.GetAll(),
Platform: options.platform,

View File

@ -7,7 +7,6 @@ import (
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@ -18,7 +17,7 @@ func TestNewImportCommandErrors(t *testing.T) {
name string
args []string
expectedError string
imageImportFunc func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error)
imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
}{
{
name: "wrong-args",
@ -29,7 +28,7 @@ func TestNewImportCommandErrors(t *testing.T) {
name: "import-failed",
args: []string{"testdata/import-command-success.input.txt"},
expectedError: "something went wrong",
imageImportFunc: func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
return nil, errors.Errorf("something went wrong")
},
},
@ -53,7 +52,7 @@ func TestNewImportCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
imageImportFunc func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error)
imageImportFunc func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error)
}{
{
name: "simple",
@ -66,7 +65,7 @@ func TestNewImportCommandSuccess(t *testing.T) {
{
name: "double",
args: []string{"-", "image:local"},
imageImportFunc: func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
assert.Check(t, is.Equal("image:local", ref))
return io.NopCloser(strings.NewReader("")), nil
},
@ -74,7 +73,7 @@ func TestNewImportCommandSuccess(t *testing.T) {
{
name: "message",
args: []string{"--message", "test message", "-"},
imageImportFunc: func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
assert.Check(t, is.Equal("test message", options.Message))
return io.NopCloser(strings.NewReader("")), nil
},
@ -82,7 +81,7 @@ func TestNewImportCommandSuccess(t *testing.T) {
{
name: "change",
args: []string{"--change", "ENV DEBUG=true", "-"},
imageImportFunc: func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
assert.Check(t, is.Equal("ENV DEBUG=true", options.Changes[0]))
return io.NopCloser(strings.NewReader("")), nil
},
@ -90,7 +89,7 @@ func TestNewImportCommandSuccess(t *testing.T) {
{
name: "change legacy syntax",
args: []string{"--change", "ENV DEBUG true", "-"},
imageImportFunc: func(source types.ImageImportSource, ref string, options image.ImportOptions) (io.ReadCloser, error) {
imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) {
assert.Check(t, is.Equal("ENV DEBUG true", options.Changes[0]))
return io.NopCloser(strings.NewReader("")), nil
},

View File

@ -2,15 +2,13 @@ package image
import (
"context"
"fmt"
"io"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types"
"github.com/spf13/cobra"
)
@ -23,11 +21,10 @@ type imagesOptions struct {
showDigests bool
format string
filter opts.FilterOpt
calledAs string
}
// NewImagesCommand creates a new `docker images` command
func NewImagesCommand(dockerCLI command.Cli) *cobra.Command {
func NewImagesCommand(dockerCli command.Cli) *cobra.Command {
options := imagesOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
@ -38,11 +35,7 @@ func NewImagesCommand(dockerCLI command.Cli) *cobra.Command {
if len(args) > 0 {
options.matchName = args[0]
}
// Pass through how the command was invoked. We use this to print
// warnings when an ambiguous argument was passed when using the
// legacy (top-level) "docker images" subcommand.
options.calledAs = cmd.CalledAs()
return runImages(cmd.Context(), dockerCLI, options)
return runImages(cmd.Context(), dockerCli, options)
},
Annotations: map[string]string{
"category-top": "7",
@ -62,31 +55,33 @@ func NewImagesCommand(dockerCLI command.Cli) *cobra.Command {
return cmd
}
func newListCommand(dockerCLI command.Cli) *cobra.Command {
cmd := *NewImagesCommand(dockerCLI)
func newListCommand(dockerCli command.Cli) *cobra.Command {
cmd := *NewImagesCommand(dockerCli)
cmd.Aliases = []string{"list"}
cmd.Use = "ls [OPTIONS] [REPOSITORY[:TAG]]"
return &cmd
}
func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions) error {
func runImages(ctx context.Context, dockerCli command.Cli, options imagesOptions) error {
filters := options.filter.Value()
if options.matchName != "" {
filters.Add("reference", options.matchName)
}
images, err := dockerCLI.Client().ImageList(ctx, image.ListOptions{
listOptions := types.ImageListOptions{
All: options.all,
Filters: filters,
})
}
images, err := dockerCli.Client().ImageList(ctx, listOptions)
if err != nil {
return err
}
format := options.format
if len(format) == 0 {
if len(dockerCLI.ConfigFile().ImagesFormat) > 0 && !options.quiet {
format = dockerCLI.ConfigFile().ImagesFormat
if len(dockerCli.ConfigFile().ImagesFormat) > 0 && !options.quiet {
format = dockerCli.ConfigFile().ImagesFormat
} else {
format = formatter.TableFormatKey
}
@ -94,50 +89,11 @@ func runImages(ctx context.Context, dockerCLI command.Cli, options imagesOptions
imageCtx := formatter.ImageContext{
Context: formatter.Context{
Output: dockerCLI.Out(),
Output: dockerCli.Out(),
Format: formatter.NewImageFormat(format, options.quiet, options.showDigests),
Trunc: !options.noTrunc,
},
Digest: options.showDigests,
}
if err := formatter.ImageWrite(imageCtx, images); err != nil {
return err
}
if options.matchName != "" && len(images) == 0 && options.calledAs == "images" {
printAmbiguousHint(dockerCLI.Err(), options.matchName)
}
return nil
}
// printAmbiguousHint prints an informational warning if the provided filter
// argument is ambiguous.
//
// The "docker images" top-level subcommand predates the "docker <object> <verb>"
// convention (e.g. "docker image ls"), but accepts a positional argument to
// search/filter images by name (globbing). It's common for users to accidentally
// mistake these commands, and to use (e.g.) "docker images ls", expecting
// to see all images, but ending up with an empty list because no image named
// "ls" was found.
//
// Disallowing these search-terms would be a breaking change, but we can print
// and informational message to help the users correct their mistake.
func printAmbiguousHint(stdErr io.Writer, matchName string) {
switch matchName {
// List of subcommands for "docker image" and their aliases (see "docker image --help"):
case "build",
"history",
"import",
"inspect",
"list",
"load",
"ls",
"prune",
"pull",
"push",
"rm",
"save",
"tag":
_, _ = fmt.Fprintf(stdErr, "\nNo images found matching %q: did you mean \"docker image %[1]s\"?\n", matchName)
}
return formatter.ImageWrite(imageCtx, images)
}

View File

@ -7,6 +7,7 @@ import (
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
@ -19,7 +20,7 @@ func TestNewImagesCommandErrors(t *testing.T) {
name string
args []string
expectedError string
imageListFunc func(options image.ListOptions) ([]image.Summary, error)
imageListFunc func(options types.ImageListOptions) ([]image.Summary, error)
}{
{
name: "wrong-args",
@ -29,7 +30,7 @@ func TestNewImagesCommandErrors(t *testing.T) {
{
name: "failed-list",
expectedError: "something went wrong",
imageListFunc: func(options image.ListOptions) ([]image.Summary, error) {
imageListFunc: func(options types.ImageListOptions) ([]image.Summary, error) {
return []image.Summary{}, errors.Errorf("something went wrong")
},
},
@ -47,7 +48,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
name string
args []string
imageFormat string
imageListFunc func(options image.ListOptions) ([]image.Summary, error)
imageListFunc func(options types.ImageListOptions) ([]image.Summary, error)
}{
{
name: "simple",
@ -64,7 +65,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
{
name: "match-name",
args: []string{"image"},
imageListFunc: func(options image.ListOptions) ([]image.Summary, error) {
imageListFunc: func(options types.ImageListOptions) ([]image.Summary, error) {
assert.Check(t, is.Equal("image", options.Filters.Get("reference")[0]))
return []image.Summary{}, nil
},
@ -72,7 +73,7 @@ func TestNewImagesCommandSuccess(t *testing.T) {
{
name: "filters",
args: []string{"--filter", "name=value"},
imageListFunc: func(options image.ListOptions) ([]image.Summary, error) {
imageListFunc: func(options types.ImageListOptions) ([]image.Summary, error) {
assert.Check(t, is.Equal("value", options.Filters.Get("name")[0]))
return []image.Summary{}, nil
},
@ -95,17 +96,3 @@ func TestNewListCommandAlias(t *testing.T) {
assert.Check(t, cmd.HasAlias("list"))
assert.Check(t, !cmd.HasAlias("other"))
}
func TestNewListCommandAmbiguous(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{})
cmd := NewImagesCommand(cli)
cmd.SetOut(io.Discard)
// Set the Use field to mimic that the command was called as "docker images",
// not "docker image ls".
cmd.Use = "images"
cmd.SetArgs([]string{"ls"})
err := cmd.Execute()
assert.NilError(t, err)
golden.Assert(t, cli.ErrBuffer().String(), "list-command-ambiguous.golden")
}

View File

@ -67,10 +67,8 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
if options.all {
warning = allImageWarning
}
if !options.force {
if r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning); !r || err != nil {
return 0, "", err
}
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return 0, "", nil
}
report, err := dockerCli.Client().ImagesPrune(ctx, pruneFilters)

View File

@ -1,12 +1,10 @@
package image
import (
"context"
"fmt"
"io"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
@ -103,19 +101,3 @@ func TestNewPruneCommandSuccess(t *testing.T) {
golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("prune-command-success.%s.golden", tc.name))
}
}
func TestPrunePromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) {
return types.ImagesPruneReport{}, errors.New("fakeClient imagesPruneFunc should not be called")
},
})
cmd := NewPruneCommand(cli)
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
}

View File

@ -8,7 +8,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
@ -69,7 +69,7 @@ func TestNewPullCommandSuccess(t *testing.T) {
}
for _, tc := range testCases {
cli := test.NewFakeCli(&fakeClient{
imagePullFunc: func(ref string, options image.PullOptions) (io.ReadCloser, error) {
imagePullFunc: func(ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
assert.Check(t, is.Equal(tc.expectedTag, ref), tc.name)
return io.NopCloser(strings.NewReader("")), nil
},
@ -111,7 +111,7 @@ func TestNewPullCommandWithContentTrustErrors(t *testing.T) {
}
for _, tc := range testCases {
cli := test.NewFakeCli(&fakeClient{
imagePullFunc: func(ref string, options image.PullOptions) (io.ReadCloser, error) {
imagePullFunc: func(ref string, options types.ImagePullOptions) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("")), fmt.Errorf("shouldn't try to pull image")
},
}, test.EnableContentTrust)

View File

@ -10,7 +10,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/streams"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
@ -80,7 +80,7 @@ func RunPush(ctx context.Context, dockerCli command.Cli, opts pushOptions) error
return err
}
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push")
options := image.PushOptions{
options := types.ImagePushOptions{
All: opts.all,
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,

View File

@ -6,7 +6,7 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
)
@ -16,7 +16,7 @@ func TestNewPushCommandErrors(t *testing.T) {
name string
args []string
expectedError string
imagePushFunc func(ref string, options image.PushOptions) (io.ReadCloser, error)
imagePushFunc func(ref string, options types.ImagePushOptions) (io.ReadCloser, error)
}{
{
name: "wrong-args",
@ -32,7 +32,7 @@ func TestNewPushCommandErrors(t *testing.T) {
name: "push-failed",
args: []string{"image:repo"},
expectedError: "Failed to push",
imagePushFunc: func(ref string, options image.PushOptions) (io.ReadCloser, error) {
imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("")), errors.Errorf("Failed to push")
},
},
@ -67,7 +67,7 @@ func TestNewPushCommandSuccess(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
imagePushFunc: func(ref string, options image.PushOptions) (io.ReadCloser, error) {
imagePushFunc: func(ref string, options types.ImagePushOptions) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("")), nil
},
})

View File

@ -7,7 +7,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -52,7 +52,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
func runRemove(ctx context.Context, dockerCli command.Cli, opts removeOptions, images []string) error {
client := dockerCli.Client()
options := image.RemoveOptions{
options := types.ImageRemoveOptions{
Force: opts.force,
PruneChildren: !opts.noPrune,
}

View File

@ -6,6 +6,7 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
@ -35,7 +36,7 @@ func TestNewRemoveCommandErrors(t *testing.T) {
name string
args []string
expectedError string
imageRemoveFunc func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error)
imageRemoveFunc func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error)
}{
{
name: "wrong args",
@ -45,7 +46,7 @@ func TestNewRemoveCommandErrors(t *testing.T) {
name: "ImageRemove fail with force option",
args: []string{"-f", "image1"},
expectedError: "error removing image",
imageRemoveFunc: func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
imageRemoveFunc: func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) {
assert.Check(t, is.Equal("image1", img))
return []image.DeleteResponse{}, errors.Errorf("error removing image")
},
@ -54,7 +55,7 @@ func TestNewRemoveCommandErrors(t *testing.T) {
name: "ImageRemove fail",
args: []string{"arg1"},
expectedError: "error removing image",
imageRemoveFunc: func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
imageRemoveFunc: func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) {
assert.Check(t, !options.Force)
assert.Check(t, options.PruneChildren)
return []image.DeleteResponse{}, errors.Errorf("error removing image")
@ -77,13 +78,13 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
imageRemoveFunc func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error)
imageRemoveFunc func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error)
expectedStderr string
}{
{
name: "Image Deleted",
args: []string{"image1"},
imageRemoveFunc: func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
imageRemoveFunc: func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) {
assert.Check(t, is.Equal("image1", img))
return []image.DeleteResponse{{Deleted: img}}, nil
},
@ -91,7 +92,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
{
name: "Image not found with force option",
args: []string{"-f", "image1"},
imageRemoveFunc: func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
imageRemoveFunc: func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) {
assert.Check(t, is.Equal("image1", img))
assert.Check(t, is.Equal(true, options.Force))
return []image.DeleteResponse{}, notFound{"image1"}
@ -102,7 +103,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
{
name: "Image Untagged",
args: []string{"image1"},
imageRemoveFunc: func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
imageRemoveFunc: func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) {
assert.Check(t, is.Equal("image1", img))
return []image.DeleteResponse{{Untagged: img}}, nil
},
@ -110,7 +111,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) {
{
name: "Image Deleted and Untagged",
args: []string{"image1", "image2"},
imageRemoveFunc: func(img string, options image.RemoveOptions) ([]image.DeleteResponse, error) {
imageRemoveFunc: func(img string, options types.ImageRemoveOptions) ([]image.DeleteResponse, error) {
if img == "image1" {
return []image.DeleteResponse{{Untagged: img}}, nil
}

View File

@ -5,6 +5,8 @@
"RepoDigests": null,
"Parent": "",
"Comment": "",
"Container": "",
"ContainerConfig": null,
"DockerVersion": "",
"Author": "",
"Config": null,
@ -26,6 +28,8 @@
"RepoDigests": null,
"Parent": "",
"Comment": "",
"Container": "",
"ContainerConfig": null,
"DockerVersion": "",
"Author": "",
"Config": null,

View File

@ -5,6 +5,8 @@
"RepoDigests": null,
"Parent": "",
"Comment": "",
"Container": "",
"ContainerConfig": null,
"DockerVersion": "",
"Author": "",
"Config": null,

View File

@ -1,2 +0,0 @@
No images found matching "ls": did you mean "docker image ls"?

View File

@ -13,7 +13,6 @@ import (
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/cli/trust"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/registry"
@ -31,7 +30,7 @@ type target struct {
}
// TrustedPush handles content trust pushing of an image
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.AuthConfig, options image.PushOptions) error {
func TrustedPush(ctx context.Context, cli command.Cli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig registrytypes.AuthConfig, options types.ImagePushOptions) error {
responseBody, err := cli.Client().ImagePush(ctx, reference.FamiliarString(ref), options)
if err != nil {
return err
@ -268,7 +267,7 @@ func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth tru
return err
}
requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(cli, imgRefAndAuth.RepoInfo().Index, "pull")
responseBody, err := cli.Client().ImagePull(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), image.PullOptions{
responseBody, err := cli.Client().ImagePull(ctx, reference.FamiliarString(imgRefAndAuth.Reference()), types.ImagePullOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
All: opts.all,

View File

@ -4,7 +4,6 @@ import (
"context"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/client"
)
@ -16,8 +15,6 @@ type fakeClient struct {
networkDisconnectFunc func(ctx context.Context, networkID, container string, force bool) error
networkRemoveFunc func(ctx context.Context, networkID string) error
networkListFunc func(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
networkPruneFunc func(ctx context.Context, pruneFilters filters.Args) (types.NetworksPruneReport, error)
networkInspectFunc func(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error)
}
func (c *fakeClient) NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error) {
@ -55,16 +52,6 @@ func (c *fakeClient) NetworkRemove(ctx context.Context, networkID string) error
return nil
}
func (c *fakeClient) NetworkInspectWithRaw(ctx context.Context, networkID string, opts types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
if c.networkInspectFunc != nil {
return c.networkInspectFunc(ctx, networkID, opts)
}
func (c *fakeClient) NetworkInspectWithRaw(context.Context, string, types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
return types.NetworkResource{}, nil, nil
}
func (c *fakeClient) NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error) {
if c.networkPruneFunc != nil {
return c.networkPruneFunc(ctx, pruneFilter)
}
return types.NetworksPruneReport{}, nil
}

View File

@ -49,10 +49,8 @@ Are you sure you want to continue?`
func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) (output string, err error) {
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
if !options.force {
if r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning); !r || err != nil {
return "", err
}
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return "", nil
}
report, err := dockerCli.Client().NetworksPrune(ctx, pruneFilters)

View File

@ -1,29 +0,0 @@
package network
import (
"context"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
)
func TestNetworkPrunePromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
networkPruneFunc: func(ctx context.Context, pruneFilters filters.Args) (types.NetworksPruneReport, error) {
return types.NetworksPruneReport{}, errors.New("fakeClient networkPruneFunc should not be called")
},
})
cmd := NewPruneCommand(cli)
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
}

View File

@ -46,15 +46,10 @@ func runRemove(ctx context.Context, dockerCli command.Cli, networks []string, op
status := 0
for _, name := range networks {
nw, _, err := client.NetworkInspectWithRaw(ctx, name, types.NetworkInspectOptions{})
if err == nil && nw.Ingress {
r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), ingressWarning)
if err != nil {
return err
}
if !r {
continue
}
if nw, _, err := client.NetworkInspectWithRaw(ctx, name, types.NetworkInspectOptions{}); err == nil &&
nw.Ingress &&
!command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), ingressWarning) {
continue
}
if err := client.NetworkRemove(ctx, name); err != nil {
if opts.force && errdefs.IsNotFound(err) {

View File

@ -5,9 +5,7 @@ import (
"io"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
@ -96,27 +94,3 @@ func TestNetworkRemoveForce(t *testing.T) {
})
}
}
func TestNetworkRemovePromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
networkRemoveFunc: func(ctx context.Context, networkID string) error {
return errors.New("fakeClient networkRemoveFunc should not be called")
},
networkInspectFunc: func(ctx context.Context, networkID string, options types.NetworkInspectOptions) (types.NetworkResource, []byte, error) {
return types.NetworkResource{
ID: "existing-network",
Name: "existing-network",
Ingress: true,
}, nil, nil
},
})
cmd := newRemoveCommand(cli)
cmd.SetArgs([]string{"existing-network"})
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
}

View File

@ -19,7 +19,6 @@ type fakeClient struct {
pluginInstallFunc func(name string, options types.PluginInstallOptions) (io.ReadCloser, error)
pluginListFunc func(filter filters.Args) (types.PluginsListResponse, error)
pluginInspectFunc func(name string) (*types.Plugin, []byte, error)
pluginUpgradeFunc func(name string, options types.PluginInstallOptions) (io.ReadCloser, error)
}
func (c *fakeClient) PluginCreate(_ context.Context, createContext io.Reader, createOptions types.PluginCreateOptions) error {
@ -76,10 +75,3 @@ func (c *fakeClient) PluginInspectWithRaw(_ context.Context, name string) (*type
func (c *fakeClient) Info(context.Context) (system.Info, error) {
return system.Info{}, nil
}
func (c *fakeClient) PluginUpgrade(ctx context.Context, name string, options types.PluginInstallOptions) (io.ReadCloser, error) {
if c.pluginUpgradeFunc != nil {
return c.pluginUpgradeFunc(name, options)
}
return nil, nil
}

View File

@ -114,6 +114,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, options pluginCreateO
createCtx, err = archive.TarWithOptions(absContextDir, &archive.TarOptions{
Compression: compression,
})
if err != nil {
return err
}

View File

@ -142,7 +142,6 @@ func acceptPrivileges(dockerCli command.Cli, name string) func(privileges types.
for _, privilege := range privileges {
fmt.Fprintf(dockerCli.Out(), " - %s: %v\n", privilege.Name, privilege.Value)
}
ctx := context.TODO()
return command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), "Do you grant the above permissions?")
return command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Do you grant the above permissions?"), nil
}
}

View File

@ -1,2 +0,0 @@
Upgrading plugin foo/bar from localhost:5000/foo/bar:v0.1.0 to localhost:5000/foo/bar:v1.0.0
Plugin images do not match, are you sure? [y/N]

View File

@ -63,10 +63,7 @@ func runUpgrade(ctx context.Context, dockerCli command.Cli, opts pluginOptions)
fmt.Fprintf(dockerCli.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, reference.FamiliarString(old), reference.FamiliarString(remote))
if !opts.skipRemoteCheck && remote.String() != old.String() {
if r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?"); !r || err != nil {
if err != nil {
return errors.Wrap(err, "canceling upgrade request")
}
if !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), "Plugin images do not match, are you sure?") {
return errors.New("canceling upgrade request")
}
}

View File

@ -1,42 +0,0 @@
package plugin
import (
"context"
"io"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)
func TestUpgradePromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
pluginUpgradeFunc: func(name string, options types.PluginInstallOptions) (io.ReadCloser, error) {
return nil, errors.New("should not be called")
},
pluginInspectFunc: func(name string) (*types.Plugin, []byte, error) {
return &types.Plugin{
ID: "5724e2c8652da337ab2eedd19fc6fc0ec908e4bd907c7421bf6a8dfc70c4c078",
Name: "foo/bar",
Enabled: false,
PluginReference: "localhost:5000/foo/bar:v0.1.0",
}, []byte{}, nil
},
})
cmd := newUpgradeCommand(cli)
// need to set a remote address that does not match the plugin
// reference sent by the `pluginInspectFunc`
cmd.SetArgs([]string{"foo/bar", "localhost:5000/foo/bar:v1.0.0"})
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
golden.Assert(t, cli.OutBuffer().String(), "plugin-upgrade-terminate.golden")
}

View File

@ -55,7 +55,7 @@ func NewLoginCommand(dockerCli command.Cli) *cobra.Command {
flags.StringVarP(&opts.user, "username", "u", "", "Username")
flags.StringVarP(&opts.password, "password", "p", "", "Password")
flags.BoolVar(&opts.passwordStdin, "password-stdin", false, "Take the password from stdin")
flags.BoolVarP(&opts.passwordStdin, "password-stdin", "", false, "Take the password from stdin")
return cmd
}

View File

@ -38,7 +38,7 @@ func newSecretListCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display IDs")
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
flags.StringVarP(&options.format, "format", "", "", flagsHelper.FormatHelp)
flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
return cmd

View File

@ -137,7 +137,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet,
return nil
}
return WaitOnService(ctx, dockerCli, response.ID, opts.quiet)
return waitOnService(ctx, dockerCli, response.ID, opts.quiet)
}
// setConfigs does double duty: it both sets the ConfigReferences of the

View File

@ -9,9 +9,9 @@ import (
"github.com/docker/docker/pkg/jsonmessage"
)
// WaitOnService waits for the service to converge. It outputs a progress bar,
// waitOnService waits for the service to converge. It outputs a progress bar,
// if appropriate based on the CLI flags.
func WaitOnService(ctx context.Context, dockerCli command.Cli, serviceID string, quiet bool) error {
func waitOnService(ctx context.Context, dockerCli command.Cli, serviceID string, quiet bool) error {
errChan := make(chan error, 1)
pipeReader, pipeWriter := io.Pipe()

View File

@ -143,7 +143,7 @@ func ServiceProgress(ctx context.Context, apiClient client.APIClient, serviceID
if converged && time.Since(convergedAt) >= monitor {
progressOut.WriteProgress(progress.Progress{
ID: "verify",
Action: fmt.Sprintf("Service %s converged", serviceID),
Action: "Service converged",
})
if message != nil {
progressOut.WriteProgress(*message)

View File

@ -62,5 +62,5 @@ func runRollback(ctx context.Context, dockerCli command.Cli, options *serviceOpt
return nil
}
return WaitOnService(ctx, dockerCli, serviceID, options.quiet)
return waitOnService(ctx, dockerCli, serviceID, options.quiet)
}

View File

@ -80,7 +80,7 @@ func runScale(ctx context.Context, dockerCli command.Cli, options *scaleOptions,
if len(serviceIDs) > 0 {
if !options.detach && versions.GreaterThanOrEqualTo(dockerCli.Client().ClientVersion(), "1.29") {
for _, serviceID := range serviceIDs {
if err := WaitOnService(ctx, dockerCli, serviceID, false); err != nil {
if err := waitOnService(ctx, dockerCli, serviceID, false); err != nil {
errs = append(errs, fmt.Sprintf("%s: %v", serviceID, err))
}
}

View File

@ -249,7 +249,7 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet,
return nil
}
return WaitOnService(ctx, dockerCli, serviceID, options.quiet)
return waitOnService(ctx, dockerCli, serviceID, options.quiet)
}
//nolint:gocyclo

View File

@ -26,7 +26,7 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
if err != nil {
return err
}
return swarm.RunDeploy(cmd.Context(), dockerCli, cmd.Flags(), &opts, config)
return swarm.RunDeploy(cmd.Context(), dockerCli, opts, config)
},
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return completeNames(dockerCli)(cmd, args, toComplete)
@ -42,7 +42,5 @@ func newDeployCommand(dockerCli command.Cli) *cobra.Command {
flags.StringVar(&opts.ResolveImage, "resolve-image", swarm.ResolveImageAlways,
`Query the registry to resolve image digest and supported platforms ("`+swarm.ResolveImageAlways+`", "`+swarm.ResolveImageChanged+`", "`+swarm.ResolveImageNever+`")`)
flags.SetAnnotation("resolve-image", "version", []string{"1.30"})
flags.BoolVarP(&opts.Detach, "detach", "d", true, "Exit immediately instead of waiting for the stack services to converge")
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Suppress progress output")
return cmd
}

View File

@ -9,8 +9,6 @@ type Deploy struct {
ResolveImage string
SendRegistryAuth bool
Prune bool
Detach bool
Quiet bool
}
// Config holds docker stack config options
@ -38,7 +36,6 @@ type PS struct {
// Remove holds docker stack remove options
type Remove struct {
Namespaces []string
Detach bool
}
// Services holds docker stack services options

View File

@ -27,8 +27,5 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
return completeNames(dockerCli)(cmd, args, toComplete)
},
}
flags := cmd.Flags()
flags.BoolVarP(&opts.Detach, "detach", "d", true, "Do not wait for stack removal")
return cmd
}

View File

@ -44,7 +44,3 @@ func getStackSecrets(ctx context.Context, apiclient client.APIClient, namespace
func getStackConfigs(ctx context.Context, apiclient client.APIClient, namespace string) ([]swarm.Config, error) {
return apiclient.ConfigList(ctx, types.ConfigListOptions{Filters: getStackFilter(namespace)})
}
func getStackTasks(ctx context.Context, apiclient client.APIClient, namespace string) ([]swarm.Task, error) {
return apiclient.TaskList(ctx, types.TaskListOptions{Filters: getStackFilter(namespace)})
}

View File

@ -11,7 +11,6 @@ import (
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/versions"
"github.com/pkg/errors"
"github.com/spf13/pflag"
)
// Resolve image constants
@ -23,8 +22,8 @@ const (
)
// RunDeploy is the swarm implementation of docker stack deploy
func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet, opts *options.Deploy, cfg *composetypes.Config) error {
if err := validateResolveImageFlag(opts); err != nil {
func RunDeploy(ctx context.Context, dockerCli command.Cli, opts options.Deploy, cfg *composetypes.Config) error {
if err := validateResolveImageFlag(&opts); err != nil {
return err
}
// client side image resolution should not be done when the supported
@ -33,11 +32,6 @@ func RunDeploy(ctx context.Context, dockerCli command.Cli, flags *pflag.FlagSet,
opts.ResolveImage = ResolveImageNever
}
if opts.Detach && !flags.Changed("detach") {
fmt.Fprintln(dockerCli.Err(), "Since --detach=false was not specified, tasks will be created in the background.\n"+
"In a future release, --detach=false will become the default.")
}
return deployCompose(ctx, dockerCli, opts, cfg)
}

View File

@ -2,11 +2,9 @@ package swarm
import (
"context"
"errors"
"fmt"
"github.com/docker/cli/cli/command"
servicecli "github.com/docker/cli/cli/command/service"
"github.com/docker/cli/cli/command/stack/options"
"github.com/docker/cli/cli/compose/convert"
composetypes "github.com/docker/cli/cli/compose/types"
@ -15,9 +13,10 @@ import (
"github.com/docker/docker/api/types/swarm"
apiclient "github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
"github.com/pkg/errors"
)
func deployCompose(ctx context.Context, dockerCli command.Cli, opts *options.Deploy, config *composetypes.Config) error {
func deployCompose(ctx context.Context, dockerCli command.Cli, opts options.Deploy, config *composetypes.Config) error {
if err := checkDaemonIsSwarmManager(ctx, dockerCli); err != nil {
return err
}
@ -61,17 +60,7 @@ func deployCompose(ctx context.Context, dockerCli command.Cli, opts *options.Dep
if err != nil {
return err
}
serviceIDs, err := deployServices(ctx, dockerCli, services, namespace, opts.SendRegistryAuth, opts.ResolveImage)
if err != nil {
return err
}
if opts.Detach {
return nil
}
return waitOnServices(ctx, dockerCli, serviceIDs, opts.Quiet)
return deployServices(ctx, dockerCli, services, namespace, opts.SendRegistryAuth, opts.ResolveImage)
}
func getServicesDeclaredNetworks(serviceConfigs []composetypes.ServiceConfig) map[string]struct{} {
@ -98,11 +87,11 @@ func validateExternalNetworks(ctx context.Context, client apiclient.NetworkAPICl
network, err := client.NetworkInspect(ctx, networkName, types.NetworkInspectOptions{})
switch {
case errdefs.IsNotFound(err):
return fmt.Errorf("network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed", networkName)
return errors.Errorf("network %q is declared as external, but could not be found. You need to create a swarm-scoped network before the stack is deployed", networkName)
case err != nil:
return err
case network.Scope != "swarm":
return fmt.Errorf("network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"", networkName, network.Scope)
return errors.Errorf("network %q is declared as external, but it is not in the right scope: %q instead of \"swarm\"", networkName, network.Scope)
}
}
return nil
@ -117,13 +106,13 @@ func createSecrets(ctx context.Context, dockerCli command.Cli, secrets []swarm.S
case err == nil:
// secret already exists, then we update that
if err := client.SecretUpdate(ctx, secret.ID, secret.Meta.Version, secretSpec); err != nil {
return fmt.Errorf("failed to update secret %s: %w", secretSpec.Name, err)
return errors.Wrapf(err, "failed to update secret %s", secretSpec.Name)
}
case errdefs.IsNotFound(err):
// secret does not exist, then we create a new one.
fmt.Fprintf(dockerCli.Out(), "Creating secret %s\n", secretSpec.Name)
if _, err := client.SecretCreate(ctx, secretSpec); err != nil {
return fmt.Errorf("failed to create secret %s: %w", secretSpec.Name, err)
return errors.Wrapf(err, "failed to create secret %s", secretSpec.Name)
}
default:
return err
@ -141,13 +130,13 @@ func createConfigs(ctx context.Context, dockerCli command.Cli, configs []swarm.C
case err == nil:
// config already exists, then we update that
if err := client.ConfigUpdate(ctx, config.ID, config.Meta.Version, configSpec); err != nil {
return fmt.Errorf("failed to update config %s: %w", configSpec.Name, err)
return errors.Wrapf(err, "failed to update config %s", configSpec.Name)
}
case errdefs.IsNotFound(err):
// config does not exist, then we create a new one.
fmt.Fprintf(dockerCli.Out(), "Creating config %s\n", configSpec.Name)
if _, err := client.ConfigCreate(ctx, configSpec); err != nil {
return fmt.Errorf("failed to create config %s: %w", configSpec.Name, err)
return errors.Wrapf(err, "failed to create config %s", configSpec.Name)
}
default:
return err
@ -180,19 +169,19 @@ func createNetworks(ctx context.Context, dockerCli command.Cli, namespace conver
fmt.Fprintf(dockerCli.Out(), "Creating network %s\n", name)
if _, err := client.NetworkCreate(ctx, name, createOpts); err != nil {
return fmt.Errorf("failed to create network %s: %w", name, err)
return errors.Wrapf(err, "failed to create network %s", name)
}
}
return nil
}
func deployServices(ctx context.Context, dockerCli command.Cli, services map[string]swarm.ServiceSpec, namespace convert.Namespace, sendAuth bool, resolveImage string) ([]string, error) {
func deployServices(ctx context.Context, dockerCli command.Cli, services map[string]swarm.ServiceSpec, namespace convert.Namespace, sendAuth bool, resolveImage string) error {
apiClient := dockerCli.Client()
out := dockerCli.Out()
existingServices, err := getStackServices(ctx, apiClient, namespace.Name())
if err != nil {
return nil, err
return err
}
existingServiceMap := make(map[string]swarm.Service)
@ -200,8 +189,6 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
existingServiceMap[service.Spec.Name] = service
}
var serviceIDs []string
for internalName, serviceSpec := range services {
var (
name = namespace.Scope(internalName)
@ -213,7 +200,7 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
// Retrieve encoded auth token from the image reference
encodedAuth, err = command.RetrieveAuthTokenFromImage(dockerCli.ConfigFile(), image)
if err != nil {
return nil, err
return err
}
}
@ -254,14 +241,12 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
response, err := apiClient.ServiceUpdate(ctx, service.ID, service.Version, serviceSpec, updateOpts)
if err != nil {
return nil, fmt.Errorf("failed to update service %s: %w", name, err)
return errors.Wrapf(err, "failed to update service %s", name)
}
for _, warning := range response.Warnings {
fmt.Fprintln(dockerCli.Err(), warning)
}
serviceIDs = append(serviceIDs, service.ID)
} else {
fmt.Fprintf(out, "Creating service %s\n", name)
@ -272,29 +257,10 @@ func deployServices(ctx context.Context, dockerCli command.Cli, services map[str
createOpts.QueryRegistry = true
}
response, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts)
if err != nil {
return nil, fmt.Errorf("failed to create service %s: %w", name, err)
if _, err := apiClient.ServiceCreate(ctx, serviceSpec, createOpts); err != nil {
return errors.Wrapf(err, "failed to create service %s", name)
}
serviceIDs = append(serviceIDs, response.ID)
}
}
return serviceIDs, nil
}
func waitOnServices(ctx context.Context, dockerCli command.Cli, serviceIDs []string, quiet bool) error {
var errs []error
for _, serviceID := range serviceIDs {
if err := servicecli.WaitOnService(ctx, dockerCli, serviceID, quiet); err != nil {
errs = append(errs, fmt.Errorf("%s: %w", serviceID, err))
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
return nil
}

View File

@ -99,7 +99,7 @@ func TestServiceUpdateResolveImageChanged(t *testing.T) {
},
},
}
_, err := deployServices(ctx, client, spec, namespace, false, ResolveImageChanged)
err := deployServices(ctx, client, spec, namespace, false, ResolveImageChanged)
assert.NilError(t, err)
assert.Check(t, is.Equal(receivedOptions.QueryRegistry, tc.expectedQueryRegistry))
assert.Check(t, is.Equal(receivedService.TaskTemplate.ContainerSpec.Image, tc.expectedImage))

View File

@ -11,7 +11,6 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/versions"
apiclient "github.com/docker/docker/client"
"github.com/pkg/errors"
)
@ -59,14 +58,6 @@ func RunRemove(ctx context.Context, dockerCli command.Cli, opts options.Remove)
if hasError {
errs = append(errs, fmt.Sprintf("Failed to remove some resources from stack: %s", namespace))
continue
}
if !opts.Detach {
err = waitOnTasks(ctx, client, namespace)
if err != nil {
errs = append(errs, fmt.Sprintf("Failed to wait on tasks of stack: %s: %s", namespace, err))
}
}
}
@ -146,45 +137,3 @@ func removeConfigs(
}
return hasError
}
var numberedStates = map[swarm.TaskState]int64{
swarm.TaskStateNew: 1,
swarm.TaskStateAllocated: 2,
swarm.TaskStatePending: 3,
swarm.TaskStateAssigned: 4,
swarm.TaskStateAccepted: 5,
swarm.TaskStatePreparing: 6,
swarm.TaskStateReady: 7,
swarm.TaskStateStarting: 8,
swarm.TaskStateRunning: 9,
swarm.TaskStateComplete: 10,
swarm.TaskStateShutdown: 11,
swarm.TaskStateFailed: 12,
swarm.TaskStateRejected: 13,
}
func terminalState(state swarm.TaskState) bool {
return numberedStates[state] > numberedStates[swarm.TaskStateRunning]
}
func waitOnTasks(ctx context.Context, client apiclient.APIClient, namespace string) error {
terminalStatesReached := 0
for {
tasks, err := getStackTasks(ctx, client, namespace)
if err != nil {
return fmt.Errorf("failed to get tasks: %w", err)
}
for _, task := range tasks {
if terminalState(task.Status.State) {
terminalStatesReached++
break
}
}
if terminalStatesReached == len(tasks) {
break
}
}
return nil
}

View File

@ -20,7 +20,7 @@ func equalCIDR(c1 net.IPNet, c2 net.IPNet) bool {
func setUpIPNetFlagSet(ipsp *[]net.IPNet) *pflag.FlagSet {
f := pflag.NewFlagSet("test", pflag.ContinueOnError)
f.Var(newIPNetSliceValue([]net.IPNet{}, ipsp), "cidrs", "Command separated list!")
f.VarP(newIPNetSliceValue([]net.IPNet{}, ipsp), "cidrs", "", "Command separated list!")
return f
}

View File

@ -5,18 +5,15 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
)
type fakeClient struct {
client.Client
version string
serverVersion func(ctx context.Context) (types.Version, error)
eventsFn func(context.Context, types.EventsOptions) (<-chan events.Message, <-chan error)
containerPruneFunc func(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error)
networkPruneFunc func(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error)
version string
serverVersion func(ctx context.Context) (types.Version, error)
eventsFn func(context.Context, types.EventsOptions) (<-chan events.Message, <-chan error)
}
func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) {
@ -30,17 +27,3 @@ func (cli *fakeClient) ClientVersion() string {
func (cli *fakeClient) Events(ctx context.Context, opts types.EventsOptions) (<-chan events.Message, <-chan error) {
return cli.eventsFn(ctx, opts)
}
func (cli *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) {
if cli.containerPruneFunc != nil {
return cli.containerPruneFunc(ctx, pruneFilters)
}
return types.ContainersPruneReport{}, nil
}
func (cli *fakeClient) NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error) {
if cli.networkPruneFunc != nil {
return cli.networkPruneFunc(ctx, pruneFilter)
}
return types.NetworksPruneReport{}, nil
}

View File

@ -21,6 +21,7 @@ import (
"github.com/docker/cli/templates"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/api/types/system"
"github.com/docker/docker/api/types/versions"
"github.com/docker/docker/registry"
"github.com/docker/go-units"
"github.com/spf13/cobra"
@ -379,10 +380,7 @@ func prettyPrintServerInfo(streams command.Streams, info *dockerInfo) []error {
}
fprintln(output)
for _, w := range info.Warnings {
fprintln(streams.Err(), w)
}
printServerWarnings(streams.Err(), info)
return errs
}
@ -456,6 +454,84 @@ func printSwarmInfo(output io.Writer, info system.Info) {
}
}
func printServerWarnings(stdErr io.Writer, info *dockerInfo) {
if versions.LessThan(info.ClientInfo.APIVersion, "1.42") {
printSecurityOptionsWarnings(stdErr, *info.Info)
}
if len(info.Warnings) > 0 {
fprintln(stdErr, strings.Join(info.Warnings, "\n"))
return
}
// daemon didn't return warnings. Fallback to old behavior
printServerWarningsLegacy(stdErr, *info.Info)
}
// printSecurityOptionsWarnings prints warnings based on the security options
// returned by the daemon.
//
// Deprecated: warnings are now generated by the daemon, and returned in
// info.Warnings. This function is used to provide backward compatibility with
// daemons that do not provide these warnings. No new warnings should be added
// here.
func printSecurityOptionsWarnings(stdErr io.Writer, info system.Info) {
if info.OSType == "windows" {
return
}
kvs, _ := system.DecodeSecurityOptions(info.SecurityOptions)
for _, so := range kvs {
if so.Name != "seccomp" {
continue
}
for _, o := range so.Options {
if o.Key == "profile" && o.Value != "default" && o.Value != "builtin" {
_, _ = fmt.Fprintln(stdErr, "WARNING: You're not using the default seccomp profile")
}
}
}
}
// printServerWarningsLegacy generates warnings based on information returned by the daemon.
//
// Deprecated: warnings are now generated by the daemon, and returned in
// info.Warnings. This function is used to provide backward compatibility with
// daemons that do not provide these warnings. No new warnings should be added
// here.
func printServerWarningsLegacy(stdErr io.Writer, info system.Info) {
if info.OSType == "windows" {
return
}
if !info.MemoryLimit {
fprintln(stdErr, "WARNING: No memory limit support")
}
if !info.SwapLimit {
fprintln(stdErr, "WARNING: No swap limit support")
}
if !info.OomKillDisable && info.CgroupVersion != "2" {
fprintln(stdErr, "WARNING: No oom kill disable support")
}
if !info.CPUCfsQuota {
fprintln(stdErr, "WARNING: No cpu cfs quota support")
}
if !info.CPUCfsPeriod {
fprintln(stdErr, "WARNING: No cpu cfs period support")
}
if !info.CPUShares {
fprintln(stdErr, "WARNING: No cpu shares support")
}
if !info.CPUSet {
fprintln(stdErr, "WARNING: No cpuset support")
}
if !info.IPv4Forwarding {
fprintln(stdErr, "WARNING: IPv4 forwarding is disabled")
}
if !info.BridgeNfIptables {
fprintln(stdErr, "WARNING: bridge-nf-call-iptables is disabled")
}
if !info.BridgeNfIP6tables {
fprintln(stdErr, "WARNING: bridge-nf-call-ip6tables is disabled")
}
}
func formatInfo(output io.Writer, info dockerInfo, format string) error {
if format == formatter.JSONFormatKey {
format = formatter.JSONFormat

View File

@ -333,6 +333,23 @@ func TestPrettyPrintInfo(t *testing.T) {
prettyGolden: "docker-info-with-swarm",
jsonGolden: "docker-info-with-swarm",
},
{
doc: "info with legacy warnings",
dockerInfo: dockerInfo{
Info: &infoWithWarningsLinux,
ClientInfo: &clientInfo{
clientVersion: clientVersion{
Platform: &platformInfo{Name: "Docker Engine - Community"},
Version: "24.0.0",
Context: "default",
},
Debug: true,
},
},
prettyGolden: "docker-info-no-swarm",
warningsGolden: "docker-info-warnings",
jsonGolden: "docker-info-legacy-warnings",
},
{
doc: "info with daemon warnings",
dockerInfo: dockerInfo{

View File

@ -74,10 +74,8 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
if options.pruneVolumes && options.filter.Value().Contains("until") {
return fmt.Errorf(`ERROR: The "until" filter is not supported with "--volumes"`)
}
if !options.force {
if r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), confirmationMessage(dockerCli, options)); !r || err != nil {
return err
}
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), confirmationMessage(dockerCli, options)) {
return nil
}
pruneFuncs := []func(ctx context.Context, dockerCli command.Cli, all bool, filter opts.FilterOpt) (uint64, string, error){
container.RunPrune,

View File

@ -1,15 +1,10 @@
package system
import (
"context"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
@ -54,23 +49,3 @@ func TestPrunePromptFilters(t *testing.T) {
Are you sure you want to continue? [y/N] `
assert.Check(t, is.Equal(expected, cli.OutBuffer().String()))
}
func TestSystemPrunePromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
containerPruneFunc: func(ctx context.Context, pruneFilters filters.Args) (types.ContainersPruneReport, error) {
return types.ContainersPruneReport{}, errors.New("fakeClient containerPruneFunc should not be called")
},
networkPruneFunc: func(ctx context.Context, pruneFilters filters.Args) (types.NetworksPruneReport, error) {
return types.NetworksPruneReport{}, errors.New("fakeClient networkPruneFunc should not be called")
},
})
cmd := newPruneCommand(cli)
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
}

View File

@ -0,0 +1 @@
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"overlay2","DriverStatus":[["Backing Filesystem","extfs"],["Supports d_type","true"],["Using metacopy","false"],["Native Overlay Diff","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"PidsLimit":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"CDISpecDirs":["/etc/cdi","/var/run/cdi"],"Warnings":null,"ClientInfo":{"Debug":true,"Platform":{"Name":"Docker Engine - Community"},"Version":"24.0.0","Context":"default","Plugins":[],"Warnings":null}}

View File

@ -11,7 +11,6 @@ import (
"github.com/docker/cli/internal/test"
notaryfake "github.com/docker/cli/internal/test/notary"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/system"
apiclient "github.com/docker/docker/client"
"github.com/theupdateframework/notary"
@ -37,7 +36,7 @@ func (c *fakeClient) ImageInspectWithRaw(context.Context, string) (types.ImageIn
return types.ImageInspect{}, []byte{}, nil
}
func (c *fakeClient) ImagePush(context.Context, string, image.PushOptions) (io.ReadCloser, error) {
func (c *fakeClient) ImagePush(context.Context, string, types.ImagePushOptions) (io.ReadCloser, error) {
return &utils.NoopCloser{Reader: bytes.NewBuffer([]byte{})}, nil
}

View File

@ -3,6 +3,7 @@ package trust
import (
"context"
"fmt"
"os"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@ -43,11 +44,7 @@ func revokeTrust(ctx context.Context, dockerCLI command.Cli, remote string, opti
return fmt.Errorf("cannot use a digest reference for IMAGE:TAG")
}
if imgRefAndAuth.Tag() == "" && !options.forceYes {
deleteRemote, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), fmt.Sprintf("Please confirm you would like to delete all signature data for %s?", remote))
if err != nil {
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
return errors.Wrap(err, "aborting action")
}
deleteRemote := command.PromptForConfirmation(os.Stdin, dockerCLI.Out(), fmt.Sprintf("Please confirm you would like to delete all signature data for %s?", remote))
if !deleteRemote {
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
return nil

View File

@ -1,11 +1,9 @@
package trust
import (
"context"
"io"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
@ -14,7 +12,6 @@ import (
"github.com/theupdateframework/notary/trustpinning"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
)
func TestTrustRevokeCommandErrors(t *testing.T) {
@ -151,18 +148,3 @@ func TestGetSignableRolesForTargetAndRemoveError(t *testing.T) {
err = getSignableRolesForTargetAndRemove(target, notaryRepo)
assert.Error(t, err, "client is offline")
}
func TestRevokeTrustPromptTermination(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{})
cmd := newRevokeCommand(cli)
cmd.SetArgs([]string{"example/trust-demo"})
test.TerminatePrompt(ctx, t, cmd, cli, func(t *testing.T, err error) {
t.Helper()
assert.ErrorIs(t, err, command.ErrPromptTerminated)
})
assert.Equal(t, cli.ErrBuffer().String(), "")
golden.Assert(t, cli.OutBuffer().String(), "trust-revoke-prompt-termination.golden")
}

View File

@ -12,7 +12,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/image"
"github.com/docker/cli/cli/trust"
imagetypes "github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry"
apiclient "github.com/docker/docker/client"
"github.com/pkg/errors"
@ -98,7 +98,7 @@ func runSignImage(ctx context.Context, dockerCLI command.Cli, options signOption
if err != nil {
return err
}
options := imagetypes.PushOptions{
options := types.ImagePushOptions{
RegistryAuth: encodedAuth,
PrivilegeFunc: requestPrivilege,
}

View File

@ -3,6 +3,7 @@ package trust
import (
"context"
"fmt"
"os"
"strings"
"github.com/docker/cli/cli"
@ -75,22 +76,6 @@ func isLastSignerForReleases(roleWithSig data.Role, allRoles []client.RoleWithSi
return counter < releasesRoleWithSigs.Threshold, nil
}
func maybePromptForSignerRemoval(ctx context.Context, dockerCLI command.Cli, repoName, signerName string, isLastSigner, forceYes bool) (bool, error) {
if isLastSigner && !forceYes {
message := fmt.Sprintf("The signer \"%s\" signed the last released version of %s. "+
"Removing this signer will make %s unpullable. "+
"Are you sure you want to continue?",
signerName, repoName, repoName,
)
removeSigner, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), message)
if err != nil {
return false, err
}
return removeSigner, nil
}
return false, nil
}
// removeSingleSigner attempts to remove a single signer and returns whether signer removal happened.
// The signer not being removed doesn't necessarily raise an error e.g. user choosing "No" when prompted for confirmation.
func removeSingleSigner(ctx context.Context, dockerCLI command.Cli, repoName, signerName string, forceYes bool) (bool, error) {
@ -125,26 +110,28 @@ func removeSingleSigner(ctx context.Context, dockerCLI command.Cli, repoName, si
if err != nil {
return false, err
}
if ok, err := isLastSignerForReleases(role, allRoles); ok && !forceYes {
removeSigner := command.PromptForConfirmation(os.Stdin, dockerCLI.Out(), fmt.Sprintf("The signer \"%s\" signed the last released version of %s. "+
"Removing this signer will make %s unpullable. "+
"Are you sure you want to continue?",
signerName, repoName, repoName,
))
isLastSigner, err := isLastSignerForReleases(role, allRoles)
if err != nil {
if !removeSigner {
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
return false, nil
}
} else if err != nil {
return false, err
}
if err = notaryRepo.RemoveDelegationKeys(releasesRoleTUFName, role.KeyIDs); err != nil {
return false, err
}
if err = notaryRepo.RemoveDelegationRole(signerDelegation); err != nil {
return false, err
}
ok, err := maybePromptForSignerRemoval(ctx, dockerCLI, repoName, signerName, isLastSigner, forceYes)
if err != nil || !ok {
fmt.Fprintf(dockerCLI.Out(), "\nAborting action.\n")
return false, err
}
if err := notaryRepo.RemoveDelegationKeys(releasesRoleTUFName, role.KeyIDs); err != nil {
return false, err
}
if err := notaryRepo.RemoveDelegationRole(signerDelegation); err != nil {
return false, err
}
if err := notaryRepo.Publish(); err != nil {
if err = notaryRepo.Publish(); err != nil {
return false, err
}

View File

@ -111,8 +111,7 @@ func TestIsLastSignerForReleases(t *testing.T) {
releaserole.Name = releasesRoleTUFName
releaserole.Threshold = 1
allrole := []client.RoleWithSignatures{releaserole}
lastsigner, err := isLastSignerForReleases(role, allrole)
assert.Error(t, err, "all signed tags are currently revoked, use docker trust sign to fix")
lastsigner, _ := isLastSignerForReleases(role, allrole)
assert.Check(t, is.Equal(false, lastsigner))
role.KeyIDs = []string{"deadbeef"}
@ -121,15 +120,13 @@ func TestIsLastSignerForReleases(t *testing.T) {
releaserole.Signatures = []data.Signature{sig}
releaserole.Threshold = 1
allrole = []client.RoleWithSignatures{releaserole}
lastsigner, err = isLastSignerForReleases(role, allrole)
assert.NilError(t, err)
lastsigner, _ = isLastSignerForReleases(role, allrole)
assert.Check(t, is.Equal(true, lastsigner))
sig.KeyID = "8badf00d"
releaserole.Signatures = []data.Signature{sig}
releaserole.Threshold = 1
allrole = []client.RoleWithSignatures{releaserole}
lastsigner, err = isLastSignerForReleases(role, allrole)
assert.NilError(t, err)
lastsigner, _ = isLastSignerForReleases(role, allrole)
assert.Check(t, is.Equal(false, lastsigner))
}

View File

@ -1,2 +0,0 @@
Please confirm you would like to delete all signature data for example/trust-demo? [y/N]
Aborting action.

View File

@ -5,15 +5,12 @@ package command
import (
"bufio"
"context"
"fmt"
"io"
"os"
"os/signal"
"path/filepath"
"runtime"
"strings"
"syscall"
"github.com/docker/cli/cli/streams"
"github.com/docker/docker/api/types/filters"
@ -75,21 +72,12 @@ func PrettyPrint(i any) string {
}
}
type PromptError error
var ErrPromptTerminated = PromptError(errors.New("prompt terminated"))
// PromptForConfirmation requests and checks confirmation from the user.
// This will display the provided message followed by ' [y/N] '. If the user
// input 'y' or 'Y' it returns true otherwise false. If no message is provided,
// "Are you sure you want to proceed? [y/N] " will be used instead.
//
// If the user terminates the CLI with SIGINT or SIGTERM while the prompt is
// active, the prompt will return false with an ErrPromptTerminated error.
// When the prompt returns an error, the caller should propagate the error up
// the stack and close the io.Reader used for the prompt which will prevent the
// background goroutine from blocking indefinitely.
func PromptForConfirmation(ctx context.Context, ins io.Reader, outs io.Writer, message string) (bool, error) {
// PromptForConfirmation requests and checks confirmation from user.
// This will display the provided message followed by ' [y/N] '. If
// the user input 'y' or 'Y' it returns true other false. If no
// message is provided "Are you sure you want to proceed? [y/N] "
// will be used instead.
func PromptForConfirmation(ins io.Reader, outs io.Writer, message string) bool {
if message == "" {
message = "Are you sure you want to proceed?"
}
@ -102,31 +90,9 @@ func PromptForConfirmation(ctx context.Context, ins io.Reader, outs io.Writer, m
ins = streams.NewIn(os.Stdin)
}
result := make(chan bool)
// Catch the termination signal and exit the prompt gracefully.
// The caller is responsible for properly handling the termination.
notifyCtx, notifyCancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM)
defer notifyCancel()
go func() {
var res bool
scanner := bufio.NewScanner(ins)
if scanner.Scan() {
answer := strings.TrimSpace(scanner.Text())
if strings.EqualFold(answer, "y") {
res = true
}
}
result <- res
}()
select {
case <-notifyCtx.Done():
return false, ErrPromptTerminated
case r := <-result:
return r, nil
}
reader := bufio.NewReader(ins)
answer, _, _ := reader.ReadLine()
return strings.ToLower(string(answer)) == "y"
}
// PruneFilters returns consolidated prune filters obtained from config.json and cli

View File

@ -1,46 +1,36 @@
package command_test
package command
import (
"bufio"
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/internal/test"
"github.com/pkg/errors"
"gotest.tools/v3/assert"
)
func TestStringSliceReplaceAt(t *testing.T) {
out, ok := command.StringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, -1)
out, ok := StringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, -1)
assert.Assert(t, ok)
assert.DeepEqual(t, []string{"abc", "baz", "bax"}, out)
out, ok = command.StringSliceReplaceAt([]string{"foo"}, []string{"foo", "bar"}, []string{"baz"}, -1)
out, ok = StringSliceReplaceAt([]string{"foo"}, []string{"foo", "bar"}, []string{"baz"}, -1)
assert.Assert(t, !ok)
assert.DeepEqual(t, []string{"foo"}, out)
out, ok = command.StringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0)
out, ok = StringSliceReplaceAt([]string{"abc", "foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0)
assert.Assert(t, !ok)
assert.DeepEqual(t, []string{"abc", "foo", "bar", "bax"}, out)
out, ok = command.StringSliceReplaceAt([]string{"foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0)
out, ok = StringSliceReplaceAt([]string{"foo", "bar", "bax"}, []string{"foo", "bar"}, []string{"baz"}, 0)
assert.Assert(t, ok)
assert.DeepEqual(t, []string{"baz", "bax"}, out)
out, ok = command.StringSliceReplaceAt([]string{"abc", "foo", "bar", "baz"}, []string{"foo", "bar"}, nil, -1)
out, ok = StringSliceReplaceAt([]string{"abc", "foo", "bar", "baz"}, []string{"foo", "bar"}, nil, -1)
assert.Assert(t, ok)
assert.DeepEqual(t, []string{"abc", "baz"}, out)
out, ok = command.StringSliceReplaceAt([]string{"foo"}, nil, []string{"baz"}, -1)
out, ok = StringSliceReplaceAt([]string{"foo"}, nil, []string{"baz"}, -1)
assert.Assert(t, !ok)
assert.DeepEqual(t, []string{"foo"}, out)
}
@ -69,7 +59,7 @@ func TestValidateOutputPath(t *testing.T) {
for _, testcase := range testcases {
t.Run(testcase.path, func(t *testing.T) {
err := command.ValidateOutputPath(testcase.path)
err := ValidateOutputPath(testcase.path)
if testcase.err == nil {
assert.NilError(t, err)
} else {
@ -78,177 +68,3 @@ func TestValidateOutputPath(t *testing.T) {
})
}
}
func TestPromptForConfirmation(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
type promptResult struct {
result bool
err error
}
buf := new(bytes.Buffer)
bufioWriter := bufio.NewWriter(buf)
var (
promptWriter *io.PipeWriter
promptReader *io.PipeReader
)
defer func() {
if promptWriter != nil {
promptWriter.Close()
}
if promptReader != nil {
promptReader.Close()
}
}()
for _, tc := range []struct {
desc string
f func(*testing.T, context.Context, chan promptResult)
}{
{"SIGINT", func(t *testing.T, ctx context.Context, c chan promptResult) {
t.Helper()
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
select {
case <-ctx.Done():
t.Fatal("PromptForConfirmation did not return after SIGINT")
case r := <-c:
assert.Check(t, !r.result)
assert.ErrorContains(t, r.err, "prompt terminated")
}
}},
{"no", func(t *testing.T, ctx context.Context, c chan promptResult) {
t.Helper()
_, err := fmt.Fprint(promptWriter, "n\n")
assert.NilError(t, err)
select {
case <-ctx.Done():
t.Fatal("PromptForConfirmation did not return after user input `n`")
case r := <-c:
assert.Check(t, !r.result)
assert.NilError(t, r.err)
}
}},
{"yes", func(t *testing.T, ctx context.Context, c chan promptResult) {
t.Helper()
_, err := fmt.Fprint(promptWriter, "y\n")
assert.NilError(t, err)
select {
case <-ctx.Done():
t.Fatal("PromptForConfirmation did not return after user input `y`")
case r := <-c:
assert.Check(t, r.result)
assert.NilError(t, r.err)
}
}},
{"any", func(t *testing.T, ctx context.Context, c chan promptResult) {
t.Helper()
_, err := fmt.Fprint(promptWriter, "a\n")
assert.NilError(t, err)
select {
case <-ctx.Done():
t.Fatal("PromptForConfirmation did not return after user input `a`")
case r := <-c:
assert.Check(t, !r.result)
assert.NilError(t, r.err)
}
}},
{"with space", func(t *testing.T, ctx context.Context, c chan promptResult) {
t.Helper()
_, err := fmt.Fprint(promptWriter, " y\n")
assert.NilError(t, err)
select {
case <-ctx.Done():
t.Fatal("PromptForConfirmation did not return after user input ` y`")
case r := <-c:
assert.Check(t, r.result)
assert.NilError(t, r.err)
}
}},
{"reader closed", func(t *testing.T, ctx context.Context, c chan promptResult) {
t.Helper()
assert.NilError(t, promptReader.Close())
select {
case <-ctx.Done():
t.Fatal("PromptForConfirmation did not return after promptReader was closed")
case r := <-c:
assert.Check(t, !r.result)
assert.NilError(t, r.err)
}
}},
} {
t.Run("case="+tc.desc, func(t *testing.T) {
buf.Reset()
promptReader, promptWriter = io.Pipe()
wroteHook := make(chan struct{}, 1)
defer close(wroteHook)
promptOut := test.NewWriterWithHook(bufioWriter, func(p []byte) {
wroteHook <- struct{}{}
})
result := make(chan promptResult, 1)
defer close(result)
go func() {
r, err := command.PromptForConfirmation(ctx, promptReader, promptOut, "")
result <- promptResult{r, err}
}()
// wait for the Prompt to write to the buffer
pollForPromptOutput(ctx, t, wroteHook)
drainChannel(ctx, wroteHook)
assert.NilError(t, bufioWriter.Flush())
assert.Equal(t, strings.TrimSpace(buf.String()), "Are you sure you want to proceed? [y/N]")
resultCtx, resultCancel := context.WithTimeout(ctx, 100*time.Millisecond)
defer resultCancel()
tc.f(t, resultCtx, result)
})
}
}
func drainChannel(ctx context.Context, ch <-chan struct{}) {
go func() {
for {
select {
case <-ctx.Done():
return
case <-ch:
}
}
}()
}
func pollForPromptOutput(ctx context.Context, t *testing.T, wroteHook <-chan struct{}) {
t.Helper()
ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
defer cancel()
for {
select {
case <-ctx.Done():
t.Fatal("Prompt output was not written to before ctx was cancelled")
return
case <-wroteHook:
return
}
}
}

View File

@ -32,10 +32,6 @@ func NewPruneCommand(dockerCli command.Cli) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
spaceReclaimed, output, err := runPrune(cmd.Context(), dockerCli, options)
if err != nil {
if errdefs.IsCancelled(err) {
fmt.Fprintln(dockerCli.Out(), output)
return nil
}
return err
}
if output != "" {
@ -80,10 +76,8 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
// API < v1.42 removes all volumes (anonymous and named) by default.
warning = allVolumesWarning
}
if !options.force {
if r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning); !r || err != nil {
return 0, "", errdefs.Cancelled(errors.New("user cancelled operation"))
}
if !options.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return 0, "", nil
}
report, err := dockerCli.Client().VolumesPrune(ctx, pruneFilters)

View File

@ -1,7 +1,6 @@
package volume
import (
"context"
"fmt"
"io"
"runtime"
@ -74,15 +73,13 @@ func TestVolumePruneSuccess(t *testing.T) {
testCases := []struct {
name string
args []string
input string
volumePruneFunc func(args filters.Args) (types.VolumesPruneReport, error)
}{
{
name: "all",
args: []string{"--all"},
input: "y",
name: "all",
args: []string{"--all"},
volumePruneFunc: func(pruneFilter filters.Args) (types.VolumesPruneReport, error) {
assert.Check(t, is.DeepEqual([]string{"true"}, pruneFilter.Get("all")))
assert.Check(t, is.Equal([]string{"true"}, pruneFilter.Get("all")))
return types.VolumesPruneReport{}, nil
},
},
@ -94,11 +91,10 @@ func TestVolumePruneSuccess(t *testing.T) {
},
},
{
name: "label-filter",
args: []string{"--filter", "label=foobar"},
input: "y",
name: "label-filter",
args: []string{"--filter", "label=foobar"},
volumePruneFunc: func(pruneFilter filters.Args) (types.VolumesPruneReport, error) {
assert.Check(t, is.DeepEqual([]string{"foobar"}, pruneFilter.Get("label")))
assert.Check(t, is.Equal([]string{"foobar"}, pruneFilter.Get("label")))
return types.VolumesPruneReport{}, nil
},
},
@ -108,9 +104,6 @@ func TestVolumePruneSuccess(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{volumePruneFunc: tc.volumePruneFunc})
cmd := NewPruneCommand(cli)
if tc.input != "" {
cli.SetIn(streams.NewIn(io.NopCloser(strings.NewReader(tc.input))))
}
cmd.SetOut(io.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
@ -184,19 +177,3 @@ func simplePruneFunc(filters.Args) (types.VolumesPruneReport, error) {
SpaceReclaimed: 2000,
}, nil
}
func TestVolumePrunePromptTerminate(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
volumePruneFunc: func(filter filters.Args) (types.VolumesPruneReport, error) {
return types.VolumesPruneReport{}, errors.New("fakeClient volumePruneFunc should not be called")
},
})
cmd := NewPruneCommand(cli)
test.TerminatePrompt(ctx, t, cmd, cli, nil)
golden.Assert(t, cli.OutBuffer().String(), "volume-prune-terminate.golden")
}

View File

@ -1,2 +1,2 @@
WARNING! This will remove anonymous local volumes not used by at least one container.
Are you sure you want to continue? [y/N]
Are you sure you want to continue? [y/N] Total reclaimed space: 0B

View File

@ -1,2 +0,0 @@
WARNING! This will remove anonymous local volumes not used by at least one container.
Are you sure you want to continue? [y/N]

View File

@ -17,7 +17,9 @@ import (
// For https://github.com/docker/cli/pull/1014#issuecomment-409308139
func TestEOFWithError(t *testing.T) {
ctx := context.TODO()
c, err := New(ctx, "sh", "-c", "echo hello; echo some error >&2; exit 42")
cmd := "sh"
args := []string{"-c", "echo hello; echo some error >&2; exit 42"}
c, err := New(ctx, cmd, args...)
assert.NilError(t, err)
b := make([]byte, 32)
n, err := c.Read(b)
@ -31,7 +33,9 @@ func TestEOFWithError(t *testing.T) {
func TestEOFWithoutError(t *testing.T) {
ctx := context.TODO()
c, err := New(ctx, "sh", "-c", "echo hello; echo some debug log >&2; exit 0")
cmd := "sh"
args := []string{"-c", "echo hello; echo some debug log >&2; exit 0"}
c, err := New(ctx, cmd, args...)
assert.NilError(t, err)
b := make([]byte, 32)
n, err := c.Read(b)
@ -43,12 +47,14 @@ func TestEOFWithoutError(t *testing.T) {
}
func TestCloseRunningCommand(t *testing.T) {
ctx := context.TODO()
cmd := "sh"
args := []string{"-c", "while true; sleep 1; done"}
done := make(chan struct{})
defer close(done)
go func() {
c, err := New(ctx, "sh", "-c", "while true; sleep 1; done")
c, err := New(context.TODO(), cmd, args...)
assert.NilError(t, err)
cmdConn := c.(*commandConn)
assert.Check(t, process.Alive(cmdConn.cmd.Process.Pid))
@ -73,10 +79,12 @@ func TestCloseRunningCommand(t *testing.T) {
}
func TestCloseTwice(t *testing.T) {
ctx := context.TODO()
cmd := "sh"
args := []string{"-c", "echo hello; sleep 1; exit 0"}
done := make(chan struct{})
go func() {
c, err := New(ctx, "sh", "-c", "echo hello; sleep 1; exit 0")
c, err := New(context.TODO(), cmd, args...)
assert.NilError(t, err)
cmdConn := c.(*commandConn)
assert.Check(t, process.Alive(cmdConn.cmd.Process.Pid))
@ -105,10 +113,12 @@ func TestCloseTwice(t *testing.T) {
}
func TestEOFTimeout(t *testing.T) {
ctx := context.TODO()
cmd := "sh"
args := []string{"-c", "sleep 20"}
done := make(chan struct{})
go func() {
c, err := New(ctx, "sh", "-c", "sleep 20")
c, err := New(context.TODO(), cmd, args...)
assert.NilError(t, err)
cmdConn := c.(*commandConn)
assert.Check(t, process.Alive(cmdConn.cmd.Process.Pid))
@ -144,8 +154,10 @@ func (mockStdoutEOF) Close() error {
}
func TestCloseWhileWriting(t *testing.T) {
ctx := context.TODO()
c, err := New(ctx, "sh", "-c", "while true; sleep 1; done")
cmd := "sh"
args := []string{"-c", "while true; sleep 1; done"}
c, err := New(context.TODO(), cmd, args...)
assert.NilError(t, err)
cmdConn := c.(*commandConn)
assert.Check(t, process.Alive(cmdConn.cmd.Process.Pid))
@ -172,8 +184,10 @@ func TestCloseWhileWriting(t *testing.T) {
}
func TestCloseWhileReading(t *testing.T) {
ctx := context.TODO()
c, err := New(ctx, "sh", "-c", "while true; sleep 1; done")
cmd := "sh"
args := []string{"-c", "while true; sleep 1; done"}
c, err := New(context.TODO(), cmd, args...)
assert.NilError(t, err)
cmdConn := c.(*commandConn)
assert.Check(t, process.Alive(cmdConn.cmd.Process.Pid))

View File

@ -69,7 +69,7 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
}
// is this a build that should be forwarded to the builder?
fwargs, fwosargs, fwcmdpath, forwarded := forwardBuilder(builderAlias, args, osargs)
fwargs, fwosargs, forwarded := forwardBuilder(builderAlias, args, osargs)
if !forwarded {
return args, osargs, nil, nil
}
@ -116,38 +116,31 @@ func processBuilder(dockerCli command.Cli, cmd *cobra.Command, args, osargs []st
envs = append([]string{"BUILDX_BUILDER=" + dockerCli.CurrentContext()}, envs...)
}
// overwrite the command path for this plugin using the alias name.
cmd.Annotations[pluginmanager.CommandAnnotationPluginCommandPath] = strings.Join(append([]string{cmd.CommandPath()}, fwcmdpath...), " ")
return fwargs, fwosargs, envs, nil
}
func forwardBuilder(alias string, args, osargs []string) ([]string, []string, []string, bool) {
aliases := [][3][]string{
func forwardBuilder(alias string, args, osargs []string) ([]string, []string, bool) {
aliases := [][2][]string{
{
{"builder"},
{alias},
{"builder"},
},
{
{"build"},
{alias, "build"},
{},
},
{
{"image", "build"},
{alias, "build"},
{"image"},
},
}
for _, al := range aliases {
if fwargs, changed := command.StringSliceReplaceAt(args, al[0], al[1], 0); changed {
fwosargs, _ := command.StringSliceReplaceAt(osargs, al[0], al[1], -1)
fwcmdpath := al[2]
return fwargs, fwosargs, fwcmdpath, true
return fwargs, fwosargs, true
}
}
return args, osargs, nil, false
return args, osargs, false
}
// hasBuilderName checks if a builder name is defined in args or env vars

View File

@ -1,5 +1,5 @@
variable "GO_VERSION" {
default = "1.21.9"
default = "1.21.8"
}
variable "VERSION" {
default = ""

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.9
ARG GO_VERSION=1.21.8
ARG ALPINE_VERSION=3.18
ARG BUILDX_VERSION=0.12.1
@ -11,7 +11,7 @@ ENV GOTOOLCHAIN=local
ENV CGO_ENABLED=0
FROM golang AS gofumpt
ARG GOFUMPT_VERSION=v0.6.0
ARG GOFUMPT_VERSION=v0.4.0
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
--mount=type=tmpfs,target=/go/src/ \

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.9
ARG GO_VERSION=1.21.8
ARG ALPINE_VERSION=3.18
ARG GOLANGCI_LINT_VERSION=v1.55.2

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21.9
ARG GO_VERSION=1.21.8
ARG ALPINE_VERSION=3.18
ARG MODOUTDATED_VERSION=v0.8.0

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