Compare commits

...

18 Commits

Author SHA1 Message Date
Rob Murray 25607f7c4c
Merge 81380b9268 into 6c76914532 2024-11-21 23:56:18 +03:00
Sebastiaan van Stijn 6c76914532
Merge pull request #5637 from thaJeztah/bump_rosetta
vendor: github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
2024-11-21 17:53:16 +01:00
Sebastiaan van Stijn 7b67057c32
Merge pull request #5636 from thaJeztah/bump_codecov_action
build(deps): bump codecov/codecov-action from 4 to 5
2024-11-21 17:52:41 +01:00
Sebastiaan van Stijn 9ccc462005
Merge pull request #5634 from thaJeztah/bump_mapstructure
vendor: github.com/go-viper/mapstructure/v2 v2.2.1
2024-11-21 17:52:21 +01:00
Sebastiaan van Stijn 35bf069da2
Merge pull request #5633 from thaJeztah/bump_capability
vendor: github.com/moby/sys/capability v0.4.0
2024-11-21 17:51:57 +01:00
Sebastiaan van Stijn 9b72a58d35
Merge pull request #5632 from thaJeztah/bump_golangci_lint
update golangci-lint to v1.62.0
2024-11-21 17:51:22 +01:00
Sebastiaan van Stijn 6aeba15e55
Merge pull request #5630 from thaJeztah/bump_goversioninfo
Dockerfile: bump github.com/josephspurrier/goversioninfo to v1.4.1
2024-11-21 17:50:45 +01:00
Sebastiaan van Stijn a93fb1678a
Merge pull request #5638 from thaJeztah/bump_engine
vendor: github.com/docker/docker e5c2b5e10d68 (master, v28.0.0-dev)
2024-11-21 09:54:53 +01:00
Sebastiaan van Stijn d41b80fafc
vendor: github.com/docker/docker e5c2b5e10d68 (master, v28.0.0-dev)
full diff: 6ac445c42b...e5c2b5e10d

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-20 15:54:27 +01:00
Sebastiaan van Stijn 11fbc99939
vendor: github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
full diff: f79598599c...3f4430f2d3

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-19 21:51:51 +01:00
dependabot[bot] b0c0cd5e32
build(deps): bump codecov/codecov-action from 4 to 5
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 4 to 5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-19 13:53:08 +01:00
Sebastiaan van Stijn 81401f37f2
Merge pull request #5625 from Giedriusj1/master
Optimise `docker stats` to not require clearing the whole screen
2024-11-19 12:48:26 +01:00
Sebastiaan van Stijn f6599300ff
vendor: github.com/go-viper/mapstructure/v2 v2.2.1
full diff: https://github.com/go-viper/mapstructure/compare/v2.0.0...v2.2.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-19 11:49:17 +01:00
Sebastiaan van Stijn 446d4138ed
vendor: github.com/moby/sys/capability v0.4.0
full diff: https://github.com/moby/sys/compare/capability/v0.3.0...capability/v0.4.0

Added

* New separate API for ambient ([GetAmbient], [SetAmbient], [ResetAmbient])
  and bound ([GetBound], [DropBound]) capabilities, modelled after libcap.

Fixed

* [Apply] now returns an error if called for non-zero `pid`. Before this change,
  it could silently change some capabilities of the current process, instead of
  the one identified by the `pid`.
* Fixed tests that change capabilities to be run in a separate process.
* Other improvements in tests.

Changed

* Use raw syscalls (which are slightly faster).
* Most tests are now limited to testing the public API of the package.
* Simplify parsing /proc/*pid*/status, add a test case.
* Optimize the number of syscall to set ambient capabilities in Apply
  by clearing them first; add a test case.
* Better documentation for [Apply], [NewFile], [NewFile2], [NewPid], [NewPid2].

Removed

* `.golangci.yml` and `.codespellrc` are no longer part of the package.

<!-- Doc links (please keep sorted). -->
[Apply]: https://pkg.go.dev/github.com/moby/sys/capability#Capabilities.Apply
[DropBound]: https://pkg.go.dev/github.com/moby/sys/capability#DropBound
[GetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#GetAmbient
[GetBound]: https://pkg.go.dev/github.com/moby/sys/capability#GetBound
[LastCap]: https://pkg.go.dev/github.com/moby/sys/capability#LastCap
[ListKnown]: https://pkg.go.dev/github.com/moby/sys/capability#ListKnown
[ListSupported]: https://pkg.go.dev/github.com/moby/sys/capability#ListSupported
[List]: https://pkg.go.dev/github.com/moby/sys/capability#List
[NewFile2]: https://pkg.go.dev/github.com/moby/sys/capability#NewFile2
[NewFile]: https://pkg.go.dev/github.com/moby/sys/capability#NewFile
[NewPid2]: https://pkg.go.dev/github.com/moby/sys/capability#NewPid2
[NewPid]: https://pkg.go.dev/github.com/moby/sys/capability#NewPid
[ResetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#ResetAmbient
[SetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#SetAmbient

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-19 11:37:18 +01:00
Sebastiaan van Stijn 07e5ddd054
update golangci-lint to v1.62.0
full diff: https://github.com/golangci/golangci-lint/compare/v1.61.0...v1.62.0
Changelog: https://golangci-lint.run/product/changelog/#v1620

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-19 11:05:27 +01:00
Sebastiaan van Stijn 93a931920b
Dockerfile: bump github.com/josephspurrier/goversioninfo to v1.4.1
full diff: https://github.com/josephspurrier/goversioninfo/compare/v1.3.0...v1.4.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-11-19 00:36:15 +01:00
Giedrius Jonikas cb2f95ceee Optimise `docker stats` to not require clearing the whole screen
Instead of clearing the whole screen and then writing the new stats,
we now write the new stats on top of the old text, and then clear
the remaining text.

This is a more efficient way to update the stats, as it avoids the
flickering that happens when the screen is cleared and rewritten.

Signed-off-by: Giedrius Jonikas <giedriusj1@gmail.com>
2024-11-16 15:29:57 +00:00
Rob Murray 81380b9268 Docs: host-gateway-ip daemon option IPv4+IPv6
The host-gateway-ip daemon option now accepts two addresses, one
IPv4 and one IPv6.

Signed-off-by: Rob Murray <rob.murray@docker.com>
2024-11-11 11:57:39 +00:00
42 changed files with 584 additions and 185 deletions

View File

@ -75,7 +75,7 @@ jobs:
TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt TESTFLAGS: -coverprofile=/tmp/coverage/coverage.txt
- -
name: Send to Codecov name: Send to Codecov
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
file: ./build/coverage/coverage.txt files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -40,9 +40,9 @@ jobs:
targets: test-coverage targets: test-coverage
- -
name: Send to Codecov name: Send to Codecov
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
file: ./build/coverage/coverage.txt files: ./build/coverage/coverage.txt
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
host: host:
@ -78,8 +78,8 @@ jobs:
shell: bash shell: bash
- -
name: Send to Codecov name: Send to Codecov
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v5
with: with:
file: /tmp/coverage.txt files: /tmp/coverage.txt
working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli working-directory: ${{ env.GOPATH }}/src/github.com/docker/cli
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -6,7 +6,7 @@ ARG BASE_DEBIAN_DISTRO=bookworm
ARG GO_VERSION=1.23.3 ARG GO_VERSION=1.23.3
ARG XX_VERSION=1.5.0 ARG XX_VERSION=1.5.0
ARG GOVERSIONINFO_VERSION=v1.3.0 ARG GOVERSIONINFO_VERSION=v1.4.1
ARG GOTESTSUM_VERSION=v1.10.0 ARG GOTESTSUM_VERSION=v1.10.0
ARG BUILDX_VERSION=0.18.0 ARG BUILDX_VERSION=0.18.0
ARG COMPOSE_VERSION=v2.30.3 ARG COMPOSE_VERSION=v2.30.3

View File

@ -287,16 +287,26 @@ func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
cStats.mu.RUnlock() cStats.mu.RUnlock()
if !options.NoStream { if !options.NoStream {
// Start by clearing the screen and moving the cursor to the top-left // Start by moving the cursor to the top-left
_, _ = fmt.Fprint(&statsTextBuffer, "\033[2J\033[H") _, _ = fmt.Fprint(&statsTextBuffer, "\033[H")
} }
if err = statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil { if err = statsFormatWrite(statsCtx, ccStats, daemonOSType, !options.NoTrunc); err != nil {
break break
} }
_, _ = fmt.Fprint(dockerCLI.Out(), statsTextBuffer.String()) 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.Fprint(&statsTextBuffer, line, "\033[K\n")
}
// 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() statsTextBuffer.Reset()
if len(cStats.cs) == 0 && !showAll { if len(cStats.cs) == 0 && !showAll {

View File

@ -78,7 +78,8 @@ func runLoad(ctx context.Context, dockerCli command.Cli, opts loadOptions) error
if err != nil { if err != nil {
return errors.Wrap(err, "invalid platform") return errors.Wrap(err, "invalid platform")
} }
options.Platform = &p // TODO(thaJeztah): change flag-type to support multiple platforms.
options.Platforms = append(options.Platforms, p)
} }
response, err := dockerCli.Client().ImageLoad(ctx, input, options) response, err := dockerCli.Client().ImageLoad(ctx, input, options)

View File

@ -109,7 +109,7 @@ func TestNewLoadCommandSuccess(t *testing.T) {
name: "with platform", name: "with platform",
args: []string{"--platform", "linux/amd64"}, args: []string{"--platform", "linux/amd64"},
imageLoadFunc: func(input io.Reader, options image.LoadOptions) (image.LoadResponse, error) { imageLoadFunc: func(input io.Reader, options image.LoadOptions) (image.LoadResponse, error) {
assert.Check(t, is.DeepEqual(ocispec.Platform{OS: "linux", Architecture: "amd64"}, *options.Platform)) assert.Check(t, is.DeepEqual([]ocispec.Platform{{OS: "linux", Architecture: "amd64"}}, options.Platforms))
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
}, },
}, },

View File

@ -63,7 +63,8 @@ func RunSave(ctx context.Context, dockerCli command.Cli, opts saveOptions) error
if err != nil { if err != nil {
return errors.Wrap(err, "invalid platform") return errors.Wrap(err, "invalid platform")
} }
options.Platform = &p // TODO(thaJeztah): change flag-type to support multiple platforms.
options.Platforms = append(options.Platforms, p)
} }
responseBody, err := dockerCli.Client().ImageSave(ctx, opts.images, options) responseBody, err := dockerCli.Client().ImageSave(ctx, opts.images, options)

