Compare commits

...

15 Commits

Author SHA1 Message Date
Sebastiaan van Stijn 6dc8a31385
Merge be3646b87c into 6c76914532 2024-11-22 01:35:59 +00: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 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 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
Sebastiaan van Stijn be3646b87c
image ls --tree: order images alphabetically
The tree output currently uses the same sort order as the existing
non-tree output, and orders the images by "created" time in descending
order;

    docker image ls
    REPOSITORY       TAG       IMAGE ID       CREATED          SIZE
    <none>           <none>    8262a6d8c38a   7 minutes ago    13.6MB
    docker-cli-dev   latest    f5f0547476ee   12 minutes ago   762MB
    nginx            alpine    2140dad235c1   2 weeks ago      76.7MB
    alpine           latest    beefdbd8a1da   6 weeks ago      24.2MB

However, the `--tree` view does not have a `CREATED` column, which makes
the output order seem "random". With the tree view being more verbose,
it may also be harder to find back images in the list when they're not sorted
in an easy to discover way.

This patch changes the sort order:

- alphabetically (natural sort) for tagged images
- untagged images are sorted last, as they're likely less relevant
  to the user, and should not be "polluting" th top of the list.
- if multiple untagged images exist, they are sorted by created
  date (descending) to get a stable order.

Before this patch:

    $ docker image ls --tree

    IMAGE                   ID             DISK USAGE   CONTENT SIZE   USED
    <untagged>              20ad73eca911       13.6MB         4.09MB    ✔
    └─ linux/arm64          1ab6fc68586e       13.6MB         4.09MB    ✔

    <untagged>              b3e87f642f5c       13.6MB         4.09MB
    └─ linux/arm64          1ab6fc68586e       13.6MB         4.09MB

    docker-cli-dev:latest   f5f0547476ee        762MB          179MB    ✔
    └─ linux/arm64          18ca7881145d        762MB          179MB    ✔

    nginx:alpine            2140dad235c1       76.7MB         21.5MB
    ├─ linux/arm64/v8       d1f949a77b81       76.7MB         21.5MB
    ├─ linux/amd64          ae136e431e76           0B             0B
    ├─ linux/arm/v6         ae1ee4b63c14           0B             0B
    ├─ linux/arm/v7         20ad73eca911           0B             0B
    ├─ linux/386            1e69bfb21757           0B             0B
    ├─ linux/ppc64le        7fef8bcf8b6c           0B             0B
    └─ linux/s390x          8c310bf29cfa           0B             0B

    alpine:latest           beefdbd8a1da       24.2MB         7.46MB
    ├─ linux/riscv64        80cde017a105       10.6MB         3.37MB
    ├─ linux/arm64/v8       9cee2b382fe2       13.6MB         4.09MB
    ├─ linux/amd64          33735bd63cf8           0B             0B
    ├─ linux/arm/v6         50f635c8b04d           0B             0B
    ├─ linux/arm/v7         f2f82d424957           0B             0B
    ├─ linux/386            b3e87f642f5c           0B             0B
    ├─ linux/ppc64le        c7a6800e3dc5           0B             0B
    └─ linux/s390x          2b5b26e09ca2           0B             0B

