Compare commits

..
Author SHA1 Message Date
p4u1 2fbb22f658 fix(loader): Allows multiple protocols on one port
codeql / codeql (push) Has been cancelled
2025-12-26 11:05:12 +01:00
Sebastiaan van Stijn 890dcca877 Merge pull request #6688 from vvoland/update-go
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29, local) (push) Has been cancelled
e2e / tests (alpine, rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29, local) (push) Has been cancelled
e2e / tests (debian, rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
update to go1.25.5
2025-12-02 19:58:46 +01:00
Paweł Gronowski d544885316 update to go1.25.5
These releases include 2 security fixes following the security policy:

- crypto/x509: excessive resource consumption in printing error string for host certificate validation

    Within HostnameError.Error(), when constructing an error string, there is no limit to the number of hosts that will be printed out.
    Furthermore, the error string is constructed by repeated string concatenation, leading to quadratic runtime.

    Therefore, a certificate provided by a malicious actor can result in excessive resource consumption.
    HostnameError.Error() now limits the number of hosts and utilizes strings.Builder when constructing an error string.

    Thanks to Philippe Antoine (Catena cyber) for reporting this issue.

    This is CVE-2025-61729 and Go issue https://go.dev/issue/76445.

- crypto/x509: excluded subdomain constraint does not restrict wildcard SANs

    An excluded subdomain constraint in a certificate chain does not restrict the
    usage of wildcard SANs in the leaf certificate. For example a constraint that
    excludes the subdomain test.example.com does not prevent a leaf certificate from
    claiming the SAN *.example.com.

    This is CVE-2025-61727 and Go issue https://go.dev/issue/76442.

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

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-12-02 18:03:18 +01:00
Paweł Gronowski c197aa70ee Merge pull request #6687 from thaJeztah/use_subtests
opts/swarmopts: use sub-tests
2025-12-01 13:08:30 +00:00
Sebastiaan van Stijn ba683d8df3 opts/swarmopts: use sub-tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-12-01 13:01:37 +01:00
Paweł Gronowski 0aedba58c2 Merge pull request #6669 from vvoland/29-norc
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29, local) (push) Has been cancelled
e2e / tests (alpine, rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29, local) (push) Has been cancelled
e2e / tests (debian, rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
gha/e2e: Switch to 29 from 29-rc
2025-11-28 12:26:43 +01:00
Paweł Gronowski dd2be022c0 gha/e2e: Switch to rc and 29 latest
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-28 12:22:06 +01:00
Paweł Gronowski 360952c8d3 Merge pull request #6680 from thaJeztah/bump_modules
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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/moby/moby/client v0.2.1
2025-11-27 17:36:37 +01:00
Sebastiaan van Stijn 8fc15eaf2c Merge pull request #6579 from dvdksn/doc-daemon-buildc-example
docs: update buildgc example config to use new buildkit v0.17 options
2025-11-27 17:35:32 +01:00
Sebastiaan van Stijn 1abfbf298c vendor: github.com/moby/moby/client v0.2.1
full diff: https://github.com/moby/moby/compare/client/v0.1.0...v0.2.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 17:25:03 +01:00
David Karlsson e0d30db115 docs: update buildgc example config to use new buildkit v0.17 options
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2025-11-27 16:24:42 +01:00
Paweł Gronowski 5691ade75a Merge pull request #6682 from thaJeztah/bump_dct_deps
cmd/docker-trust: update dependencies
2025-11-27 15:38:49 +01:00
Paweł Gronowski 848dcad809 Merge pull request #6681 from thaJeztah/bump_x_deps2
vendor: update various golang.org/x/xxx dependencies
2025-11-27 15:38:16 +01:00
Sebastiaan van Stijn 6a0099bc8a cmd/docker-trust: bump golang.org/x/crypto v0.45.0
Hello gophers,

We have tagged version v0.45.0 of golang.org/x/crypto in order to address two
security issues.

This version fixes a vulnerability in the golang.org/x/crypto/ssh package and a
vulnerability in the golang.org/x/crypto/ssh/agent package which could cause
programs to consume unbounded memory or panic respectively.

SSH servers parsing GSSAPI authentication requests don't validate the number of
mechanisms specified in the request, allowing an attacker to cause unbounded
memory consumption.

Thanks to Jakub Ciolek for reporting this issue.

This is CVE-2025-58181 and Go issue https://go.dev/issue/76363.

SSH Agent servers do not validate the size of messages when processing new
identity requests, which may cause the program to panic if the message is
malformed due to an out of bounds read.

Thanks to Jakub Ciolek for reporting this issue.

This is CVE-2025-47914 and Go issue https://go.dev/issue/76364.

Cheers, Go Security team

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 14:38:30 +01:00
Sebastiaan van Stijn c90166ffa6 cmd/docker-trust: update dependencies
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 14:37:10 +01:00
Sebastiaan van Stijn ac5e886124 vendor: golang.org/x/net v0.47.0
full diff: https://github.com/golang/net/compare/v0.46.0...v0.47.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 14:28:29 +01:00
Sebastiaan van Stijn 3ec414638c vendor: golang.org/x/term v0.37.0
full diff: https://github.com/golang/term/compare/v0.36.0...v0.37.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 14:27:21 +01:00
Sebastiaan van Stijn 616e93a0c2 vendor: golang.org/x/text v0.31.0
full diff: https://github.com/golang/text/compare/v0.30.0...v0.31.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 14:24:45 +01:00
Sebastiaan van Stijn 1202f8a642 vendor: golang.org/x/sync v0.18.0
full diff: https://github.com/golang/sync/compare/v0.17.0...v0.18.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 14:23:31 +01:00
Sebastiaan van Stijn b67055c963 vendor: golang.org/x/sys v0.38.0
- cpu: add HPDS, LOR, PAN detection for arm64
- cpu: also use MRS instruction in getmmfr1
- cpu: use MRS instruction to read arm64 system registers
- unix: add consts for ELF handling
- unix: add SetMemPolicy and its mode/flag values
- unix: add SizeofNhmsg and SizeofNexthopGrp
- windows: add iphlpapi routing functions

full diff: https://github.com/golang/sys/compare/v0.37.0...v0.38.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-27 14:22:32 +01:00
Sebastiaan van Stijn eee3e3d015 Merge pull request #6671 from docker/dependabot/github_actions/actions/checkout-6
build(deps): bump actions/checkout from 5 to 6
2025-11-27 10:42:16 +01:00
Paweł Gronowski 3247a5aae3 Merge pull request #6675 from vvoland/img-list-noellipsis
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
image/tree: Allow image names to overflow instead of truncating
2025-11-24 21:35:37 +00:00
Paweł Gronowski 4759615835 image/tree: Allow image names to overflow instead of truncating
Users were experiencing poor UX when image names were truncated in the
table output.

Instead of cutting off long image names with ellipsis, the names now
wrap to the next line to ensure full visibility.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-24 22:12:37 +01:00
dependabot[bot] 3099d4716c build(deps): bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-21 08:04:55 +00:00
Paweł Gronowski 511dad69d0 Merge pull request #6667 from thaJeztah/use_format
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
image ls: allow custom format in cli config
2025-11-20 16:31:07 +00:00
Paweł Gronowski 11f24b8458 Merge pull request #6668 from robmry/builttime-format
docker version: restore top-level BuildTime to RFC3339Nano format
2025-11-20 16:21:40 +00:00
Sebastiaan van Stijn d84396d4eb image ls: allow custom format in cli config
Setting a custom format in the cli cofig should still be supported,
and not produce an error when specifying "--tree". Specifyihg both
"--tree" and "--format" still produces an error, but we could consider
allowing "json" format in a future update.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-20 16:20:33 +00:00
Rob Murray 6751cd1690 docker version: restore top-level BuildTime to RFC3339Nano
Introduced by bff56f0 (cli/command/system: define struct for
formatting version).

In the "docker info" result, the Engine component's BuildTime should
be in time.ANSIC format, but the top level BuildTime field should use
time.RFC3339Nano.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2025-11-20 15:57:24 +00:00
Sebastiaan van Stijn 8108357bcb Merge pull request #6662 from dvdksn/doc-update-http-proxy-link
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
chore: update link/linktext to dockerd proxy config
2025-11-17 11:19:50 +01:00
David Karlsson 3a842587f9 chore: update link/linktext to dockerd proxy config
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2025-11-17 11:00:04 +01:00
Sebastiaan van Stijn eedd9698e9 Merge pull request #6659 from vvoland/fix-system-version
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
cli/command/system: Fix missing components in version output
2025-11-13 22:27:39 +01:00
Paweł Gronowski dd2c493825 cli/command/system: Fix missing components in version output
The `Components` weren't actually copied to the output struct.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-13 21:19:41 +01:00
Paweł Gronowski 67cef775fe Merge pull request #6658 from vvoland/img-list-all-dangling
image/tree: Only show untagged images when --all flag is used
2025-11-13 20:53:26 +01:00
Paweł Gronowski 207bf52c27 image/tree: Only show untagged images when --all flag is used
In non-expanded view, untagged images should only be displayed when the
--all flag is explicitly provided by the user.

Previously, untagged images were accidentally always shown in the
non-expanded view regardless of the --all flag setting.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-13 20:23:42 +01:00
Sebastiaan van Stijn 2cfd9df568 Merge pull request #6654 from vvoland/img-list-nocolor
image/tree: Respect NO_COLOR env variable
2025-11-13 15:10:10 +01:00
Paweł Gronowski be9e6308f5 image/tree: Respect NO_COLOR env variable
Do not use the fancy colored output if NO_COLOR variable is set to 1
following the https://no-color.org/ convention.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-13 14:56:27 +01:00
Sebastiaan van Stijn 88e324150b Merge pull request #6657 from vvoland/img-list-nonexpanded-untagged
image/tree: Fix untagged images in non-expanded view
2025-11-13 13:20:46 +01:00
Sebastiaan van Stijn 2ae51e2d69 Merge pull request #6656 from vvoland/img-list-notty-width
image/tree: Don't limit name width if non tty
2025-11-13 13:19:30 +01:00
Paweł Gronowski ed281ddf52 image/list: Print legend only if limiting width
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-13 13:00:02 +01:00
Paweł Gronowski aa5d00a3a4 image/tree: Don't limit name width if non tty
Previously when no terminal was attached the width was assumed to be 80.
This is too short for most image names which truncated the names when
output was redirect (for example to `grep`).

This disabled the name truncation if the terminal width can't be
determined.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-13 12:59:46 +01:00
Paweł Gronowski b66b93130c image/tree: Fix untagged images in non-expanded view
In the expanded view there is a separate image entry per each tag.

Fix a bug which caused no entry to be added for untagged images.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-13 12:47:24 +01:00
Sebastiaan van Stijn c44e8a0727 Merge pull request #6648 from thaJeztah/cli_version_json_format
cli/command/system: define struct for formatting version
2025-11-12 18:09:53 +01:00
Sebastiaan van Stijn bff56f0493 cli/command/system: define struct for formatting version
The client.ServerVersion method in the moby/client module defines
an output struct that's separate from the API response. These output
structs are not designed to be marshaled as JSON, but the CLI depended
on them defining `json` labels, which it used to format the output
as JSON (`docker version --format=json`); as a result, the JSON output
changed in docker v29, as it would now use the naming based on the Go
struct's fields (`APIVersion` instead of `ApiVersion`).

In future, we should consider having a `--raw` (or similar) option for
the CLI to print API responses as-is, instead of using client structs
or CLI structs for this (this would also make sure the JSON output does
not inherit client-side formatting of fields).

For now, let's create a struct for formatting the output, similar to what
we do for the client-side information.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-12 14:57:13 +01:00
Sebastiaan van Stijn 3d4129b9ea Merge pull request #6644 from thaJeztah/connhelper_nowarn
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
cli/connhelper/commandcon: remove warn logs
2025-11-10 22:42:38 +01:00
Sebastiaan van Stijn d787e70a14 cli/connhelper/commandcon: remove warn logs
These were originally added in 6f61cf053a,
but at the time, the error wasn't returned. Now that it is, we shouldn't
log _and_ return the error.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-10 22:36:41 +01:00
Paweł Gronowski e730f6f0f3 Merge pull request #6643 from thaJeztah/bump_modules2
vendor: github.com/moby/moby/api v1.52.0, moby/client v0.1.0
2025-11-10 22:04:44 +01:00
Paweł Gronowski 6ac3f93755 Merge pull request #6578 from thaJeztah/bump_otel_semconv
cli/command: update to semconv v1.37.0, otel v1.38.0
2025-11-10 22:04:29 +01:00
Sebastiaan van Stijn ebc1995f9f vendor: github.com/moby/moby/api v1.52.0, moby/client v0.1.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-10 21:57:40 +01:00
Sebastiaan van Stijn 31d1a59d07 Merge pull request #6642 from vvoland/swarm-compose-work
swarm: revert compose/stack support for memory swappiness
2025-11-10 19:09:49 +01:00
Paweł Gronowski ad96811f12 swarm: Add memory swap support (no stack/compose support)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-10 17:48:56 +01:00
Paweł Gronowski 6ba06b5fb4 Revert "cli/compose: add schema 3.14 (no changes from 3.13 yet)"
This reverts commit d0c86d39ef.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-10 17:48:55 +01:00
Paweł Gronowski e0716b571f Revert "Add memory swap to swarm"
This reverts commit 71828f2792.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-10 17:48:54 +01:00
Paweł Gronowski 179efae8b0 Merge pull request #6641 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api, moby/client master
2025-11-10 17:03:43 +01:00
Sebastiaan van Stijn 4b450f113b vendor: github.com/moby/moby/api, moby/client master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-10 16:32:05 +01:00
Paweł Gronowski ee244f2f44 Merge pull request #6636 from thaJeztah/add_missing_gobuild
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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
cli/command/system: add missing "go:build"
2025-11-07 01:34:14 +01:00
Paweł Gronowski 0c101c4aa7 Merge pull request #6635 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api v1.52.0-rc.1, moby/client v0.1.0-rc.1
2025-11-07 01:31:10 +01:00
Sebastiaan van Stijn 1d789e4099 cli/command/system: add missing "go:build"
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 01:27:47 +01:00
Sebastiaan van Stijn b3824015d6 vendor: github.com/moby/moby/api v1.52.0-rc.1, moby/client v0.1.0-rc.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 01:26:13 +01:00
Sebastiaan van Stijn c4f240cc7d Merge pull request #6606 from dvdksn/update-libnetwork-docs-link
docs: update link to libnetwork protocol doc
2025-11-07 01:12:41 +01:00
Sebastiaan van Stijn 5b443bf269 Merge pull request #6619 from dperny/swarm-memory-swap
Add memory swap to swarm
2025-11-07 01:03:26 +01:00
Sebastiaan van Stijn eaa6114d9e Merge pull request #6634 from thaJeztah/remove_replace
vendor.mod: remove replace
2025-11-07 00:55:53 +01:00
Sebastiaan van Stijn c9e6b41293 Merge pull request #6633 from dvdksn/docs-update-dd-documentation-link
chore: update broken link to restrucured docker desktop documentation
2025-11-07 00:34:43 +01:00
Sebastiaan van Stijn d67291026e vendor.mod: remove replace
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 00:32:57 +01:00
Austin Vazquez 41088ed7d0 vendor: go.opentelemetry.io/auto/sdk v1.2.1
Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 00:29:26 +01:00
Austin Vazquez 712f569f17 vendor: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 00:29:26 +01:00
Sebastiaan van Stijn 7736f5e606 vendor: align other otel packages to v1.38.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 00:29:24 +01:00
Jonathan A. Sternberg d45551dac9 cli/command: update to semconv v1.37.0, otel v1.38.0
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 00:27:34 +01:00
Drew Erny 71828f2792 Add memory swap to swarm
Adds support for setting memory swap settings on Swarm services

* Adds flags `memory-swap` and `memory-swappiness` to `docker service
create` and `docker service update` commands.
* Adds compose fields `memswap_limit` and `mem_swappiness` for `docker
stack` commands.

Signed-off-by: Drew Erny <derny@mirantis.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 00:24:44 +01:00
Sebastiaan van Stijn d0c86d39ef cli/compose: add schema 3.14 (no changes from 3.13 yet)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-07 00:24:44 +01:00
Paweł Gronowski 16d2036cde Merge pull request #6620 from austinvazquez/vendor-system-disk-usage-changes
vendor: github.com/moby/moby/api master, moby/client master
2025-11-07 00:20:18 +01:00
Austin Vazquez 5039eee77f vendor: github.com/moby/moby/api master, moby/client master
Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
2025-11-06 17:02:53 -06:00
Sebastiaan van Stijn cc7275c4e5 Merge pull request #6121 from thaJeztah/trust_plugin
implement `docker trust` as plugin
2025-11-06 20:29:58 +01:00
Sebastiaan van Stijn f7d6d5bdb3 Merge pull request #6632 from vvoland/update-go
update to go1.25.4
2025-11-06 17:13:16 +01:00
Sebastiaan van Stijn cee9ea67fc lint: run in go-modules mode
Prevent the linter from recursing to other modules (cmd/docker-trust),
which don't have their dependencies vendored.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-06 15:24:49 +01:00
Sebastiaan van Stijn b2aa690b26 scripts/build/binary: remove pkcs11 build tag
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-06 15:24:49 +01:00
Sebastiaan van Stijn c1a53ae7b6 cmd/docker-trust: remove dependency on cli/internal
Create a copy of the registry package to use, so that code used only
for trust can be removed from the cli/internal package.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-06 15:24:49 +01:00
Sebastiaan van Stijn 06914dd0ff make trust-plugin a separate module
skip cmd/docker-trust in tests, as it's a separate module.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-06 15:24:48 +01:00
Sebastiaan van Stijn c9bb291154 implement docker trust as plugin
move the `trust` subcommands to a plugin, so that the subcommands can
be installed separate from the `docker trust` integration in push/pull
(for situations where trust verification happens on the daemon side).

    make binary
    go build -o /usr/libexec/docker/cli-plugins/docker-trust ./cmd/docker-trust

    docker info
    Client:
     Version:    28.2.0-dev
     Context:    default
     Debug Mode: false
     Plugins:
      buildx: Docker Buildx (Docker Inc.)
        Version:  v0.24.0
        Path:     /usr/libexec/docker/cli-plugins/docker-buildx
      trust: Manage trust on Docker images (Docker Inc.)
        Version:  unknown-version
        Path:     /usr/libexec/docker/cli-plugins/docker-trust

    docker trust --help
    Usage:  docker trust [OPTIONS] COMMAND

    Extended build capabilities with BuildKit

    Options:
      -D, --debug   Enable debug logging

    Management Commands:
      key         Manage keys for signing Docker images
      signer      Manage entities who can sign Docker images

    Commands:
      inspect     Return low-level information about keys and signatures
      revoke      Remove trust for an image
      sign        Sign an image

    Run 'docker trust COMMAND --help' for more information on a command.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-06 15:24:46 +01:00
Sebastiaan van Stijn face4a61be Merge pull request #6631 from thaJeztah/fix_static_builds
Fix static build + CGO
2025-11-06 15:24:18 +01:00
David Karlsson dd70b43bc1 chore: update broken link to restrucured docker desktop documentation
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2025-11-06 14:40:16 +01:00
Paweł Gronowski f2755b02d7 update to go1.25.4
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-06 14:24:03 +01:00
Tianon Gravi 880ef756b7 Fix static build + CGO
Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
2025-11-06 12:39:18 +01:00
Paweł Gronowski de00f53cb9 Merge pull request #6629 from thaJeztah/bump_x_deps
vendor: golang.org/x/* dependencies
2025-11-06 11:22:48 +01:00
Sebastiaan van Stijn 0976389e14 vendor: golang.org/x/net v0.46.0, golang.org/x/crypto v0.43.0
full diff: https://github.com/golang/net/compare/v0.39.0...v0.46.0
full diff: https://github.com/golang/crypto/compare/v0.39.0...v0.43.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 22:58:37 +01:00
Sebastiaan van Stijn c733cb0532 vendor: golang.org/x/time v0.14.0
full diff: https://github.com/golang/time/compare/v0.11.0...v0.14.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 22:47:59 +01:00
Sebastiaan van Stijn 1f77c6f1c8 vendor: golang.org/x/term v0.36.0
- term: remove duplicate flag and add comment on windows
- term: allow multi-line bracketed paste to not create single line
  with verbatim LFs (fixes "x/term: multi line bracketed paste fails"
  to issue line by line commands

full diff: https://github.com/golang/term/compare/v0.32.0...v0.36.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 22:47:50 +01:00
Sebastiaan van Stijn dcce972f47 vendor: golang.org/x/text v0.30.0
full diff: https://github.com/golang/text/compare/v0.26.0...v0.30.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 22:47:22 +01:00
Sebastiaan van Stijn b11d143cd1 vendor: golang.org/x/sync v0.17.0
full diff: https://github.com/golang/sync/compare/v0.16.0...v0.17.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 22:46:32 +01:00
Sebastiaan van Stijn 0c8ce84a62 vendor: golang.org/x/sys v0.37.0
full diff: https://github.com/golang/sys/compare/v0.33.0...v0.37.0

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 22:45:52 +01:00
Sebastiaan van Stijn 1c572a10de Merge pull request #6628 from thaJeztah/swarm_enums
cli/command/service: use enum-consts defined in API
2025-11-05 17:32:30 +01:00
Sebastiaan van Stijn d9f7e4b0c8 cli/command/service: use enum-consts defined in API
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 13:41:04 +01:00
Sebastiaan van Stijn 1686e45501 Merge pull request #6612 from jo-so/master
completion/zsh: Allow multiple volumes of 'volume rm'
2025-11-05 13:27:15 +01:00
Sebastiaan van Stijn 050aca80a5 Merge pull request #6627 from thaJeztah/local_parsegeneric_resource
cli/command/service: parse generic resources without protobufs
2025-11-05 12:45:12 +01:00
Paweł Gronowski 30b02813f1 Merge pull request #6625 from thaJeztah/bump_golangci_lint
Dockerfile: update golangci-lint to v2.6.1
2025-11-05 12:40:19 +01:00
Sebastiaan van Stijn 774f1d60a1 cli/command/service: parse generic resources without protobufs
This code was using swarmkit's genericresource package as intermediate;
add a local copy of that code that skips the protobufs as intermediate.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 12:26:02 +01:00
Sebastiaan van Stijn 437ed4c1e4 Dockerfile: update golangci-lint to v2.6.1
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 12:24:12 +01:00
Paweł Gronowski 9fc247e0e5 Merge pull request #6626 from thaJeztah/fix_perfsprint
fix perfsprint (concat-loop) linting
2025-11-05 12:23:06 +01:00
Paweł Gronowski 924ed097c5 Merge pull request #6623 from thaJeztah/bump_swarmkit
vendor: github.com/moby/swarmkit/v2 v2.1.1
2025-11-05 12:22:42 +01:00
Paweł Gronowski b52826a55e Merge pull request #6622 from thaJeztah/bump_platforms
vendor: github.com/containerd/platforms v1.0.0-rc.2
2025-11-05 12:22:30 +01:00
Paweł Gronowski 38d271a8e4 Merge pull request #6617 from thaJeztah/bump_jose
vendor: github.com/go-jose/go-jose/v4 v4.1.3
2025-11-05 12:22:08 +01:00
Paweł Gronowski e6d6ddf38d Merge pull request #6616 from thaJeztah/bump_runewidth
vendor: github.com/mattn/go-runewidth v0.0.19
2025-11-05 12:21:51 +01:00
Sebastiaan van Stijn c4a28d0d3c vendor: github.com/go-jose/go-jose/v4 v4.1.3
- remove Go 1.23 support
- removes dependency on golang.org/x/crypto
- reject JWS with an unprotected critical b64 header

full diff: https://github.com/go-jose/go-jose/compare/v4.1.2...v4.1.3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 11:55:19 +01:00
Sebastiaan van Stijn 5b68e72ad3 Merge pull request #6624 from thaJeztah/bump_go_minimum
update minimum go version to go1.24
2025-11-05 11:47:27 +01:00
Sebastiaan van Stijn f8d0365127 fix perfsprint (concat-loop) linting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 09:27:52 +01:00
Sebastiaan van Stijn 7b624841c4 update minimum go version to go1.24
Various dependencies, including "golang.org/x/.."  started to update
the minimum required version,so we should follow suit for the next
release.

Note that the `//go:build` directives not necesserily have to be
updated, but it's good to keep them in sync until we have a go.mod
to control this.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 08:24:06 +01:00
Sebastiaan van Stijn 82b47c8e57 vendor: github.com/moby/swarmkit/v2 v2.1.1
full diff: https://github.com/moby/swarmkit/compare/v2.1.0...v2.1.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 08:05:32 +01:00
Sebastiaan van Stijn 3a91788135 vendor: github.com/containerd/platforms v1.0.0-rc.2
- Add WS2025 to Windows matcher and code optimizations
- use windowsMatchComparer for OSVersion match order
  Windows OS version should match based on the full OSVersion. When
  sorting a manifest, the entries should be sorted using the `Less`
  function.

full diff: https://github.com/containerd/platforms/compare/v1.0.0-rc.1...v1.0.0-rc.2

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-05 07:52:02 +01:00
Sebastiaan van Stijn 0b3c028108 Merge pull request #6618 from thaJeztah/lint_mod
lint: don't disable modules
2025-11-05 07:31:43 +01:00
Sebastiaan van Stijn 61d88c9519 lint: don't disable modules
prevent the linter from traversing the docker-trust plugin module

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 14:44:11 +01:00
Sebastiaan van Stijn 9bce085b13 vendor: github.com/mattn/go-runewidth v0.0.19
full diff: https://github.com/mattn/go-runewidth/compare/v0.0.17...v0.0.19

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 14:28:14 +01:00
Austin Vazquez 7414d73fc1 Merge pull request #6615 from thaJeztah/fix_generics
cli/command/container: fix use of generics
2025-11-04 07:26:44 -06:00
Sebastiaan van Stijn c01696ffde Merge pull request #6603 from thaJeztah/remove_trust_integration
remove support for client-side docker content trust validation
2025-11-04 14:05:39 +01:00
Sebastiaan van Stijn 8d1525a011 Merge pull request #6614 from vvoland/list-tree-header-ansi
image/tree: Fix table header having escape codes when not tty
2025-11-04 14:02:28 +01:00
Sebastiaan van Stijn e0b1ab68fe cli/command/container: fix use of generics
This was introduced in dad1d367c8, which
did not add a `//go:build` constraint to enable the use of generics (`any`).

Which causes an error when used;

 could not import github.com/docker/cli/cli/command/container (-: # github.com/docker/cli/cli/command/container
 /Users/thajeztah/go/pkg/mod/github.com/docker/cli@v29.0.0-rc.2+incompatible/cli/command/container/stats.go:148:39: predeclared any requires go1.18 or later (-lang was set to go1.16; check go.mod)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-04 13:56:23 +01:00
Paweł Gronowski e5f46499b3 image/tree: Fix table header having escape codes when not tty
When stdout is redirected to a non-tty there should be no ANSI escape
codes emitted.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-04 13:55:30 +01:00
Paweł Gronowski d5d2ed5baa image/tree: Add test for checking ansi escape output
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-04 13:55:30 +01:00
Paweł Gronowski 1a261e3f50 image/tree: Use streams interface
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-11-04 13:55:30 +01:00
Jörg Sommer 4893a5d5e3 completion/zsh: Allow multiple volumes of 'volume rm'
It is possible to give more than one volume for `docker volume rm`.

Signed-off-by: Jörg Sommer <joerg@jo-so.de>
2025-11-04 09:36:42 +01:00
Sebastiaan van Stijn 30d597df10 Merge pull request #6605 from Benehiko/plugins/hidden
Plugin may set itself as hidden
2025-11-03 16:58:57 +01:00
Alano Terblanche 700875b666 Plugin may set itself as hidden
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
2025-11-03 16:36:46 +01:00
Sebastiaan van Stijn 7e32fdf1b3 Merge pull request #6611 from thaJeztah/skip_hidden_plugin_commands
cli: allManagementSubCommands: improve handling of plugin stubs
2025-11-03 15:34:01 +01:00
Sebastiaan van Stijn ad776d1e10 remove support for client-side docker content trust validation
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-03 14:21:19 +01:00
Sebastiaan van Stijn 259df25a96 cli: allManagementSubCommands: improve handling of plugin stubs
The allManagementSubCommands function is used to present plugin-commands
in the docker --help output; these commands are included in the "management
commands" section, but for plugins we don't know if they have sub-commands.

However, plugin stubs may be hidden (for placeholders that are not yet loaded),
or not be runnable, which was previously ignored.

This patch treats plugin-stubs the same as other commands, with the exception
of checking if they have subcommands (which is not yet known for plugin-stubs).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-11-03 13:57:01 +01:00
David Karlsson aa62a6a97a docs: update link to libnetwork protocol doc
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2025-11-03 13:32:30 +01:00
Paweł Gronowski b5bac44972 Merge pull request #6601 from thaJeztah/bump_modules2
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 / tests (alpine, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 25, local) (push) Has been cancelled
e2e / tests (alpine, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 27, local) (push) Has been cancelled
e2e / tests (alpine, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 28, local) (push) Has been cancelled
e2e / tests (alpine, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (alpine, 29-rc, local) (push) Has been cancelled
e2e / tests (debian, 25, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 25, local) (push) Has been cancelled
e2e / tests (debian, 27, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 27, local) (push) Has been cancelled
e2e / tests (debian, 28, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 28, local) (push) Has been cancelled
e2e / tests (debian, 29-rc, connhelper-ssh) (push) Has been cancelled
e2e / tests (debian, 29-rc, local) (push) Has been cancelled
test / ctn (push) Has been cancelled
test / host (macos-14) (push) Has been cancelled
test / host (macos-15) (push) Has been cancelled
test / host (macos-15-intel) (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/moby/moby/api v1.52.0-beta.4, client v0.1.0-beta.3
2025-10-31 19:36:12 +01:00
Sebastiaan van Stijn ef16d82301 vendor: github.com/moby/moby/api v1.52.0-beta.4, client v0.1.0-beta.3
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 19:30:20 +01:00
Sebastiaan van Stijn 73aade19c1 Merge pull request #6566 from vvoland/img-list
image/list: Show collapsed tree by default
2025-10-31 18:22:07 +01:00
Sebastiaan van Stijn 0f589b35c5 Merge pull request #6565 from thaJeztah/add_29
e2e: add docker v29-rc
2025-10-31 18:16:16 +01:00
Paweł Gronowski 6fa5900339 image/tree: Remove longest->shortest sort
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 18:10:19 +01:00
Paweł Gronowski 5836040ec9 Update golden files
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 18:10:19 +01:00
Paweł Gronowski f6feef8fe2 image/test: Fix go test args being used by CLI commands
By default cobra inherit the `os.Args` if there's no non-nil Args slice
set.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 18:10:19 +01:00
Paweł Gronowski 631f32ee9d images/list: Add print ambiguous warning for tree
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 18:10:19 +01:00
Paweł Gronowski c41815f17a image/list: Show collapsed tree by default
Use the new tree view by default and only fallback if format or old
view-related options are used.

The expanded view is shown when `--tree` is passed.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 18:10:19 +01:00
Sebastiaan van Stijn 5d599e9322 e2e: add docker v29-rc
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 17:56:37 +01:00
Paweł Gronowski cdaae144e9 Merge pull request #6600 from thaJeztah/remove_trust_e2e
e2e: remove DCT tests
2025-10-31 17:56:22 +01:00
Paweł Gronowski 7fb94dae5b Merge pull request #6599 from vvoland/update-docker
vendor: github.com/moby/moby master
2025-10-31 17:50:18 +01:00
Sebastiaan van Stijn 50598d21c9 skip TestBuildIidFileSquash
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 17:45:30 +01:00
Sebastiaan van Stijn cf9e1778d3 Dockerfile: remove notary
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 17:45:30 +01:00
Sebastiaan van Stijn c98d9647d3 e2e: remove DCT tests
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 17:45:29 +01:00
Sebastiaan van Stijn f8e871344f Merge pull request #6596 from vvoland/img-list-all-dangling
image/tree: Fix dangling filter condition
2025-10-31 17:42:33 +01:00
Sebastiaan van Stijn 173808f8b6 Merge pull request #6598 from AkihiroSuda/deprecate-cgroup1
docs: deprecated: deprecate cgroup v1
2025-10-31 17:40:42 +01:00
Paweł Gronowski 8444c911bd vendor: github.com/moby/moby master
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 17:38:52 +01:00
Paweł Gronowski 96bc39b36d Merge pull request #6597 from thaJeztah/use_pull_for_pull
cli/command/container: use ImagePull instead of ImageCreate
2025-10-31 17:29:13 +01:00
Sebastiaan van Stijn 918ec8c48a Merge pull request #6595 from vvoland/list-tree-sortby-name
image/tree: Sort image tree by name instead of creation date
2025-10-31 16:55:07 +01:00
Akihiro Suda 7f86de9319 docs: deprecated: deprecate cgroup v1
See
- moby/moby issue 51111
- moby/moby PR    51360

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2025-11-01 00:55:03 +09:00
Sebastiaan van Stijn 7bdb4df07d cli/command/container: use ImagePull instead of ImageCreate
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 15:50:18 +01:00
Paweł Gronowski 9e7937746c image/tree: Fix dangling filter condition
The logic for applying the dangling filter when `--all` is not used was
inverted. The filter was being applied when the dangling filter was
present, but it should be applied when the dangling filter is NOT
present.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 14:58:02 +01:00
Paweł Gronowski 8e2943c6c5 image/tree: Sort image tree by name instead of creation date
Sort images alphabetically by their repository tags rather than by
creation date.

When an image has multiple tags, they are sorted internally and the
first tag is used as the representative for sorting the image in the
list. Untagged images are placed at the end.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-31 11:30:59 +01:00
Austin Vazquez 513ee76bac Merge pull request #6593 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api master, moby/client master
2025-10-30 22:45:41 -05:00
Sebastiaan van Stijn 8767904ae8 vendor: github.com/moby/moby/api master, moby/client master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 03:25:19 +01:00
Sebastiaan van Stijn b8b4f54a89 fix typo in TODO comment
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-31 03:15:47 +01:00
Sebastiaan van Stijn 03fb1df2dd Merge pull request #6577 from docker/dependabot/github_actions/actions/upload-artifact-5
build(deps): bump actions/upload-artifact from 4 to 5
2025-10-31 03:02:22 +01:00
Sebastiaan van Stijn 58e3de8781 Merge pull request #6594 from thaJeztah/dont_push_me
cli/trust: use local definition for PushResult Aux message
2025-10-31 02:13:01 +01:00
Sebastiaan van Stijn 65496c5557 cli/trust: use local definition for PushResult Aux message
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-30 20:22:25 +01:00
Sebastiaan van Stijn 3434869388 Merge pull request #6591 from vvoland/update-docker
vendor: github.com/moby/moby master
2025-10-30 00:32:57 +01:00
Sebastiaan van Stijn 9dde80abd8 Merge pull request #6574 from vvoland/img-dangling
image/list: Hide untagged images without `--all`
2025-10-30 00:24:29 +01:00
Paweł Gronowski e636a2a069 cli/container_rename: Move to API validation
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-30 00:20:47 +01:00
Paweł Gronowski af255accaa vendor: github.com/moby/moby master
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-30 00:02:41 +01:00
Paweł Gronowski fcac1d5b2a Merge pull request #6586 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api, moby/moby/client master
2025-10-29 23:53:04 +01:00
Sebastiaan van Stijn 053aa376ea vendor: github.com/moby/moby/api, moby/moby/client master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-29 23:17:29 +01:00
Sebastiaan van Stijn 606f1c65d0 Merge pull request #6584 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api, moby/moby/client master
2025-10-28 19:26:27 +01:00
Sebastiaan van Stijn 83319f09f7 cli/command/container: use per-stats OSType if present
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 16:27:00 +01:00
Sebastiaan van Stijn 6ed16a2cc1 vendor: github.com/moby/moby/api, moby/moby/client master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 16:26:53 +01:00
Sebastiaan van Stijn 10281ff5a5 Merge pull request #6583 from thaJeztah/stats_cleanups
cli/command/container: cleanup stats code
2025-10-28 15:17:20 +01:00
Sebastiaan van Stijn 5007c96b0d cli/command/container: collect(): split windows/unix branches
Use separate branches to handle windows/unix results to reduce intermediate
variables, and make it more transparent what's set for each platform.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 15:08:05 +01:00
Sebastiaan van Stijn c467ebafd8 cli/command/container: calculateCPUPercentWindows minor cleanup
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 15:08:05 +01:00
Sebastiaan van Stijn 689152a804 cli/command/container: calculateCPUPercentUnix: simplify
Pass the whole CPUStats struct instead of deconstructing it to separate
variables.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 15:07:58 +01:00
Sebastiaan van Stijn e809e65ba6 Merge pull request #6582 from thaJeztah/fix_stats_cancellation
cli/command/container: RunStats: simplify, and fix context-cancellation
2025-10-28 14:12:16 +01:00
Sebastiaan van Stijn e01ce69ff9 cli/command/container: collect: handle context-cancellation
construct the decoder inside the go-routine, including closing the body,
and add handling for context-cancellation.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 13:29:43 +01:00
Sebastiaan van Stijn 292001a451 cli/command/container: RunStats: early return for non-streaming
We should consider splitting this out to a separate function, but
start with just an early return before we hit the timer-loop.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 12:36:14 +01:00
Sebastiaan van Stijn 0b1c7bc0f1 cli/command/container: RunStats: small tweaks on closeChan
Some suggestions from ChatGPT to prevent deadlocks.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 12:36:10 +01:00
Sebastiaan van Stijn d309027d58 cli/command/container: RunStats: gracefully handle io.EOF
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 12:08:55 +01:00
Sebastiaan van Stijn 15b422b317 cli/command/container: RunStats: handle context-cancellation
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 12:08:54 +01:00
Sebastiaan van Stijn 832fc66ca7 cli/command/container: RunStats: simplify stats loop
Use a single select for the ticker and the closeChan; use early returns.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 12:08:54 +01:00
Sebastiaan van Stijn dad1d367c8 cli/command/container: move debug logs to call-site
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-28 12:08:54 +01:00
Austin Vazquez e50c94f21a Merge pull request #6581 from thaJeztah/stats_ostype
cli/command/container: don't depend on result.OSType
2025-10-27 21:43:53 -05:00
Austin Vazquez 93cd6793b3 Merge pull request #6580 from thaJeztah/no_json
cli/command/image: remove uses of JSON field
2025-10-27 21:42:29 -05:00
Sebastiaan van Stijn f594a7f09b cli/command/image: remove uses of JSON field
The JSON field was added in [moby@9fd2c0f], to address [moby#19177], which
reported an incompatibility with Classic (V1) Swarm, which produced a non-
standard response;

> Make docker load to output json when the response content type is json
> Swarm hijacks the response from docker load and returns JSON rather
> than plain text like the Engine does. This makes the API library to return
> information to figure that out.

A later change in [moby@96d7db6] added additional logic to make sure the
correct content-type was returned, depending on whether the `quiet` option
was set (which produced a non-JSON response). This caused inconsistency in
the API response, and [moby@2f27632] changed the endpoint to always produce
JSON (only skipping the "progress" output if `quiet` was set).

This means that the "load" endpoint ([`imageRouter.postImagesLoad`]) now
unconditionally returns JSON, making the `JSON` field fully redundant.

This patch removes the use of the JSON field, as it's redundant, and the way it handles
the content-type is incorrect because it would not handle correct, but different
formatted response-headers (`application/json; charset=utf-8`), which could
result in malformed output on the client.

[moby@9fd2c0f]: https://github.com/moby/moby/commit/9fd2c0feb0c131d01d727d50baa7183b976c7bdc
[moby#19177]: https://github.com/moby/moby/issues/19177
[moby@96d7db6]: https://github.com/moby/moby/commit/96d7db665b06cc0bbede22d818c69dc5f6921f66
[moby@2f27632]: https://github.com/moby/moby/commit/2f27632cde2f0e514bd3a8de77cc1934e5193a83
[`imageRouter.postImagesLoad`]: https://github.com/moby/moby/blob/7b9d2ef6e5518a3d3f3cc418459f8df786cfbbd1/api/server/router/image/image_routes.go#L248-L255

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-27 21:37:36 +01:00
Sebastiaan van Stijn 4b498addce cli/command/container: don't depend on result.OSType
This field is set for the request as a whole, so can be obtained
from the server-info instead. Docker v29 will provide per-stats
information.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-27 21:35:25 +01:00
Sebastiaan van Stijn 715467c9d8 Merge pull request #6569 from jsternberg/otel-sdk-lock-version
telemetry: lock the semconv version of the otel sdk
2025-10-27 21:17:09 +01:00
Sebastiaan van Stijn dafbfc2f7e Merge pull request #6576 from robmry/bump_modules_again
vendor: github.com/moby/moby/api, moby/moby/client master
2025-10-27 20:35:49 +01:00
Rob Murray 4a608069a7 vendor: github.com/moby/moby/api, moby/moby/client master
Signed-off-by: Rob Murray <rob.murray@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-27 20:26:27 +01:00
Jonathan A. Sternberg 153f7f10c9 telemetry: lock the semconv version of the otel sdk
This change prevents changes to the otel version from affecting the otel
sdk version. This is done by copying the telemetry sdk implementation
locally and using our own choice for semconv from within that.

This prevents a schema conflict from happening since the otel version of
the sdk gets implicitly updated whenever the semconv changes while we
have to manually change ours. Now, we manually change both and they're
locked to each other.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-27 11:44:59 +01:00
dependabot[bot] aef2ef8c77 build(deps): bump actions/upload-artifact from 4 to 5
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 5.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 08:30:59 +00:00
Sebastiaan van Stijn 965a0e3518 Merge pull request #6575 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api, moby/moby/client master
2025-10-26 17:40:19 +01:00
Sebastiaan van Stijn 4afbd6146b implement some ad-hoc mocks for responses
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-25 01:36:38 +02:00
Sebastiaan van Stijn 056e314645 vendor: github.com/moby/moby/api, moby/moby/client master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-25 00:48:49 +02:00
Paweł Gronowski 64805c2959 image/list: Respect dangling filter when not using --all
Otherwise `docker images --filter dangling=true` won't work without
`--all`.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-24 20:46:41 +02:00
Paweł Gronowski e9a941001c image/list: Hide untagged images without --all
The `--tree` implementation already does this.

Make the behavior consistent for the legacy image list implementation.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-24 20:46:41 +02:00
Sebastiaan van Stijn f74cd147bb Merge pull request #6571 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api, moby/moby/client master
2025-10-24 16:43:53 +02:00
Sebastiaan van Stijn 4f7c07cfc2 update local code for updated modules
Some tests had to be skipped as there's some issues to address, and
some of the result-types cannot be mocked / stubbed.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-24 10:28:54 +02:00
Sebastiaan van Stijn aeb78091a0 vendor: github.com/moby/moby/api, moby/moby/client master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-24 10:28:26 +02:00
Sebastiaan van Stijn 7b4cde6967 Merge pull request #6567 from thaJeztah/bump_modules
vendor: github.com/moby/moby/api, moby/moby/client master
2025-10-20 16:52:25 +02:00
Sebastiaan van Stijn 563f5fe335 vendor: github.com/moby/moby/api, moby/moby/client master
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2025-10-20 12:30:05 +02:00
Sebastiaan van Stijn 171a9b70b2 Merge pull request #6564 from vvoland/container-go123
cli/command/container: add go1.23 build constraint for range-over-func
2025-10-14 15:00:57 +02:00
Paweł Gronowski 5ba4c17d78 cli/command/container: Simplify with slices.Contains
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-14 14:47:59 +02:00
Paweł Gronowski d252afa6b0 cli/command/container: add go1.23 build constraint for range-over-func
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-10-14 14:43:54 +02:00
1181 changed files with 38458 additions and 57173 deletions
+3 -3
View File
@@ -35,7 +35,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
-
name: Create matrix
id: platforms
@@ -88,7 +88,7 @@ jobs:
fi
-
name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ${{ env.ARTIFACT_NAME }}
path: /tmp/out/*
@@ -143,7 +143,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
-
name: Create matrix
id: platforms
+2 -2
View File
@@ -46,7 +46,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 2
# CodeQL 2.16.4's auto-build added support for multi-module repositories,
@@ -63,7 +63,7 @@ jobs:
name: Update Go
uses: actions/setup-go@v6
with:
go-version: "1.25.3"
go-version: "1.25.5"
-
name: Initialize CodeQL
uses: github/codeql-action/init@v4
+4 -3
View File
@@ -37,13 +37,14 @@ jobs:
- alpine
- debian
engine-version:
- 28 # latest
- 27 # latest - 1
- rc # latest rc
- 29 # latest
- 28 # latest - 1
- 25 # mirantis lts
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
-
name: Update daemon.json
run: |
+3 -3
View File
@@ -60,18 +60,18 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
path: ${{ env.GOPATH }}/src/github.com/docker/cli
-
name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.25.3"
go-version: "1.25.5"
-
name: Test
run: |
go test -coverprofile=/tmp/coverage.txt $(go list ./... | grep -vE '/vendor/|/e2e/')
go test -coverprofile=/tmp/coverage.txt $(go list ./... | grep -vE '/vendor/|/e2e/|/cmd/docker-trust')
go tool cover -func=/tmp/coverage.txt
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
shell: bash
+2 -2
View File
@@ -48,7 +48,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
-
name: Generate
shell: 'script --return --quiet --command "bash {0}"'
@@ -74,7 +74,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
-
name: Run
shell: 'script --return --quiet --command "bash {0}"'
+1 -1
View File
@@ -5,7 +5,7 @@ run:
# which causes it to fallback to go1.17 semantics.
#
# TODO(thaJeztah): update "usetesting" settings to enable go1.24 features once our minimum version is go1.24
go: "1.25.3"
go: "1.25.5"
timeout: 5m
+2 -6
View File
@@ -8,7 +8,7 @@ ARG BASE_VARIANT=alpine
ARG ALPINE_VERSION=3.22
ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.25.3
ARG GO_VERSION=1.25.5
# XX_VERSION specifies the version of the xx utility to use.
# It must be a valid tag in the docker.io/tonistiigi/xx image repository.
@@ -97,7 +97,7 @@ ENV GO111MODULE=auto
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
gotestsum -- -coverprofile=/tmp/coverage.txt $(go list ./... | grep -vE '/vendor/|/e2e/')
gotestsum -- -coverprofile=/tmp/coverage.txt $(go list ./... | grep -vE '/vendor/|/e2e/|/cmd/docker-trust')
FROM scratch AS test-coverage
COPY --from=test /tmp/coverage.txt /coverage.txt
@@ -122,10 +122,6 @@ FROM docker/buildx-bin:${BUILDX_VERSION} AS buildx
FROM docker/compose-bin:${COMPOSE_VERSION} AS compose
FROM e2e-base-${BASE_VARIANT} AS e2e
ARG NOTARY_VERSION=v0.6.1
ADD --chmod=0755 https://github.com/theupdateframework/notary/releases/download/${NOTARY_VERSION}/notary-Linux-amd64 /usr/local/bin/notary
COPY --link e2e/testdata/notary/root-ca.cert /usr/share/ca-certificates/notary.cert
RUN echo 'notary.cert' >> /etc/ca-certificates.conf && update-ca-certificates
COPY --link --from=gotestsum /out/gotestsum /usr/bin/gotestsum
COPY --link --from=build /out ./build/
COPY --link --from=build-plugins /out ./build/
+12 -3
View File
@@ -34,12 +34,12 @@ test: test-unit ## run tests
.PHONY: test-unit
test-unit: ## run unit tests, to change the output format use: GOTESTSUM_FORMAT=(dots|short|standard-quiet|short-verbose|standard-verbose) make test-unit
gotestsum -- $${TESTDIRS:-$(shell go list ./... | grep -vE '/vendor/|/e2e/')} $(TESTFLAGS)
gotestsum -- $${TESTDIRS:-$(shell go list ./... | grep -vE '/vendor/|/e2e/|/cmd/docker-trust')} $(TESTFLAGS)
.PHONY: test-coverage
test-coverage: ## run test coverage
mkdir -p $(CURDIR)/build/coverage
gotestsum -- $(shell go list ./... | grep -vE '/vendor/|/e2e/') -coverprofile=$(CURDIR)/build/coverage/coverage.txt
gotestsum -- $(shell go list ./... | grep -vE '/vendor/|/e2e/|/cmd/docker-trust') -coverprofile=$(CURDIR)/build/coverage/coverage.txt
.PHONY: lint
lint: ## run all the lint tools
@@ -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.23 . ; \
gofumpt -w -d -lang=1.24 . ; \
else \
go list -f {{.Dir}} ./... | xargs gofmt -w -s -d ; \
fi
@@ -69,6 +69,15 @@ dynbinary: ## build dynamically linked binary
plugins: ## build example CLI plugins
scripts/build/plugins
.PHONY: trust-plugin
trust-plugin: ## build docker-trust CLI plugins
scripts/build/trust-plugin
.PHONY: install-trust-plugin
install-trust-plugin: trust-plugin
install-trust-plugin: ## install docker-trust CLI plugins
install -D -m 0755 "$$(readlink -f build/docker-trust)" /usr/libexec/docker/cli-plugins/docker-trust
.PHONY: vendor
vendor: ## update vendor with go modules
rm -rf vendor
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"github.com/docker/cli/cli-plugins/metadata"
"github.com/docker/cli/cli-plugins/plugin"
"github.com/docker/cli/cli/command"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -25,7 +26,7 @@ func main() {
Short: "Print the API version of the server",
RunE: func(_ *cobra.Command, _ []string) error {
apiClient := dockerCLI.Client()
ping, err := apiClient.Ping(context.Background())
ping, err := apiClient.Ping(context.Background(), client.PingOptions{})
if err != nil {
return err
}
+1
View File
@@ -38,6 +38,7 @@ func AddPluginCommandStubs(dockerCLI config.Provider, rootCmd *cobra.Command) (e
rootCmd.AddCommand(&cobra.Command{
Use: p.Name,
Short: p.ShortDescription,
Hidden: p.Hidden,
Run: func(_ *cobra.Command, _ []string) {},
Annotations: annotations,
DisableFlagParsing: true,
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package manager
+2
View File
@@ -33,4 +33,6 @@ type Metadata struct {
ShortDescription string `json:",omitempty"`
// URL is a pointer to the plugin's homepage.
URL string `json:",omitempty"`
// Hidden hides the plugin in completion and help message output.
Hidden bool `json:",omitempty"`
}
+1 -1
View File
@@ -139,7 +139,7 @@ func withPluginClientConn(name string) command.CLIOption {
if err != nil {
return err
}
apiClient, err := client.NewClientWithOpts(client.WithDialContext(helper.Dialer))
apiClient, err := client.New(client.WithDialContext(helper.Dialer))
if err != nil {
return err
}
+6 -8
View File
@@ -250,11 +250,12 @@ func commandAliases(cmd *cobra.Command) string {
if cmd.HasParent() {
parentPath = cmd.Parent().CommandPath() + " "
}
aliases := cmd.CommandPath()
var aliases strings.Builder
aliases.WriteString(cmd.CommandPath())
for _, alias := range cmd.Aliases {
aliases += ", " + parentPath + alias
aliases.WriteString(", " + parentPath + alias)
}
return aliases
return aliases.String()
}
func topCommands(cmd *cobra.Command) []*cobra.Command {
@@ -350,13 +351,10 @@ func orchestratorSubCommands(cmd *cobra.Command) []*cobra.Command {
func allManagementSubCommands(cmd *cobra.Command) []*cobra.Command {
cmds := []*cobra.Command{}
for _, sub := range cmd.Commands() {
if isPlugin(sub) {
if invalidPluginReason(sub) == "" {
cmds = append(cmds, sub)
}
if invalidPluginReason(sub) != "" {
continue
}
if sub.IsAvailableCommand() && sub.HasSubCommands() {
if sub.IsAvailableCommand() && (isPlugin(sub) || sub.HasSubCommands()) {
cmds = append(cmds, sub)
}
}
+27
View File
@@ -56,6 +56,33 @@ func TestInvalidPlugin(t *testing.T) {
assert.DeepEqual(t, invalidPlugins(root), []*cobra.Command{sub1}, cmpopts.IgnoreUnexported(cobra.Command{}))
}
func TestHiddenPlugin(t *testing.T) {
root := &cobra.Command{Use: "root"}
sub1 := &cobra.Command{
Use: "sub1",
Hidden: true,
Annotations: map[string]string{
metadata.CommandAnnotationPlugin: "true",
},
Run: func(cmd *cobra.Command, args []string) {},
}
sub1sub1 := &cobra.Command{Use: "sub1sub1"}
sub1sub2 := &cobra.Command{Use: "sub1sub2"}
sub2 := &cobra.Command{
Use: "sub2",
Annotations: map[string]string{
metadata.CommandAnnotationPlugin: "true",
},
Run: func(cmd *cobra.Command, args []string) {},
}
root.AddCommand(sub1, sub2)
sub1.AddCommand(sub1sub1, sub1sub2)
assert.DeepEqual(t, allManagementSubCommands(root), []*cobra.Command{sub2}, cmpopts.IgnoreFields(cobra.Command{}, "Run"), cmpopts.IgnoreUnexported(cobra.Command{}))
}
func TestCommandAliases(t *testing.T) {
root := &cobra.Command{Use: "root"}
sub := &cobra.Command{Use: "subcommand", Aliases: []string{"alias1", "alias2"}}
+6 -6
View File
@@ -8,23 +8,23 @@ import (
type fakeClient struct {
client.Client
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) error
checkpointDeleteFunc func(container string, options client.CheckpointDeleteOptions) error
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error)
checkpointDeleteFunc func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error)
checkpointListFunc func(container string, options client.CheckpointListOptions) (client.CheckpointListResult, error)
}
func (cli *fakeClient) CheckpointCreate(_ context.Context, container string, options client.CheckpointCreateOptions) error {
func (cli *fakeClient) CheckpointCreate(_ context.Context, container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error) {
if cli.checkpointCreateFunc != nil {
return cli.checkpointCreateFunc(container, options)
}
return nil
return client.CheckpointCreateResult{}, nil
}
func (cli *fakeClient) CheckpointDelete(_ context.Context, container string, options client.CheckpointDeleteOptions) error {
func (cli *fakeClient) CheckpointRemove(_ context.Context, container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error) {
if cli.checkpointDeleteFunc != nil {
return cli.checkpointDeleteFunc(container, options)
}
return nil
return client.CheckpointRemoveResult{}, nil
}
func (cli *fakeClient) CheckpointList(_ context.Context, container string, options client.CheckpointListOptions) (client.CheckpointListResult, error) {
+1 -1
View File
@@ -41,7 +41,7 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
}
func runCreate(ctx context.Context, dockerCLI command.Cli, opts createOptions) error {
err := dockerCLI.Client().CheckpointCreate(ctx, opts.container, client.CheckpointCreateOptions{
_, err := dockerCLI.Client().CheckpointCreate(ctx, opts.container, client.CheckpointCreateOptions{
CheckpointID: opts.checkpoint,
CheckpointDir: opts.checkpointDir,
Exit: !opts.leaveRunning,
+5 -5
View File
@@ -16,7 +16,7 @@ import (
func TestCheckpointCreateErrors(t *testing.T) {
testCases := []struct {
args []string
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) error
checkpointCreateFunc func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error)
expectedError string
}{
{
@@ -29,8 +29,8 @@ func TestCheckpointCreateErrors(t *testing.T) {
},
{
args: []string{"foo", "bar"},
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) error {
return errors.New("error creating checkpoint for container foo")
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error) {
return client.CheckpointCreateResult{}, errors.New("error creating checkpoint for container foo")
},
expectedError: "error creating checkpoint for container foo",
},
@@ -61,10 +61,10 @@ func TestCheckpointCreateWithOptions(t *testing.T) {
var actualContainerName string
var actualOptions client.CheckpointCreateOptions
cli := test.NewFakeCli(&fakeClient{
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) error {
checkpointCreateFunc: func(container string, options client.CheckpointCreateOptions) (client.CheckpointCreateResult, error) {
actualContainerName = container
actualOptions = options
return nil
return client.CheckpointCreateResult{}, nil
},
})
cmd := newCreateCommand(cli)
+1 -1
View File
@@ -48,5 +48,5 @@ func runList(ctx context.Context, dockerCLI command.Cli, container string, opts
Output: dockerCLI.Out(),
Format: newFormat(formatter.TableFormatKey),
}
return formatWrite(cpCtx, checkpoints.Checkpoints)
return formatWrite(cpCtx, checkpoints.Items)
}
+2 -2
View File
@@ -55,7 +55,7 @@ func TestCheckpointListWithOptions(t *testing.T) {
containerID = container
checkpointDir = options.CheckpointDir
return client.CheckpointListResult{
Checkpoints: []checkpoint.Summary{
Items: []checkpoint.Summary{
{Name: "checkpoint-foo"},
},
}, nil
@@ -63,7 +63,7 @@ func TestCheckpointListWithOptions(t *testing.T) {
})
cmd := newListCommand(cli)
cmd.SetArgs([]string{"container-foo"})
cmd.Flags().Set("checkpoint-dir", "/dir/foo")
assert.Check(t, cmd.Flags().Set("checkpoint-dir", "/dir/foo"))
assert.NilError(t, cmd.Execute())
assert.Check(t, is.Equal("container-foo", containerID))
assert.Check(t, is.Equal("/dir/foo", checkpointDir))
+6 -10
View File
@@ -1,8 +1,6 @@
package checkpoint
import (
"context"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/moby/client"
@@ -22,7 +20,12 @@ func newRemoveCommand(dockerCLI command.Cli) *cobra.Command {
Short: "Remove a checkpoint",
Args: cli.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
return runRemove(cmd.Context(), dockerCLI, args[0], args[1], opts)
containerID, checkpointID := args[0], args[1]
_, err := dockerCLI.Client().CheckpointRemove(cmd.Context(), containerID, client.CheckpointRemoveOptions{
CheckpointID: checkpointID,
CheckpointDir: opts.checkpointDir,
})
return err
},
DisableFlagsInUseLine: true,
}
@@ -32,10 +35,3 @@ func newRemoveCommand(dockerCLI command.Cli) *cobra.Command {
return cmd
}
func runRemove(ctx context.Context, dockerCli command.Cli, container string, checkpointID string, opts removeOptions) error {
return dockerCli.Client().CheckpointDelete(ctx, container, client.CheckpointDeleteOptions{
CheckpointID: checkpointID,
CheckpointDir: opts.checkpointDir,
})
}
+6 -6
View File
@@ -14,7 +14,7 @@ import (
func TestCheckpointRemoveErrors(t *testing.T) {
testCases := []struct {
args []string
checkpointDeleteFunc func(container string, options client.CheckpointDeleteOptions) error
checkpointDeleteFunc func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error)
expectedError string
}{
{
@@ -27,8 +27,8 @@ func TestCheckpointRemoveErrors(t *testing.T) {
},
{
args: []string{"foo", "bar"},
checkpointDeleteFunc: func(container string, options client.CheckpointDeleteOptions) error {
return errors.New("error deleting checkpoint")
checkpointDeleteFunc: func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error) {
return client.CheckpointRemoveResult{}, errors.New("error deleting checkpoint")
},
expectedError: "error deleting checkpoint",
},
@@ -49,16 +49,16 @@ func TestCheckpointRemoveErrors(t *testing.T) {
func TestCheckpointRemoveWithOptions(t *testing.T) {
var containerID, checkpointID, checkpointDir string
cli := test.NewFakeCli(&fakeClient{
checkpointDeleteFunc: func(container string, options client.CheckpointDeleteOptions) error {
checkpointDeleteFunc: func(container string, options client.CheckpointRemoveOptions) (client.CheckpointRemoveResult, error) {
containerID = container
checkpointID = options.CheckpointID
checkpointDir = options.CheckpointDir
return nil
return client.CheckpointRemoveResult{}, nil
},
})
cmd := newRemoveCommand(cli)
cmd.SetArgs([]string{"container-foo", "checkpoint-bar"})
cmd.Flags().Set("checkpoint-dir", "/dir/foo")
assert.Check(t, cmd.Flags().Set("checkpoint-dir", "/dir/foo"))
assert.NilError(t, cmd.Execute())
assert.Check(t, is.Equal("container-foo", containerID))
assert.Check(t, is.Equal("checkpoint-bar", checkpointID))
+7 -11
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package command
@@ -25,7 +25,6 @@ import (
"github.com/docker/cli/cli/version"
dopts "github.com/docker/cli/opts"
"github.com/moby/moby/api/types/build"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -317,7 +316,7 @@ func newAPIClientFromEndpoint(ep docker.Endpoint, configFile *configfile.ConfigF
opts = append(opts, withCustomHeaders)
}
opts = append(opts, extraOpts...)
return client.NewClientWithOpts(opts...)
return client.New(opts...)
}
func resolveDockerEndpoint(s store.Reader, contextName string) (docker.Endpoint, error) {
@@ -378,24 +377,21 @@ func (cli *DockerCli) initializeFromClient() {
ctx, cancel := context.WithTimeout(cli.baseCtx, cli.getInitTimeout())
defer cancel()
ping, err := cli.client.Ping(ctx)
ping, err := cli.client.Ping(ctx, client.PingOptions{
NegotiateAPIVersion: true,
ForceNegotiate: true,
})
if err != nil {
// Default to true if we fail to connect to daemon
cli.serverInfo = ServerInfo{HasExperimental: true}
if ping.APIVersion != "" {
cli.client.NegotiateAPIVersionPing(ping)
}
return
}
cli.serverInfo = ServerInfo{
HasExperimental: ping.Experimental,
OSType: ping.OSType,
BuildkitVersion: ping.BuilderVersion,
SwarmStatus: ping.SwarmStatus,
}
cli.client.NegotiateAPIVersionPing(ping)
}
// ContextStore returns the ContextStore
@@ -564,7 +560,7 @@ type ServerInfo struct {
// in the ping response, or if an error occurred, in which case the client
// should use other ways to get the current swarm status, such as the /swarm
// endpoint.
SwarmStatus *swarm.Status
SwarmStatus *client.SwarmStatus
}
// NewDockerCli returns a DockerCli instance with all operators applied on it.
+25 -22
View File
@@ -20,7 +20,6 @@ import (
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/context/store"
"github.com/docker/cli/cli/flags"
"github.com/moby/moby/api/types"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
)
@@ -80,7 +79,7 @@ func TestNewAPIClientFromFlagsWithCustomHeaders(t *testing.T) {
"My-Header": "Custom-Value",
"User-Agent": UserAgent(),
}
_, err = apiClient.Ping(context.Background())
_, err = apiClient.Ping(context.TODO(), client.PingOptions{})
assert.NilError(t, err)
assert.DeepEqual(t, received, expectedHeaders)
}
@@ -115,14 +114,14 @@ func TestNewAPIClientFromFlagsWithCustomHeadersFromEnv(t *testing.T) {
"Four": []string{"four-value-override"},
"User-Agent": []string{UserAgent()},
}
_, err = apiClient.Ping(context.Background())
_, err = apiClient.Ping(context.TODO(), client.PingOptions{})
assert.NilError(t, err)
assert.DeepEqual(t, received, expectedHeaders)
}
func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) {
const customVersion = "v3.3.3"
const expectedVersion = "3.3.3"
const customVersion = "v3.3"
const expectedVersion = "3.3"
t.Setenv("DOCKER_API_VERSION", customVersion)
t.Setenv("DOCKER_HOST", ":2375")
@@ -135,51 +134,55 @@ func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) {
type fakeClient struct {
client.Client
pingFunc func() (types.Ping, error)
pingFunc func() (client.PingResult, error)
version string
negotiated bool
}
func (c *fakeClient) Ping(_ context.Context) (types.Ping, error) {
return c.pingFunc()
func (c *fakeClient) Ping(_ context.Context, options client.PingOptions) (client.PingResult, error) {
res, err := c.pingFunc()
if options.NegotiateAPIVersion {
if res.APIVersion != "" {
if c.negotiated || options.ForceNegotiate {
c.negotiated = true
}
}
}
return res, err
}
func (c *fakeClient) ClientVersion() string {
return c.version
}
func (c *fakeClient) NegotiateAPIVersionPing(types.Ping) {
c.negotiated = true
}
func TestInitializeFromClient(t *testing.T) {
const defaultVersion = "v1.55"
testcases := []struct {
doc string
pingFunc func() (types.Ping, error)
pingFunc func() (client.PingResult, error)
expectedServer ServerInfo
negotiated bool
}{
{
doc: "successful ping",
pingFunc: func() (types.Ping, error) {
return types.Ping{Experimental: true, OSType: "linux", APIVersion: "v1.44"}, nil
pingFunc: func() (client.PingResult, error) {
return client.PingResult{Experimental: true, OSType: "linux", APIVersion: "v1.44"}, nil
},
expectedServer: ServerInfo{HasExperimental: true, OSType: "linux"},
negotiated: true,
},
{
doc: "failed ping, no API version",
pingFunc: func() (types.Ping, error) {
return types.Ping{}, errors.New("failed")
pingFunc: func() (client.PingResult, error) {
return client.PingResult{}, errors.New("failed")
},
expectedServer: ServerInfo{HasExperimental: true},
},
{
doc: "failed ping, with API version",
pingFunc: func() (types.Ping, error) {
return types.Ping{APIVersion: "v1.44"}, errors.New("failed")
pingFunc: func() (client.PingResult, error) {
return client.PingResult{APIVersion: "v1.44"}, errors.New("failed")
},
expectedServer: ServerInfo{HasExperimental: true},
negotiated: true,
@@ -211,7 +214,7 @@ func TestInitializeFromClientHangs(t *testing.T) {
assert.NilError(t, err)
receiveReqCh := make(chan bool)
timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second)
timeoutCtx, cancel := context.WithTimeout(context.TODO(), time.Second)
defer cancel()
// Simulate a server that hangs on connections.
@@ -289,7 +292,7 @@ func TestNewDockerCliAndOperators(t *testing.T) {
func TestInitializeShouldAlwaysCreateTheContextStore(t *testing.T) {
cli, err := NewDockerCli()
assert.NilError(t, err)
apiClient, err := client.NewClientWithOpts()
apiClient, err := client.New()
assert.NilError(t, err)
assert.NilError(t, cli.Initialize(flags.NewClientOptions(), WithAPIClient(apiClient)))
assert.Check(t, cli.ContextStore() != nil)
@@ -385,7 +388,7 @@ func TestNewDockerCliWithCustomUserAgent(t *testing.T) {
cli.options = opts
cli.configFile = &configfile.ConfigFile{}
_, err = cli.Client().Ping(context.Background())
_, err = cli.Client().Ping(context.TODO(), client.PingOptions{})
assert.NilError(t, err)
assert.DeepEqual(t, received, "fake-agent/0.0.1")
}
-1
View File
@@ -18,7 +18,6 @@ import (
_ "github.com/docker/cli/cli/command/stack"
_ "github.com/docker/cli/cli/command/swarm"
_ "github.com/docker/cli/cli/command/system"
_ "github.com/docker/cli/cli/command/trust"
_ "github.com/docker/cli/cli/command/volume"
"github.com/docker/cli/internal/commands"
"github.com/spf13/cobra"
+10 -10
View File
@@ -27,12 +27,12 @@ func ImageNames(dockerCLI APIClientProvider, limit int) cobra.CompletionFunc {
if limit > 0 && len(args) >= limit {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, err := dockerCLI.Client().ImageList(cmd.Context(), client.ImageListOptions{})
res, err := dockerCLI.Client().ImageList(cmd.Context(), client.ImageListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
for _, img := range list {
for _, img := range res.Items {
names = append(names, img.RepoTags...)
}
return names, cobra.ShellCompDirectiveNoFileComp
@@ -47,13 +47,13 @@ func ImageNamesWithBase(dockerCLI APIClientProvider, limit int) cobra.Completion
if limit > 0 && len(args) >= limit {
return nil, cobra.ShellCompDirectiveNoFileComp
}
list, err := dockerCLI.Client().ImageList(cmd.Context(), client.ImageListOptions{})
res, err := dockerCLI.Client().ImageList(cmd.Context(), client.ImageListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
baseNameCounts := make(map[string]int)
for _, img := range list {
for _, img := range res.Items {
names = append(names, img.RepoTags...)
for _, tag := range img.RepoTags {
ref, err := reference.ParseNormalizedNamed(tag)
@@ -77,7 +77,7 @@ func ImageNamesWithBase(dockerCLI APIClientProvider, limit int) cobra.Completion
// Set DOCKER_COMPLETION_SHOW_CONTAINER_IDS=yes to also complete IDs.
func ContainerNames(dockerCLI APIClientProvider, all bool, filters ...func(container.Summary) bool) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
list, err := dockerCLI.Client().ContainerList(cmd.Context(), client.ContainerListOptions{
res, err := dockerCLI.Client().ContainerList(cmd.Context(), client.ContainerListOptions{
All: all,
})
if err != nil {
@@ -87,7 +87,7 @@ func ContainerNames(dockerCLI APIClientProvider, all bool, filters ...func(conta
showContainerIDs := os.Getenv("DOCKER_COMPLETION_SHOW_CONTAINER_IDS") == "yes"
var names []string
for _, ctr := range list {
for _, ctr := range res.Items {
skip := false
for _, fn := range filters {
if fn != nil && !fn(ctr) {
@@ -110,12 +110,12 @@ func ContainerNames(dockerCLI APIClientProvider, all bool, filters ...func(conta
// VolumeNames offers completion for volumes
func VolumeNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
list, err := dockerCLI.Client().VolumeList(cmd.Context(), client.VolumeListOptions{})
res, err := dockerCLI.Client().VolumeList(cmd.Context(), client.VolumeListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
for _, vol := range list.Volumes {
for _, vol := range res.Items {
names = append(names, vol.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
@@ -125,12 +125,12 @@ func VolumeNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
// NetworkNames offers completion for networks
func NetworkNames(dockerCLI APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
list, err := dockerCLI.Client().NetworkList(cmd.Context(), client.NetworkListOptions{})
res, err := dockerCLI.Client().NetworkList(cmd.Context(), client.NetworkListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
for _, nw := range list {
for _, nw := range res.Items {
names = append(names, nw.Name)
}
return names, cobra.ShellCompDirectiveNoFileComp
+29 -29
View File
@@ -28,38 +28,38 @@ func (c fakeCLI) Client() client.APIClient {
type fakeClient struct {
client.Client
containerListFunc func(options client.ContainerListOptions) ([]container.Summary, error)
imageListFunc func(options client.ImageListOptions) ([]image.Summary, error)
networkListFunc func(ctx context.Context, options client.NetworkListOptions) ([]network.Summary, error)
volumeListFunc func(filter client.Filters) (volume.ListResponse, error)
containerListFunc func(context.Context, client.ContainerListOptions) (client.ContainerListResult, error)
imageListFunc func(context.Context, client.ImageListOptions) (client.ImageListResult, error)
networkListFunc func(context.Context, client.NetworkListOptions) (client.NetworkListResult, error)
volumeListFunc func(context.Context, client.VolumeListOptions) (client.VolumeListResult, error)
}
func (c *fakeClient) ContainerList(_ context.Context, options client.ContainerListOptions) ([]container.Summary, error) {
func (c *fakeClient) ContainerList(ctx context.Context, options client.ContainerListOptions) (client.ContainerListResult, error) {
if c.containerListFunc != nil {
return c.containerListFunc(options)
return c.containerListFunc(ctx, options)
}
return []container.Summary{}, nil
return client.ContainerListResult{}, nil
}
func (c *fakeClient) ImageList(_ context.Context, options client.ImageListOptions) ([]image.Summary, error) {
func (c *fakeClient) ImageList(ctx context.Context, options client.ImageListOptions) (client.ImageListResult, error) {
if c.imageListFunc != nil {
return c.imageListFunc(options)
return c.imageListFunc(ctx, options)
}
return []image.Summary{}, nil
return client.ImageListResult{}, nil
}
func (c *fakeClient) NetworkList(ctx context.Context, options client.NetworkListOptions) ([]network.Summary, error) {
func (c *fakeClient) NetworkList(ctx context.Context, options client.NetworkListOptions) (client.NetworkListResult, error) {
if c.networkListFunc != nil {
return c.networkListFunc(ctx, options)
}
return []network.Summary{}, nil
return client.NetworkListResult{}, nil
}
func (c *fakeClient) VolumeList(_ context.Context, options client.VolumeListOptions) (volume.ListResponse, error) {
func (c *fakeClient) VolumeList(ctx context.Context, options client.VolumeListOptions) (client.VolumeListResult, error) {
if c.volumeListFunc != nil {
return c.volumeListFunc(options.Filters)
return c.volumeListFunc(ctx, options)
}
return volume.ListResponse{}, nil
return client.VolumeListResult{}, nil
}
func TestCompleteContainerNames(t *testing.T) {
@@ -153,12 +153,12 @@ func TestCompleteContainerNames(t *testing.T) {
t.Setenv("DOCKER_COMPLETION_SHOW_CONTAINER_IDS", "yes")
}
comp := ContainerNames(fakeCLI{&fakeClient{
containerListFunc: func(opts client.ContainerListOptions) ([]container.Summary, error) {
containerListFunc: func(_ context.Context, opts client.ContainerListOptions) (client.ContainerListResult, error) {
assert.Check(t, is.DeepEqual(opts, tc.expOpts))
if tc.expDirective == cobra.ShellCompDirectiveError {
return nil, errors.New("some error occurred")
return client.ContainerListResult{}, errors.New("some error occurred")
}
return tc.containers, nil
return client.ContainerListResult{Items: tc.containers}, nil
},
}}, tc.showAll, tc.filters...)
@@ -226,11 +226,11 @@ func TestCompleteImageNames(t *testing.T) {
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
comp := ImageNames(fakeCLI{&fakeClient{
imageListFunc: func(options client.ImageListOptions) ([]image.Summary, error) {
imageListFunc: func(context.Context, client.ImageListOptions) (client.ImageListResult, error) {
if tc.expDirective == cobra.ShellCompDirectiveError {
return nil, errors.New("some error occurred")
return client.ImageListResult{}, errors.New("some error occurred")
}
return tc.images, nil
return client.ImageListResult{Items: tc.images}, nil
},
}}, -1)
@@ -286,11 +286,11 @@ func TestCompleteNetworkNames(t *testing.T) {
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
comp := NetworkNames(fakeCLI{&fakeClient{
networkListFunc: func(ctx context.Context, options client.NetworkListOptions) ([]network.Summary, error) {
networkListFunc: func(context.Context, client.NetworkListOptions) (client.NetworkListResult, error) {
if tc.expDirective == cobra.ShellCompDirectiveError {
return nil, errors.New("some error occurred")
return client.NetworkListResult{}, errors.New("some error occurred")
}
return tc.networks, nil
return client.NetworkListResult{Items: tc.networks}, nil
},
}})
@@ -310,7 +310,7 @@ func TestCompletePlatforms(t *testing.T) {
func TestCompleteVolumeNames(t *testing.T) {
tests := []struct {
doc string
volumes []*volume.Volume
volumes []volume.Volume
expOut []string
expDirective cobra.ShellCompDirective
}{
@@ -320,7 +320,7 @@ func TestCompleteVolumeNames(t *testing.T) {
},
{
doc: "with results",
volumes: []*volume.Volume{
volumes: []volume.Volume{
{Name: "volume-c"},
{Name: "volume-b"},
{Name: "volume-a"},
@@ -337,11 +337,11 @@ func TestCompleteVolumeNames(t *testing.T) {
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
comp := VolumeNames(fakeCLI{&fakeClient{
volumeListFunc: func(filter client.Filters) (volume.ListResponse, error) {
volumeListFunc: func(context.Context, client.VolumeListOptions) (client.VolumeListResult, error) {
if tc.expDirective == cobra.ShellCompDirectiveError {
return volume.ListResponse{}, errors.New("some error occurred")
return client.VolumeListResult{}, errors.New("some error occurred")
}
return volume.ListResponse{Volumes: tc.volumes}, nil
return client.VolumeListResult{Items: tc.volumes}, nil
},
}})
+15 -16
View File
@@ -3,42 +3,41 @@ package config
import (
"context"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
)
type fakeClient struct {
client.Client
configCreateFunc func(context.Context, swarm.ConfigSpec) (swarm.ConfigCreateResponse, error)
configInspectFunc func(context.Context, string) (swarm.Config, []byte, error)
configListFunc func(context.Context, client.ConfigListOptions) ([]swarm.Config, error)
configRemoveFunc func(string) error
configCreateFunc func(context.Context, client.ConfigCreateOptions) (client.ConfigCreateResult, error)
configInspectFunc func(context.Context, string, client.ConfigInspectOptions) (client.ConfigInspectResult, error)
configListFunc func(context.Context, client.ConfigListOptions) (client.ConfigListResult, error)
configRemoveFunc func(context.Context, string, client.ConfigRemoveOptions) (client.ConfigRemoveResult, error)
}
func (c *fakeClient) ConfigCreate(ctx context.Context, spec swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
func (c *fakeClient) ConfigCreate(ctx context.Context, options client.ConfigCreateOptions) (client.ConfigCreateResult, error) {
if c.configCreateFunc != nil {
return c.configCreateFunc(ctx, spec)
return c.configCreateFunc(ctx, options)
}
return swarm.ConfigCreateResponse{}, nil
return client.ConfigCreateResult{}, nil
}
func (c *fakeClient) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.Config, []byte, error) {
func (c *fakeClient) ConfigInspect(ctx context.Context, id string, options client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
if c.configInspectFunc != nil {
return c.configInspectFunc(ctx, id)
return c.configInspectFunc(ctx, id, options)
}
return swarm.Config{}, nil, nil
return client.ConfigInspectResult{}, nil
}
func (c *fakeClient) ConfigList(ctx context.Context, options client.ConfigListOptions) ([]swarm.Config, error) {
func (c *fakeClient) ConfigList(ctx context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
if c.configListFunc != nil {
return c.configListFunc(ctx, options)
}
return []swarm.Config{}, nil
return client.ConfigListResult{}, nil
}
func (c *fakeClient) ConfigRemove(_ context.Context, name string) error {
func (c *fakeClient) ConfigRemove(ctx context.Context, name string, options client.ConfigRemoveOptions) (client.ConfigRemoveResult, error) {
if c.configRemoveFunc != nil {
return c.configRemoveFunc(name)
return c.configRemoveFunc(ctx, name, options)
}
return nil
return client.ConfigRemoveResult{}, nil
}
+2 -2
View File
@@ -38,12 +38,12 @@ func newConfigCommand(dockerCLI command.Cli) *cobra.Command {
// completeNames offers completion for swarm configs
func completeNames(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
list, err := dockerCLI.Client().ConfigList(cmd.Context(), client.ConfigListOptions{})
res, err := dockerCLI.Client().ConfigList(cmd.Context(), client.ConfigListOptions{})
if err != nil {
return nil, cobra.ShellCompDirectiveError
}
var names []string
for _, config := range list {
for _, config := range res.Items {
names = append(names, config.ID)
}
return names, cobra.ShellCompDirectiveNoFileComp
+4 -1
View File
@@ -10,6 +10,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"github.com/moby/sys/sequential"
"github.com/spf13/cobra"
)
@@ -84,7 +85,9 @@ func runCreate(ctx context.Context, dockerCLI command.Cli, options createOptions
Name: options.templateDriver,
}
}
r, err := apiClient.ConfigCreate(ctx, spec)
r, err := apiClient.ConfigCreate(ctx, client.ConfigCreateOptions{
Spec: spec,
})
if err != nil {
return err
}
+22 -21
View File
@@ -13,6 +13,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/golden"
@@ -23,7 +24,7 @@ const configDataFile = "config-create-with-name.golden"
func TestConfigCreateErrors(t *testing.T) {
testCases := []struct {
args []string
configCreateFunc func(context.Context, swarm.ConfigSpec) (swarm.ConfigCreateResponse, error)
configCreateFunc func(context.Context, client.ConfigCreateOptions) (client.ConfigCreateResult, error)
expectedError string
}{
{
@@ -36,8 +37,8 @@ func TestConfigCreateErrors(t *testing.T) {
},
{
args: []string{"name", filepath.Join("testdata", configDataFile)},
configCreateFunc: func(_ context.Context, configSpec swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
return swarm.ConfigCreateResponse{}, errors.New("error creating config")
configCreateFunc: func(_ context.Context, options client.ConfigCreateOptions) (client.ConfigCreateResult, error) {
return client.ConfigCreateResult{}, errors.New("error creating config")
},
expectedError: "error creating config",
},
@@ -61,15 +62,15 @@ func TestConfigCreateWithName(t *testing.T) {
const name = "config-with-name"
var actual []byte
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
if spec.Name != name {
return swarm.ConfigCreateResponse{}, fmt.Errorf("expected name %q, got %q", name, spec.Name)
configCreateFunc: func(_ context.Context, options client.ConfigCreateOptions) (client.ConfigCreateResult, error) {
if options.Spec.Name != name {
return client.ConfigCreateResult{}, fmt.Errorf("expected name %q, got %q", name, options.Spec.Name)
}
actual = spec.Data
actual = options.Spec.Data
return swarm.ConfigCreateResponse{
ID: "ID-" + spec.Name,
return client.ConfigCreateResult{
ID: "ID-" + options.Spec.Name,
}, nil
},
})
@@ -100,13 +101,13 @@ func TestConfigCreateWithLabels(t *testing.T) {
}
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
if !reflect.DeepEqual(spec, expected) {
return swarm.ConfigCreateResponse{}, fmt.Errorf("expected %+v, got %+v", expected, spec)
configCreateFunc: func(_ context.Context, options client.ConfigCreateOptions) (client.ConfigCreateResult, error) {
if !reflect.DeepEqual(options.Spec, expected) {
return client.ConfigCreateResult{}, fmt.Errorf("expected %+v, got %+v", expected, options.Spec)
}
return swarm.ConfigCreateResponse{
ID: "ID-" + spec.Name,
return client.ConfigCreateResult{
ID: "ID-" + options.Spec.Name,
}, nil
},
})
@@ -126,17 +127,17 @@ func TestConfigCreateWithTemplatingDriver(t *testing.T) {
const name = "config-with-template-driver"
cli := test.NewFakeCli(&fakeClient{
configCreateFunc: func(_ context.Context, spec swarm.ConfigSpec) (swarm.ConfigCreateResponse, error) {
if spec.Name != name {
return swarm.ConfigCreateResponse{}, fmt.Errorf("expected name %q, got %q", name, spec.Name)
configCreateFunc: func(_ context.Context, options client.ConfigCreateOptions) (client.ConfigCreateResult, error) {
if options.Spec.Name != name {
return client.ConfigCreateResult{}, fmt.Errorf("expected name %q, got %q", name, options.Spec.Name)
}
if spec.Templating.Name != expectedDriver.Name {
return swarm.ConfigCreateResponse{}, fmt.Errorf("expected driver %v, got %v", expectedDriver, spec.Labels)
if options.Spec.Templating.Name != expectedDriver.Name {
return client.ConfigCreateResult{}, fmt.Errorf("expected driver %v, got %v", expectedDriver, options.Spec.Labels)
}
return swarm.ConfigCreateResponse{
ID: "ID-" + spec.Name,
return client.ConfigCreateResult{
ID: "ID-" + options.Spec.Name,
}, nil
},
})
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command/inspect"
"github.com/docker/go-units"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
)
const (
@@ -44,7 +45,7 @@ func newFormat(source string, quiet bool) formatter.Format {
}
// formatWrite writes the context
func formatWrite(fmtCtx formatter.Context, configs []swarm.Config) error {
func formatWrite(fmtCtx formatter.Context, configs client.ConfigListResult) error {
cCtx := &configContext{
HeaderContext: formatter.HeaderContext{
Header: formatter.SubHeaderContext{
@@ -57,7 +58,7 @@ func formatWrite(fmtCtx formatter.Context, configs []swarm.Config) error {
},
}
return fmtCtx.Write(cCtx, func(format func(subContext formatter.SubContext) error) error {
for _, config := range configs {
for _, config := range configs.Items {
configCtx := &configContext{c: config}
if err := format(configCtx); err != nil {
return err
+14 -11
View File
@@ -7,6 +7,7 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
)
@@ -48,23 +49,25 @@ id_rsa
},
}
configs := []swarm.Config{
{
ID: "1",
Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "passwords"}},
},
{
ID: "2",
Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "id_rsa"}},
res := client.ConfigListResult{
Items: []swarm.Config{
{
ID: "1",
Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "passwords"}},
},
{
ID: "2",
Meta: swarm.Meta{CreatedAt: time.Now(), UpdatedAt: time.Now()},
Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "id_rsa"}},
},
},
}
for _, tc := range cases {
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
if err := formatWrite(tc.context, configs); err != nil {
if err := formatWrite(tc.context, res); err != nil {
assert.ErrorContains(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
+4 -2
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package config
@@ -12,6 +12,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -50,7 +51,8 @@ func runInspect(ctx context.Context, dockerCLI command.Cli, opts inspectOptions)
}
getRef := func(id string) (any, []byte, error) {
return apiClient.ConfigInspectWithRaw(ctx, id)
res, err := apiClient.ConfigInspect(ctx, id, client.ConfigInspectOptions{})
return res.Config, res.Raw, err
}
// check if the user is trying to apply a template to the pretty format, which
+48 -32
View File
@@ -10,7 +10,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/builders"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)
@@ -19,7 +19,7 @@ func TestConfigInspectErrors(t *testing.T) {
testCases := []struct {
args []string
flags map[string]string
configInspectFunc func(_ context.Context, configID string) (swarm.Config, []byte, error)
configInspectFunc func(_ context.Context, configID string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error)
expectedError string
}{
{
@@ -27,8 +27,8 @@ func TestConfigInspectErrors(t *testing.T) {
},
{
args: []string{"foo"},
configInspectFunc: func(_ context.Context, configID string) (swarm.Config, []byte, error) {
return swarm.Config{}, nil, errors.New("error while inspecting the config")
configInspectFunc: func(context.Context, string, client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
return client.ConfigInspectResult{}, errors.New("error while inspecting the config")
},
expectedError: "error while inspecting the config",
},
@@ -41,11 +41,13 @@ func TestConfigInspectErrors(t *testing.T) {
},
{
args: []string{"foo", "bar"},
configInspectFunc: func(_ context.Context, configID string) (swarm.Config, []byte, error) {
configInspectFunc: func(_ context.Context, configID string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
if configID == "foo" {
return *builders.Config(builders.ConfigName("foo")), nil, nil
return client.ConfigInspectResult{
Config: *builders.Config(builders.ConfigName("foo")),
}, nil
}
return swarm.Config{}, nil, errors.New("error while inspecting the config")
return client.ConfigInspectResult{}, errors.New("error while inspecting the config")
},
expectedError: "error while inspecting the config",
},
@@ -70,25 +72,34 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
testCases := []struct {
name string
args []string
configInspectFunc func(_ context.Context, configID string) (swarm.Config, []byte, error)
configInspectFunc func(_ context.Context, configID string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error)
}{
{
name: "single-config",
args: []string{"foo"},
configInspectFunc: func(_ context.Context, name string) (swarm.Config, []byte, error) {
configInspectFunc: func(_ context.Context, name string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
if name != "foo" {
return swarm.Config{}, nil, fmt.Errorf("invalid name, expected %s, got %s", "foo", name)
return client.ConfigInspectResult{}, fmt.Errorf("invalid name, expected %s, got %s", "foo", name)
}
return *builders.Config(builders.ConfigID("ID-foo"), builders.ConfigName("foo")), nil, nil
return client.ConfigInspectResult{
Config: *builders.Config(
builders.ConfigID("ID-foo"),
builders.ConfigName("foo"),
),
}, nil
},
},
{
name: "multiple-configs-with-labels",
args: []string{"foo", "bar"},
configInspectFunc: func(_ context.Context, name string) (swarm.Config, []byte, error) {
return *builders.Config(builders.ConfigID("ID-"+name), builders.ConfigName(name), builders.ConfigLabels(map[string]string{
"label1": "label-foo",
})), nil, nil
configInspectFunc: func(_ context.Context, name string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
return client.ConfigInspectResult{
Config: *builders.Config(
builders.ConfigID("ID-"+name),
builders.ConfigName(name),
builders.ConfigLabels(map[string]string{"label1": "label-foo"}),
),
}, nil
},
},
}
@@ -102,16 +113,19 @@ func TestConfigInspectWithoutFormat(t *testing.T) {
}
func TestConfigInspectWithFormat(t *testing.T) {
configInspectFunc := func(_ context.Context, name string) (swarm.Config, []byte, error) {
return *builders.Config(builders.ConfigName("foo"), builders.ConfigLabels(map[string]string{
"label1": "label-foo",
})), nil, nil
configInspectFunc := func(_ context.Context, name string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
return client.ConfigInspectResult{
Config: *builders.Config(
builders.ConfigName("foo"),
builders.ConfigLabels(map[string]string{"label1": "label-foo"}),
),
}, nil
}
testCases := []struct {
name string
format string
args []string
configInspectFunc func(_ context.Context, name string) (swarm.Config, []byte, error)
configInspectFunc func(_ context.Context, name string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error)
}{
{
name: "simple-template",
@@ -141,21 +155,23 @@ func TestConfigInspectWithFormat(t *testing.T) {
func TestConfigInspectPretty(t *testing.T) {
testCases := []struct {
name string
configInspectFunc func(context.Context, string) (swarm.Config, []byte, error)
configInspectFunc func(context.Context, string, client.ConfigInspectOptions) (client.ConfigInspectResult, error)
}{
{
name: "simple",
configInspectFunc: func(_ context.Context, id string) (swarm.Config, []byte, error) {
return *builders.Config(
builders.ConfigLabels(map[string]string{
"lbl1": "value1",
}),
builders.ConfigID("configID"),
builders.ConfigName("configName"),
builders.ConfigCreatedAt(time.Time{}),
builders.ConfigUpdatedAt(time.Time{}),
builders.ConfigData([]byte("payload here")),
), []byte{}, nil
configInspectFunc: func(_ context.Context, id string, _ client.ConfigInspectOptions) (client.ConfigInspectResult, error) {
return client.ConfigInspectResult{
Config: *builders.Config(
builders.ConfigLabels(map[string]string{
"lbl1": "value1",
}),
builders.ConfigID("configID"),
builders.ConfigName("configName"),
builders.ConfigCreatedAt(time.Time{}),
builders.ConfigUpdatedAt(time.Time{}),
builders.ConfigData([]byte("payload here")),
),
}, nil
},
},
}
+4 -4
View File
@@ -48,7 +48,7 @@ func newConfigListCommand(dockerCLI command.Cli) *cobra.Command {
func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) error {
apiClient := dockerCLI.Client()
configs, err := apiClient.ConfigList(ctx, client.ConfigListOptions{Filters: options.filter.Value()})
res, err := apiClient.ConfigList(ctx, client.ConfigListOptions{Filters: options.filter.Value()})
if err != nil {
return err
}
@@ -62,13 +62,13 @@ func runList(ctx context.Context, dockerCLI command.Cli, options listOptions) er
}
}
sort.Slice(configs, func(i, j int) bool {
return sortorder.NaturalLess(configs[i].Spec.Name, configs[j].Spec.Name)
sort.Slice(res.Items, func(i, j int) bool {
return sortorder.NaturalLess(res.Items[i].Spec.Name, res.Items[j].Spec.Name)
})
configCtx := formatter.Context{
Output: dockerCLI.Out(),
Format: newFormat(format, options.quiet),
}
return formatWrite(configCtx, configs)
return formatWrite(configCtx, res)
}
+65 -55
View File
@@ -19,7 +19,7 @@ import (
func TestConfigListErrors(t *testing.T) {
testCases := []struct {
args []string
configListFunc func(context.Context, client.ConfigListOptions) ([]swarm.Config, error)
configListFunc func(context.Context, client.ConfigListOptions) (client.ConfigListResult, error)
expectedError string
}{
{
@@ -27,8 +27,8 @@ func TestConfigListErrors(t *testing.T) {
expectedError: "accepts no argument",
},
{
configListFunc: func(_ context.Context, options client.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{}, errors.New("error listing configs")
configListFunc: func(_ context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
return client.ConfigListResult{}, errors.New("error listing configs")
},
expectedError: "error listing configs",
},
@@ -48,26 +48,28 @@ func TestConfigListErrors(t *testing.T) {
func TestConfigList(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options client.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*builders.Config(builders.ConfigID("ID-1-foo"),
builders.ConfigName("1-foo"),
builders.ConfigVersion(swarm.Version{Index: 10}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
*builders.Config(builders.ConfigID("ID-10-foo"),
builders.ConfigName("10-foo"),
builders.ConfigVersion(swarm.Version{Index: 11}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
*builders.Config(builders.ConfigID("ID-2-foo"),
builders.ConfigName("2-foo"),
builders.ConfigVersion(swarm.Version{Index: 11}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
configListFunc: func(_ context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
return client.ConfigListResult{
Items: []swarm.Config{
*builders.Config(builders.ConfigID("ID-1-foo"),
builders.ConfigName("1-foo"),
builders.ConfigVersion(swarm.Version{Index: 10}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
*builders.Config(builders.ConfigID("ID-10-foo"),
builders.ConfigName("10-foo"),
builders.ConfigVersion(swarm.Version{Index: 11}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
*builders.Config(builders.ConfigID("ID-2-foo"),
builders.ConfigName("2-foo"),
builders.ConfigVersion(swarm.Version{Index: 11}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
},
}, nil
},
})
@@ -78,12 +80,14 @@ func TestConfigList(t *testing.T) {
func TestConfigListWithQuietOption(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options client.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"), builders.ConfigName("foo")),
*builders.Config(builders.ConfigID("ID-bar"), builders.ConfigName("bar"), builders.ConfigLabels(map[string]string{
"label": "label-bar",
})),
configListFunc: func(_ context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
return client.ConfigListResult{
Items: []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"), builders.ConfigName("foo")),
*builders.Config(builders.ConfigID("ID-bar"), builders.ConfigName("bar"), builders.ConfigLabels(map[string]string{
"label": "label-bar",
})),
},
}, nil
},
})
@@ -95,12 +99,14 @@ func TestConfigListWithQuietOption(t *testing.T) {
func TestConfigListWithConfigFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options client.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"), builders.ConfigName("foo")),
*builders.Config(builders.ConfigID("ID-bar"), builders.ConfigName("bar"), builders.ConfigLabels(map[string]string{
"label": "label-bar",
})),
configListFunc: func(_ context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
return client.ConfigListResult{
Items: []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"), builders.ConfigName("foo")),
*builders.Config(builders.ConfigID("ID-bar"), builders.ConfigName("bar"), builders.ConfigLabels(map[string]string{
"label": "label-bar",
})),
},
}, nil
},
})
@@ -114,12 +120,14 @@ func TestConfigListWithConfigFormat(t *testing.T) {
func TestConfigListWithFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options client.ConfigListOptions) ([]swarm.Config, error) {
return []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"), builders.ConfigName("foo")),
*builders.Config(builders.ConfigID("ID-bar"), builders.ConfigName("bar"), builders.ConfigLabels(map[string]string{
"label": "label-bar",
})),
configListFunc: func(_ context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
return client.ConfigListResult{
Items: []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"), builders.ConfigName("foo")),
*builders.Config(builders.ConfigID("ID-bar"), builders.ConfigName("bar"), builders.ConfigLabels(map[string]string{
"label": "label-bar",
})),
},
}, nil
},
})
@@ -131,22 +139,24 @@ func TestConfigListWithFormat(t *testing.T) {
func TestConfigListWithFilter(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
configListFunc: func(_ context.Context, options client.ConfigListOptions) ([]swarm.Config, error) {
configListFunc: func(_ context.Context, options client.ConfigListOptions) (client.ConfigListResult, error) {
assert.Check(t, options.Filters["name"]["foo"])
assert.Check(t, options.Filters["label"]["lbl1=Label-bar"])
return []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"),
builders.ConfigName("foo"),
builders.ConfigVersion(swarm.Version{Index: 10}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
*builders.Config(builders.ConfigID("ID-bar"),
builders.ConfigName("bar"),
builders.ConfigVersion(swarm.Version{Index: 11}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
return client.ConfigListResult{
Items: []swarm.Config{
*builders.Config(builders.ConfigID("ID-foo"),
builders.ConfigName("foo"),
builders.ConfigVersion(swarm.Version{Index: 10}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
*builders.Config(builders.ConfigID("ID-bar"),
builders.ConfigName("bar"),
builders.ConfigVersion(swarm.Version{Index: 11}),
builders.ConfigCreatedAt(time.Now().Add(-2*time.Hour)),
builders.ConfigUpdatedAt(time.Now().Add(-1*time.Hour)),
),
},
}, nil
},
})
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -30,7 +31,7 @@ func runRemove(ctx context.Context, dockerCLI command.Cli, names []string) error
var errs []error
for _, name := range names {
if err := apiClient.ConfigRemove(ctx, name); err != nil {
if _, err := apiClient.ConfigRemove(ctx, name, client.ConfigRemoveOptions{}); err != nil {
errs = append(errs, err)
continue
}
+10 -8
View File
@@ -1,12 +1,14 @@
package config
import (
"context"
"errors"
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
@@ -14,7 +16,7 @@ import (
func TestConfigRemoveErrors(t *testing.T) {
testCases := []struct {
args []string
configRemoveFunc func(string) error
configRemoveFunc func(context.Context, string, client.ConfigRemoveOptions) (client.ConfigRemoveResult, error)
expectedError string
}{
{
@@ -23,8 +25,8 @@ func TestConfigRemoveErrors(t *testing.T) {
},
{
args: []string{"foo"},
configRemoveFunc: func(name string) error {
return errors.New("error removing config")
configRemoveFunc: func(ctx context.Context, name string, options client.ConfigRemoveOptions) (client.ConfigRemoveResult, error) {
return client.ConfigRemoveResult{}, errors.New("error removing config")
},
expectedError: "error removing config",
},
@@ -46,9 +48,9 @@ func TestConfigRemoveWithName(t *testing.T) {
names := []string{"foo", "bar"}
var removedConfigs []string
cli := test.NewFakeCli(&fakeClient{
configRemoveFunc: func(name string) error {
configRemoveFunc: func(_ context.Context, name string, _ client.ConfigRemoveOptions) (client.ConfigRemoveResult, error) {
removedConfigs = append(removedConfigs, name)
return nil
return client.ConfigRemoveResult{}, nil
},
})
cmd := newConfigRemoveCommand(cli)
@@ -63,12 +65,12 @@ func TestConfigRemoveContinueAfterError(t *testing.T) {
var removedConfigs []string
cli := test.NewFakeCli(&fakeClient{
configRemoveFunc: func(name string) error {
configRemoveFunc: func(_ context.Context, name string, _ client.ConfigRemoveOptions) (client.ConfigRemoveResult, error) {
removedConfigs = append(removedConfigs, name)
if name == "foo" {
return errors.New("error removing config: " + name)
return client.ConfigRemoveResult{}, errors.New("error removing config: " + name)
}
return nil
return client.ConfigRemoveResult{}, nil
},
})
+16 -16
View File
@@ -23,21 +23,21 @@ type AttachOptions struct {
}
func inspectContainerAndCheckState(ctx context.Context, apiClient client.APIClient, args string) (*container.InspectResponse, error) {
c, err := apiClient.ContainerInspect(ctx, args)
c, err := apiClient.ContainerInspect(ctx, args, client.ContainerInspectOptions{})
if err != nil {
return nil, err
}
if !c.State.Running {
if !c.Container.State.Running {
return nil, errors.New("cannot attach to a stopped container, start it first")
}
if c.State.Paused {
if c.Container.State.Paused {
return nil, errors.New("cannot attach to a paused container, unpause it first")
}
if c.State.Restarting {
if c.Container.State.Restarting {
return nil, errors.New("cannot attach to a restarting container, wait until it is running")
}
return &c, nil
return &c.Container, nil
}
// newAttachCommand creates a new cobra.Command for `docker attach`
@@ -74,7 +74,7 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
// request channel to wait for client
waitCtx := context.WithoutCancel(ctx)
resultC, errC := apiClient.ContainerWait(waitCtx, containerID, "")
waitRes := apiClient.ContainerWait(waitCtx, containerID, client.ContainerWaitOptions{})
c, err := inspectContainerAndCheckState(ctx, apiClient, containerID)
if err != nil {
@@ -114,11 +114,11 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
defer signal.StopCatch(sigc)
}
resp, errAttach := apiClient.ContainerAttach(ctx, containerID, options)
if errAttach != nil {
return errAttach
res, err := apiClient.ContainerAttach(ctx, containerID, options)
if err != nil {
return err
}
defer resp.Close()
defer res.HijackedResponse.Close()
// If use docker attach command to attach to a stop container, it will return
// "You cannot attach to a stopped container" error, it's ok, but when
@@ -142,7 +142,7 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
inputStream: in,
outputStream: dockerCLI.Out(),
errorStream: dockerCLI.Err(),
resp: resp,
resp: res.HijackedResponse,
tty: c.Config.Tty,
detachKeys: options.DetachKeys,
}
@@ -152,19 +152,19 @@ func RunAttach(ctx context.Context, dockerCLI command.Cli, containerID string, o
return err
}
return getExitStatus(errC, resultC)
return getExitStatus(waitRes)
}
func getExitStatus(errC <-chan error, resultC <-chan container.WaitResponse) error {
func getExitStatus(waitRes client.ContainerWaitResult) error {
select {
case result := <-resultC:
case result := <-waitRes.Result:
if result.Error != nil {
return errors.New(result.Error.Message)
}
if result.StatusCode != 0 {
return cli.StatusError{StatusCode: int(result.StatusCode)}
}
case err := <-errC:
case err := <-waitRes.Error:
return err
}
@@ -177,7 +177,7 @@ func resizeTTY(ctx context.Context, dockerCli command.Cli, containerID string) {
// terminal, the only way to get the shell prompt to display for attaches 2+ is to artificially
// resize it, then go back to normal. Without this, every attach after the first will
// require the user to manually resize or hit enter.
resizeTtyTo(ctx, dockerCli.Client(), containerID, height+1, width+1, false)
resizeTTYTo(ctx, dockerCli.Client(), containerID, height+1, width+1, false)
// After the above resizing occurs, the call to MonitorTtySize below will handle resetting back
// to the actual size.
+29 -19
View File
@@ -8,6 +8,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
)
@@ -16,24 +17,26 @@ func TestNewAttachCommandErrors(t *testing.T) {
name string
args []string
expectedError string
containerInspectFunc func(img string) (container.InspectResponse, error)
containerInspectFunc func(img string) (client.ContainerInspectResult, error)
}{
{
name: "client-error",
args: []string{"5cb5bb5e4a3b"},
expectedError: "something went wrong",
containerInspectFunc: func(containerID string) (container.InspectResponse, error) {
return container.InspectResponse{}, errors.New("something went wrong")
containerInspectFunc: func(containerID string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{}, errors.New("something went wrong")
},
},
{
name: "client-stopped",
args: []string{"5cb5bb5e4a3b"},
expectedError: "cannot attach to a stopped container",
containerInspectFunc: func(containerID string) (container.InspectResponse, error) {
return container.InspectResponse{
State: &container.State{
Running: false,
containerInspectFunc: func(containerID string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{
Container: container.InspectResponse{
State: &container.State{
Running: false,
},
},
}, nil
},
@@ -42,11 +45,13 @@ func TestNewAttachCommandErrors(t *testing.T) {
name: "client-paused",
args: []string{"5cb5bb5e4a3b"},
expectedError: "cannot attach to a paused container",
containerInspectFunc: func(containerID string) (container.InspectResponse, error) {
return container.InspectResponse{
State: &container.State{
Running: true,
Paused: true,
containerInspectFunc: func(containerID string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{
Container: container.InspectResponse{
State: &container.State{
Running: true,
Paused: true,
},
},
}, nil
},
@@ -55,12 +60,14 @@ func TestNewAttachCommandErrors(t *testing.T) {
name: "client-restarting",
args: []string{"5cb5bb5e4a3b"},
expectedError: "cannot attach to a restarting container",
containerInspectFunc: func(containerID string) (container.InspectResponse, error) {
return container.InspectResponse{
State: &container.State{
Running: true,
Paused: false,
Restarting: true,
containerInspectFunc: func(containerID string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{
Container: container.InspectResponse{
State: &container.State{
Running: true,
Paused: false,
Restarting: true,
},
},
}, nil
},
@@ -118,7 +125,10 @@ func TestGetExitStatus(t *testing.T) {
resultC <- *testcase.result
}
err := getExitStatus(errC, resultC)
err := getExitStatus(client.ContainerWaitResult{
Result: resultC,
Error: errC,
})
if testcase.expectedError == nil {
assert.NilError(t, err)
+100 -97
View File
@@ -3,229 +3,232 @@ package container
import (
"context"
"io"
"net/http"
"strings"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/api/types/system"
"github.com/moby/moby/client"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
func mockContainerExportResult(content string) client.ContainerExportResult {
return io.NopCloser(strings.NewReader(content))
}
func mockContainerLogsResult(content string) client.ContainerLogsResult {
return io.NopCloser(strings.NewReader(content))
}
type fakeStreamResult struct {
io.ReadCloser
client.ImagePushResponse // same interface as [client.ImagePushResponse]
}
func (e fakeStreamResult) Read(p []byte) (int, error) { return e.ReadCloser.Read(p) }
func (e fakeStreamResult) Close() error { return e.ReadCloser.Close() }
type fakeClient struct {
client.Client
inspectFunc func(string) (container.InspectResponse, error)
execInspectFunc func(execID string) (client.ExecInspect, error)
execCreateFunc func(containerID string, options client.ExecCreateOptions) (container.ExecCreateResponse, error)
createContainerFunc func(config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform,
containerName string) (container.CreateResponse, error)
containerStartFunc func(containerID string, options client.ContainerStartOptions) error
imageCreateFunc func(ctx context.Context, parentReference string, options client.ImageCreateOptions) (io.ReadCloser, error)
infoFunc func() (system.Info, error)
containerStatPathFunc func(containerID, path string) (container.PathStat, error)
containerCopyFromFunc func(containerID, srcPath string) (io.ReadCloser, container.PathStat, error)
logFunc func(string, client.ContainerLogsOptions) (io.ReadCloser, error)
waitFunc func(string) (<-chan container.WaitResponse, <-chan error)
containerListFunc func(client.ContainerListOptions) ([]container.Summary, error)
containerExportFunc func(string) (io.ReadCloser, error)
containerExecResizeFunc func(id string, options client.ContainerResizeOptions) error
containerRemoveFunc func(ctx context.Context, containerID string, options client.ContainerRemoveOptions) error
containerRestartFunc func(ctx context.Context, containerID string, options client.ContainerStopOptions) error
containerStopFunc func(ctx context.Context, containerID string, options client.ContainerStopOptions) error
containerKillFunc func(ctx context.Context, containerID, signal string) error
containerPruneFunc func(ctx context.Context, pruneFilters client.Filters) (container.PruneReport, error)
containerAttachFunc func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.HijackedResponse, error)
containerDiffFunc func(ctx context.Context, containerID string) ([]container.FilesystemChange, error)
inspectFunc func(string) (client.ContainerInspectResult, error)
execInspectFunc func(execID string) (client.ExecInspectResult, error)
execCreateFunc func(containerID string, options client.ExecCreateOptions) (client.ExecCreateResult, error)
createContainerFunc func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error)
containerStartFunc func(containerID string, options client.ContainerStartOptions) (client.ContainerStartResult, error)
imagePullFunc func(ctx context.Context, parentReference string, options client.ImagePullOptions) (client.ImagePullResponse, error)
infoFunc func() (client.SystemInfoResult, error)
containerStatPathFunc func(containerID, path string) (client.ContainerStatPathResult, error)
containerCopyFromFunc func(containerID, srcPath string) (client.CopyFromContainerResult, error)
logFunc func(string, client.ContainerLogsOptions) (client.ContainerLogsResult, error)
waitFunc func(string) client.ContainerWaitResult
containerListFunc func(client.ContainerListOptions) (client.ContainerListResult, error)
containerExportFunc func(string) (client.ContainerExportResult, error)
containerExecResizeFunc func(id string, options client.ExecResizeOptions) (client.ExecResizeResult, error)
containerRemoveFunc func(ctx context.Context, containerID string, options client.ContainerRemoveOptions) (client.ContainerRemoveResult, error)
containerRestartFunc func(ctx context.Context, containerID string, options client.ContainerRestartOptions) (client.ContainerRestartResult, error)
containerStopFunc func(ctx context.Context, containerID string, options client.ContainerStopOptions) (client.ContainerStopResult, error)
containerKillFunc func(ctx context.Context, containerID string, options client.ContainerKillOptions) (client.ContainerKillResult, error)
containerPruneFunc func(ctx context.Context, options client.ContainerPruneOptions) (client.ContainerPruneResult, error)
containerAttachFunc func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error)
containerDiffFunc func(ctx context.Context, containerID string) (client.ContainerDiffResult, error)
containerRenameFunc func(ctx context.Context, oldName, newName string) error
containerCommitFunc func(ctx context.Context, container string, options client.ContainerCommitOptions) (container.CommitResponse, error)
containerPauseFunc func(ctx context.Context, container string) error
containerCommitFunc func(ctx context.Context, container string, options client.ContainerCommitOptions) (client.ContainerCommitResult, error)
containerPauseFunc func(ctx context.Context, container string, options client.ContainerPauseOptions) (client.ContainerPauseResult, error)
Version string
}
func (f *fakeClient) ContainerList(_ context.Context, options client.ContainerListOptions) ([]container.Summary, error) {
func (f *fakeClient) ContainerList(_ context.Context, options client.ContainerListOptions) (client.ContainerListResult, error) {
if f.containerListFunc != nil {
return f.containerListFunc(options)
}
return []container.Summary{}, nil
return client.ContainerListResult{}, nil
}
func (f *fakeClient) ContainerInspect(_ context.Context, containerID string) (container.InspectResponse, error) {
func (f *fakeClient) ContainerInspect(_ context.Context, containerID string, _ client.ContainerInspectOptions) (client.ContainerInspectResult, error) {
if f.inspectFunc != nil {
return f.inspectFunc(containerID)
}
return container.InspectResponse{}, nil
return client.ContainerInspectResult{}, nil
}
func (f *fakeClient) ContainerExecCreate(_ context.Context, containerID string, config client.ExecCreateOptions) (container.ExecCreateResponse, error) {
func (f *fakeClient) ExecCreate(_ context.Context, containerID string, config client.ExecCreateOptions) (client.ExecCreateResult, error) {
if f.execCreateFunc != nil {
return f.execCreateFunc(containerID, config)
}
return container.ExecCreateResponse{}, nil
return client.ExecCreateResult{}, nil
}
func (f *fakeClient) ContainerExecInspect(_ context.Context, execID string) (client.ExecInspect, error) {
func (f *fakeClient) ExecInspect(_ context.Context, execID string, _ client.ExecInspectOptions) (client.ExecInspectResult, error) {
if f.execInspectFunc != nil {
return f.execInspectFunc(execID)
}
return client.ExecInspect{}, nil
return client.ExecInspectResult{}, nil
}
func (*fakeClient) ContainerExecStart(context.Context, string, client.ExecStartOptions) error {
return nil
func (*fakeClient) ExecStart(context.Context, string, client.ExecStartOptions) (client.ExecStartResult, error) {
return client.ExecStartResult{}, nil
}
func (f *fakeClient) ContainerCreate(
_ context.Context,
config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform,
containerName string,
) (container.CreateResponse, error) {
func (f *fakeClient) ContainerCreate(_ context.Context, options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
if f.createContainerFunc != nil {
return f.createContainerFunc(config, hostConfig, networkingConfig, platform, containerName)
return f.createContainerFunc(options)
}
return container.CreateResponse{}, nil
return client.ContainerCreateResult{}, nil
}
func (f *fakeClient) ContainerRemove(ctx context.Context, containerID string, options client.ContainerRemoveOptions) error {
func (f *fakeClient) ContainerRemove(ctx context.Context, containerID string, options client.ContainerRemoveOptions) (client.ContainerRemoveResult, error) {
if f.containerRemoveFunc != nil {
return f.containerRemoveFunc(ctx, containerID, options)
}
return nil
return client.ContainerRemoveResult{}, nil
}
func (f *fakeClient) ImageCreate(ctx context.Context, parentReference string, options client.ImageCreateOptions) (io.ReadCloser, error) {
if f.imageCreateFunc != nil {
return f.imageCreateFunc(ctx, parentReference, options)
func (f *fakeClient) ImagePull(ctx context.Context, parentReference string, options client.ImagePullOptions) (client.ImagePullResponse, error) {
if f.imagePullFunc != nil {
return f.imagePullFunc(ctx, parentReference, options)
}
return nil, nil
return fakeStreamResult{}, nil
}
func (f *fakeClient) Info(_ context.Context) (system.Info, error) {
func (f *fakeClient) Info(context.Context, client.InfoOptions) (client.SystemInfoResult, error) {
if f.infoFunc != nil {
return f.infoFunc()
}
return system.Info{}, nil
return client.SystemInfoResult{}, nil
}
func (f *fakeClient) ContainerStatPath(_ context.Context, containerID, path string) (container.PathStat, error) {
func (f *fakeClient) ContainerStatPath(_ context.Context, containerID string, options client.ContainerStatPathOptions) (client.ContainerStatPathResult, error) {
if f.containerStatPathFunc != nil {
return f.containerStatPathFunc(containerID, path)
return f.containerStatPathFunc(containerID, options.Path)
}
return container.PathStat{}, nil
return client.ContainerStatPathResult{}, nil
}
func (f *fakeClient) CopyFromContainer(_ context.Context, containerID, srcPath string) (io.ReadCloser, container.PathStat, error) {
func (f *fakeClient) CopyFromContainer(_ context.Context, containerID string, options client.CopyFromContainerOptions) (client.CopyFromContainerResult, error) {
if f.containerCopyFromFunc != nil {
return f.containerCopyFromFunc(containerID, srcPath)
return f.containerCopyFromFunc(containerID, options.SourcePath)
}
return nil, container.PathStat{}, nil
return client.CopyFromContainerResult{}, nil
}
func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, options client.ContainerLogsOptions) (io.ReadCloser, error) {
func (f *fakeClient) ContainerLogs(_ context.Context, containerID string, options client.ContainerLogsOptions) (client.ContainerLogsResult, error) {
if f.logFunc != nil {
return f.logFunc(containerID, options)
}
return nil, nil
return http.NoBody, nil
}
func (f *fakeClient) ClientVersion() string {
return f.Version
}
func (f *fakeClient) ContainerWait(_ context.Context, containerID string, _ container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
func (f *fakeClient) ContainerWait(_ context.Context, containerID string, _ client.ContainerWaitOptions) client.ContainerWaitResult {
if f.waitFunc != nil {
return f.waitFunc(containerID)
}
return nil, nil
return client.ContainerWaitResult{}
}
func (f *fakeClient) ContainerStart(_ context.Context, containerID string, options client.ContainerStartOptions) error {
func (f *fakeClient) ContainerStart(_ context.Context, containerID string, options client.ContainerStartOptions) (client.ContainerStartResult, error) {
if f.containerStartFunc != nil {
return f.containerStartFunc(containerID, options)
}
return nil
return client.ContainerStartResult{}, nil
}
func (f *fakeClient) ContainerExport(_ context.Context, containerID string) (io.ReadCloser, error) {
func (f *fakeClient) ContainerExport(_ context.Context, containerID string, _ client.ContainerExportOptions) (client.ContainerExportResult, error) {
if f.containerExportFunc != nil {
return f.containerExportFunc(containerID)
}
return nil, nil
return http.NoBody, nil
}
func (f *fakeClient) ContainerExecResize(_ context.Context, id string, options client.ContainerResizeOptions) error {
func (f *fakeClient) ExecResize(_ context.Context, id string, options client.ExecResizeOptions) (client.ExecResizeResult, error) {
if f.containerExecResizeFunc != nil {
return f.containerExecResizeFunc(id, options)
}
return nil
return client.ExecResizeResult{}, nil
}
func (f *fakeClient) ContainerKill(ctx context.Context, containerID, signal string) error {
func (f *fakeClient) ContainerKill(ctx context.Context, containerID string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
if f.containerKillFunc != nil {
return f.containerKillFunc(ctx, containerID, signal)
return f.containerKillFunc(ctx, containerID, options)
}
return nil
return client.ContainerKillResult{}, nil
}
func (f *fakeClient) ContainersPrune(ctx context.Context, pruneFilters client.Filters) (container.PruneReport, error) {
func (f *fakeClient) ContainerPrune(ctx context.Context, options client.ContainerPruneOptions) (client.ContainerPruneResult, error) {
if f.containerPruneFunc != nil {
return f.containerPruneFunc(ctx, pruneFilters)
return f.containerPruneFunc(ctx, options)
}
return container.PruneReport{}, nil
return client.ContainerPruneResult{}, nil
}
func (f *fakeClient) ContainerRestart(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
func (f *fakeClient) ContainerRestart(ctx context.Context, containerID string, options client.ContainerRestartOptions) (client.ContainerRestartResult, error) {
if f.containerRestartFunc != nil {
return f.containerRestartFunc(ctx, containerID, options)
}
return nil
return client.ContainerRestartResult{}, nil
}
func (f *fakeClient) ContainerStop(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
func (f *fakeClient) ContainerStop(ctx context.Context, containerID string, options client.ContainerStopOptions) (client.ContainerStopResult, error) {
if f.containerStopFunc != nil {
return f.containerStopFunc(ctx, containerID, options)
}
return nil
return client.ContainerStopResult{}, nil
}
func (f *fakeClient) ContainerAttach(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.HijackedResponse, error) {
func (f *fakeClient) ContainerAttach(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error) {
if f.containerAttachFunc != nil {
return f.containerAttachFunc(ctx, containerID, options)
}
return client.HijackedResponse{}, nil
return client.ContainerAttachResult{}, nil
}
func (f *fakeClient) ContainerDiff(ctx context.Context, containerID string) ([]container.FilesystemChange, error) {
func (f *fakeClient) ContainerDiff(ctx context.Context, containerID string, _ client.ContainerDiffOptions) (client.ContainerDiffResult, error) {
if f.containerDiffFunc != nil {
return f.containerDiffFunc(ctx, containerID)
}
return []container.FilesystemChange{}, nil
return client.ContainerDiffResult{}, nil
}
func (f *fakeClient) ContainerRename(ctx context.Context, oldName, newName string) error {
func (f *fakeClient) ContainerRename(ctx context.Context, oldName string, options client.ContainerRenameOptions) (client.ContainerRenameResult, error) {
if f.containerRenameFunc != nil {
return f.containerRenameFunc(ctx, oldName, newName)
return client.ContainerRenameResult{}, f.containerRenameFunc(ctx, oldName, options.NewName)
}
return nil
return client.ContainerRenameResult{}, nil
}
func (f *fakeClient) ContainerCommit(ctx context.Context, containerID string, options client.ContainerCommitOptions) (container.CommitResponse, error) {
func (f *fakeClient) ContainerCommit(ctx context.Context, containerID string, options client.ContainerCommitOptions) (client.ContainerCommitResult, error) {
if f.containerCommitFunc != nil {
return f.containerCommitFunc(ctx, containerID, options)
}
return container.CommitResponse{}, nil
return client.ContainerCommitResult{}, nil
}
func (f *fakeClient) ContainerPause(ctx context.Context, containerID string) error {
func (f *fakeClient) ContainerPause(ctx context.Context, containerID string, options client.ContainerPauseOptions) (client.ContainerPauseResult, error) {
if f.containerPauseFunc != nil {
return f.containerPauseFunc(ctx, containerID)
return f.containerPauseFunc(ctx, containerID, options)
}
return nil
return client.ContainerPauseResult{}, nil
}
+4 -13
View File
@@ -7,7 +7,6 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -15,18 +14,14 @@ import (
func TestRunCommit(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerCommitFunc: func(
ctx context.Context,
ctr string,
options client.ContainerCommitOptions,
) (container.CommitResponse, error) {
containerCommitFunc: func(ctx context.Context, ctr string, options client.ContainerCommitOptions) (client.ContainerCommitResult, error) {
assert.Check(t, is.Equal(options.Author, "Author Name <author@name.com>"))
assert.Check(t, is.DeepEqual(options.Changes, []string{"EXPOSE 80"}))
assert.Check(t, is.Equal(options.Comment, "commit message"))
assert.Check(t, is.Equal(options.NoPause, true))
assert.Check(t, is.Equal(ctr, "container-id"))
return container.CommitResponse{ID: "image-id"}, nil
return client.ContainerCommitResult{ID: "image-id"}, nil
},
})
@@ -52,12 +47,8 @@ func TestRunCommitClientError(t *testing.T) {
clientError := errors.New("client error")
cli := test.NewFakeCli(&fakeClient{
containerCommitFunc: func(
ctx context.Context,
ctr string,
options client.ContainerCommitOptions,
) (container.CommitResponse, error) {
return container.CommitResponse{}, clientError
containerCommitFunc: func(ctx context.Context, ctr string, options client.ContainerCommitOptions) (client.ContainerCommitResult, error) {
return client.ContainerCommitResult{}, clientError
},
})
+6 -5
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package container
@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/moby/sys/capability"
"github.com/moby/sys/signal"
"github.com/spf13/cobra"
@@ -186,11 +187,11 @@ func completeLink(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
// of the build-in log drivers.
func completeLogDriver(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
res, err := dockerCLI.Client().Info(cmd.Context(), client.InfoOptions{})
if err != nil {
return builtInLogDrivers(), cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Log
drivers := res.Info.Plugins.Log
return drivers, cobra.ShellCompDirectiveNoFileComp
}
}
@@ -279,12 +280,12 @@ func completeUlimit(_ *cobra.Command, _ []string, _ string) ([]string, cobra.She
// completeVolumeDriver contacts the API to get the built-in and installed volume drivers.
func completeVolumeDriver(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
return func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
info, err := dockerCLI.Client().Info(cmd.Context())
res, err := dockerCLI.Client().Info(cmd.Context(), client.InfoOptions{})
if err != nil {
// fallback: the built-in drivers
return []string{"local"}, cobra.ShellCompDirectiveNoFileComp
}
drivers := info.Plugins.Volume
drivers := res.Info.Plugins.Volume
return drivers, cobra.ShellCompDirectiveNoFileComp
}
}
+7 -5
View File
@@ -27,7 +27,7 @@ func TestCompleteLinuxCapabilityNames(t *testing.T) {
func TestCompletePid(t *testing.T) {
tests := []struct {
containerListFunc func(client.ContainerListOptions) ([]container.Summary, error)
containerListFunc func(client.ContainerListOptions) (client.ContainerListResult, error)
toComplete string
expectedCompletions []string
expectedDirective cobra.ShellCompDirective
@@ -43,10 +43,12 @@ func TestCompletePid(t *testing.T) {
expectedDirective: cobra.ShellCompDirectiveNoSpace,
},
{
containerListFunc: func(client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2"),
containerListFunc: func(client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2"),
},
}, nil
},
toComplete: "container:",
+25 -16
View File
@@ -230,11 +230,13 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
// if client requests to follow symbol link, then must decide target file to be copied
var rebaseName string
if copyConfig.followLink {
srcStat, err := apiClient.ContainerStatPath(ctx, copyConfig.container, srcPath)
src, err := apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{
Path: srcPath,
})
// If the destination is a symbolic link, we should follow it.
if err == nil && srcStat.Mode&os.ModeSymlink != 0 {
linkTarget := srcStat.LinkTarget
if err == nil && src.Stat.Mode&os.ModeSymlink != 0 {
linkTarget := src.Stat.LinkTarget
if !isAbs(linkTarget) {
// Join with the parent directory.
srcParent, _ := archive.SplitPathDirEntry(srcPath)
@@ -249,11 +251,14 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
defer cancel()
content, stat, err := apiClient.CopyFromContainer(ctx, copyConfig.container, srcPath)
cpRes, err := apiClient.CopyFromContainer(ctx, copyConfig.container, client.CopyFromContainerOptions{
SourcePath: srcPath,
})
if err != nil {
return err
}
defer content.Close()
content := cpRes.Content
defer func() { _ = content.Close() }()
if dstPath == "-" {
_, err = io.Copy(dockerCLI.Out(), content)
@@ -263,7 +268,7 @@ func copyFromContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cp
srcInfo := archive.CopyInfo{
Path: srcPath,
Exists: true,
IsDir: stat.Mode.IsDir(),
IsDir: cpRes.Stat.Mode.IsDir(),
RebaseName: rebaseName,
}
@@ -315,10 +320,10 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
apiClient := dockerCLI.Client()
// Prepare destination copy info by stat-ing the container path.
dstInfo := archive.CopyInfo{Path: dstPath}
if dstStat, err := apiClient.ContainerStatPath(ctx, copyConfig.container, dstPath); err == nil {
if dst, err := apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{Path: dstPath}); err == nil {
// If the destination is a symbolic link, we should evaluate it.
if dstStat.Mode&os.ModeSymlink != 0 {
linkTarget := dstStat.LinkTarget
if dst.Stat.Mode&os.ModeSymlink != 0 {
linkTarget := dst.Stat.LinkTarget
if !isAbs(linkTarget) {
// Join with the parent directory.
dstParent, _ := archive.SplitPathDirEntry(dstPath)
@@ -326,14 +331,14 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
}
dstInfo.Path = linkTarget
dstStat, err = apiClient.ContainerStatPath(ctx, copyConfig.container, linkTarget)
dst, err = apiClient.ContainerStatPath(ctx, copyConfig.container, client.ContainerStatPathOptions{Path: linkTarget})
}
// Validate the destination path
if err == nil {
if err := command.ValidateOutputPathFileMode(dstStat.Mode); err != nil {
if err := command.ValidateOutputPathFileMode(dst.Stat.Mode); err != nil {
return fmt.Errorf(`destination "%s:%s" must be a directory or a regular file: %w`, copyConfig.container, dstPath, err)
}
dstInfo.Exists, dstInfo.IsDir = true, dstStat.Mode.IsDir()
dstInfo.Exists, dstInfo.IsDir = true, dst.Stat.Mode.IsDir()
}
// Ignore any error and assume that the parent directory of the destination
@@ -399,22 +404,26 @@ func copyToContainer(ctx context.Context, dockerCLI command.Cli, copyConfig cpCo
}
options := client.CopyToContainerOptions{
CopyUIDGID: copyConfig.copyUIDGID,
DestinationPath: resolvedDstPath,
Content: content,
CopyUIDGID: copyConfig.copyUIDGID,
}
if copyConfig.quiet {
return apiClient.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
_, err := apiClient.CopyToContainer(ctx, copyConfig.container, options)
return err
}
ctx, cancel := signal.NotifyContext(ctx, os.Interrupt)
restore, done := copyProgress(ctx, dockerCLI.Err(), copyToContainerHeader, &copiedSize)
res := apiClient.CopyToContainer(ctx, copyConfig.container, resolvedDstPath, content, options)
// TODO(thaJeztah): error-handling looks odd here; should it be handled differently?
_, err := apiClient.CopyToContainer(ctx, copyConfig.container, options)
cancel()
<-done
restore()
_, _ = fmt.Fprintln(dockerCLI.Err(), "Successfully copied", progressHumanSize(copiedSize), "to", copyConfig.container+":"+dstInfo.Path)
return res
return err
}
// We use `:` as a delimiter between CONTAINER and PATH, but `:` could also be
+13 -7
View File
@@ -11,7 +11,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
"gotest.tools/v3/fs"
@@ -52,9 +52,11 @@ func TestRunCopyFromContainerToStdout(t *testing.T) {
tarContent := "the tar content"
cli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (io.ReadCloser, container.PathStat, error) {
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
assert.Check(t, is.Equal("container", ctr))
return io.NopCloser(strings.NewReader(tarContent)), container.PathStat{}, nil
return client.CopyFromContainerResult{
Content: io.NopCloser(strings.NewReader(tarContent)),
}, nil
},
})
err := runCopy(context.TODO(), cli, copyOptions{
@@ -73,10 +75,12 @@ func TestRunCopyFromContainerToFilesystem(t *testing.T) {
destDir := fs.NewDir(t, "cp-test")
cli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (io.ReadCloser, container.PathStat, error) {
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
assert.Check(t, is.Equal("container", ctr))
readCloser, err := archive.Tar(srcDir.Path(), compression.None)
return readCloser, container.PathStat{}, err
return client.CopyFromContainerResult{
Content: readCloser,
}, err
},
})
err := runCopy(context.TODO(), cli, copyOptions{
@@ -99,10 +103,12 @@ func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing.
defer destDir.Remove()
cli := test.NewFakeCli(&fakeClient{
containerCopyFromFunc: func(ctr, srcPath string) (io.ReadCloser, container.PathStat, error) {
containerCopyFromFunc: func(ctr, srcPath string) (client.CopyFromContainerResult, error) {
assert.Check(t, is.Equal("container", ctr))
readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{})
return readCloser, container.PathStat{}, err
return client.CopyFromContainerResult{
Content: readCloser,
}, err
},
})
err := runCopy(context.TODO(), cli, copyOptions{
+43 -29
View File
@@ -17,11 +17,9 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/image"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/config/types"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/cli/trust"
"github.com/docker/cli/internal/jsonstream"
"github.com/docker/cli/opts"
"github.com/moby/moby/api/types/mount"
@@ -41,7 +39,6 @@ const (
type createOptions struct {
name string
platform string
untrusted bool
pull string // always, missing, never
quiet bool
useAPISocket bool
@@ -88,7 +85,9 @@ func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms())
flags.BoolVar(&options.untrusted, "disable-content-trust", !trust.Enabled(), "Skip image verification")
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
copts = addFlags(flags)
addCompletions(cmd, dockerCLI)
@@ -135,22 +134,27 @@ func pullImage(ctx context.Context, dockerCli command.Cli, img string, options *
return err
}
responseBody, err := dockerCli.Client().ImageCreate(ctx, img, client.ImageCreateOptions{
var ociPlatforms []ocispec.Platform
if options.platform != "" {
// Already validated.
ociPlatforms = append(ociPlatforms, platforms.MustParse(options.platform))
}
resp, err := dockerCli.Client().ImagePull(ctx, img, client.ImagePullOptions{
RegistryAuth: encodedAuth,
Platform: options.platform,
Platforms: ociPlatforms,
})
if err != nil {
return err
}
defer func() {
_ = responseBody.Close()
_ = resp.Close()
}()
out := dockerCli.Err()
if options.quiet {
out = streams.NewOut(io.Discard)
}
return jsonstream.Display(ctx, responseBody, out)
return jsonstream.Display(ctx, resp, out)
}
type cidFile struct {
@@ -208,10 +212,15 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
hostConfig := containerCfg.HostConfig
networkingConfig := containerCfg.NetworkingConfig
var (
trustedRef reference.Canonical
namedRef reference.Named
)
var namedRef reference.Named
// TODO(thaJeztah): add a platform option-type / flag-type.
if options.platform != "" {
_, err = platforms.Parse(options.platform)
if err != nil {
return "", err
}
}
containerIDFile, err := newCIDFile(hostConfig.ContainerIDFile)
if err != nil {
@@ -227,15 +236,6 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
}
if named, ok := ref.(reference.Named); ok {
namedRef = reference.TagNameOnly(named)
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !options.untrusted {
var err error
trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef)
if err != nil {
return "", err
}
config.Image = reference.FamiliarString(trustedRef)
}
}
const dockerConfigPathInContainer = "/run/secrets/docker/config.json"
@@ -318,9 +318,6 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
if err := pullImage(ctx, dockerCli, config.Image, options); err != nil {
return err
}
if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil {
return trust.TagTrusted(ctx, dockerCli.Client(), dockerCli.Err(), trustedRef, taggedRef)
}
return nil
}
@@ -332,7 +329,14 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.Out().GetTtySize()
response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, options.name)
response, err := dockerCli.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
Name: options.name,
// Image: config.Image, // TODO(thaJeztah): pass image-ref separate
Platform: platform,
Config: config,
HostConfig: hostConfig,
NetworkingConfig: networkingConfig,
})
if err != nil {
// Pull image if it does not exist locally and we have the PullImageMissing option. Default behavior.
if errdefs.IsNotFound(err) && namedRef != nil && options.pull == PullImageMissing {
@@ -346,7 +350,14 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c
}
var retryErr error
response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, platform, options.name)
response, retryErr = dockerCli.Client().ContainerCreate(ctx, client.ContainerCreateOptions{
Name: options.name,
// Image: config.Image, // TODO(thaJeztah): pass image-ref separate
Platform: platform,
Config: config,
HostConfig: hostConfig,
NetworkingConfig: networkingConfig,
})
if retryErr != nil {
return "", retryErr
}
@@ -396,7 +407,7 @@ func validatePullOpt(val string) error {
//
// The path should be an absolute path in the container, commonly
// /root/.docker/config.json.
func copyDockerConfigIntoContainer(ctx context.Context, dockerAPI client.APIClient, containerID string, configPath string, config *configfile.ConfigFile) error {
func copyDockerConfigIntoContainer(ctx context.Context, apiClient client.APIClient, containerID string, configPath string, config *configfile.ConfigFile) error {
var configBuf bytes.Buffer
if err := config.SaveToWriter(&configBuf); err != nil {
return fmt.Errorf("saving creds: %w", err)
@@ -419,8 +430,11 @@ func copyDockerConfigIntoContainer(ctx context.Context, dockerAPI client.APIClie
return fmt.Errorf("closing tar for config copy failed: %w", err)
}
if err := dockerAPI.CopyToContainer(ctx, containerID, "/",
&tarBuf, client.CopyToContainerOptions{}); err != nil {
_, err := apiClient.CopyToContainer(ctx, containerID, client.CopyToContainerOptions{
DestinationPath: "/",
Content: &tarBuf,
})
if err != nil {
return fmt.Errorf("copying config.json into container failed: %w", err)
}
+18 -86
View File
@@ -13,13 +13,10 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/google/go-cmp/cmp"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/api/types/system"
"github.com/moby/moby/client"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/pflag"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -117,35 +114,30 @@ func TestCreateContainerImagePullPolicy(t *testing.T) {
pullCounter := 0
apiClient := &fakeClient{
createContainerFunc: func(
config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform,
containerName string,
) (container.CreateResponse, error) {
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
defer func() { tc.ResponseCounter++ }()
switch tc.ResponseCounter {
case 0:
return container.CreateResponse{}, fakeNotFound{}
return client.ContainerCreateResult{}, fakeNotFound{}
default:
return container.CreateResponse{ID: containerID}, nil
return client.ContainerCreateResult{ID: containerID}, nil
}
},
imageCreateFunc: func(ctx context.Context, parentReference string, options client.ImageCreateOptions) (io.ReadCloser, error) {
imagePullFunc: func(ctx context.Context, parentReference string, options client.ImagePullOptions) (client.ImagePullResponse, error) {
defer func() { pullCounter++ }()
return io.NopCloser(strings.NewReader("")), nil
return fakeStreamResult{ReadCloser: io.NopCloser(strings.NewReader(""))}, nil
},
infoFunc: func() (system.Info, error) {
return system.Info{IndexServerAddress: "https://indexserver.example.com"}, nil
infoFunc: func() (client.SystemInfoResult, error) {
return client.SystemInfoResult{
Info: system.Info{IndexServerAddress: "https://indexserver.example.com"},
}, nil
},
}
fakeCLI := test.NewFakeCli(apiClient)
id, err := createContainer(context.Background(), fakeCLI, config, &createOptions{
name: "name",
platform: runtime.GOOS,
untrusted: true,
pull: tc.PullPolicy,
name: "name",
platform: runtime.GOOS,
pull: tc.PullPolicy,
})
if tc.ExpectedErrMsg != "" {
@@ -221,56 +213,6 @@ func TestCreateContainerValidateFlags(t *testing.T) {
}
}
func TestNewCreateCommandWithContentTrustErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
notaryFunc test.NotaryClientFuncType
}{
{
name: "offline-notary-server",
notaryFunc: notary.GetOfflineNotaryRepository,
expectedError: "client is offline",
args: []string{"image:tag"},
},
{
name: "uninitialized-notary-server",
notaryFunc: notary.GetUninitializedNotaryRepository,
expectedError: "remote trust data does not exist",
args: []string{"image:tag"},
},
{
name: "empty-notary-server",
notaryFunc: notary.GetEmptyTargetsNotaryRepository,
expectedError: "No valid trust data for tag",
args: []string{"image:tag"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Setenv("DOCKER_CONTENT_TRUST", "true")
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform,
containerName string,
) (container.CreateResponse, error) {
return container.CreateResponse{}, errors.New("shouldn't try to pull image")
},
})
fakeCLI.SetNotaryClient(tc.notaryFunc)
cmd := newCreateCommand(fakeCLI)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs(tc.args)
err := cmd.Execute()
assert.ErrorContains(t, err, tc.expectedError)
})
}
}
func TestNewCreateCommandWithWarnings(t *testing.T) {
testCases := []struct {
name string
@@ -296,13 +238,8 @@ func TestNewCreateCommandWithWarnings(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform,
containerName string,
) (container.CreateResponse, error) {
return container.CreateResponse{Warnings: tc.warnings}, nil
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
return client.ContainerCreateResult{Warnings: tc.warnings}, nil
},
})
cmd := newCreateCommand(fakeCLI)
@@ -335,15 +272,10 @@ func TestCreateContainerWithProxyConfig(t *testing.T) {
sort.Strings(expected)
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform,
containerName string,
) (container.CreateResponse, error) {
sort.Strings(config.Env)
assert.DeepEqual(t, config.Env, expected)
return container.CreateResponse{}, nil
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
sort.Strings(options.Config.Env)
assert.DeepEqual(t, options.Config.Env, expected)
return client.ContainerCreateResult{}, nil
},
})
fakeCLI.SetConfigFile(&configfile.ConfigFile{
+3 -2
View File
@@ -7,6 +7,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/formatter"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -28,7 +29,7 @@ func newDiffCommand(dockerCLI command.Cli) *cobra.Command {
}
func runDiff(ctx context.Context, dockerCLI command.Cli, containerID string) error {
changes, err := dockerCLI.Client().ContainerDiff(ctx, containerID)
res, err := dockerCLI.Client().ContainerDiff(ctx, containerID, client.ContainerDiffOptions{})
if err != nil {
return err
}
@@ -36,5 +37,5 @@ func runDiff(ctx context.Context, dockerCLI command.Cli, containerID string) err
Output: dockerCLI.Out(),
Format: newDiffFormat("{{.Type}} {{.Path}}"),
}
return diffFormatWrite(diffCtx, changes)
return diffFormatWrite(diffCtx, res)
}
+18 -21
View File
@@ -9,28 +9,28 @@ import (
"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunDiff(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerDiffFunc: func(
ctx context.Context,
containerID string,
) ([]container.FilesystemChange, error) {
return []container.FilesystemChange{
{
Kind: container.ChangeModify,
Path: "/path/to/file0",
},
{
Kind: container.ChangeAdd,
Path: "/path/to/file1",
},
{
Kind: container.ChangeDelete,
Path: "/path/to/file2",
containerDiffFunc: func(ctx context.Context, containerID string) (client.ContainerDiffResult, error) {
return client.ContainerDiffResult{
Changes: []container.FilesystemChange{
{
Kind: container.ChangeModify,
Path: "/path/to/file0",
},
{
Kind: container.ChangeAdd,
Path: "/path/to/file1",
},
{
Kind: container.ChangeDelete,
Path: "/path/to/file2",
},
},
}, nil
},
@@ -60,11 +60,8 @@ func TestRunDiffClientError(t *testing.T) {
clientError := errors.New("client error")
cli := test.NewFakeCli(&fakeClient{
containerDiffFunc: func(
ctx context.Context,
containerID string,
) ([]container.FilesystemChange, error) {
return nil, clientError
containerDiffFunc: func(ctx context.Context, containerID string) (client.ContainerDiffResult, error) {
return client.ContainerDiffResult{}, clientError
},
})
+22 -21
View File
@@ -72,11 +72,11 @@ func newExecCommand(dockerCLI command.Cli) *cobra.Command {
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.VarP(&options.Env, "env", "e", "Set environment variables")
flags.SetAnnotation("env", "version", []string{"1.25"})
_ = flags.SetAnnotation("env", "version", []string{"1.25"})
flags.Var(&options.EnvFile, "env-file", "Read in a file of environment variables")
flags.SetAnnotation("env-file", "version", []string{"1.25"})
_ = flags.SetAnnotation("env-file", "version", []string{"1.25"})
flags.StringVarP(&options.Workdir, "workdir", "w", "", "Working directory inside the container")
flags.SetAnnotation("workdir", "version", []string{"1.35"})
_ = flags.SetAnnotation("workdir", "version", []string{"1.35"})
_ = cmd.RegisterFlagCompletionFunc("env", completion.EnvVarNames())
_ = cmd.RegisterFlagCompletionFunc("env-file", completion.FileNames())
@@ -97,18 +97,18 @@ func RunExec(ctx context.Context, dockerCLI command.Cli, containerIDorName strin
// otherwise if we error out we will leak execIDs on the server (and
// there's no easy way to clean those up). But also in order to make "not
// exist" errors take precedence we do a dummy inspect first.
if _, err := apiClient.ContainerInspect(ctx, containerIDorName); err != nil {
if _, err := apiClient.ContainerInspect(ctx, containerIDorName, client.ContainerInspectOptions{}); err != nil {
return err
}
if !options.Detach {
if err := dockerCLI.In().CheckTty(execOptions.AttachStdin, execOptions.Tty); err != nil {
if err := dockerCLI.In().CheckTty(execOptions.AttachStdin, execOptions.TTY); err != nil {
return err
}
}
fillConsoleSize(execOptions, dockerCLI)
response, err := apiClient.ContainerExecCreate(ctx, containerIDorName, *execOptions)
response, err := apiClient.ExecCreate(ctx, containerIDorName, *execOptions)
if err != nil {
return err
}
@@ -119,19 +119,20 @@ func RunExec(ctx context.Context, dockerCLI command.Cli, containerIDorName strin
}
if options.Detach {
return apiClient.ContainerExecStart(ctx, execID, client.ExecStartOptions{
_, err := apiClient.ExecStart(ctx, execID, client.ExecStartOptions{
Detach: options.Detach,
Tty: execOptions.Tty,
ConsoleSize: execOptions.ConsoleSize,
TTY: execOptions.TTY,
ConsoleSize: client.ConsoleSize{Height: execOptions.ConsoleSize.Height, Width: execOptions.ConsoleSize.Width},
})
return err
}
return interactiveExec(ctx, dockerCLI, execOptions, execID)
}
func fillConsoleSize(execOptions *client.ExecCreateOptions, dockerCli command.Cli) {
if execOptions.Tty {
if execOptions.TTY {
height, width := dockerCli.Out().GetTtySize()
execOptions.ConsoleSize = &[2]uint{height, width}
execOptions.ConsoleSize = client.ConsoleSize{Height: height, Width: width}
}
}
@@ -149,7 +150,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl
out = dockerCli.Out()
}
if execOptions.AttachStderr {
if execOptions.Tty {
if execOptions.TTY {
stderr = dockerCli.Out()
} else {
stderr = dockerCli.Err()
@@ -158,9 +159,9 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl
fillConsoleSize(execOptions, dockerCli)
apiClient := dockerCli.Client()
resp, err := apiClient.ContainerExecAttach(ctx, execID, client.ExecAttachOptions{
Tty: execOptions.Tty,
ConsoleSize: execOptions.ConsoleSize,
resp, err := apiClient.ExecAttach(ctx, execID, client.ExecAttachOptions{
TTY: execOptions.TTY,
ConsoleSize: client.ConsoleSize{Height: execOptions.ConsoleSize.Height, Width: execOptions.ConsoleSize.Width},
})
if err != nil {
return err
@@ -177,8 +178,8 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl
inputStream: in,
outputStream: out,
errorStream: stderr,
resp: resp,
tty: execOptions.Tty,
resp: resp.HijackedResponse,
tty: execOptions.TTY,
detachKeys: execOptions.DetachKeys,
}
@@ -186,7 +187,7 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl
}()
}()
if execOptions.Tty && dockerCli.In().IsTerminal() {
if execOptions.TTY && dockerCli.In().IsTerminal() {
if err := MonitorTtySize(ctx, dockerCli, execID, true); err != nil {
_, _ = fmt.Fprintln(dockerCli.Err(), "Error monitoring TTY size:", err)
}
@@ -200,8 +201,8 @@ func interactiveExec(ctx context.Context, dockerCli command.Cli, execOptions *cl
return getExecExitStatus(ctx, apiClient, execID)
}
func getExecExitStatus(ctx context.Context, apiClient client.ContainerAPIClient, execID string) error {
resp, err := apiClient.ContainerExecInspect(ctx, execID)
func getExecExitStatus(ctx context.Context, apiClient client.ExecAPIClient, execID string) error {
resp, err := apiClient.ExecInspect(ctx, execID, client.ExecInspectOptions{})
if err != nil {
// If we can't connect, then the daemon probably died.
if !client.IsErrConnectionFailed(err) {
@@ -222,7 +223,7 @@ func parseExec(execOpts ExecOptions, configFile *configfile.ConfigFile) (*client
execOptions := &client.ExecCreateOptions{
User: execOpts.User,
Privileged: execOpts.Privileged,
Tty: execOpts.TTY,
TTY: execOpts.TTY,
Cmd: execOpts.Command,
WorkingDir: execOpts.Workdir,
}
+16 -17
View File
@@ -12,7 +12,6 @@ import (
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/opts"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -70,7 +69,7 @@ TWO=2
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
Tty: true,
TTY: true,
Cmd: []string{"command"},
},
},
@@ -87,7 +86,7 @@ TWO=2
Detach: true,
}),
expected: client.ExecCreateOptions{
Tty: true,
TTY: true,
Cmd: []string{"command"},
},
},
@@ -119,7 +118,7 @@ TWO=2
},
options: func() ExecOptions {
o := withDefaultOpts(ExecOptions{})
o.EnvFile.Set(tmpFile.Path())
_ = o.EnvFile.Set(tmpFile.Path())
return o
}(),
},
@@ -132,8 +131,8 @@ TWO=2
},
options: func() ExecOptions {
o := withDefaultOpts(ExecOptions{})
o.EnvFile.Set(tmpFile.Path())
o.Env.Set("ONE=override")
_ = o.EnvFile.Set(tmpFile.Path())
_ = o.Env.Set("ONE=override")
return o
}(),
},
@@ -150,7 +149,7 @@ TWO=2
func TestParseExecNoSuchFile(t *testing.T) {
execOpts := withDefaultOpts(ExecOptions{})
execOpts.EnvFile.Set("no-such-env-file")
assert.Check(t, execOpts.EnvFile.Set("no-such-env-file"))
execConfig, err := parseExec(execOpts, &configfile.ConfigFile{})
assert.ErrorContains(t, err, "no-such-env-file")
assert.Check(t, os.IsNotExist(err))
@@ -177,8 +176,8 @@ func TestRunExec(t *testing.T) {
doc: "inspect error",
options: NewExecOptions(),
client: &fakeClient{
inspectFunc: func(string) (container.InspectResponse, error) {
return container.InspectResponse{}, errors.New("failed inspect")
inspectFunc: func(string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{}, errors.New("failed inspect")
},
},
expectedError: "failed inspect",
@@ -195,7 +194,7 @@ func TestRunExec(t *testing.T) {
t.Run(testcase.doc, func(t *testing.T) {
fakeCLI := test.NewFakeCli(testcase.client)
err := RunExec(context.TODO(), fakeCLI, "thecontainer", testcase.options)
err := RunExec(context.TODO(), fakeCLI, "the-container", testcase.options)
if testcase.expectedError != "" {
assert.ErrorContains(t, err, testcase.expectedError)
} else if !assert.Check(t, err) {
@@ -207,8 +206,8 @@ func TestRunExec(t *testing.T) {
}
}
func execCreateWithID(_ string, _ client.ExecCreateOptions) (container.ExecCreateResponse, error) {
return container.ExecCreateResponse{ID: "execid"}, nil
func execCreateWithID(_ string, _ client.ExecCreateOptions) (client.ExecCreateResult, error) {
return client.ExecCreateResult{ID: "exec-id"}, nil
}
func TestGetExecExitStatus(t *testing.T) {
@@ -236,9 +235,9 @@ func TestGetExecExitStatus(t *testing.T) {
for _, testcase := range testcases {
apiClient := &fakeClient{
execInspectFunc: func(id string) (client.ExecInspect, error) {
execInspectFunc: func(id string) (client.ExecInspectResult, error) {
assert.Check(t, is.Equal(execID, id))
return client.ExecInspect{ExitCode: testcase.exitCode}, testcase.inspectError
return client.ExecInspectResult{ExitCode: testcase.exitCode}, testcase.inspectError
},
}
err := getExecExitStatus(context.Background(), apiClient, execID)
@@ -251,14 +250,14 @@ func TestNewExecCommandErrors(t *testing.T) {
name string
args []string
expectedError string
containerInspectFunc func(img string) (container.InspectResponse, error)
containerInspectFunc func(img string) (client.ContainerInspectResult, error)
}{
{
name: "client-error",
args: []string{"5cb5bb5e4a3b", "-t", "-i", "bash"},
expectedError: "something went wrong",
containerInspectFunc: func(containerID string) (container.InspectResponse, error) {
return container.InspectResponse{}, errors.New("something went wrong")
containerInspectFunc: func(containerID string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{}, errors.New("something went wrong")
},
},
}
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client"
"github.com/moby/sys/atomicwriter"
"github.com/spf13/cobra"
)
@@ -60,7 +61,7 @@ func runExport(ctx context.Context, dockerCLI command.Cli, opts exportOptions) e
output = writer
}
responseBody, err := dockerCLI.Client().ContainerExport(ctx, opts.container)
responseBody, err := dockerCLI.Client().ContainerExport(ctx, opts.container, client.ContainerExportOptions{})
if err != nil {
return err
}
+7 -5
View File
@@ -2,10 +2,10 @@ package container
import (
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
"gotest.tools/v3/fs"
)
@@ -15,8 +15,9 @@ func TestContainerExportOutputToFile(t *testing.T) {
defer dir.Remove()
cli := test.NewFakeCli(&fakeClient{
containerExportFunc: func(container string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("bar")), nil
containerExportFunc: func(container string) (client.ContainerExportResult, error) {
// FIXME(thaJeztah): how to mock this?
return mockContainerExportResult("bar"), nil
},
})
cmd := newExportCommand(cli)
@@ -33,8 +34,9 @@ func TestContainerExportOutputToFile(t *testing.T) {
func TestContainerExportOutputToIrregularFile(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerExportFunc: func(container string) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("foo")), nil
containerExportFunc: func(container string) (client.ContainerExportResult, error) {
// FIXME(thaJeztah): how to mock this?
return mockContainerExportResult("foo"), nil
},
})
cmd := newExportCommand(cli)
+3 -2
View File
@@ -3,6 +3,7 @@ package container
import (
"github.com/docker/cli/cli/command/formatter"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
)
const (
@@ -21,9 +22,9 @@ func newDiffFormat(source string) formatter.Format {
}
// diffFormatWrite writes formatted diff using the [formatter.Context].
func diffFormatWrite(fmtCtx formatter.Context, changes []container.FilesystemChange) error {
func diffFormatWrite(fmtCtx formatter.Context, changes client.ContainerDiffResult) error {
return fmtCtx.Write(newDiffContext(), func(format func(subContext formatter.SubContext) error) error {
for _, change := range changes {
for _, change := range changes.Changes {
if err := format(&diffContext{c: change}); err != nil {
return err
}
+7 -4
View File
@@ -6,6 +6,7 @@ import (
"github.com/docker/cli/cli/command/formatter"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
)
@@ -40,10 +41,12 @@ D: /usr/app/old_app.js
},
}
diffs := []container.FilesystemChange{
{Kind: container.ChangeModify, Path: "/var/log/app.log"},
{Kind: container.ChangeAdd, Path: "/usr/app/app.js"},
{Kind: container.ChangeDelete, Path: "/usr/app/old_app.js"},
diffs := client.ContainerDiffResult{
Changes: []container.FilesystemChange{
{Kind: container.ChangeModify, Path: "/var/log/app.log"},
{Kind: container.ChangeAdd, Path: "/usr/app/app.js"},
{Kind: container.ChangeDelete, Path: "/usr/app/old_app.js"},
},
}
for _, tc := range cases {
+7 -2
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package container
@@ -11,6 +11,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/inspect"
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -46,6 +47,10 @@ func newInspectCommand(dockerCLI command.Cli) *cobra.Command {
func runInspect(ctx context.Context, dockerCLI command.Cli, opts inspectOptions) error {
apiClient := dockerCLI.Client()
return inspect.Inspect(dockerCLI.Out(), opts.refs, opts.format, func(ref string) (any, []byte, error) {
return apiClient.ContainerInspectWithRaw(ctx, ref, opts.size)
res, err := apiClient.ContainerInspect(ctx, ref, client.ContainerInspectOptions{Size: opts.size})
if err != nil {
return nil, nil, err
}
return &res.Container, res.Raw, nil
})
}
+5 -1
View File
@@ -8,6 +8,7 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -47,7 +48,10 @@ func newKillCommand(dockerCLI command.Cli) *cobra.Command {
func runKill(ctx context.Context, dockerCLI command.Cli, opts *killOptions) error {
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
return apiClient.ContainerKill(ctx, container, opts.signal)
_, err := apiClient.ContainerKill(ctx, container, client.ContainerKillOptions{
Signal: opts.signal,
})
return err
})
var errs []error
+6 -13
View File
@@ -8,19 +8,16 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunKill(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerKillFunc: func(
ctx context.Context,
container string,
signal string,
) error {
assert.Assert(t, is.Equal(signal, "STOP"))
return nil
containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
assert.Assert(t, is.Equal(options.Signal, "STOP"))
return client.ContainerKillResult{}, nil
},
})
@@ -47,12 +44,8 @@ func TestRunKill(t *testing.T) {
func TestRunKillClientError(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerKillFunc: func(
ctx context.Context,
container string,
signal string,
) error {
return fmt.Errorf("client error for container %s", container)
containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
return client.ContainerKillResult{}, fmt.Errorf("client error for container %s", container)
},
})
+6 -6
View File
@@ -68,8 +68,8 @@ func newListCommand(dockerCLI command.Cli) *cobra.Command {
return &cmd
}
func buildContainerListOptions(options *psOptions) (*client.ContainerListOptions, error) {
listOptions := &client.ContainerListOptions{
func buildContainerListOptions(options *psOptions) (client.ContainerListOptions, error) {
listOptions := client.ContainerListOptions{
All: options.all,
Limit: options.last,
Size: options.size,
@@ -84,7 +84,7 @@ func buildContainerListOptions(options *psOptions) (*client.ContainerListOptions
if len(options.format) > 0 {
tmpl, err := templates.Parse(options.format)
if err != nil {
return nil, fmt.Errorf("failed to parse template: %w", err)
return client.ContainerListOptions{}, fmt.Errorf("failed to parse template: %w", err)
}
optionsProcessor := formatter.NewContainerContext()
@@ -92,7 +92,7 @@ func buildContainerListOptions(options *psOptions) (*client.ContainerListOptions
// This shouldn't error out but swallowing the error makes it harder
// to track down if preProcessor issues come up.
if err := tmpl.Execute(io.Discard, optionsProcessor); err != nil {
return nil, fmt.Errorf("failed to execute template: %w", err)
return client.ContainerListOptions{}, fmt.Errorf("failed to execute template: %w", err)
}
// if `size` was not explicitly set to false (with `--size=false`)
@@ -127,7 +127,7 @@ func runPs(ctx context.Context, dockerCLI command.Cli, options *psOptions) error
return err
}
containers, err := dockerCLI.Client().ContainerList(ctx, *listOptions)
res, err := dockerCLI.Client().ContainerList(ctx, listOptions)
if err != nil {
return err
}
@@ -137,5 +137,5 @@ func runPs(ctx context.Context, dockerCLI command.Cli, options *psOptions) error
Format: formatter.NewContainerFormat(options.format, options.quiet, listOptions.Size),
Trunc: !options.noTrunc,
}
return formatter.ContainerWrite(containerCtx, containers)
return formatter.ContainerWrite(containerCtx, res.Items)
}
+44 -32
View File
@@ -109,7 +109,7 @@ func TestContainerListBuildContainerListOptions(t *testing.T) {
func TestContainerListErrors(t *testing.T) {
testCases := []struct {
flags map[string]string
containerListFunc func(client.ContainerListOptions) ([]container.Summary, error)
containerListFunc func(client.ContainerListOptions) (client.ContainerListResult, error)
expectedError string
}{
{
@@ -125,8 +125,8 @@ func TestContainerListErrors(t *testing.T) {
expectedError: `wrong number of args for join`,
},
{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return nil, errors.New("error listing containers")
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{}, errors.New("error listing containers")
},
expectedError: "error listing containers",
},
@@ -149,13 +149,15 @@ func TestContainerListErrors(t *testing.T) {
func TestContainerListWithoutFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo")),
*builders.Container("c3", builders.WithPort(80, 80, builders.TCP), builders.WithPort(81, 81, builders.TCP), builders.WithPort(82, 82, builders.TCP)),
*builders.Container("c4", builders.WithPort(81, 81, builders.UDP)),
*builders.Container("c5", builders.WithPort(82, 82, builders.IP("8.8.8.8"), builders.TCP)),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo")),
*builders.Container("c3", builders.WithPort(80, 80, builders.TCP), builders.WithPort(81, 81, builders.TCP), builders.WithPort(82, 82, builders.TCP)),
*builders.Container("c4", builders.WithPort(81, 81, builders.UDP)),
*builders.Container("c5", builders.WithPort(82, 82, builders.IP("8.8.8.8"), builders.TCP)),
},
}, nil
},
})
@@ -169,10 +171,12 @@ func TestContainerListWithoutFormat(t *testing.T) {
func TestContainerListNoTrunc(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
},
}, nil
},
})
@@ -188,10 +192,12 @@ func TestContainerListNoTrunc(t *testing.T) {
// Test for GitHub issue docker/docker#21772
func TestContainerListNamesMultipleTime(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1"),
*builders.Container("c2", builders.WithName("foo/bar")),
},
}, nil
},
})
@@ -207,10 +213,12 @@ func TestContainerListNamesMultipleTime(t *testing.T) {
// Test for GitHub issue docker/docker#30291
func TestContainerListFormatTemplateWithArg(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
},
}, nil
},
})
@@ -260,9 +268,9 @@ func TestContainerListFormatSizeSetsOption(t *testing.T) {
for _, tc := range tests {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(options client.ContainerListOptions) ([]container.Summary, error) {
containerListFunc: func(options client.ContainerListOptions) (client.ContainerListResult, error) {
assert.Check(t, is.Equal(options.Size, tc.sizeExpected))
return []container.Summary{}, nil
return client.ContainerListResult{}, nil
},
})
cmd := newListCommand(cli)
@@ -280,10 +288,12 @@ func TestContainerListFormatSizeSetsOption(t *testing.T) {
func TestContainerListWithConfigFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value"), builders.WithSize(10700000)),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar"), builders.WithSize(3200000)),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value"), builders.WithSize(10700000)),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar"), builders.WithSize(3200000)),
},
}, nil
},
})
@@ -300,10 +310,12 @@ func TestContainerListWithConfigFormat(t *testing.T) {
func TestContainerListWithFormat(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerListFunc: func(_ client.ContainerListOptions) ([]container.Summary, error) {
return []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
containerListFunc: func(_ client.ContainerListOptions) (client.ContainerListResult, error) {
return client.ContainerListResult{
Items: []container.Summary{
*builders.Container("c1", builders.WithLabel("some.label", "value")),
*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
},
}, nil
},
})
+6 -6
View File
@@ -54,12 +54,12 @@ func newLogsCommand(dockerCLI command.Cli) *cobra.Command {
}
func runLogs(ctx context.Context, dockerCli command.Cli, opts *logsOptions) error {
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container, client.ContainerInspectOptions{})
if err != nil {
return err
}
responseBody, err := dockerCli.Client().ContainerLogs(ctx, c.ID, client.ContainerLogsOptions{
resp, err := dockerCli.Client().ContainerLogs(ctx, c.Container.ID, client.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Since: opts.since,
@@ -72,12 +72,12 @@ func runLogs(ctx context.Context, dockerCli command.Cli, opts *logsOptions) erro
if err != nil {
return err
}
defer responseBody.Close()
defer func() { _ = resp.Close() }()
if c.Config.Tty {
_, err = io.Copy(dockerCli.Out(), responseBody)
if c.Container.Config.Tty {
_, err = io.Copy(dockerCli.Out(), resp)
} else {
_, err = stdcopy.StdCopy(dockerCli.Out(), dockerCli.Err(), responseBody)
_, err = stdcopy.StdCopy(dockerCli.Out(), dockerCli.Err(), resp)
}
return err
}
+13 -13
View File
@@ -2,8 +2,6 @@ package container
import (
"context"
"io"
"strings"
"testing"
"github.com/docker/cli/internal/test"
@@ -13,17 +11,13 @@ import (
is "gotest.tools/v3/assert/cmp"
)
var logFn = func(expectedOut string) func(string, client.ContainerLogsOptions) (io.ReadCloser, error) {
return func(container string, opts client.ContainerLogsOptions) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader(expectedOut)), nil
}
}
func TestRunLogs(t *testing.T) {
inspectFn := func(containerID string) (container.InspectResponse, error) {
return container.InspectResponse{
Config: &container.Config{Tty: true},
State: &container.State{Running: false},
inspectFn := func(containerID string) (client.ContainerInspectResult, error) {
return client.ContainerInspectResult{
Container: container.InspectResponse{
Config: &container.Config{Tty: true},
State: &container.State{Running: false},
},
}, nil
}
@@ -39,7 +33,13 @@ func TestRunLogs(t *testing.T) {
doc: "successful logs",
expectedOut: "foo",
options: &logsOptions{},
client: &fakeClient{logFunc: logFn("foo"), inspectFunc: inspectFn},
client: &fakeClient{
logFunc: func(container string, opts client.ContainerLogsOptions) (client.ContainerLogsResult, error) {
// FIXME(thaJeztah): how to mock this?
return mockContainerLogsResult("foo"), nil
},
inspectFunc: inspectFn,
},
},
}
+9 -6
View File
@@ -1,3 +1,6 @@
// FIXME(vvoland): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package container
import (
@@ -11,6 +14,7 @@ import (
"path"
"path/filepath"
"reflect"
"slices"
"strings"
"time"
@@ -881,10 +885,11 @@ func parseNetworkAttachmentOpt(ep opts.NetworkAttachmentOpts) (*network.Endpoint
}
}
if ep.MacAddress != "" {
if _, err := net.ParseMAC(strings.TrimSpace(ep.MacAddress)); err != nil {
ma, err := net.ParseMAC(strings.TrimSpace(ep.MacAddress))
if err != nil {
return nil, fmt.Errorf("%s is not a valid mac address", ep.MacAddress)
}
epConfig.MacAddress = ep.MacAddress
epConfig.MacAddress = network.HardwareAddr(ma)
}
return epConfig, nil
}
@@ -1130,10 +1135,8 @@ func validateLinuxPath(val string, validator func(string) bool) (string, error)
// validateAttach validates that the specified string is a valid attach option.
func validateAttach(val string) (string, error) {
s := strings.ToLower(val)
for _, str := range []string{"stdin", "stdout", "stderr"} {
if s == str {
return s, nil
}
if slices.Contains([]string{"stdin", "stdout", "stderr"}, s) {
return s, nil
}
return val, errors.New("valid streams are STDIN, STDOUT and STDERR")
}
+13 -4
View File
@@ -4,6 +4,7 @@ import (
"errors"
"fmt"
"io"
"net"
"net/netip"
"os"
"runtime"
@@ -20,6 +21,14 @@ import (
"gotest.tools/v3/skip"
)
func mustParseMAC(s string) networktypes.HardwareAddr {
mac, err := net.ParseMAC(s)
if err != nil {
panic(err)
}
return networktypes.HardwareAddr(mac)
}
func TestValidateAttach(t *testing.T) {
valid := []string{
"stdin",
@@ -353,7 +362,7 @@ func TestParseWithMacAddress(t *testing.T) {
}
_, hostConfig, nwConfig := mustParse(t, validMacAddress)
defaultNw := hostConfig.NetworkMode.NetworkName()
if nwConfig.EndpointsConfig[defaultNw].MacAddress != "92:d0:c6:0a:29:33" {
if nwConfig.EndpointsConfig[defaultNw].MacAddress.String() != "92:d0:c6:0a:29:33" {
t.Fatalf("Expected the default endpoint to have the MacAddress '92:d0:c6:0a:29:33' set, got '%v'", nwConfig.EndpointsConfig[defaultNw].MacAddress)
}
}
@@ -650,7 +659,7 @@ func TestParseNetworkConfig(t *testing.T) {
Aliases: []string{"web3"},
},
"net4": {
MacAddress: "02:32:1c:23:00:04",
MacAddress: mustParseMAC("02:32:1c:23:00:04"),
IPAMConfig: &networktypes.EndpointIPAMConfig{
LinkLocalIPs: []netip.Addr{netip.MustParseAddr("169.254.169.254")},
},
@@ -672,7 +681,7 @@ func TestParseNetworkConfig(t *testing.T) {
IPv6Address: netip.MustParseAddr("2001:db8::8822"),
},
Aliases: []string{"web1", "web2"},
MacAddress: "02:32:1c:23:00:04",
MacAddress: mustParseMAC("02:32:1c:23:00:04"),
},
},
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
@@ -689,7 +698,7 @@ func TestParseNetworkConfig(t *testing.T) {
expected: map[string]*networktypes.EndpointSettings{
"net1": {
Aliases: []string{"foobar"},
MacAddress: "52:0f:f3:dc:50:10",
MacAddress: mustParseMAC("52:0f:f3:dc:50:10"),
},
},
expectedHostCfg: container.HostConfig{NetworkMode: "net1"},
+5 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -40,7 +41,10 @@ func newPauseCommand(dockerCLI command.Cli) *cobra.Command {
func runPause(ctx context.Context, dockerCLI command.Cli, opts *pauseOptions) error {
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, apiClient.ContainerPause)
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
_, err := apiClient.ContainerPause(ctx, container, client.ContainerPauseOptions{})
return err
})
var errs []error
for _, ctr := range opts.containers {
+4 -9
View File
@@ -8,18 +8,13 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestRunPause(t *testing.T) {
cli := test.NewFakeCli(
&fakeClient{
containerPauseFunc: func(ctx context.Context, container string) error {
return nil
},
},
)
cli := test.NewFakeCli(&fakeClient{})
cmd := newPauseCommand(cli)
cmd.SetOut(io.Discard)
@@ -41,8 +36,8 @@ func TestRunPause(t *testing.T) {
func TestRunPauseClientError(t *testing.T) {
cli := test.NewFakeCli(
&fakeClient{
containerPauseFunc: func(ctx context.Context, container string) error {
return fmt.Errorf("client error for container %s", container)
containerPauseFunc: func(ctx context.Context, container string, options client.ContainerPauseOptions) (client.ContainerPauseResult, error) {
return client.ContainerPauseResult{}, fmt.Errorf("client error for container %s", container)
},
},
)
+4 -3
View File
@@ -12,6 +12,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/fvbommel/sortorder"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -52,7 +53,7 @@ func newPortCommand(dockerCLI command.Cli) *cobra.Command {
// proto is specified. We should consider changing this to "any" protocol
// for the given private port.
func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) error {
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container)
c, err := dockerCli.Client().ContainerInspect(ctx, opts.container, client.ContainerInspectOptions{})
if err != nil {
return err
}
@@ -63,7 +64,7 @@ func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) erro
if err != nil {
return err
}
frontends, exists := c.NetworkSettings.Ports[port]
frontends, exists := c.Container.NetworkSettings.Ports[port]
if !exists || len(frontends) == 0 {
return fmt.Errorf("no public port '%s' published for %s", opts.port, opts.container)
}
@@ -71,7 +72,7 @@ func runPort(ctx context.Context, dockerCli command.Cli, opts *portOptions) erro
out = append(out, net.JoinHostPort(frontend.HostIP.String(), frontend.HostPort))
}
} else {
for from, frontends := range c.NetworkSettings.Ports {
for from, frontends := range c.Container.NetworkSettings.Ports {
for _, frontend := range frontends {
out = append(out, fmt.Sprintf("%s -> %s", from, net.JoinHostPort(frontend.HostIP.String(), frontend.HostPort)))
}
+3 -2
View File
@@ -8,6 +8,7 @@ import (
"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
"gotest.tools/v3/assert"
"gotest.tools/v3/golden"
)
@@ -46,7 +47,7 @@ func TestNewPortCommandOutput(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
inspectFunc: func(string) (container.InspectResponse, error) {
inspectFunc: func(string) (client.ContainerInspectResult, error) {
ci := container.InspectResponse{NetworkSettings: &container.NetworkSettings{}}
ci.NetworkSettings.Ports = network.PortMap{
network.MustParsePort("80/tcp"): make([]network.PortBinding, len(tc.ips)),
@@ -64,7 +65,7 @@ func TestNewPortCommandOutput(t *testing.T) {
HostIP: ip, HostPort: "5678",
}
}
return ci, nil
return client.ContainerInspectResult{Container: ci}, nil
},
})
cmd := newPortCommand(cli)
+13 -8
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -11,6 +12,7 @@ import (
"github.com/docker/cli/internal/prompt"
"github.com/docker/cli/opts"
"github.com/docker/go-units"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -60,7 +62,7 @@ func newPruneCommand(dockerCLI command.Cli) *cobra.Command {
const warning = `WARNING! This will remove all stopped containers.
Are you sure you want to continue?`
func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, err error) {
func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions) (spaceReclaimed uint64, output string, _ error) {
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
if !options.force {
@@ -73,20 +75,23 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
}
}
report, err := dockerCli.Client().ContainersPrune(ctx, pruneFilters)
res, err := dockerCli.Client().ContainerPrune(ctx, client.ContainerPruneOptions{
Filters: pruneFilters,
})
if err != nil {
return 0, "", err
}
if len(report.ContainersDeleted) > 0 {
output = "Deleted Containers:\n"
for _, id := range report.ContainersDeleted {
output += id + "\n"
var out strings.Builder
if len(res.Report.ContainersDeleted) > 0 {
out.WriteString("Deleted Containers:\n")
for _, id := range res.Report.ContainersDeleted {
out.WriteString(id + "\n")
}
spaceReclaimed = report.SpaceReclaimed
spaceReclaimed = res.Report.SpaceReclaimed
}
return spaceReclaimed, output, nil
return spaceReclaimed, out.String(), nil
}
type cancelledErr struct{ error }
+2 -3
View File
@@ -7,7 +7,6 @@ import (
"testing"
"github.com/docker/cli/internal/test"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
)
@@ -16,8 +15,8 @@ func TestContainerPrunePromptTermination(t *testing.T) {
t.Cleanup(cancel)
cli := test.NewFakeCli(&fakeClient{
containerPruneFunc: func(ctx context.Context, pruneFilters client.Filters) (container.PruneReport, error) {
return container.PruneReport{}, errors.New("fakeClient containerPruneFunc should not be called")
containerPruneFunc: func(ctx context.Context, opts client.ContainerPruneOptions) (client.ContainerPruneResult, error) {
return client.ContainerPruneResult{}, errors.New("fakeClient containerPruneFunc should not be called")
},
})
cmd := newPruneCommand(cli)
+9 -17
View File
@@ -1,14 +1,12 @@
package container
import (
"context"
"errors"
"fmt"
"strings"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -19,7 +17,14 @@ func newRenameCommand(dockerCLI command.Cli) *cobra.Command {
Short: "Rename a container",
Args: cli.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
return runRename(cmd.Context(), dockerCLI, args[0], args[1])
oldName, newName := args[0], args[1]
_, err := dockerCLI.Client().ContainerRename(cmd.Context(), oldName, client.ContainerRenameOptions{
NewName: newName,
})
if err != nil {
return fmt.Errorf("failed to rename container: %w", err)
}
return nil
},
Annotations: map[string]string{
"aliases": "docker container rename, docker rename",
@@ -29,16 +34,3 @@ func newRenameCommand(dockerCLI command.Cli) *cobra.Command {
}
return cmd
}
func runRename(ctx context.Context, dockerCLI command.Cli, oldName, newName string) error {
newName = strings.TrimSpace(newName)
if newName == "" {
// TODO(thaJeztah): improve validation in ContainerRename and daemon; the daemon returns an obscure error when providing whitespace-only new-name:
// Error response from daemon: Error when allocating new name: Invalid container name (/ ), only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed
return errors.New("new name cannot be blank")
}
if err := dockerCLI.Client().ContainerRename(ctx, oldName, newName); err != nil {
return fmt.Errorf("failed to rename container: %w", err)
}
return nil
}
-62
View File
@@ -1,62 +0,0 @@
package container
import (
"context"
"errors"
"io"
"testing"
"github.com/docker/cli/internal/test"
"gotest.tools/v3/assert"
)
func TestRunRename(t *testing.T) {
testcases := []struct {
doc, oldName, newName, expectedErr string
}{
{
doc: "success",
oldName: "oldName",
newName: "newName",
expectedErr: "",
},
{
doc: "empty old name",
oldName: "",
newName: "newName",
expectedErr: "invalid container name or ID: value is empty",
},
{
doc: "empty new name",
oldName: "oldName",
newName: "",
expectedErr: "new name cannot be blank",
},
}
for _, tc := range testcases {
t.Run(tc.doc, func(t *testing.T) {
cli := test.NewFakeCli(&fakeClient{
containerRenameFunc: func(ctx context.Context, oldName, newName string) error {
if oldName == "" {
return errors.New("invalid container name or ID: value is empty")
}
return nil
},
})
cmd := newRenameCommand(cli)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
cmd.SetArgs([]string{tc.oldName, tc.newName})
err := cmd.Execute()
if tc.expectedErr != "" {
assert.ErrorContains(t, err, tc.expectedErr)
} else {
assert.NilError(t, err)
}
})
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ func runRestart(ctx context.Context, dockerCLI command.Cli, opts *restartOptions
var errs []error
// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
for _, name := range opts.containers {
err := apiClient.ContainerRestart(ctx, name, client.ContainerStopOptions{
_, err := apiClient.ContainerRestart(ctx, name, client.ContainerRestartOptions{
Signal: opts.signal,
Timeout: timeout,
})
+7 -7
View File
@@ -19,7 +19,7 @@ func TestRestart(t *testing.T) {
name string
args []string
restarted []string
expectedOpts client.ContainerStopOptions
expectedOpts client.ContainerRestartOptions
expectedErr string
}{
{
@@ -36,19 +36,19 @@ func TestRestart(t *testing.T) {
{
name: "with -t",
args: []string{"-t", "2", "container-1"},
expectedOpts: client.ContainerStopOptions{Timeout: func(to int) *int { return &to }(2)},
expectedOpts: client.ContainerRestartOptions{Timeout: func(to int) *int { return &to }(2)},
restarted: []string{"container-1"},
},
{
name: "with --timeout",
args: []string{"--timeout", "2", "container-1"},
expectedOpts: client.ContainerStopOptions{Timeout: func(to int) *int { return &to }(2)},
expectedOpts: client.ContainerRestartOptions{Timeout: func(to int) *int { return &to }(2)},
restarted: []string{"container-1"},
},
{
name: "with --time",
args: []string{"--time", "2", "container-1"},
expectedOpts: client.ContainerStopOptions{Timeout: func(to int) *int { return &to }(2)},
expectedOpts: client.ContainerRestartOptions{Timeout: func(to int) *int { return &to }(2)},
restarted: []string{"container-1"},
},
{
@@ -62,17 +62,17 @@ func TestRestart(t *testing.T) {
mutex := new(sync.Mutex)
cli := test.NewFakeCli(&fakeClient{
containerRestartFunc: func(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
containerRestartFunc: func(ctx context.Context, containerID string, options client.ContainerRestartOptions) (client.ContainerRestartResult, error) {
assert.Check(t, is.DeepEqual(options, tc.expectedOpts))
if containerID == "nosuchcontainer" {
return notFound(errors.New("Error: no such container: " + containerID))
return client.ContainerRestartResult{}, notFound(errors.New("Error: no such container: " + containerID))
}
// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
mutex.Lock()
restarted = append(restarted, containerID)
mutex.Unlock()
return nil
return client.ContainerRestartResult{}, nil
},
Version: "1.36",
})
+2 -1
View File
@@ -67,11 +67,12 @@ func runRm(ctx context.Context, dockerCLI command.Cli, opts *rmOptions) error {
if ctrID == "" {
return errors.New("container name cannot be empty")
}
return apiClient.ContainerRemove(ctx, ctrID, client.ContainerRemoveOptions{
_, err := apiClient.ContainerRemove(ctx, ctrID, client.ContainerRemoveOptions{
RemoveVolumes: opts.rmVolumes,
RemoveLinks: opts.rmLink,
Force: opts.force,
})
return err
})
var errs []error
+3 -3
View File
@@ -27,7 +27,7 @@ func TestRemoveForce(t *testing.T) {
mutex := new(sync.Mutex)
cli := test.NewFakeCli(&fakeClient{
containerRemoveFunc: func(ctx context.Context, container string, options client.ContainerRemoveOptions) error {
containerRemoveFunc: func(ctx context.Context, container string, options client.ContainerRemoveOptions) (client.ContainerRemoveResult, error) {
// containerRemoveFunc is called in parallel for each container
// by the remove command so append must be synchronized.
mutex.Lock()
@@ -35,9 +35,9 @@ func TestRemoveForce(t *testing.T) {
mutex.Unlock()
if container == "nosuchcontainer" {
return notFound(errors.New("Error: no such container: " + container))
return client.ContainerRemoveResult{}, notFound(errors.New("Error: no such container: " + container))
}
return nil
return client.ContainerRemoveResult{}, nil
},
Version: "1.36",
})
+7 -5
View File
@@ -12,7 +12,6 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/trust"
"github.com/docker/cli/opts"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
@@ -73,7 +72,10 @@ func newRunCommand(dockerCLI command.Cli) *cobra.Command {
// TODO(thaJeztah): consider adding platform as "image create option" on containerOptions
flags.StringVar(&options.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Set platform if server is multi-platform capable")
_ = flags.SetAnnotation("platform", "version", []string{"1.32"})
flags.BoolVar(&options.untrusted, "disable-content-trust", !trust.Enabled(), "Skip image verification")
// TODO(thaJeztah): DEPRECATED: remove in v29.1 or v30
flags.Bool("disable-content-trust", true, "Skip image verification (deprecated)")
_ = flags.MarkDeprecated("disable-content-trust", "support for docker content trust was removed")
copts = addFlags(flags)
_ = cmd.RegisterFlagCompletionFunc("detach-keys", completeDetachKeys)
@@ -193,7 +195,7 @@ func runContainer(ctx context.Context, dockerCli command.Cli, runOpts *runOption
statusChan := waitExitOrRemoved(statusCtx, apiClient, containerID, copts.autoRemove)
// start the container
if err := apiClient.ContainerStart(ctx, containerID, client.ContainerStartOptions{}); err != nil {
if _, err := apiClient.ContainerStart(ctx, containerID, client.ContainerStartOptions{}); err != nil {
// If we have hijackedIOStreamer, we should notify
// hijackedIOStreamer we are going to exit and wait
// to avoid the terminal are not restored.
@@ -290,7 +292,7 @@ func attachContainer(ctx context.Context, dockerCli command.Cli, containerID str
inputStream: in,
outputStream: out,
errorStream: cerr,
resp: resp,
resp: resp.HijackedResponse,
tty: config.Tty,
detachKeys: options.DetachKeys,
}
@@ -301,7 +303,7 @@ func attachContainer(ctx context.Context, dockerCli command.Cli, containerID str
return errAttach
}()
}()
return resp.Close, nil
return resp.HijackedResponse.Close, nil
}
// withHelp decorates the error with a suggestion to use "--help".
+35 -89
View File
@@ -13,14 +13,11 @@ import (
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/test"
"github.com/docker/cli/internal/test/notary"
"github.com/moby/moby/api/pkg/progress"
"github.com/moby/moby/api/pkg/streamformatter"
"github.com/moby/moby/api/types"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/moby/moby/client/pkg/progress"
"github.com/moby/moby/client/pkg/streamformatter"
"github.com/spf13/pflag"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
@@ -56,10 +53,8 @@ func TestRunValidateFlags(t *testing.T) {
func TestRunLabel(t *testing.T) {
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *ocispec.Platform, _ string) (container.CreateResponse, error) {
return container.CreateResponse{
ID: "id",
}, nil
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
return client.ContainerCreateResult{ID: "id"}, nil
},
Version: client.MaxAPIVersion,
})
@@ -79,28 +74,31 @@ func TestRunAttach(t *testing.T) {
var conn net.Conn
attachCh := make(chan struct{})
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *ocispec.Platform, _ string) (container.CreateResponse, error) {
return container.CreateResponse{
ID: "id",
}, nil
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
return client.ContainerCreateResult{ID: "id"}, nil
},
containerAttachFunc: func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.HijackedResponse, error) {
containerAttachFunc: func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error) {
server, clientConn := net.Pipe()
conn = server
t.Cleanup(func() {
_ = server.Close()
})
attachCh <- struct{}{}
return client.NewHijackedResponse(clientConn, types.MediaTypeRawStream), nil
return client.ContainerAttachResult{
HijackedResponse: client.NewHijackedResponse(clientConn, types.MediaTypeRawStream),
}, nil
},
waitFunc: func(_ string) (<-chan container.WaitResponse, <-chan error) {
waitFunc: func(_ string) client.ContainerWaitResult {
responseChan := make(chan container.WaitResponse, 1)
errChan := make(chan error)
responseChan <- container.WaitResponse{
StatusCode: 33,
}
return responseChan, errChan
return client.ContainerWaitResult{
Result: responseChan,
Error: errChan,
}
},
// use new (non-legacy) wait API
// see: https://github.com/docker/cli/commit/38591f20d07795aaef45d400df89ca12f29c603b
@@ -150,34 +148,37 @@ func TestRunAttachTermination(t *testing.T) {
killCh := make(chan struct{})
attachCh := make(chan struct{})
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(_ *container.Config, _ *container.HostConfig, _ *network.NetworkingConfig, _ *ocispec.Platform, _ string) (container.CreateResponse, error) {
return container.CreateResponse{
ID: "id",
}, nil
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
return client.ContainerCreateResult{ID: "id"}, nil
},
containerKillFunc: func(ctx context.Context, containerID, sig string) error {
if sig == "TERM" {
containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
if options.Signal == "TERM" {
close(killCh)
}
return nil
return client.ContainerKillResult{}, nil
},
containerAttachFunc: func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.HijackedResponse, error) {
containerAttachFunc: func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error) {
server, clientConn := net.Pipe()
conn = server
t.Cleanup(func() {
_ = server.Close()
})
attachCh <- struct{}{}
return client.NewHijackedResponse(clientConn, types.MediaTypeRawStream), nil
return client.ContainerAttachResult{
HijackedResponse: client.NewHijackedResponse(clientConn, types.MediaTypeRawStream),
}, nil
},
waitFunc: func(_ string) (<-chan container.WaitResponse, <-chan error) {
waitFunc: func(_ string) client.ContainerWaitResult {
responseChan := make(chan container.WaitResponse, 1)
errChan := make(chan error)
<-killCh
responseChan <- container.WaitResponse{
StatusCode: 130,
}
return responseChan, errChan
return client.ContainerWaitResult{
Result: responseChan,
Error: errChan,
}
},
// use new (non-legacy) wait API
// see: https://github.com/docker/cli/commit/38591f20d07795aaef45d400df89ca12f29c603b
@@ -227,15 +228,13 @@ func TestRunPullTermination(t *testing.T) {
attachCh := make(chan struct{})
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform, containerName string,
) (container.CreateResponse, error) {
return container.CreateResponse{}, errors.New("shouldn't try to create a container")
createContainerFunc: func(options client.ContainerCreateOptions) (client.ContainerCreateResult, error) {
return client.ContainerCreateResult{}, errors.New("shouldn't try to create a container")
},
containerAttachFunc: func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.HijackedResponse, error) {
return client.HijackedResponse{}, errors.New("shouldn't try to attach to a container")
containerAttachFunc: func(ctx context.Context, containerID string, options client.ContainerAttachOptions) (client.ContainerAttachResult, error) {
return client.ContainerAttachResult{}, errors.New("shouldn't try to attach to a container")
},
imageCreateFunc: func(ctx context.Context, parentReference string, options client.ImageCreateOptions) (io.ReadCloser, error) {
imagePullFunc: func(ctx context.Context, parentReference string, options client.ImagePullOptions) (client.ImagePullResponse, error) {
server, respReader := net.Pipe()
t.Cleanup(func() {
_ = server.Close()
@@ -260,7 +259,7 @@ func TestRunPullTermination(t *testing.T) {
}
}()
attachCh <- struct{}{}
return respReader, nil
return fakeStreamResult{ReadCloser: respReader}, nil
},
Version: client.MaxAPIVersion,
})
@@ -295,59 +294,6 @@ func TestRunPullTermination(t *testing.T) {
}
}
func TestRunCommandWithContentTrustErrors(t *testing.T) {
testCases := []struct {
name string
args []string
expectedError string
notaryFunc test.NotaryClientFuncType
}{
{
name: "offline-notary-server",
notaryFunc: notary.GetOfflineNotaryRepository,
expectedError: "client is offline",
args: []string{"image:tag"},
},
{
name: "uninitialized-notary-server",
notaryFunc: notary.GetUninitializedNotaryRepository,
expectedError: "remote trust data does not exist",
args: []string{"image:tag"},
},
{
name: "empty-notary-server",
notaryFunc: notary.GetEmptyTargetsNotaryRepository,
expectedError: "No valid trust data for tag",
args: []string{"image:tag"},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Setenv("DOCKER_CONTENT_TRUST", "true")
fakeCLI := test.NewFakeCli(&fakeClient{
createContainerFunc: func(config *container.Config,
hostConfig *container.HostConfig,
networkingConfig *network.NetworkingConfig,
platform *ocispec.Platform,
containerName string,
) (container.CreateResponse, error) {
return container.CreateResponse{}, errors.New("shouldn't try to pull image")
},
})
fakeCLI.SetNotaryClient(tc.notaryFunc)
cmd := newRunCommand(fakeCLI)
cmd.SetArgs(tc.args)
cmd.SetOut(io.Discard)
cmd.SetErr(io.Discard)
err := cmd.Execute()
statusErr := cli.StatusError{}
assert.Check(t, errors.As(err, &statusErr))
assert.Check(t, is.Equal(statusErr.StatusCode, 125))
assert.Check(t, is.ErrorContains(err, tc.expectedError))
})
}
}
func TestRunContainerImagePullPolicyInvalid(t *testing.T) {
cases := []struct {
PullPolicy string
+4 -1
View File
@@ -48,7 +48,10 @@ func ForwardAllSignals(ctx context.Context, apiClient client.ContainerAPIClient,
continue
}
if err := apiClient.ContainerKill(ctx, cid, sig); err != nil {
_, err := apiClient.ContainerKill(ctx, cid, client.ContainerKillOptions{
Signal: sig,
})
if err != nil {
logrus.Debugf("Error sending signal: %s", err)
}
}
+3 -2
View File
@@ -6,6 +6,7 @@ import (
"testing"
"time"
"github.com/moby/moby/client"
"github.com/moby/sys/signal"
)
@@ -14,9 +15,9 @@ func TestForwardSignals(t *testing.T) {
defer cancel()
called := make(chan struct{})
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container, signal string) error {
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
close(called)
return nil
return client.ContainerKillResult{}, nil
}}
sigc := make(chan os.Signal)
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/moby/moby/client"
"golang.org/x/sys/unix"
"gotest.tools/v3/assert"
)
@@ -22,9 +23,9 @@ func TestIgnoredSignals(t *testing.T) {
defer cancel()
var called bool
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container, signal string) error {
apiClient := &fakeClient{containerKillFunc: func(ctx context.Context, container string, options client.ContainerKillOptions) (client.ContainerKillResult, error) {
called = true
return nil
return client.ContainerKillResult{}, nil
}}
sigc := make(chan os.Signal)
+18 -16
View File
@@ -80,16 +80,16 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
// 2. Attach to the container.
ctr := opts.Containers[0]
c, err := dockerCli.Client().ContainerInspect(ctx, ctr)
c, err := dockerCli.Client().ContainerInspect(ctx, ctr, client.ContainerInspectOptions{})
if err != nil {
return err
}
// We always use c.ID instead of container to maintain consistency during `docker start`
if !c.Config.Tty {
if !c.Container.Config.Tty {
sigc := notifyAllSignals()
bgCtx := context.WithoutCancel(ctx)
go ForwardAllSignals(bgCtx, dockerCli.Client(), c.ID, sigc)
go ForwardAllSignals(bgCtx, dockerCli.Client(), c.Container.ID, sigc)
defer signal.StopCatch(sigc)
}
@@ -100,7 +100,7 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
options := client.ContainerAttachOptions{
Stream: true,
Stdin: opts.OpenStdin && c.Config.OpenStdin,
Stdin: opts.OpenStdin && c.Container.Config.OpenStdin,
Stdout: true,
Stderr: true,
DetachKeys: detachKeys,
@@ -112,11 +112,11 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
in = dockerCli.In()
}
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options)
resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.Container.ID, options)
if errAttach != nil {
return errAttach
}
defer resp.Close()
defer resp.HijackedResponse.Close()
cErr := make(chan error, 1)
@@ -127,8 +127,8 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
inputStream: in,
outputStream: dockerCli.Out(),
errorStream: dockerCli.Err(),
resp: resp,
tty: c.Config.Tty,
resp: resp.HijackedResponse,
tty: c.Container.Config.Tty,
detachKeys: options.DetachKeys,
}
@@ -142,17 +142,17 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
// 3. We should open a channel for receiving status code of the container
// no matter it's detached, removed on daemon side(--rm) or exit normally.
statusChan := waitExitOrRemoved(ctx, dockerCli.Client(), c.ID, c.HostConfig.AutoRemove)
statusChan := waitExitOrRemoved(ctx, dockerCli.Client(), c.Container.ID, c.Container.HostConfig.AutoRemove)
// 4. Start the container.
err = dockerCli.Client().ContainerStart(ctx, c.ID, client.ContainerStartOptions{
_, err = dockerCli.Client().ContainerStart(ctx, c.Container.ID, client.ContainerStartOptions{
CheckpointID: opts.Checkpoint,
CheckpointDir: opts.CheckpointDir,
})
if err != nil {
cancelFun()
<-cErr
if c.HostConfig.AutoRemove {
if c.Container.HostConfig.AutoRemove {
// wait container to be removed
<-statusChan
}
@@ -160,13 +160,14 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
}
// 5. Wait for attachment to break.
if c.Config.Tty && dockerCli.Out().IsTerminal() {
if err := MonitorTtySize(ctx, dockerCli, c.ID, false); err != nil {
if c.Container.Config.Tty && dockerCli.Out().IsTerminal() {
if err := MonitorTtySize(ctx, dockerCli, c.Container.ID, false); err != nil {
_, _ = fmt.Fprintln(dockerCli.Err(), "Error monitoring TTY size:", err)
}
}
if attachErr := <-cErr; attachErr != nil {
if _, ok := attachErr.(term.EscapeError); ok {
var escapeError term.EscapeError
if errors.As(attachErr, &escapeError) {
// The user entered the detach escape sequence.
return nil
}
@@ -182,10 +183,11 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
return errors.New("you cannot restore multiple containers at once")
}
ctr := opts.Containers[0]
return dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{
_, err := dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{
CheckpointID: opts.Checkpoint,
CheckpointDir: opts.CheckpointDir,
})
return err
default:
// We're not going to attach to anything.
// Start as many containers as we want.
@@ -196,7 +198,7 @@ func RunStart(ctx context.Context, dockerCli command.Cli, opts *StartOptions) er
func startContainersWithoutAttachments(ctx context.Context, dockerCli command.Cli, containers []string) error {
var failedContainers []string
for _, ctr := range containers {
if err := dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{}); err != nil {
if _, err := dockerCli.Client().ContainerStart(ctx, ctr, client.ContainerStartOptions{}); err != nil {
_, _ = fmt.Fprintln(dockerCli.Err(), err)
failedContainers = append(failedContainers, ctr)
continue
+89 -61
View File
@@ -11,6 +11,7 @@ import (
"time"
"github.com/containerd/errdefs"
"github.com/containerd/log"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
@@ -18,7 +19,6 @@ import (
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/moby/moby/api/types/events"
"github.com/moby/moby/client"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
@@ -108,10 +108,17 @@ var acceptedStatsFilters = map[string]bool{
func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions) error {
apiClient := dockerCLI.Client()
// Get the daemonOSType to handle platform-specific stats fields.
// This value is used as a fallback for docker < v29, which did not
// include the OSType field per stats.
daemonOSType = dockerCLI.ServerInfo().OSType
// 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.
closeChan := make(chan error)
// closeChan is used to collect errors from goroutines. It uses a small buffer
// to avoid blocking sends when sends occur after closeChan is set to nil or
// after the reader has exited, preventing deadlocks.
closeChan := make(chan error, 4)
cStats := stats{}
showAll := len(options.Containers) == 0
@@ -136,24 +143,34 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
eh := newEventHandler()
if options.All {
eh.setHandler(events.ActionCreate, func(e events.Message) {
s := NewStats(e.Actor.ID)
if cStats.add(s) {
if s := NewStats(e.Actor.ID); cStats.add(s) {
waitFirst.Add(1)
log.G(ctx).WithFields(log.Fields{
"event": e.Action,
"container": e.Actor.ID,
}).Debug("collecting stats for container")
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
}
})
}
eh.setHandler(events.ActionStart, func(e events.Message) {
s := NewStats(e.Actor.ID)
if cStats.add(s) {
if s := NewStats(e.Actor.ID); cStats.add(s) {
waitFirst.Add(1)
log.G(ctx).WithFields(log.Fields{
"event": e.Action,
"container": e.Actor.ID,
}).Debug("collecting stats for container")
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
}
})
if !options.All {
eh.setHandler(events.ActionDie, func(e events.Message) {
log.G(ctx).WithFields(log.Fields{
"event": e.Action,
"container": e.Actor.ID,
}).Debug("stop collecting stats for container")
cStats.remove(e.Actor.ID)
})
}
@@ -166,7 +183,7 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
// to list containers and to filter events, but the "type" filter
// is not valid for filtering containers.
f := options.Filters.Clone().Add("type", string(events.ContainerEventType))
eventChan, errChan := apiClient.Events(ctx, client.EventsListOptions{
res := apiClient.Events(ctx, client.EventsListOptions{
Filters: f,
})
@@ -179,10 +196,17 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
select {
case <-stopped:
return
case event := <-eventChan:
case <-ctx.Done():
return
case event := <-res.Messages:
c <- event
case err := <-errChan:
closeChan <- err
case err := <-res.Err:
// Prevent blocking if closeChan is full or unread
select {
case closeChan <- err:
default:
// drop if not read; avoids deadlock
}
return
}
}
@@ -205,10 +229,12 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
if err != nil {
return err
}
for _, ctr := range cs {
s := NewStats(ctr.ID)
if cStats.add(s) {
for _, ctr := range cs.Items {
if s := NewStats(ctr.ID); cStats.add(s) {
waitFirst.Add(1)
log.G(ctx).WithFields(log.Fields{
"container": ctr.ID,
}).Debug("collecting stats for container")
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
}
}
@@ -227,15 +253,18 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
// Create the list of containers, and start collecting stats for all
// containers passed.
for _, ctr := range options.Containers {
s := NewStats(ctr)
if cStats.add(s) {
if s := NewStats(ctr); cStats.add(s) {
waitFirst.Add(1)
log.G(ctx).WithFields(log.Fields{
"container": ctr,
}).Debug("collecting stats for container")
go collect(ctx, s, apiClient, !options.NoStream, waitFirst)
}
}
// We don't expect any asynchronous errors: closeChan can be closed.
// We don't expect any asynchronous errors: closeChan can be closed and disabled.
close(closeChan)
closeChan = nil
// make sure each container get at least one valid stat data
waitFirst.Wait()
@@ -254,19 +283,13 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
}
format := options.Format
if len(format) == 0 {
if format == "" {
if len(dockerCLI.ConfigFile().StatsFormat) > 0 {
format = dockerCLI.ConfigFile().StatsFormat
} else {
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
}
// Buffer to store formatted stats text.
// Once formatted, it will be printed in one write to avoid screen flickering.
@@ -277,63 +300,68 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
Format: NewStatsFormat(format, daemonOSType),
}
var err error
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for range ticker.C {
var ccStats []StatsEntry
if options.NoStream {
cStats.mu.RLock()
ccStats := make([]StatsEntry, 0, len(cStats.cs))
for _, c := range cStats.cs {
ccStats = append(ccStats, c.GetStatistics())
}
cStats.mu.RUnlock()
if !options.NoStream {
if len(ccStats) == 0 {
return nil
}
if err := statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
return err
}
_, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String())
return nil
}
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for {
select {
case <-ticker.C:
cStats.mu.RLock()
ccStats := make([]StatsEntry, 0, len(cStats.cs))
for _, c := range cStats.cs {
ccStats = append(ccStats, c.GetStatistics())
}
cStats.mu.RUnlock()
// Start by moving the cursor to the top-left
_, _ = fmt.Fprint(&statsTextBuffer, "\033[H")
}
if err = statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
break
}
if err := statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
return err
}
if !options.NoStream {
for _, line := range strings.Split(statsTextBuffer.String(), "\n") {
// In case the new text is shorter than the one we are writing over,
// we'll append the "erase line" escape sequence to clear the remaining text.
_, _ = fmt.Fprintln(&statsTextBuffer, line, "\033[K")
}
// We might have fewer containers than before, so let's clear the remaining text
_, _ = fmt.Fprint(&statsTextBuffer, "\033[J")
}
_, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String())
statsTextBuffer.Reset()
_, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String())
statsTextBuffer.Reset()
if len(cStats.cs) == 0 && !showAll {
break
}
if options.NoStream {
break
}
select {
case err, ok := <-closeChan:
if ok {
if err != nil {
// Suppress "unexpected EOF" errors in the CLI so that
// it shuts down cleanly when the daemon restarts.
if errors.Is(err, io.ErrUnexpectedEOF) {
return nil
}
return err
}
if len(ccStats) == 0 && !showAll {
return nil
}
default:
// just skip
case err, ok := <-closeChan:
if !ok || err == nil || errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
// Suppress "unexpected EOF" errors in the CLI so that
// it shuts down cleanly when the daemon restarts.
return nil
}
return err
case <-ctx.Done():
return ctx.Err()
}
}
return err
}
// newEventHandler initializes and returns an eventHandler
@@ -360,11 +388,11 @@ func (eh *eventHandler) watch(c <-chan events.Message) {
continue
}
if e.Actor.ID == "" {
logrus.WithField("event", e).Errorf("event handler: received %s event with empty ID", e.Action)
log.G(context.TODO()).WithField("event", e).Errorf("event handler: received %s event with empty ID", e.Action)
continue
}
logrus.WithField("event", e).Debugf("event handler: received %s event for: %s", e.Action, e.Actor.ID)
log.G(context.TODO()).WithField("event", e).Debugf("event handler: received %s event for: %s", e.Action, e.Actor.ID)
go h(e)
}
}
+59 -60
View File
@@ -10,7 +10,6 @@ import (
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/sirupsen/logrus"
)
type stats struct {
@@ -50,41 +49,35 @@ func (s *stats) isKnownContainer(cid string) (int, bool) {
return -1, false
}
func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, streamStats bool, waitFirst *sync.WaitGroup) {
logrus.Debugf("collecting stats for %s", s.Container)
var (
getFirst bool
previousCPU uint64
previousSystem uint64
u = make(chan error, 1)
)
func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, streamStats bool, waitFirst *sync.WaitGroup) { //nolint:gocyclo
var getFirst bool
defer func() {
// if error happens and we get nothing of stats, release wait group whatever
// if error happens, and we get nothing of stats, release wait group whatever
if !getFirst {
getFirst = true
waitFirst.Done()
}
}()
response, err := cli.ContainerStats(ctx, s.Container, streamStats)
response, err := cli.ContainerStats(ctx, s.Container, client.ContainerStatsOptions{
Stream: streamStats,
IncludePreviousSample: !streamStats, // collect previous CPU value for the first result when not streaming.
})
if err != nil {
s.SetError(err)
return
}
defer response.Body.Close()
dec := json.NewDecoder(response.Body)
u := make(chan error, 1)
go func() {
defer response.Body.Close()
dec := json.NewDecoder(response.Body)
for {
var (
v *container.StatsResponse
memPercent, cpuPercent float64
blkRead, blkWrite uint64 // Only used on Linux
mem, memLimit float64
pidsStatsCurrent uint64
)
if ctx.Err() != nil {
return
}
var v container.StatsResponse
if err := dec.Decode(&v); err != nil {
dec = json.NewDecoder(io.MultiReader(dec.Buffered(), response.Body))
u <- err
@@ -95,37 +88,42 @@ func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, strea
continue
}
daemonOSType = response.OSType
// Daemon versions before v29 did not return per-stats OSType;
// fall back to using the daemon's OSType.
if v.OSType == "" {
v.OSType = daemonOSType
}
if daemonOSType != "windows" {
previousCPU = v.PreCPUStats.CPUUsage.TotalUsage
previousSystem = v.PreCPUStats.SystemUsage
cpuPercent = calculateCPUPercentUnix(previousCPU, previousSystem, v)
blkRead, blkWrite = calculateBlockIO(v.BlkioStats)
mem = calculateMemUsageUnixNoCache(v.MemoryStats)
memLimit = float64(v.MemoryStats.Limit)
memPercent = calculateMemPercentUnixNoCache(memLimit, mem)
pidsStatsCurrent = v.PidsStats.Current
if daemonOSType == "windows" {
netRx, netTx := calculateNetwork(v.Networks)
s.SetStatistics(StatsEntry{
Name: v.Name,
ID: v.ID,
CPUPercentage: calculateCPUPercentWindows(&v),
Memory: float64(v.MemoryStats.PrivateWorkingSet),
NetworkRx: netRx,
NetworkTx: netTx,
BlockRead: float64(v.StorageStats.ReadSizeBytes),
BlockWrite: float64(v.StorageStats.WriteSizeBytes),
})
} else {
cpuPercent = calculateCPUPercentWindows(v)
blkRead = v.StorageStats.ReadSizeBytes
blkWrite = v.StorageStats.WriteSizeBytes
mem = float64(v.MemoryStats.PrivateWorkingSet)
memUsage := calculateMemUsageUnixNoCache(v.MemoryStats)
netRx, netTx := calculateNetwork(v.Networks)
blkRead, blkWrite := calculateBlockIO(v.BlkioStats)
s.SetStatistics(StatsEntry{
Name: v.Name,
ID: v.ID,
CPUPercentage: calculateCPUPercentUnix(v.PreCPUStats, v.CPUStats),
Memory: memUsage,
MemoryPercentage: calculateMemPercentUnixNoCache(float64(v.MemoryStats.Limit), memUsage),
MemoryLimit: float64(v.MemoryStats.Limit),
NetworkRx: netRx,
NetworkTx: netTx,
BlockRead: float64(blkRead),
BlockWrite: float64(blkWrite),
PidsCurrent: v.PidsStats.Current,
})
}
netRx, netTx := calculateNetwork(v.Networks)
s.SetStatistics(StatsEntry{
Name: v.Name,
ID: v.ID,
CPUPercentage: cpuPercent,
Memory: mem,
MemoryPercentage: memPercent,
MemoryLimit: memLimit,
NetworkRx: netRx,
NetworkTx: netTx,
BlockRead: float64(blkRead),
BlockWrite: float64(blkWrite),
PidsCurrent: pidsStatsCurrent,
})
u <- nil
if !streamStats {
return
@@ -145,8 +143,8 @@ func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, strea
}
case err := <-u:
s.SetError(err)
if err == io.EOF {
break
if errors.Is(err, io.EOF) {
return
}
if err != nil {
continue
@@ -156,6 +154,9 @@ func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, strea
getFirst = true
waitFirst.Done()
}
case <-ctx.Done():
s.SetError(ctx.Err())
return
}
if !streamStats {
return
@@ -163,18 +164,18 @@ func collect(ctx context.Context, s *Stats, cli client.ContainerAPIClient, strea
}
}
func calculateCPUPercentUnix(previousCPU, previousSystem uint64, v *container.StatsResponse) float64 {
func calculateCPUPercentUnix(previousCPU container.CPUStats, curCPUStats container.CPUStats) float64 {
var (
cpuPercent = 0.0
// calculate the change for the cpu usage of the container in between readings
cpuDelta = float64(v.CPUStats.CPUUsage.TotalUsage) - float64(previousCPU)
cpuDelta = float64(curCPUStats.CPUUsage.TotalUsage) - float64(previousCPU.CPUUsage.TotalUsage)
// calculate the change for the entire system between readings
systemDelta = float64(v.CPUStats.SystemUsage) - float64(previousSystem)
onlineCPUs = float64(v.CPUStats.OnlineCPUs)
systemDelta = float64(curCPUStats.SystemUsage) - float64(previousCPU.SystemUsage)
onlineCPUs = float64(curCPUStats.OnlineCPUs)
)
if onlineCPUs == 0.0 {
onlineCPUs = float64(len(v.CPUStats.CPUUsage.PercpuUsage))
onlineCPUs = float64(len(curCPUStats.CPUUsage.PercpuUsage))
}
if systemDelta > 0.0 && cpuDelta > 0.0 {
cpuPercent = (cpuDelta / systemDelta) * onlineCPUs * 100.0
@@ -186,13 +187,11 @@ func calculateCPUPercentWindows(v *container.StatsResponse) float64 {
// Max number of 100ns intervals between the previous time read and now
possIntervals := uint64(v.Read.Sub(v.PreRead).Nanoseconds()) // Start with number of ns intervals
possIntervals /= 100 // Convert to number of 100ns intervals
possIntervals *= uint64(v.NumProcs) // Multiple by the number of processors
// Intervals used
intervalsUsed := v.CPUStats.CPUUsage.TotalUsage - v.PreCPUStats.CPUUsage.TotalUsage
possIntervals *= uint64(v.NumProcs) // Multiply by the number of processors
// Percentage avoiding divide-by-zero
if possIntervals > 0 {
intervalsUsed := v.CPUStats.CPUUsage.TotalUsage - v.PreCPUStats.CPUUsage.TotalUsage
return float64(intervalsUsed) / float64(possIntervals) * 100.0
}
return 0.00
+2 -1
View File
@@ -64,10 +64,11 @@ func runStop(ctx context.Context, dockerCLI command.Cli, opts *stopOptions) erro
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, id string) error {
return apiClient.ContainerStop(ctx, id, client.ContainerStopOptions{
_, err := apiClient.ContainerStop(ctx, id, client.ContainerStopOptions{
Signal: opts.signal,
Timeout: timeout,
})
return err
})
var errs []error
for _, ctr := range opts.containers {
+3 -3
View File
@@ -62,10 +62,10 @@ func TestStop(t *testing.T) {
mutex := new(sync.Mutex)
cli := test.NewFakeCli(&fakeClient{
containerStopFunc: func(ctx context.Context, containerID string, options client.ContainerStopOptions) error {
containerStopFunc: func(ctx context.Context, containerID string, options client.ContainerStopOptions) (client.ContainerStopResult, error) {
assert.Check(t, is.DeepEqual(options, tc.expectedOpts))
if containerID == "nosuchcontainer" {
return notFound(errors.New("Error: no such container: " + containerID))
return client.ContainerStopResult{}, notFound(errors.New("Error: no such container: " + containerID))
}
// containerStopFunc is called in parallel for each container
@@ -73,7 +73,7 @@ func TestStop(t *testing.T) {
mutex.Lock()
stopped = append(stopped, containerID)
mutex.Unlock()
return nil
return client.ContainerStopResult{}, nil
},
Version: "1.36",
})
+7 -4
View File
@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/cli/command/formatter/tabwriter"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -45,17 +46,19 @@ func newTopCommand(dockerCLI command.Cli) *cobra.Command {
}
func runTop(ctx context.Context, dockerCli command.Cli, opts *topOptions) error {
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, opts.args)
procList, err := dockerCli.Client().ContainerTop(ctx, opts.container, client.ContainerTopOptions{
Arguments: opts.args,
})
if err != nil {
return err
}
w := tabwriter.NewWriter(dockerCli.Out(), 20, 1, 3, ' ', 0)
fmt.Fprintln(w, strings.Join(procList.Titles, "\t"))
_, _ = fmt.Fprintln(w, strings.Join(procList.Titles, "\t"))
for _, proc := range procList.Processes {
fmt.Fprintln(w, strings.Join(proc, "\t"))
_, _ = fmt.Fprintln(w, strings.Join(proc, "\t"))
}
w.Flush()
_ = w.Flush()
return nil
}
+26 -19
View File
@@ -14,22 +14,29 @@ import (
"github.com/sirupsen/logrus"
)
// resizeTtyTo resizes tty to specific height and width
func resizeTtyTo(ctx context.Context, apiClient client.ContainerAPIClient, id string, height, width uint, isExec bool) error {
// TODO(thaJeztah): split resizeTTYTo
type resizeClient interface {
client.ExecAPIClient
client.ContainerAPIClient
}
// resizeTTYTo resizes TTY to specific height and width.
func resizeTTYTo(ctx context.Context, apiClient resizeClient, id string, height, width uint, isExec bool) error {
if height == 0 && width == 0 {
return nil
}
options := client.ContainerResizeOptions{
Height: height,
Width: width,
}
var err error
if isExec {
err = apiClient.ContainerExecResize(ctx, id, options)
_, err = apiClient.ExecResize(ctx, id, client.ExecResizeOptions{
Height: height,
Width: width,
})
} else {
err = apiClient.ContainerResize(ctx, id, options)
_, err = apiClient.ContainerResize(ctx, id, client.ContainerResizeOptions{
Height: height,
Width: width,
})
}
if err != nil {
@@ -41,26 +48,26 @@ func resizeTtyTo(ctx context.Context, apiClient client.ContainerAPIClient, id st
// resizeTty is to resize the tty with cli out's tty size
func resizeTty(ctx context.Context, cli command.Cli, id string, isExec bool) error {
height, width := cli.Out().GetTtySize()
return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec)
return resizeTTYTo(ctx, cli.Client(), id, height, width, isExec)
}
// initTtySize is to init the tty's size to the same as the window, if there is an error, it will retry 10 times.
// initTtySize is to init the TTYs size to the same as the window, if there is an error, it will retry 10 times.
func initTtySize(ctx context.Context, cli command.Cli, id string, isExec bool, resizeTtyFunc func(ctx context.Context, cli command.Cli, id string, isExec bool) error) {
rttyFunc := resizeTtyFunc
if rttyFunc == nil {
rttyFunc = resizeTty
rTTYfunc := resizeTtyFunc
if rTTYfunc == nil {
rTTYfunc = resizeTty
}
if err := rttyFunc(ctx, cli, id, isExec); err != nil {
if err := rTTYfunc(ctx, cli, id, isExec); err != nil {
go func() {
var err error
for retry := 0; retry < 10; retry++ {
time.Sleep(time.Duration(retry+1) * 10 * time.Millisecond)
if err = rttyFunc(ctx, cli, id, isExec); err == nil {
if err = rTTYfunc(ctx, cli, id, isExec); err == nil {
break
}
}
if err != nil {
fmt.Fprintln(cli.Err(), "failed to resize tty, using default size")
_, _ = fmt.Fprintln(cli.Err(), "failed to resize tty, using default size")
}
}()
}
@@ -77,7 +84,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool
h, w := cli.Out().GetTtySize()
if prevW != w || prevH != h {
resizeTty(ctx, cli, id, isExec)
_ = resizeTty(ctx, cli, id, isExec)
}
prevH = h
prevW = w
@@ -88,7 +95,7 @@ func MonitorTtySize(ctx context.Context, cli command.Cli, id string, isExec bool
gosignal.Notify(sigchan, signal.SIGWINCH)
go func() {
for range sigchan {
resizeTty(ctx, cli, id, isExec)
_ = resizeTty(ctx, cli, id, isExec)
}
}()
}
+3 -3
View File
@@ -15,12 +15,12 @@ import (
func TestInitTtySizeErrors(t *testing.T) {
expectedError := "failed to resize tty, using default size\n"
fakeContainerExecResizeFunc := func(id string, options client.ContainerResizeOptions) error {
return errors.New("Error response from daemon: no such exec")
fakeContainerExecResizeFunc := func(id string, options client.ExecResizeOptions) (client.ExecResizeResult, error) {
return client.ExecResizeResult{}, errors.New("error response from daemon: no such exec")
}
fakeResizeTtyFunc := func(ctx context.Context, cli command.Cli, id string, isExec bool) error {
height, width := uint(1024), uint(768)
return resizeTtyTo(ctx, cli.Client(), id, height, width, isExec)
return resizeTTYTo(ctx, cli.Client(), id, height, width, isExec)
}
ctx := context.Background()
cli := test.NewFakeCli(&fakeClient{containerExecResizeFunc: fakeContainerExecResizeFunc})
+5 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -41,7 +42,10 @@ func newUnpauseCommand(dockerCLI command.Cli) *cobra.Command {
func runUnpause(ctx context.Context, dockerCLI command.Cli, opts *unpauseOptions) error {
apiClient := dockerCLI.Client()
errChan := parallelOperation(ctx, opts.containers, apiClient.ContainerUnpause)
errChan := parallelOperation(ctx, opts.containers, func(ctx context.Context, container string) error {
_, err := apiClient.ContainerUnpause(ctx, container, client.ContainerUnpauseOptions{})
return err
})
var errs []error
for _, ctr := range opts.containers {
if err := <-errChan; err != nil {
+8 -7
View File
@@ -11,6 +11,7 @@ import (
"github.com/docker/cli/cli/command/completion"
"github.com/docker/cli/opts"
containertypes "github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -60,9 +61,9 @@ func newUpdateCommand(dockerCLI command.Cli) *cobra.Command {
flags.Int64Var(&options.cpuPeriod, "cpu-period", 0, "Limit CPU CFS (Completely Fair Scheduler) period")
flags.Int64Var(&options.cpuQuota, "cpu-quota", 0, "Limit CPU CFS (Completely Fair Scheduler) quota")
flags.Int64Var(&options.cpuRealtimePeriod, "cpu-rt-period", 0, "Limit the CPU real-time period in microseconds")
flags.SetAnnotation("cpu-rt-period", "version", []string{"1.25"})
_ = flags.SetAnnotation("cpu-rt-period", "version", []string{"1.25"})
flags.Int64Var(&options.cpuRealtimeRuntime, "cpu-rt-runtime", 0, "Limit the CPU real-time runtime in microseconds")
flags.SetAnnotation("cpu-rt-runtime", "version", []string{"1.25"})
_ = flags.SetAnnotation("cpu-rt-runtime", "version", []string{"1.25"})
flags.StringVar(&options.cpusetCpus, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
flags.StringVar(&options.cpusetMems, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
flags.Int64VarP(&options.cpuShares, "cpu-shares", "c", 0, "CPU shares (relative weight)")
@@ -72,10 +73,10 @@ func newUpdateCommand(dockerCLI command.Cli) *cobra.Command {
flags.StringVar(&options.restartPolicy, "restart", "", "Restart policy to apply when a container exits")
flags.Int64Var(&options.pidsLimit, "pids-limit", 0, `Tune container pids limit (set -1 for unlimited)`)
flags.SetAnnotation("pids-limit", "version", []string{"1.40"})
_ = flags.SetAnnotation("pids-limit", "version", []string{"1.40"})
flags.Var(&options.cpus, "cpus", "Number of CPUs")
flags.SetAnnotation("cpus", "version", []string{"1.29"})
_ = flags.SetAnnotation("cpus", "version", []string{"1.29"})
_ = cmd.RegisterFlagCompletionFunc("restart", completeRestartPolicies)
@@ -107,8 +108,8 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, options *updateOption
pidsLimit = &options.pidsLimit
}
updateConfig := containertypes.UpdateConfig{
Resources: containertypes.Resources{
updateConfig := client.ContainerUpdateOptions{
Resources: &containertypes.Resources{
BlkioWeight: options.blkioWeight,
CpusetCpus: options.cpusetCpus,
CpusetMems: options.cpusetMems,
@@ -123,7 +124,7 @@ func runUpdate(ctx context.Context, dockerCli command.Cli, options *updateOption
NanoCPUs: options.cpus.Value(),
PidsLimit: pidsLimit,
},
RestartPolicy: restartPolicy,
RestartPolicy: &restartPolicy,
}
var (
+5 -3
View File
@@ -20,7 +20,9 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe
condition = container.WaitConditionRemoved
}
resultC, errC := apiClient.ContainerWait(ctx, containerID, condition)
waitRes := apiClient.ContainerWait(ctx, containerID, client.ContainerWaitOptions{
Condition: condition,
})
statusC := make(chan int)
go func() {
@@ -28,14 +30,14 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe
select {
case <-ctx.Done():
return
case result := <-resultC:
case result := <-waitRes.Result:
if result.Error != nil {
logrus.Errorf("Error waiting for container: %v", result.Error.Message)
statusC <- 125
} else {
statusC <- int(result.StatusCode)
}
case err := <-errC:
case err := <-waitRes.Error:
if errors.Is(err, context.Canceled) {
return
}
+5 -3
View File
@@ -12,7 +12,7 @@ import (
is "gotest.tools/v3/assert/cmp"
)
func waitFn(cid string) (<-chan container.WaitResponse, <-chan error) {
func waitFn(cid string) client.ContainerWaitResult {
resC := make(chan container.WaitResponse)
errC := make(chan error, 1)
var res container.WaitResponse
@@ -33,8 +33,10 @@ func waitFn(cid string) (<-chan container.WaitResponse, <-chan error) {
resC <- res
}
}()
return resC, errC
return client.ContainerWaitResult{
Result: resC,
Error: errC,
}
}
func TestWaitExitOrRemoved(t *testing.T) {
+6 -4
View File
@@ -4,10 +4,12 @@ import (
"context"
"errors"
"fmt"
"strconv"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client"
"github.com/spf13/cobra"
)
@@ -42,12 +44,12 @@ func runWait(ctx context.Context, dockerCLI command.Cli, opts *waitOptions) erro
var errs []error
for _, ctr := range opts.containers {
resultC, errC := apiClient.ContainerWait(ctx, ctr, "")
res := apiClient.ContainerWait(ctx, ctr, client.ContainerWaitOptions{})
select {
case result := <-resultC:
_, _ = fmt.Fprintf(dockerCLI.Out(), "%d\n", result.StatusCode)
case err := <-errC:
case result := <-res.Result:
_, _ = fmt.Fprintln(dockerCLI.Out(), strconv.FormatInt(result.StatusCode, 10))
case err := <-res.Error:
errs = append(errs, err)
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package command
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package context
+1 -1
View File
@@ -1,5 +1,5 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.23
//go:build go1.24
package context

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