View File

@ -106,7 +106,7 @@ func TestNewSaveCommandSuccess(t *testing.T) {
imageSaveFunc: func(images []string, options image.SaveOptions) (io.ReadCloser, error) { imageSaveFunc: func(images []string, options image.SaveOptions) (io.ReadCloser, error) {
assert.Assert(t, is.Len(images, 1)) assert.Assert(t, is.Len(images, 1))
assert.Check(t, is.Equal("arg1", images[0])) assert.Check(t, is.Equal("arg1", images[0]))
assert.Check(t, is.DeepEqual(ocispec.Platform{OS: "linux", Architecture: "amd64"}, *options.Platform)) assert.Check(t, is.DeepEqual([]ocispec.Platform{{OS: "linux", Architecture: "amd64"}}, options.Platforms))
return io.NopCloser(strings.NewReader("")), nil return io.NopCloser(strings.NewReader("")), nil
}, },
}, },

View File

@ -2,7 +2,7 @@
ARG GO_VERSION=1.23.3 ARG GO_VERSION=1.23.3
ARG ALPINE_VERSION=3.20 ARG ALPINE_VERSION=3.20
ARG GOLANGCI_LINT_VERSION=v1.61.0 ARG GOLANGCI_LINT_VERSION=v1.62.0
FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint

View File

@ -62,8 +62,8 @@ Options:
-G, --group string Group for the unix socket (default "docker") -G, --group string Group for the unix socket (default "docker")
--help Print usage --help Print usage
-H, --host list Daemon socket(s) to connect to -H, --host list Daemon socket(s) to connect to
--host-gateway-ip ip IP address that the special 'host-gateway' string in --add-host resolves to. --host-gateway-ip ip-list IP addresses that the special 'host-gateway' string in --add-host resolves to.
Defaults to the IP address of the default bridge Defaults to the IP addresses of the default bridge
--http-proxy string HTTP proxy URL to use for outgoing traffic --http-proxy string HTTP proxy URL to use for outgoing traffic
--https-proxy string HTTPS proxy URL to use for outgoing traffic --https-proxy string HTTPS proxy URL to use for outgoing traffic
--icc Enable inter-container communication (default true) --icc Enable inter-container communication (default true)
@ -837,21 +837,34 @@ For details about how to use this feature, as well as limitations, see
The Docker daemon supports a special `host-gateway` value for the `--add-host` The Docker daemon supports a special `host-gateway` value for the `--add-host`
flag for the `docker run` and `docker build` commands. This value resolves to flag for the `docker run` and `docker build` commands. This value resolves to
the host's gateway IP and lets containers connect to services running on the addresses on the host, so that containers can connect to services running on the
host. host.
By default, `host-gateway` resolves to the IP address of the default bridge. By default, `host-gateway` resolves to the IPv4 address of the default bridge,
and its IPv6 address if it has one.
You can configure this to resolve to a different IP using the `--host-gateway-ip` You can configure this to resolve to a different IP using the `--host-gateway-ip`
flag for the dockerd command line interface, or the `host-gateway-ip` key in flag for the dockerd command line interface, or the `host-gateway-ip` key in
the daemon configuration file. the daemon configuration file.
To supply both IPv4 and IPv6 addresses on the command line, use two
`--host-gateway-ip` options.
To supply two addresses in the daemon configuration file, include two addresses
in the JSON array, as shown below. For compatibility with older versions of the
daemon, a single IP address can also be specified in a JSON string rather than
an array.
```console ```console
$ cat > /etc/docker/daemon.json $ cat > /etc/docker/daemon.json
{ "host-gateway-ip": "192.0.2.0" } { "host-gateway-ip": ["192.0.2.1", "2001:db8::1111"]}
$ sudo systemctl restart docker $ sudo systemctl restart docker
$ docker run -it --add-host host.docker.internal:host-gateway \ $ docker run -it --add-host host.docker.internal:host-gateway \
busybox ping host.docker.internal busybox ping host.docker.internal
PING host.docker.internal (192.0.2.0): 56 data bytes PING host.docker.internal (192.0.2.1): 56 data bytes
$ docker run -it --add-host host.docker.internal:host-gateway \
busybox ping -6 host.docker.internal
PING host.docker.internal (2001:db8::1111): 56 data bytes
``` ```
### Enable CDI devices ### Enable CDI devices

View File