With this patch:

    $ docker image ls --tree

    IMAGE                   ID             DISK USAGE   CONTENT SIZE   USED
    alpine:latest           beefdbd8a1da       24.2MB         7.46MB
    ├─ linux/riscv64        80cde017a105       10.6MB         3.37MB
    ├─ linux/arm64/v8       9cee2b382fe2       13.6MB         4.09MB
    ├─ linux/amd64          33735bd63cf8           0B             0B
    ├─ linux/arm/v6         50f635c8b04d           0B             0B
    ├─ linux/arm/v7         f2f82d424957           0B             0B
    ├─ linux/386            b3e87f642f5c           0B             0B
    ├─ linux/ppc64le        c7a6800e3dc5           0B             0B
    └─ linux/s390x          2b5b26e09ca2           0B             0B

    docker-cli-dev:latest   f5f0547476ee        762MB          179MB    ✔
    └─ linux/arm64          18ca7881145d        762MB          179MB    ✔

    nginx:alpine            2140dad235c1       76.7MB         21.5MB
    ├─ linux/arm64/v8       d1f949a77b81       76.7MB         21.5MB
    ├─ linux/amd64          ae136e431e76           0B             0B
    ├─ linux/arm/v6         ae1ee4b63c14           0B             0B
    ├─ linux/arm/v7         20ad73eca911           0B             0B
    ├─ linux/386            1e69bfb21757           0B             0B
    ├─ linux/ppc64le        7fef8bcf8b6c           0B             0B
    └─ linux/s390x          8c310bf29cfa           0B             0B

    <untagged>              20ad73eca911       13.6MB         4.09MB    ✔
    └─ linux/arm64          1ab6fc68586e       13.6MB         4.09MB    ✔

    <untagged>              b3e87f642f5c       13.6MB         4.09MB
    └─ linux/arm64          1ab6fc68586e       13.6MB         4.09MB

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-20 23:54:57 +02:00
Sebastiaan van Stijn 7187c78554
image ls: show each tag for an image as a separate entry
A single image can be tagged under multiple names. While they are the
same image under the hood (same digest), we always presented these as
separate images in the list.

This patch applies the same behavior for the tree view; we can consider
having some "compact" presentation in future where we collapse these iamges
(perhaps introducing a "names" column?)

Before this patch:

    $ docker pull --quiet alpine:3.20
    docker.io/library/alpine:3.20
    $ docker pull --quiet alpine:latest
    docker.io/library/alpine:latest

    $ docker image ls --tree

    IMAGE                   ID             DISK USAGE   CONTENT SIZE   USED
    alpine:3.20
    alpine:latest           beefdbd8a1da       13.6MB         4.09MB
    ├─ linux/arm64/v8       9cee2b382fe2       13.6MB         4.09MB
    ├─ linux/amd64          33735bd63cf8           0B             0B
    ├─ linux/arm/v6         50f635c8b04d           0B             0B
    ├─ linux/arm/v7         f2f82d424957           0B             0B
    ├─ linux/386            b3e87f642f5c           0B             0B
    ├─ linux/ppc64le        c7a6800e3dc5           0B             0B
    ├─ linux/riscv64        80cde017a105           0B             0B
    └─ linux/s390x          2b5b26e09ca2           0B             0B

With this patch applied:

    $ docker image ls --tree

    IMAGE                   ID             DISK USAGE   CONTENT SIZE   USED
    alpine:3.20             beefdbd8a1da       13.6MB         4.09MB
    ├─ linux/arm64/v8       9cee2b382fe2       13.6MB         4.09MB
    ├─ linux/amd64          33735bd63cf8           0B             0B
    ├─ linux/arm/v6         50f635c8b04d           0B             0B
    ├─ linux/arm/v7         f2f82d424957           0B             0B
    ├─ linux/386            b3e87f642f5c           0B             0B
    ├─ linux/ppc64le        c7a6800e3dc5           0B             0B
    ├─ linux/riscv64        80cde017a105           0B             0B
    └─ linux/s390x          2b5b26e09ca2           0B             0B

    alpine:latest           beefdbd8a1da       13.6MB         4.09MB
    ├─ linux/arm64/v8       9cee2b382fe2       13.6MB         4.09MB
    ├─ linux/amd64          33735bd63cf8           0B             0B
    ├─ linux/arm/v6         50f635c8b04d           0B             0B
    ├─ linux/arm/v7         f2f82d424957           0B             0B
    ├─ linux/386            b3e87f642f5c           0B             0B
    ├─ linux/ppc64le        c7a6800e3dc5           0B             0B
    ├─ linux/riscv64        80cde017a105           0B             0B
    └─ linux/s390x          2b5b26e09ca2           0B             0B

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-10-19 19:48:45 +02:00
21 changed files with 405 additions and 114 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