@ -13,20 +13,20 @@ require (
github.com/distribution/reference v0.6.0 github.com/distribution/reference v0.6.0
github.com/docker/cli-docs-tool v0.8.0 github.com/docker/cli-docs-tool v0.8.0
github.com/docker/distribution v2.8.3+incompatible github.com/docker/distribution v2.8.3+incompatible
github.com/docker/docker v27.0.2-0.20241031194140-6ac445c42bad+incompatible // master (v-next) github.com/docker/docker v27.0.2-0.20241120142749-e5c2b5e10d68+incompatible // master (v-next)
github.com/docker/docker-credential-helpers v0.8.2 github.com/docker/docker-credential-helpers v0.8.2
github.com/docker/go-connections v0.5.0 github.com/docker/go-connections v0.5.0
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
github.com/fvbommel/sortorder v1.1.0 github.com/fvbommel/sortorder v1.1.0
github.com/go-jose/go-jose/v4 v4.0.4 github.com/go-jose/go-jose/v4 v4.0.4
github.com/go-viper/mapstructure/v2 v2.0.0 github.com/go-viper/mapstructure/v2 v2.2.1
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/google/go-cmp v0.6.0 github.com/google/go-cmp v0.6.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/mattn/go-runewidth v0.0.15 github.com/mattn/go-runewidth v0.0.15
github.com/moby/patternmatcher v0.6.0 github.com/moby/patternmatcher v0.6.0
github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e
github.com/moby/sys/capability v0.3.0 github.com/moby/sys/capability v0.4.0
github.com/moby/sys/sequential v0.6.0 github.com/moby/sys/sequential v0.6.0
github.com/moby/sys/signal v0.7.1 github.com/moby/sys/signal v0.7.1
github.com/moby/term v0.5.0 github.com/moby/term v0.5.0
@ -39,7 +39,7 @@ require (
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0
go.opentelemetry.io/otel v1.28.0 go.opentelemetry.io/otel v1.28.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0

View File

@ -51,8 +51,8 @@ github.com/docker/cli-docs-tool v0.8.0/go.mod h1:8TQQ3E7mOXoYUs811LiPdUnAhXrcVsB
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v27.0.2-0.20241031194140-6ac445c42bad+incompatible h1:kSQ4U+63JfFxIOrTo6wMW1mqkOkPpiTe/7ZfvUdNLVE= github.com/docker/docker v27.0.2-0.20241120142749-e5c2b5e10d68+incompatible h1:ZWh4HhdUCagAd3S+gsFPOobHbc562obYFSrz3irGSsU=
github.com/docker/docker v27.0.2-0.20241031194140-6ac445c42bad+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.0.2-0.20241120142749-e5c2b5e10d68+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@ -89,8 +89,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@ -168,8 +168,8 @@ github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkV
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e h1:1yC8fRqStY6NirU/swI74fsrHvZVMbtxsHcvl8YpzDg= github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e h1:1yC8fRqStY6NirU/swI74fsrHvZVMbtxsHcvl8YpzDg=
github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e/go.mod h1:mTTGIAz/59OGZR5Qe+QByIe3Nxc+sSuJkrsStFhr6Lg= github.com/moby/swarmkit/v2 v2.0.0-20241017191044-e8ecf83ee08e/go.mod h1:mTTGIAz/59OGZR5Qe+QByIe3Nxc+sSuJkrsStFhr6Lg=
github.com/moby/sys/capability v0.3.0 h1:kEP+y6te0gEXIaeQhIi0s7vKs/w0RPoH1qPa6jROcVg= github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
github.com/moby/sys/capability v0.3.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
@ -266,8 +266,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ7tGUHvcvL1v3yR6NcCc9nOqh2L+CG6HWrYQtwzQ0= github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ7tGUHvcvL1v3yR6NcCc9nOqh2L+CG6HWrYQtwzQ0=
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw= github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw=
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d h1:wvQZpqy8p0D/FUia6ipKDhXrzPzBVJE4PZyPc5+5Ay0= github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 h1:TvtdmeYsYEij78hS4oxnwikoiLdIrgav3BA+CbhaDAI=
github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d/go.mod h1:xKQhd7snlzKFuUi1taTGWjpRE8iFTA06DeacYi3CVFQ= github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346/go.mod h1:xKQhd7snlzKFuUi1taTGWjpRE8iFTA06DeacYi3CVFQ=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss= github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE= github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=

View File

@ -1991,6 +1991,18 @@ definitions:
type: "string" type: "string"
x-nullable: false x-nullable: false
example: "sha256:ec3f0931a6e6b6855d76b2d7b0be30e81860baccd891b2e243280bf1cd8ad710" example: "sha256:ec3f0931a6e6b6855d76b2d7b0be30e81860baccd891b2e243280bf1cd8ad710"
Descriptor:
description: |
Descriptor is an OCI descriptor of the image target.
In case of a multi-platform image, this descriptor points to the OCI index
or a manifest list.
This field is only present if the daemon provides a multi-platform image store.
WARNING: This is experimental and may change at any time without any backward
compatibility.
x-nullable: true
$ref: "#/definitions/OCIDescriptor"
RepoTags: RepoTags:
description: | description: |
List of image names/tags in the local image cache that reference this List of image names/tags in the local image cache that reference this
@ -2278,6 +2290,18 @@ definitions:
x-omitempty: true x-omitempty: true
items: items:
$ref: "#/definitions/ImageManifestSummary" $ref: "#/definitions/ImageManifestSummary"
Descriptor:
description: |
Descriptor is an OCI descriptor of the image target.
In case of a multi-platform image, this descriptor points to the OCI index
or a manifest list.
This field is only present if the daemon provides a multi-platform image store.
WARNING: This is experimental and may change at any time without any backward
compatibility.
x-nullable: true
$ref: "#/definitions/OCIDescriptor"
AuthConfig: AuthConfig:
type: "object" type: "object"
@ -7242,6 +7266,14 @@ paths:
type: "string" type: "string"
Platform: Platform:
type: "string" type: "string"
ImageManifestDescriptor:
$ref: "#/definitions/OCIDescriptor"
description: |
OCI descriptor of the platform-specific manifest of the image
the container was created from.
Note: Only available if the daemon provides a multi-platform
image store.
MountLabel: MountLabel:
type: "string" type: "string"
ProcessLabel: ProcessLabel:
@ -9210,9 +9242,14 @@ paths:
type: "string" type: "string"
in: "query" in: "query"
description: | description: |
JSON encoded OCI platform describing platform to show the history for. JSON-encoded OCI platform to select the platform-variant.
If not provided, the host platform will be used. If it's not If omitted, it defaults to any locally available platform,
available, any present platform will be picked. prioritizing the daemon's host platform.
If the daemon provides a multi-platform image store, this selects
the platform-variant to show the history for. If the image is
a single-platform image, or if the multi-platform image does not
provide a variant matching the given platform, an error is returned.
Example: `{"os": "linux", "architecture": "arm", "variant": "v5"}` Example: `{"os": "linux", "architecture": "arm", "variant": "v5"}`
tags: ["Image"] tags: ["Image"]
@ -9262,6 +9299,19 @@ paths:
all tags of the given image that are present in the local image store all tags of the given image that are present in the local image store
are pushed. are pushed.
type: "string" type: "string"
- name: "platform"
type: "string"
in: "query"
description: |
JSON-encoded OCI platform to select the platform-variant to push.
If not provided, all available variants will attempt to be pushed.
If the daemon provides a multi-platform image store, this selects
the platform-variant to push to the registry. If the image is
a single-platform image, or if the multi-platform image does not
provide a variant matching the given platform, an error is returned.
Example: `{"os": "linux", "architecture": "arm", "variant": "v5"}`
- name: "X-Registry-Auth" - name: "X-Registry-Auth"
in: "header" in: "header"
description: | description: |
@ -9271,11 +9321,6 @@ paths:
details. details.
type: "string" type: "string"
required: true required: true
- name: "platform"
in: "query"
description: "Select a platform-specific manifest to be pushed. OCI platform (JSON encoded)"
type: "string"
x-nullable: true
tags: ["Image"] tags: ["Image"]
/images/{name}/tag: /images/{name}/tag:
post: post:

View File

@ -7,6 +7,7 @@ import (
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/storage" "github.com/docker/docker/api/types/storage"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
// PruneReport contains the response for Engine API: // PruneReport contains the response for Engine API:
@ -171,4 +172,6 @@ type InspectResponse struct {
Mounts []MountPoint Mounts []MountPoint
Config *Config Config *Config
NetworkSettings *NetworkSettings NetworkSettings *NetworkSettings
// ImageManifestDescriptor is the descriptor of a platform-specific manifest of the image used to create the container.
ImageManifestDescriptor *ocispec.Descriptor `json:",omitempty"`
} }

View File

@ -10,7 +10,7 @@ import (
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/strslice" "github.com/docker/docker/api/types/strslice"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
units "github.com/docker/go-units" "github.com/docker/go-units"
) )
// CgroupnsMode represents the cgroup namespace mode of the container // CgroupnsMode represents the cgroup namespace mode of the container

View File

@ -3,6 +3,7 @@ package image
import ( import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/storage" "github.com/docker/docker/api/types/storage"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
// RootFS returns Image's RootFS description including the layer IDs. // RootFS returns Image's RootFS description including the layer IDs.
@ -119,4 +120,11 @@ type InspectResponse struct {
// //
// This information is local to the daemon, and not part of the image itself. // This information is local to the daemon, and not part of the image itself.
Metadata Metadata Metadata Metadata
// Descriptor is the OCI descriptor of the image target.
// It's only set if the daemon provides a multi-platform image store.
//
// WARNING: This is experimental and may change at any time without any backward
// compatibility.
Descriptor *ocispec.Descriptor `json:"Descriptor,omitempty"`
} }

View File

@ -98,12 +98,14 @@ type LoadOptions struct {
// Quiet suppresses progress output // Quiet suppresses progress output
Quiet bool Quiet bool
// Platform is a specific platform to load when the image is a multi-platform // Platforms selects the platforms to load if the image is a
Platform *ocispec.Platform // multi-platform image and has multiple variants.
Platforms []ocispec.Platform
} }
// SaveOptions holds parameters to save images. // SaveOptions holds parameters to save images.
type SaveOptions struct { type SaveOptions struct {
// Platform is a specific platform to save if the image is a multi-platform image. // Platforms selects the platforms to save if the image is a
Platform *ocispec.Platform // multi-platform image and has multiple variants.
Platforms []ocispec.Platform
} }

View File

@ -1,5 +1,7 @@
package image package image
import ocispec "github.com/opencontainers/image-spec/specs-go/v1"
type Summary struct { type Summary struct {
// Number of containers using this image. Includes both stopped and running // Number of containers using this image. Includes both stopped and running
@ -42,6 +44,13 @@ type Summary struct {
// Required: true // Required: true
ParentID string `json:"ParentId"` ParentID string `json:"ParentId"`
// Descriptor is the OCI descriptor of the image target.
// It's only set if the daemon provides a multi-platform image store.
//
// WARNING: This is experimental and may change at any time without any backward
// compatibility.
Descriptor *ocispec.Descriptor `json:"Descriptor,omitempty"`
// Manifests is a list of image manifests available in this image. It // Manifests is a list of image manifests available in this image. It
// provides a more detailed view of the platform-specific image manifests or // provides a more detailed view of the platform-specific image manifests or
// other image-attached data like build attestations. // other image-attached data like build attestations.

View File

@ -3,7 +3,6 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"net/url" "net/url"
"github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/image"
@ -11,26 +10,26 @@ import (
// ImageHistory returns the changes in an image in history format. // ImageHistory returns the changes in an image in history format.
func (cli *Client) ImageHistory(ctx context.Context, imageID string, opts image.HistoryOptions) ([]image.HistoryResponseItem, error) { func (cli *Client) ImageHistory(ctx context.Context, imageID string, opts image.HistoryOptions) ([]image.HistoryResponseItem, error) {
values := url.Values{} query := url.Values{}
if opts.Platform != nil { if opts.Platform != nil {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil { if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return nil, err return nil, err
} }
p, err := json.Marshal(*opts.Platform) p, err := encodePlatform(opts.Platform)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid platform: %v", err) return nil, err
} }
values.Set("platform", string(p)) query.Set("platform", p)
}
serverResp, err := cli.get(ctx, "/images/"+imageID+"/history", query, nil)
defer ensureReaderClosed(serverResp)
if err != nil {
return nil, err
} }
var history []image.HistoryResponseItem var history []image.HistoryResponseItem
serverResp, err := cli.get(ctx, "/images/"+imageID+"/history", values, nil)
defer ensureReaderClosed(serverResp)
if err != nil {
return history, err
}
err = json.NewDecoder(serverResp.body).Decode(&history) err = json.NewDecoder(serverResp.body).Decode(&history)
return history, err return history, err
} }

View File

@ -21,10 +21,18 @@ func (cli *Client) ImageImport(ctx context.Context, source image.ImportSource, r
} }
query := url.Values{} query := url.Values{}
if source.SourceName != "" {
query.Set("fromSrc", source.SourceName) query.Set("fromSrc", source.SourceName)
}
if ref != "" {
query.Set("repo", ref) query.Set("repo", ref)
}
if options.Tag != "" {
query.Set("tag", options.Tag) query.Set("tag", options.Tag)
}
if options.Message != "" {
query.Set("message", options.Message) query.Set("message", options.Message)
}
if options.Platform != "" { if options.Platform != "" {
query.Set("platform", strings.ToLower(options.Platform)) query.Set("platform", strings.ToLower(options.Platform))
} }

View File

@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"encoding/json"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
@ -18,24 +17,24 @@ import (
// the provided multi-platform image. This is only has effect if the input image // the provided multi-platform image. This is only has effect if the input image
// is a multi-platform image. // is a multi-platform image.
func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error) { func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.LoadOptions) (image.LoadResponse, error) {
v := url.Values{} query := url.Values{}
v.Set("quiet", "0") query.Set("quiet", "0")
if opts.Quiet { if opts.Quiet {
v.Set("quiet", "1") query.Set("quiet", "1")
} }
if opts.Platform != nil { if len(opts.Platforms) > 0 {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil { if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return image.LoadResponse{}, err return image.LoadResponse{}, err
} }
p, err := json.Marshal(*opts.Platform) p, err := encodePlatforms(opts.Platforms...)
if err != nil { if err != nil {
return image.LoadResponse{}, err return image.LoadResponse{}, err
} }
v.Set("platform", string(p)) query["platform"] = p
} }
resp, err := cli.postRaw(ctx, "/images/load", v, input, http.Header{ resp, err := cli.postRaw(ctx, "/images/load", query, input, http.Header{
"Content-Type": {"application/x-tar"}, "Content-Type": {"application/x-tar"},
}) })
if err != nil { if err != nil {

View File

@ -2,8 +2,6 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"encoding/json"
"fmt"
"io" "io"
"net/url" "net/url"
@ -17,16 +15,15 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, opts image.
"names": imageIDs, "names": imageIDs,
} }
if opts.Platform != nil { if len(opts.Platforms) > 0 {
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil { if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
return nil, err return nil, err
} }
p, err := encodePlatforms(opts.Platforms...)
p, err := json.Marshal(*opts.Platform)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid platform: %v", err) return nil, err
} }
query.Set("platform", string(p)) query["platform"] = p
} }
resp, err := cli.get(ctx, "/images/get", query, nil) resp, err := cli.get(ctx, "/images/get", query, nil)

View File

@ -1,10 +1,14 @@
package client // import "github.com/docker/docker/client" package client // import "github.com/docker/docker/client"
import ( import (
"encoding/json"
"fmt"
"net/url" "net/url"
"regexp" "regexp"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/errdefs"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`) var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)
@ -32,3 +36,43 @@ func getFiltersQuery(f filters.Args) (url.Values, error) {
} }
return query, nil return query, nil
} }
// encodePlatforms marshals the given platform(s) to JSON format, to
// be used for query-parameters for filtering / selecting platforms.
func encodePlatforms(platform ...ocispec.Platform) ([]string, error) {
if len(platform) == 0 {
return []string{}, nil
}
if len(platform) == 1 {
p, err := encodePlatform(&platform[0])
if err != nil {
return nil, err
}
return []string{p}, nil
}
seen := make(map[string]struct{}, len(platform))
out := make([]string, 0, len(platform))
for i := range platform {
p, err := encodePlatform(&platform[i])
if err != nil {
return nil, err
}
if _, ok := seen[p]; !ok {
out = append(out, p)
seen[p] = struct{}{}
}
}
return out, nil
}
// encodePlatforms marshals the given platform to JSON format, to
// be used for query-parameters for filtering / selecting platforms. It
// is used as a helper for encodePlatforms,
func encodePlatform(platform *ocispec.Platform) (string, error) {
p, err := json.Marshal(platform)
if err != nil {
return "", errdefs.InvalidParameter(fmt.Errorf("invalid platform: %v", err))
}
return string(p), nil
}

View File

@ -261,13 +261,13 @@ func readdirnames(dirname string) (names []nameIno, err error) {
func parseDirent(buf []byte, names []nameIno) (consumed int, newnames []nameIno) { func parseDirent(buf []byte, names []nameIno) (consumed int, newnames []nameIno) {
origlen := len(buf) origlen := len(buf)
for len(buf) > 0 { for len(buf) > 0 {
dirent := (*unix.Dirent)(unsafe.Pointer(&buf[0])) dirent := (*unix.Dirent)(unsafe.Pointer(&buf[0])) // #nosec G103 -- Ignore "G103: Use of unsafe calls should be audited"
buf = buf[dirent.Reclen:] buf = buf[dirent.Reclen:]
if dirent.Ino == 0 { // File absent in directory. if dirent.Ino == 0 { // File absent in directory.
continue continue
} }
bytes := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0])) b := (*[10000]byte)(unsafe.Pointer(&dirent.Name[0])) // #nosec G103 -- Ignore "G103: Use of unsafe calls should be audited"
name := string(bytes[0:clen(bytes[:])]) name := string(b[0:clen(b[:])])
if name == "." || name == ".." { // Useless names if name == "." || name == ".." { // Useless names
continue continue
} }

View File