@ -14,6 +14,7 @@ import (
imagetypes "github.com/docker/docker/api/types/image" imagetypes "github.com/docker/docker/api/types/image"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/fvbommel/sortorder"
"github.com/morikuni/aec" "github.com/morikuni/aec"
) )
@ -81,16 +82,46 @@ func runTree(ctx context.Context, dockerCLI command.Cli, opts treeOptions) error
details.ContentSize = units.HumanSizeWithPrecision(float64(totalContent), 3) details.ContentSize = units.HumanSizeWithPrecision(float64(totalContent), 3)
view.images = append(view.images, topImage{ if len(img.RepoTags) == 0 {
Names: img.RepoTags, // Untagged image
Details: details, view.images = append(view.images, topImage{
Children: children, Names: img.RepoTags,
created: img.Created, Details: details,
}) Children: children,
created: img.Created,
})
} else {
// Present images tagged under multiple names as separate images.
for _, n := range img.RepoTags {
view.images = append(view.images, topImage{
Names: []string{n}, // Consider changing Names to be a single name for purpose of this presentation.
Details: details,
Children: children,
created: img.Created,
})
}
}
} }
// Sort images alphabetically using natural-sort, with untagged images last.
sort.Slice(view.images, func(i, j int) bool { sort.Slice(view.images, func(i, j int) bool {
return view.images[i].created > view.images[j].created iUntagged, jUntagged := len(view.images[i].Names) == 0, len(view.images[j].Names) == 0
if iUntagged || jUntagged {
switch {
case iUntagged && jUntagged:
// Both untagged images; sort by created date (desc)
return view.images[i].created > view.images[j].created
case iUntagged:
// Sort untagged images last
return false
case jUntagged:
// Sort untagged images last
return true
}
}
// Sort alphabetically, ascending
return sortorder.NaturalLess(view.images[i].Names[0], view.images[j].Names[0])
}) })
return printImageTree(dockerCLI, view) return printImageTree(dockerCLI, view)

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

@ -19,14 +19,14 @@ require (
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

@ -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

@ -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
@ -283,7 +291,8 @@ type DecoderConfig struct {
// structure. The top-level Decode method is just a convenience that sets // structure. The top-level Decode method is just a convenience that sets
// 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)
decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil
// We need to check here if input is a typed nil. Typed nils won't )
// match the "input == nil" below so we check that here. if isNil(input) {
if inputVal.Kind() == reflect.Ptr && inputVal.IsNil() { // 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,30 +472,46 @@ 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)
} }
} }
return nil if !decodeNil {
} return nil
}
if !inputVal.IsValid() { }
// If the input value is invalid, then we just set the value if !inputVal.IsValid() {
// to be the zero value. if !decodeNil {
outVal.Set(reflect.Zero(outVal.Type())) // If the input value is invalid, then we just set the value
if d.config.Metadata != nil && name != "" { // to be the zero value.
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) outVal.Set(reflect.Zero(outVal.Type()))
if d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
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())
} }
return nil
} }
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) // Ignore EINVAL since the capability may not be supported in this system.
err = ignoreEINVAL(dropBound(i))
if err != nil { if err != nil {
// Ignore EINVAL since the capability may not be supported in this system. return err
if err == syscall.EINVAL { //nolint:errorlint // Errors from syscall are bare.
err = nil
continue
}
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 {
// Ignore EINVAL as not supported on kernels before 4.3
err = ignoreEINVAL(resetAmbient())
if err != nil {
return err
}
for i := Cap(0); i <= last; i++ { for i := Cap(0); i <= last; i++ {
action := pr_CAP_AMBIENT_LOWER if !c.Get(AMBIENT, i) {
if c.Get(AMBIENT, i) { continue
action = pr_CAP_AMBIENT_RAISE
} }
err = prctl(pr_CAP_AMBIENT, action, uintptr(i), 0, 0) // Ignore EINVAL as not supported on kernels before 4.3
err = ignoreEINVAL(setAmbient(true, i))
if err != nil { if err != nil {
// Ignore EINVAL as not supported on kernels before 4.3 return err
if err == syscall.EINVAL { //nolint:errorlint // Errors from syscall are bare.
err = nil
continue
}
return
} }
} }
} }
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
} }

6
vendor/modules.txt vendored
View File

@ -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