@ -22,11 +22,11 @@ type subIDRange struct {
Length int Length int
} }
type ranges []subIDRange type subIDRanges []subIDRange
func (e ranges) Len() int { return len(e) } func (e subIDRanges) Len() int { return len(e) }
func (e ranges) Swap(i, j int) { e[i], e[j] = e[j], e[i] } func (e subIDRanges) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func (e ranges) Less(i, j int) bool { return e[i].Start < e[j].Start } func (e subIDRanges) Less(i, j int) bool { return e[i].Start < e[j].Start }
const ( const (
subuidFileName = "/etc/subuid" subuidFileName = "/etc/subuid"
@ -162,7 +162,7 @@ func (i IdentityMapping) Empty() bool {
return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0 return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
} }
func createIDMap(subidRanges ranges) []IDMap { func createIDMap(subidRanges subIDRanges) []IDMap {
idMap := []IDMap{} idMap := []IDMap{}
containerID := 0 containerID := 0
@ -177,19 +177,19 @@ func createIDMap(subidRanges ranges) []IDMap {
return idMap return idMap
} }
func parseSubuid(username string) (ranges, error) { func parseSubuid(username string) (subIDRanges, error) {
return parseSubidFile(subuidFileName, username) return parseSubidFile(subuidFileName, username)
} }
func parseSubgid(username string) (ranges, error) { func parseSubgid(username string) (subIDRanges, error) {
return parseSubidFile(subgidFileName, username) return parseSubidFile(subgidFileName, username)
} }
// parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid) // parseSubidFile will read the appropriate file (/etc/subuid or /etc/subgid)
// and return all found ranges for a specified username. If the special value // and return all found subIDRanges for a specified username. If the special value
// "ALL" is supplied for username, then all ranges in the file will be returned // "ALL" is supplied for username, then all subIDRanges in the file will be returned
func parseSubidFile(path, username string) (ranges, error) { func parseSubidFile(path, username string) (subIDRanges, error) {
var rangeList ranges var rangeList subIDRanges
subidFile, err := os.Open(path) subidFile, err := os.Open(path)
if err != nil { if err != nil {

View File

@ -145,7 +145,7 @@ func findNextGIDRange() (int, error) {
return findNextRangeStart(ranges) return findNextRangeStart(ranges)
} }
func findNextRangeStart(rangeList ranges) (int, error) { func findNextRangeStart(rangeList subIDRanges) (int, error) {
startID := defaultRangeStart startID := defaultRangeStart
for _, arange := range rangeList { for _, arange := range rangeList {
if wouldOverlap(arange, startID) { if wouldOverlap(arange, startID) {

View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
"time" "time"
units "github.com/docker/go-units" "github.com/docker/go-units"
"github.com/moby/term" "github.com/moby/term"
"github.com/morikuni/aec" "github.com/morikuni/aec"
) )

View File

@ -3,8 +3,8 @@ package system // import "github.com/docker/docker/pkg/system"
// containerdRuntimeSupported determines if containerd should be the runtime. // containerdRuntimeSupported determines if containerd should be the runtime.
var containerdRuntimeSupported = false var containerdRuntimeSupported = false
// InitContainerdRuntime sets whether to use containerd for runtime on Windows. // EnableContainerdRuntime sets whether to use containerd for runtime on Windows.
func InitContainerdRuntime(cdPath string) { func EnableContainerdRuntime(cdPath string) {
if len(cdPath) > 0 { if len(cdPath) > 0 {
containerdRuntimeSupported = true containerdRuntimeSupported = true
} }

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"net" "net"
"net/netip" "net/netip"
"net/url"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -36,6 +37,30 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
return nil return nil
} }
// cachedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
// it into a closure to be used directly
// if the type fails to convert we return a closure always erroring to keep the previous behaviour
func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (interface{}, error) {
switch f := typedDecodeHook(raw).(type) {
case DecodeHookFuncType:
return func(from reflect.Value, to reflect.Value) (interface{}, error) {
return f(from.Type(), to.Type(), from.Interface())
}
case DecodeHookFuncKind:
return func(from reflect.Value, to reflect.Value) (interface{}, error) {
return f(from.Kind(), to.Kind(), from.Interface())
}
case DecodeHookFuncValue:
return func(from reflect.Value, to reflect.Value) (interface{}, error) {
return f(from, to)
}
default:
return func(from reflect.Value, to reflect.Value) (interface{}, error) {
return nil, errors.New("invalid decode hook signature")
}
}
}
// DecodeHookExec executes the given decode hook. This should be used // DecodeHookExec executes the given decode hook. This should be used
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc // since it'll naturally degrade to the older backwards compatible DecodeHookFunc
// that took reflect.Kind instead of reflect.Type. // that took reflect.Kind instead of reflect.Type.
@ -61,13 +86,17 @@ func DecodeHookExec(
// The composed funcs are called in order, with the result of the // The composed funcs are called in order, with the result of the
// previous transformation. // previous transformation.
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(fs))
for _, f := range fs {
cached = append(cached, cachedDecodeHook(f))
}
return func(f reflect.Value, t reflect.Value) (interface{}, error) { return func(f reflect.Value, t reflect.Value) (interface{}, error) {
var err error var err error
data := f.Interface() data := f.Interface()
newFrom := f newFrom := f
for _, f1 := range fs { for _, c := range cached {
data, err = DecodeHookExec(f1, newFrom, t) data, err = c(newFrom, t)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -81,13 +110,17 @@ func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. // OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. // If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(ff))
for _, f := range ff {
cached = append(cached, cachedDecodeHook(f))
}
return func(a, b reflect.Value) (interface{}, error) { return func(a, b reflect.Value) (interface{}, error) {
var allErrs string var allErrs string
var out interface{} var out interface{}
var err error var err error
for _, f := range ff { for _, c := range cached {
out, err = DecodeHookExec(f, a, b) out, err = c(a, b)
if err != nil { if err != nil {
allErrs += err.Error() + "\n" allErrs += err.Error() + "\n"
continue continue
@ -144,6 +177,26 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
} }
} }
// StringToURLHookFunc returns a DecodeHookFunc that converts
// strings to *url.URL.
func StringToURLHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{},
) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(&url.URL{}) {
return data, nil
}
// Convert it by parsing
return url.Parse(data.(string))
}
}
// StringToIPHookFunc returns a DecodeHookFunc that converts // StringToIPHookFunc returns a DecodeHookFunc that converts
// strings to net.IP // strings to net.IP
func StringToIPHookFunc() DecodeHookFunc { func StringToIPHookFunc() DecodeHookFunc {

View File

@ -266,6 +266,10 @@ type DecoderConfig struct {
// defaults to "mapstructure" // defaults to "mapstructure"
TagName string TagName string
// The option of the value in the tag that indicates a field should
// be squashed. This defaults to "squash".
SquashTagOption string
// IgnoreUntaggedFields ignores all struct fields without explicit // IgnoreUntaggedFields ignores all struct fields without explicit
// TagName, comparable to `mapstructure:"-"` as default behaviour. // TagName, comparable to `mapstructure:"-"` as default behaviour.
IgnoreUntaggedFields bool IgnoreUntaggedFields bool
@ -274,6 +278,10 @@ type DecoderConfig struct {
// field name or tag. Defaults to `strings.EqualFold`. This can be used // field name or tag. Defaults to `strings.EqualFold`. This can be used
// to implement case-sensitive tag values, support snake casing, etc. // to implement case-sensitive tag values, support snake casing, etc.
MatchName func(mapKey, fieldName string) bool MatchName func(mapKey, fieldName string) bool
// DecodeNil, if set to true, will cause the DecodeHook (if present) to run
// even if the input is nil. This can be used to provide default values.
DecodeNil bool
} }
// A Decoder takes a raw interface value and turns it into structured // A Decoder takes a raw interface value and turns it into structured
@ -284,6 +292,7 @@ type DecoderConfig struct {
// up the most basic Decoder. // up the most basic Decoder.
type Decoder struct { type Decoder struct {
config *DecoderConfig config *DecoderConfig
cachedDecodeHook func(from reflect.Value, to reflect.Value) (interface{}, error)
} }
// Metadata contains information about decoding a structure that // Metadata contains information about decoding a structure that
@ -401,6 +410,10 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
config.TagName = "mapstructure" config.TagName = "mapstructure"
} }
if config.SquashTagOption == "" {
config.SquashTagOption = "squash"
}
if config.MatchName == nil { if config.MatchName == nil {
config.MatchName = strings.EqualFold config.MatchName = strings.EqualFold
} }
@ -408,6 +421,9 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
result := &Decoder{ result := &Decoder{
config: config, config: config,
} }
if config.DecodeHook != nil {
result.cachedDecodeHook = cachedDecodeHook(config.DecodeHook)
}
return result, nil return result, nil
} }
@ -426,19 +442,26 @@ func (d *Decoder) Decode(input interface{}) error {
return err return err
} }
// isNil returns true if the input is nil or a typed nil pointer.
func isNil(input interface{}) bool {
if input == nil {
return true
}
val := reflect.ValueOf(input)
return val.Kind() == reflect.Ptr && val.IsNil()
}
// Decodes an unknown data type into a specific reflection value. // Decodes an unknown data type into a specific reflection value.
func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
var inputVal reflect.Value var (
if input != nil {
inputVal = reflect.ValueOf(input) inputVal = reflect.ValueOf(input)
outputKind = getKind(outVal)
// We need to check here if input is a typed nil. Typed nils won't decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil
// match the "input == nil" below so we check that here. )
if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() { if isNil(input) {
// Typed nils won't match the "input == nil" below, so reset input.
input = nil input = nil
} }
}
if input == nil { if input == nil {
// If the data is nil, then we don't set anything, unless ZeroFields is set // If the data is nil, then we don't set anything, unless ZeroFields is set
// to true. // to true.
@ -449,10 +472,12 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
} }
} }
if !decodeNil {
return nil return nil
} }
}
if !inputVal.IsValid() { if !inputVal.IsValid() {
if !decodeNil {
// If the input value is invalid, then we just set the value // If the input value is invalid, then we just set the value
// to be the zero value. // to be the zero value.
outVal.Set(reflect.Zero(outVal.Type())) outVal.Set(reflect.Zero(outVal.Type()))
@ -461,18 +486,32 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
} }
return nil return nil
} }
// Hooks need a valid inputVal, so reset it to zero value of outVal type.
switch outputKind {
case reflect.Struct, reflect.Map:
var mapVal map[string]interface{}
inputVal = reflect.ValueOf(mapVal) // create nil map pointer
case reflect.Slice, reflect.Array:
var sliceVal []interface{}
inputVal = reflect.ValueOf(sliceVal) // create nil slice pointer
default:
inputVal = reflect.Zero(outVal.Type())
}
}
if d.config.DecodeHook != nil { if d.cachedDecodeHook != nil {
// We have a DecodeHook, so let's pre-process the input. // We have a DecodeHook, so let's pre-process the input.
var err error var err error
input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal) input, err = d.cachedDecodeHook(inputVal, outVal)
if err != nil { if err != nil {
return fmt.Errorf("error decoding '%s': %w", name, err) return fmt.Errorf("error decoding '%s': %w", name, err)
} }
} }
if isNil(input) {
return nil
}
var err error var err error
outputKind := getKind(outVal)
addMetaKey := true addMetaKey := true
switch outputKind { switch outputKind {
case reflect.Bool: case reflect.Bool:
@ -753,8 +792,8 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
} }
default: default:
return fmt.Errorf( return fmt.Errorf(
"'%s' expected type '%s', got unconvertible type '%s', value: '%v'", "'%s' expected type '%s', got unconvertible type '%#v', value: '%#v'",
name, val.Type(), dataVal.Type(), data) name, val, dataVal, data)
} }
return nil return nil
@ -973,7 +1012,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
} }
// If "squash" is specified in the tag, we squash the field down. // If "squash" is specified in the tag, we squash the field down.
squash = squash || strings.Index(tagValue[index+1:], "squash") != -1 squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption)
if squash { if squash {
// When squashing, the embedded type can be a pointer to a struct. // When squashing, the embedded type can be a pointer to a struct.
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
@ -1351,7 +1390,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
// We always parse the tags cause we're looking for other tags too // We always parse the tags cause we're looking for other tags too
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
for _, tag := range tagParts[1:] { for _, tag := range tagParts[1:] {
if tag == "squash" { if tag == d.config.SquashTagOption {
squash = true squash = true
break break
} }
@ -1363,10 +1402,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
} }
if squash { if squash {
if fieldVal.Kind() != reflect.Struct { switch fieldVal.Kind() {
errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind())) case reflect.Struct:
} else {
structs = append(structs, fieldVal) structs = append(structs, fieldVal)
case reflect.Interface:
if !fieldVal.IsNil() {
structs = append(structs, fieldVal.Elem().Elem())
}
default:
errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
} }
continue continue
} }

View File

@ -1,3 +0,0 @@
[codespell]
skip = ./.git
ignore-words-list = nd

View File

@ -1,6 +0,0 @@
linters:
enable:
- unconvert
- unparam
- gofumpt
- errorlint

View File

@ -5,6 +5,30 @@ from https://github.com/syndtr/gocapability/commit/42c35b4376354fd5.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.4.0] - 2024-11-11
### Added
* New separate API for ambient ([GetAmbient], [SetAmbient], [ResetAmbient])
and bound ([GetBound], [DropBound]) capabilities, modelled after libcap. (#176)
### Fixed
* [Apply] now returns an error if called for non-zero `pid`. Before this change,
it could silently change some capabilities of the current process, instead of
the one identified by the `pid`. (#168, #174)
* Fixed tests that change capabilities to be run in a separate process. (#173)
* Other improvements in tests. (#169, #170)
### Changed
* Use raw syscalls (which are slightly faster). (#176)
* Most tests are now limited to testing the public API of the package. (#162)
* Simplify parsing /proc/*pid*/status, add a test case. (#162)
* Optimize the number of syscall to set ambient capabilities in Apply
by clearing them first; add a test case. (#163, #164)
* Better documentation for [Apply], [NewFile], [NewFile2], [NewPid], [NewPid2]. (#175)
### Removed
* `.golangci.yml` and `.codespellrc` are no longer part of the package. (#158)
## [0.3.0] - 2024-09-25 ## [0.3.0] - 2024-09-25
### Added ### Added
@ -63,14 +87,24 @@ This is an initial release since the fork.
* Removed init function so programs that use this package start faster. [#6] * Removed init function so programs that use this package start faster. [#6]
* Removed `CAP_LAST_CAP` (use [LastCap] instead). [#6] * Removed `CAP_LAST_CAP` (use [LastCap] instead). [#6]
<!-- Doc links. --> <!-- Doc links (please keep sorted). -->
[Apply]: https://pkg.go.dev/github.com/moby/sys/capability#Capabilities.Apply [Apply]: https://pkg.go.dev/github.com/moby/sys/capability#Capabilities.Apply
[DropBound]: https://pkg.go.dev/github.com/moby/sys/capability#DropBound
[GetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#GetAmbient
[GetBound]: https://pkg.go.dev/github.com/moby/sys/capability#GetBound
[LastCap]: https://pkg.go.dev/github.com/moby/sys/capability#LastCap [LastCap]: https://pkg.go.dev/github.com/moby/sys/capability#LastCap
[List]: https://pkg.go.dev/github.com/moby/sys/capability#List
[ListKnown]: https://pkg.go.dev/github.com/moby/sys/capability#ListKnown [ListKnown]: https://pkg.go.dev/github.com/moby/sys/capability#ListKnown
[ListSupported]: https://pkg.go.dev/github.com/moby/sys/capability#ListSupported [ListSupported]: https://pkg.go.dev/github.com/moby/sys/capability#ListSupported
[List]: https://pkg.go.dev/github.com/moby/sys/capability#List
[NewFile2]: https://pkg.go.dev/github.com/moby/sys/capability#NewFile2
[NewFile]: https://pkg.go.dev/github.com/moby/sys/capability#NewFile
[NewPid2]: https://pkg.go.dev/github.com/moby/sys/capability#NewPid2
[NewPid]: https://pkg.go.dev/github.com/moby/sys/capability#NewPid
[ResetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#ResetAmbient
[SetAmbient]: https://pkg.go.dev/github.com/moby/sys/capability#SetAmbient
<!-- Minor releases. --> <!-- Minor releases. -->
[0.4.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.4.0
[0.3.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.3.0 [0.3.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.3.0
[0.2.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.2.0 [0.2.0]: https://github.com/moby/sys/releases/tag/capability%2Fv0.2.0
[0.1.1]: https://github.com/kolyshkin/capability/compare/v0.1.0...v0.1.1 [0.1.1]: https://github.com/kolyshkin/capability/compare/v0.1.0...v0.1.1

View File

@ -56,16 +56,16 @@ type Capabilities interface {
// outstanding changes. // outstanding changes.
Load() error Load() error
// Apply apply the capabilities settings, so all changes will take // Apply apply the capabilities settings, so all changes made by
// effect. // [Set], [Unset], [Fill], or [Clear] will take effect.
Apply(kind CapType) error Apply(kind CapType) error
} }
// NewPid initializes a new [Capabilities] object for given pid when // NewPid initializes a new [Capabilities] object for given pid when
// it is nonzero, or for the current process if pid is 0. // it is nonzero, or for the current process if pid is 0.
// //
// Deprecated: Replace with [NewPid2] followed by [Capabilities.Load]. // Deprecated: replace with [NewPid2] followed by optional [Capabilities.Load]
// For example, replace: // (only if needed). For example, replace:
// //
// c, err := NewPid(0) // c, err := NewPid(0)
// if err != nil { // if err != nil {
@ -93,16 +93,16 @@ func NewPid(pid int) (Capabilities, error) {
// NewPid2 initializes a new [Capabilities] object for given pid when // NewPid2 initializes a new [Capabilities] object for given pid when
// it is nonzero, or for the current process if pid is 0. This // it is nonzero, or for the current process if pid is 0. This
// does not load the process's current capabilities; to do that you // does not load the process's current capabilities; if needed,
// must call [Capabilities.Load] explicitly. // call [Capabilities.Load].
func NewPid2(pid int) (Capabilities, error) { func NewPid2(pid int) (Capabilities, error) {
return newPid(pid) return newPid(pid)
} }
// NewFile initializes a new Capabilities object for given file path. // NewFile initializes a new Capabilities object for given file path.
// //
// Deprecated: Replace with [NewFile2] followed by [Capabilities.Load]. // Deprecated: replace with [NewFile2] followed by optional [Capabilities.Load]
// For example, replace: // (only if needed). For example, replace:
// //
// c, err := NewFile(path) // c, err := NewFile(path)
// if err != nil { // if err != nil {
@ -130,7 +130,7 @@ func NewFile(path string) (Capabilities, error) {
// NewFile2 creates a new initialized [Capabilities] object for given // NewFile2 creates a new initialized [Capabilities] object for given
// file path. This does not load the process's current capabilities; // file path. This does not load the process's current capabilities;
// to do that you must call [Capabilities.Load] explicitly. // if needed, call [Capabilities.Load].
func NewFile2(path string) (Capabilities, error) { func NewFile2(path string) (Capabilities, error) {
return newFile(path) return newFile(path)
} }
@ -142,3 +142,35 @@ func NewFile2(path string) (Capabilities, error) {
func LastCap() (Cap, error) { func LastCap() (Cap, error) {
return lastCap() return lastCap()
} }
// GetAmbient determines if a specific ambient capability is raised in the
// calling thread.
func GetAmbient(c Cap) (bool, error) {
return getAmbient(c)
}
// SetAmbient raises or lowers specified ambient capabilities for the calling
// thread. To complete successfully, the prevailing effective capability set
// must have a raised CAP_SETPCAP. Further, to raise a specific ambient
// capability the inheritable and permitted sets of the calling thread must
// already contain the specified capability.
func SetAmbient(raise bool, caps ...Cap) error {
return setAmbient(raise, caps...)
}
// ResetAmbient resets all of the ambient capabilities for the calling thread
// to their lowered value.
func ResetAmbient() error {
return resetAmbient()
}
// GetBound determines if a specific bounding capability is raised in the
// calling thread.
func GetBound(c Cap) (bool, error) {
return getBound(c)
}
// DropBound lowers the specified bounding set capability.
func DropBound(caps ...Cap) error {
return dropBound(caps...)
}

View File

@ -117,6 +117,13 @@ func newPid(pid int) (c Capabilities, retErr error) {
return return
} }
func ignoreEINVAL(err error) error {
if errors.Is(err, syscall.EINVAL) {
err = nil
}
return err
}
type capsV3 struct { type capsV3 struct {
hdr capHeader hdr capHeader
data [2]capData data [2]capData
@ -307,15 +314,15 @@ func (c *capsV3) Load() (err error) {
} }
break break
} }
if strings.HasPrefix(line, "CapB") { if val, ok := strings.CutPrefix(line, "CapBnd:\t"); ok {
_, err = fmt.Sscanf(line[4:], "nd: %08x%08x", &c.bounds[1], &c.bounds[0]) _, err = fmt.Sscanf(val, "%08x%08x", &c.bounds[1], &c.bounds[0])
if err != nil { if err != nil {
break break
} }
continue continue
} }
if strings.HasPrefix(line, "CapA") { if val, ok := strings.CutPrefix(line, "CapAmb:\t"); ok {
_, err = fmt.Sscanf(line[4:], "mb: %08x%08x", &c.ambient[1], &c.ambient[0]) _, err = fmt.Sscanf(val, "%08x%08x", &c.ambient[1], &c.ambient[0])
if err != nil { if err != nil {
break break
} }
@ -327,7 +334,10 @@ func (c *capsV3) Load() (err error) {
return return
} }
func (c *capsV3) Apply(kind CapType) (err error) { func (c *capsV3) Apply(kind CapType) error {
if c.hdr.pid != 0 {
return errors.New("unable to modify capabilities of another process")
}
last, err := LastCap() last, err := LastCap()
if err != nil { if err != nil {
return err return err
@ -336,21 +346,17 @@ func (c *capsV3) Apply(kind CapType) (err error) {
var data [2]capData var data [2]capData
err = capget(&c.hdr, &data[0]) err = capget(&c.hdr, &data[0])
if err != nil { if err != nil {
return return err
} }
if (1<<uint(CAP_SETPCAP))&data[0].effective != 0 { if (1<<uint(CAP_SETPCAP))&data[0].effective != 0 {
for i := Cap(0); i <= last; i++ { for i := Cap(0); i <= last; i++ {
if c.Get(BOUNDING, i) { if c.Get(BOUNDING, i) {
continue continue
} }
err = prctl(syscall.PR_CAPBSET_DROP, uintptr(i), 0, 0, 0)
if err != nil {
// Ignore EINVAL since the capability may not be supported in this system. // Ignore EINVAL since the capability may not be supported in this system.
if err == syscall.EINVAL { //nolint:errorlint // Errors from syscall are bare. err = ignoreEINVAL(dropBound(i))
err = nil if err != nil {
continue return err
}
return
} }
} }
} }
@ -359,29 +365,73 @@ func (c *capsV3) Apply(kind CapType) (err error) {
if kind&CAPS == CAPS { if kind&CAPS == CAPS {
err = capset(&c.hdr, &c.data[0]) err = capset(&c.hdr, &c.data[0])
if err != nil { if err != nil {
return return err
} }
} }
if kind&AMBS == AMBS { if kind&AMBS == AMBS {
for i := Cap(0); i <= last; i++ {
action := pr_CAP_AMBIENT_LOWER
if c.Get(AMBIENT, i) {
action = pr_CAP_AMBIENT_RAISE
}
err = prctl(pr_CAP_AMBIENT, action, uintptr(i), 0, 0)
if err != nil {
// Ignore EINVAL as not supported on kernels before 4.3 // Ignore EINVAL as not supported on kernels before 4.3
if err == syscall.EINVAL { //nolint:errorlint // Errors from syscall are bare. err = ignoreEINVAL(resetAmbient())
err = nil if err != nil {
return err
}
for i := Cap(0); i <= last; i++ {
if !c.Get(AMBIENT, i) {
continue continue
} }
return // Ignore EINVAL as not supported on kernels before 4.3
err = ignoreEINVAL(setAmbient(true, i))
if err != nil {
return err
} }
} }
} }
return return nil
}
func getAmbient(c Cap) (bool, error) {
res, err := prctlRetInt(pr_CAP_AMBIENT, pr_CAP_AMBIENT_IS_SET, uintptr(c))
if err != nil {
return false, err
}
return res > 0, nil
}
func setAmbient(raise bool, caps ...Cap) error {
op := pr_CAP_AMBIENT_RAISE
if !raise {
op = pr_CAP_AMBIENT_LOWER
}
for _, val := range caps {
err := prctl(pr_CAP_AMBIENT, op, uintptr(val))
if err != nil {
return err
}
}
return nil
}
func resetAmbient() error {
return prctl(pr_CAP_AMBIENT, pr_CAP_AMBIENT_CLEAR_ALL, 0)
}
func getBound(c Cap) (bool, error) {
res, err := prctlRetInt(syscall.PR_CAPBSET_READ, uintptr(c), 0)
if err != nil {
return false, err
}
return res > 0, nil
}
func dropBound(caps ...Cap) error {
for _, val := range caps {
err := prctl(syscall.PR_CAPBSET_DROP, uintptr(val), 0)
if err != nil {
return err
}
}
return nil
} }
func newFile(path string) (c Capabilities, err error) { func newFile(path string) (c Capabilities, err error) {

View File

@ -24,3 +24,23 @@ func newFile(_ string) (Capabilities, error) {
func lastCap() (Cap, error) { func lastCap() (Cap, error) {
return -1, errNotSup return -1, errNotSup
} }
func getAmbient(_ Cap) (bool, error) {
return false, errNotSup
}
func setAmbient(_ bool, _ ...Cap) error {
return errNotSup
}
func resetAmbient() error {
return errNotSup
}
func getBound(_ Cap) (bool, error) {
return false, errNotSup
}
func dropBound(_ ...Cap) error {
return errNotSup
}

View File

@ -316,7 +316,7 @@ func ListKnown() []Cap {
return list() return list()
} }
// ListSupported retuns the list of all capabilities known to the package, // ListSupported returns the list of all capabilities known to the package,
// except those that are not supported by the currently running Linux kernel. // except those that are not supported by the currently running Linux kernel.
func ListSupported() ([]Cap, error) { func ListSupported() ([]Cap, error) {
last, err := LastCap() last, err := LastCap()

View File

@ -24,7 +24,7 @@ type capData struct {
} }
func capget(hdr *capHeader, data *capData) (err error) { func capget(hdr *capHeader, data *capData) (err error) {
_, _, e1 := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) _, _, e1 := syscall.RawSyscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }
@ -32,7 +32,7 @@ func capget(hdr *capHeader, data *capData) (err error) {
} }
func capset(hdr *capHeader, data *capData) (err error) { func capset(hdr *capHeader, data *capData) (err error) {
_, _, e1 := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0) _, _, e1 := syscall.RawSyscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(hdr)), uintptr(unsafe.Pointer(data)), 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }
@ -48,14 +48,22 @@ const (
pr_CAP_AMBIENT_CLEAR_ALL = uintptr(4) pr_CAP_AMBIENT_CLEAR_ALL = uintptr(4)
) )
func prctl(option int, arg2, arg3, arg4, arg5 uintptr) (err error) { func prctl(option int, arg2, arg3 uintptr) (err error) {
_, _, e1 := syscall.Syscall6(syscall.SYS_PRCTL, uintptr(option), arg2, arg3, arg4, arg5, 0) _, _, e1 := syscall.RawSyscall(syscall.SYS_PRCTL, uintptr(option), arg2, arg3)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }
return return
} }
func prctlRetInt(option int, arg2, arg3 uintptr) (int, error) {
ret, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, uintptr(option), arg2, arg3)
if err != 0 {
return 0, err
}
return int(ret), nil
}
const ( const (
vfsXattrName = "security.capability" vfsXattrName = "security.capability"
@ -92,7 +100,7 @@ func getVfsCap(path string, dest *vfscapData) (err error) {
if err != nil { if err != nil {
return return
} }
r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0) r0, _, e1 := syscall.RawSyscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(dest)), vfscapDataSizeV2, 0, 0)
if e1 != 0 { if e1 != 0 {
if e1 == syscall.ENODATA { if e1 == syscall.ENODATA {
dest.version = 2 dest.version = 2
@ -145,7 +153,7 @@ func setVfsCap(path string, data *vfscapData) (err error) {
} else { } else {
return syscall.EINVAL return syscall.EINVAL
} }
_, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0) _, _, e1 := syscall.RawSyscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_vfsXattrName)), uintptr(unsafe.Pointer(data)), size, 0, 0)
if e1 != 0 { if e1 != 0 {
err = e1 err = e1
} }

8
vendor/github.com/tonistiigi/go-rosetta/README.md generated vendored Normal file
View File

@ -0,0 +1,8 @@
go-rosetta
==========
[![PkgGoDev](https://pkg.go.dev/badge/github.com/tonistiigi/go-rosetta)](https://pkg.go.dev/github.com/tonistiigi/go-rosetta)
`go-rosetta` provides utilities to detect if an application is running as a
[Rosetta](https://developer.apple.com/documentation/apple_silicon/about_the_rosetta_translation_environment) translated binary, and
to determine the native architecture.

View File

@ -1,17 +1,28 @@
//go:build darwin
// +build darwin // +build darwin
package rosetta package rosetta
import ( import (
"os"
"runtime" "runtime"
"syscall" "syscall"
) )
// Available returns true if Rosetta is installed/available
func Available() bool {
_, err := os.Stat("/Library/Apple/usr/share/rosetta")
return err == nil
}
// Enabled returns true if running in a Rosetta Translated Binary, false otherwise.
func Enabled() bool { func Enabled() bool {
v, err := syscall.SysctlUint32("sysctl.proc_translated") v, err := syscall.SysctlUint32("sysctl.proc_translated")
return err == nil && v == 1 return err == nil && v == 1
} }
// NativeArch returns the native architecture, even if binary architecture
// is emulated by Rosetta.
func NativeArch() string { func NativeArch() string {
if Enabled() && runtime.GOARCH == "amd64" { if Enabled() && runtime.GOARCH == "amd64" {
return "arm64" return "arm64"

View File

@ -1,3 +1,4 @@
//go:build !darwin
// +build !darwin // +build !darwin
package rosetta package rosetta
@ -6,10 +7,18 @@ import (
"runtime" "runtime"
) )
// Available returns true if Rosetta is installed/available
func Available() bool {
return false
}
// Enabled returns true if running in a Rosetta Translated Binary, false otherwise.
func Enabled() bool { func Enabled() bool {
return false return false
} }
// NativeArch returns the native architecture, even if binary architecture
// is emulated by Rosetta.
func NativeArch() string { func NativeArch() string {
return runtime.GOARCH return runtime.GOARCH
} }

8
vendor/modules.txt vendored
View File

@ -55,7 +55,7 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid github.com/docker/distribution/uuid
# github.com/docker/docker v27.0.2-0.20241031194140-6ac445c42bad+incompatible # github.com/docker/docker v27.0.2-0.20241120142749-e5c2b5e10d68+incompatible
## explicit ## explicit
github.com/docker/docker/api github.com/docker/docker/api
github.com/docker/docker/api/types github.com/docker/docker/api/types
@ -136,7 +136,7 @@ github.com/go-logr/logr/funcr
# github.com/go-logr/stdr v1.2.2 # github.com/go-logr/stdr v1.2.2
## explicit; go 1.16 ## explicit; go 1.16
github.com/go-logr/stdr github.com/go-logr/stdr
# github.com/go-viper/mapstructure/v2 v2.0.0 # github.com/go-viper/mapstructure/v2 v2.2.1
## explicit; go 1.18 ## explicit; go 1.18
github.com/go-viper/mapstructure/v2 github.com/go-viper/mapstructure/v2
github.com/go-viper/mapstructure/v2/internal/errors github.com/go-viper/mapstructure/v2/internal/errors
@ -205,7 +205,7 @@ github.com/moby/swarmkit/v2/api/defaults
github.com/moby/swarmkit/v2/api/genericresource github.com/moby/swarmkit/v2/api/genericresource
github.com/moby/swarmkit/v2/manager/raftselector github.com/moby/swarmkit/v2/manager/raftselector
github.com/moby/swarmkit/v2/protobuf/plugin github.com/moby/swarmkit/v2/protobuf/plugin
# github.com/moby/sys/capability v0.3.0 # github.com/moby/sys/capability v0.4.0
## explicit; go 1.21 ## explicit; go 1.21
github.com/moby/sys/capability github.com/moby/sys/capability
# github.com/moby/sys/sequential v0.6.0 # github.com/moby/sys/sequential v0.6.0
@ -293,7 +293,7 @@ github.com/theupdateframework/notary/tuf/data
github.com/theupdateframework/notary/tuf/signed github.com/theupdateframework/notary/tuf/signed
github.com/theupdateframework/notary/tuf/utils github.com/theupdateframework/notary/tuf/utils
github.com/theupdateframework/notary/tuf/validation github.com/theupdateframework/notary/tuf/validation
# github.com/tonistiigi/go-rosetta v0.0.0-20200727161949-f79598599c5d # github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346
## explicit; go 1.13 ## explicit; go 1.13
github.com/tonistiigi/go-rosetta github.com/tonistiigi/go-rosetta
# github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb # github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb