mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-08-17 01:05:55 +08:00
Compare commits
163 Commits
v0.20.0-rc
...
v0.22.0-rc
Author | SHA1 | Date | |
---|---|---|---|
![]() |
18ccba0720 | ||
![]() |
f5196f1167 | ||
![]() |
ef99381eab | ||
![]() |
00fdcd38ab | ||
![]() |
97f1d47464 | ||
![]() |
337578242d | ||
![]() |
503a8925d2 | ||
![]() |
0d708c0bc2 | ||
![]() |
3a7523a117 | ||
![]() |
5dc1a3308d | ||
![]() |
eb78253dfd | ||
![]() |
5f8b78a113 | ||
![]() |
67d3ed34e4 | ||
![]() |
b88423be50 | ||
![]() |
c1e2ae5636 | ||
![]() |
23afb70e40 | ||
![]() |
812b42b329 | ||
![]() |
d5d3d3d502 | ||
![]() |
e19c729d3e | ||
![]() |
aefa49c4fa | ||
![]() |
7d927ee604 | ||
![]() |
058c098c8c | ||
![]() |
7b7dbe88b1 | ||
![]() |
cadf4a5893 | ||
![]() |
6cd9fef556 | ||
![]() |
963b9ca30d | ||
![]() |
4636c8051a | ||
![]() |
e23695d50d | ||
![]() |
6eff9b2d51 | ||
![]() |
fcbfc85f42 | ||
![]() |
9a204c44c3 | ||
![]() |
4c6eba5acd | ||
![]() |
fea7459880 | ||
![]() |
e2d52a8465 | ||
![]() |
48a591b1e1 | ||
![]() |
128acdb471 | ||
![]() |
411d3f8cea | ||
![]() |
7925a96726 | ||
![]() |
b06bddfee6 | ||
![]() |
fe17ebda89 | ||
![]() |
4ed1e07f16 | ||
![]() |
f49593ce2c | ||
![]() |
4e91fe6507 | ||
![]() |
921b576f3a | ||
![]() |
548c80ab5a | ||
![]() |
f3a4740d5f | ||
![]() |
89917dc696 | ||
![]() |
f7276201ac | ||
![]() |
beb9f515c0 | ||
![]() |
4f7d145c0e | ||
![]() |
ccdf63c644 | ||
![]() |
9a6b8754b1 | ||
![]() |
e75ac22ba6 | ||
![]() |
62f5cc7c80 | ||
![]() |
6272ae1afa | ||
![]() |
accfbf6e24 | ||
![]() |
af2d8fe555 | ||
![]() |
18f4275a92 | ||
![]() |
221a608b3c | ||
![]() |
cc0391eba5 | ||
![]() |
aef388bf7a | ||
![]() |
80c16bc28c | ||
![]() |
75160643e1 | ||
![]() |
ad18ffc018 | ||
![]() |
80c3832c94 | ||
![]() |
7762ab2c38 | ||
![]() |
b973de2dd3 | ||
![]() |
352ce7e875 | ||
![]() |
cdfc1ed750 | ||
![]() |
d0d3433b12 | ||
![]() |
b04d39494f | ||
![]() |
52f503e806 | ||
![]() |
79a978484d | ||
![]() |
f7992033bf | ||
![]() |
73f61aa338 | ||
![]() |
faa573f484 | ||
![]() |
0a4a1babd1 | ||
![]() |
461bd9e5d1 | ||
![]() |
d6fdf83f45 | ||
![]() |
ef4e9fea83 | ||
![]() |
0c296fe857 | ||
![]() |
ef73c64d2c | ||
![]() |
1784f84561 | ||
![]() |
6a6fa4f422 | ||
![]() |
2dc0350ffe | ||
![]() |
b85fc5c484 | ||
![]() |
2389d457a4 | ||
![]() |
3f82aadc6e | ||
![]() |
79e3f12305 | ||
![]() |
1dc5f0751b | ||
![]() |
7ba4da0800 | ||
![]() |
a64e628774 | ||
![]() |
1c4b1a376c | ||
![]() |
e1f690abfc | ||
![]() |
03569c2188 | ||
![]() |
350d3f0f4b | ||
![]() |
dc27815236 | ||
![]() |
1089ff7341 | ||
![]() |
7433d37183 | ||
![]() |
f9a76355b5 | ||
![]() |
cfeea34b2d | ||
![]() |
ba2d3692a6 | ||
![]() |
853b593a4d | ||
![]() |
efb300e613 | ||
![]() |
cee7b344da | ||
![]() |
67dbde6970 | ||
![]() |
295653dabb | ||
![]() |
f5802119c5 | ||
![]() |
40b9ac1ec5 | ||
![]() |
f11496448a | ||
![]() |
c8c9c72ca6 | ||
![]() |
9fe8139022 | ||
![]() |
b3e8c62635 | ||
![]() |
b8e9c28315 | ||
![]() |
3ae9970da5 | ||
![]() |
1d219100fc | ||
![]() |
464f9278d1 | ||
![]() |
7216086b8c | ||
![]() |
b195b80ddf | ||
![]() |
70a5e266d1 | ||
![]() |
689bea7963 | ||
![]() |
5176c38115 | ||
![]() |
ec440c4574 | ||
![]() |
0a4eb7ec76 | ||
![]() |
f710c93157 | ||
![]() |
d1a0a1497c | ||
![]() |
c880ecd513 | ||
![]() |
d557da1935 | ||
![]() |
417af36abc | ||
![]() |
e236b86297 | ||
![]() |
633e8a0881 | ||
![]() |
5e1ea62f92 | ||
![]() |
4b90b84995 | ||
![]() |
abc85c38f8 | ||
![]() |
ccca7c795a | ||
![]() |
04aab6958c | ||
![]() |
9d640f0e33 | ||
![]() |
b76fdcaf8d | ||
![]() |
d693e18c04 | ||
![]() |
b066ee1110 | ||
![]() |
cf8bf9e104 | ||
![]() |
3bd54b19aa | ||
![]() |
934841f329 | ||
![]() |
b2ababc7b6 | ||
![]() |
0ccdb7e248 | ||
![]() |
cacb4fb9b3 | ||
![]() |
df80bd72c6 | ||
![]() |
bb4bef2f04 | ||
![]() |
a11507344a | ||
![]() |
17af006857 | ||
![]() |
11c84973ef | ||
![]() |
cc4a291f6a | ||
![]() |
aa1fbc0421 | ||
![]() |
b2bbb337e4 | ||
![]() |
012df71b63 | ||
![]() |
a26bb271ab | ||
![]() |
3e0682f039 | ||
![]() |
3aed658dc4 | ||
![]() |
b4a0dee723 | ||
![]() |
6904512813 | ||
![]() |
d41e335466 | ||
![]() |
0954dcb5fd | ||
![]() |
38f64bf709 |
37
.github/workflows/build.yml
vendored
37
.github/workflows/build.yml
vendored
@@ -54,9 +54,9 @@ jobs:
|
|||||||
- master
|
- master
|
||||||
- latest
|
- latest
|
||||||
- buildx-stable-1
|
- buildx-stable-1
|
||||||
- v0.19.0-rc2
|
- v0.20.1
|
||||||
|
- v0.19.0
|
||||||
- v0.18.2
|
- v0.18.2
|
||||||
- v0.17.2
|
|
||||||
worker:
|
worker:
|
||||||
- docker-container
|
- docker-container
|
||||||
- remote
|
- remote
|
||||||
@@ -76,6 +76,16 @@ jobs:
|
|||||||
- worker: docker+containerd # same as docker, but with containerd snapshotter
|
- worker: docker+containerd # same as docker, but with containerd snapshotter
|
||||||
pkg: ./tests
|
pkg: ./tests
|
||||||
mode: experimental
|
mode: experimental
|
||||||
|
- worker: "docker@27.5"
|
||||||
|
pkg: ./tests
|
||||||
|
- worker: "docker+containerd@27.5" # same as docker, but with containerd snapshotter
|
||||||
|
pkg: ./tests
|
||||||
|
- worker: "docker@27.5"
|
||||||
|
pkg: ./tests
|
||||||
|
mode: experimental
|
||||||
|
- worker: "docker+containerd@27.5" # same as docker, but with containerd snapshotter
|
||||||
|
pkg: ./tests
|
||||||
|
mode: experimental
|
||||||
- worker: "docker@26.1"
|
- worker: "docker@26.1"
|
||||||
pkg: ./tests
|
pkg: ./tests
|
||||||
- worker: "docker+containerd@26.1" # same as docker, but with containerd snapshotter
|
- worker: "docker+containerd@26.1" # same as docker, but with containerd snapshotter
|
||||||
@@ -174,6 +184,11 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SKIP_INTEGRATION_TESTS: 1
|
SKIP_INTEGRATION_TESTS: 1
|
||||||
steps:
|
steps:
|
||||||
|
-
|
||||||
|
name: Setup Git config
|
||||||
|
run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
git config --global core.eol lf
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -243,12 +258,17 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- freebsd
|
- freebsd
|
||||||
|
- netbsd
|
||||||
- openbsd
|
- openbsd
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Prepare
|
name: Prepare
|
||||||
run: |
|
run: |
|
||||||
echo "VAGRANT_FILE=hack/Vagrantfile.${{ matrix.os }}" >> $GITHUB_ENV
|
echo "VAGRANT_FILE=hack/Vagrantfile.${{ matrix.os }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Sets semver Go version to be able to download tarball during vagrant setup
|
||||||
|
goVersion=$(curl --silent "https://go.dev/dl/?mode=json&include=all" | jq -r '.[].files[].version' | uniq | sed -e 's/go//' | sort -V | grep $GO_VERSION | tail -1)
|
||||||
|
echo "GO_VERSION=$goVersion" >> $GITHUB_ENV
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -264,8 +284,10 @@ jobs:
|
|||||||
name: Install vagrant
|
name: Install vagrant
|
||||||
run: |
|
run: |
|
||||||
set -x
|
set -x
|
||||||
|
wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libvirt-daemon libvirt-daemon-system vagrant vagrant-libvirt ruby-libvirt
|
sudo apt-get install -y libvirt-dev libvirt-daemon libvirt-daemon-system vagrant vagrant-libvirt ruby-libvirt
|
||||||
sudo systemctl enable --now libvirtd
|
sudo systemctl enable --now libvirtd
|
||||||
sudo chmod a+rw /var/run/libvirt/libvirt-sock
|
sudo chmod a+rw /var/run/libvirt/libvirt-sock
|
||||||
vagrant plugin install vagrant-libvirt
|
vagrant plugin install vagrant-libvirt
|
||||||
@@ -389,6 +411,15 @@ jobs:
|
|||||||
- test-unit
|
- test-unit
|
||||||
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }}
|
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }}
|
||||||
steps:
|
steps:
|
||||||
|
-
|
||||||
|
name: Free disk space
|
||||||
|
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
|
||||||
|
with:
|
||||||
|
android: true
|
||||||
|
dotnet: true
|
||||||
|
haskell: true
|
||||||
|
large-packages: true
|
||||||
|
swap-storage: true
|
||||||
-
|
-
|
||||||
name: Set up QEMU
|
name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
|
2
.github/workflows/docs-release.yml
vendored
2
.github/workflows/docs-release.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
|||||||
VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }}
|
VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }}
|
||||||
-
|
-
|
||||||
name: Create PR on docs repo
|
name: Create PR on docs repo
|
||||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
||||||
push-to-fork: docker-tools-robot/docker.github.io
|
push-to-fork: docker-tools-robot/docker.github.io
|
||||||
|
2
.github/workflows/docs-upstream.yml
vendored
2
.github/workflows/docs-upstream.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
|||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
validate:
|
validate:
|
||||||
uses: docker/docs/.github/workflows/validate-upstream.yml@6b73b05acb21edf7995cc5b3c6672d8e314cee7a # pin for artifact v4 support: https://github.com/docker/docs/pull/19220
|
uses: docker/docs/.github/workflows/validate-upstream.yml@main
|
||||||
needs:
|
needs:
|
||||||
- docs-yaml
|
- docs-yaml
|
||||||
with:
|
with:
|
||||||
|
9
.github/workflows/e2e.yml
vendored
9
.github/workflows/e2e.yml
vendored
@@ -29,7 +29,7 @@ env:
|
|||||||
SETUP_BUILDX_VERSION: "edge"
|
SETUP_BUILDX_VERSION: "edge"
|
||||||
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
|
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
|
||||||
DESTDIR: "./bin"
|
DESTDIR: "./bin"
|
||||||
K3S_VERSION: "v1.21.2-k3s1"
|
K3S_VERSION: "v1.32.2+k3s1"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
driver:
|
driver:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- build
|
- build
|
||||||
strategy:
|
strategy:
|
||||||
@@ -153,7 +153,7 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Install k3s
|
name: Install k3s
|
||||||
if: matrix.driver == 'kubernetes'
|
if: matrix.driver == 'kubernetes'
|
||||||
uses: crazy-max/.github/.github/actions/install-k3s@fa6141aedf23596fb8bdcceab9cce8dadaa31bd9
|
uses: crazy-max/.github/.github/actions/install-k3s@7730d1434364d4b9aded32735b078a7ace5ea79a
|
||||||
with:
|
with:
|
||||||
version: ${{ env.K3S_VERSION }}
|
version: ${{ env.K3S_VERSION }}
|
||||||
-
|
-
|
||||||
@@ -215,6 +215,9 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
name: Expose GitHub Runtime
|
||||||
|
uses: crazy-max/ghaction-github-runtime@v3
|
||||||
-
|
-
|
||||||
name: Environment variables
|
name: Environment variables
|
||||||
if: matrix.envs != ''
|
if: matrix.envs != ''
|
||||||
|
@@ -43,6 +43,9 @@ linters-settings:
|
|||||||
# buildkit errdefs package (or vice-versa).
|
# buildkit errdefs package (or vice-versa).
|
||||||
- pkg: "github.com/containerd/errdefs"
|
- pkg: "github.com/containerd/errdefs"
|
||||||
alias: "cerrdefs"
|
alias: "cerrdefs"
|
||||||
|
# Use a consistent alias to prevent confusion with "github.com/moby/buildkit/client"
|
||||||
|
- pkg: "github.com/docker/docker/client"
|
||||||
|
alias: "dockerclient"
|
||||||
- pkg: "github.com/opencontainers/image-spec/specs-go/v1"
|
- pkg: "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
alias: "ocispecs"
|
alias: "ocispecs"
|
||||||
- pkg: "github.com/opencontainers/go-digest"
|
- pkg: "github.com/opencontainers/go-digest"
|
||||||
|
20
Dockerfile
20
Dockerfile
@@ -5,20 +5,23 @@ ARG ALPINE_VERSION=3.21
|
|||||||
ARG XX_VERSION=1.6.1
|
ARG XX_VERSION=1.6.1
|
||||||
|
|
||||||
# for testing
|
# for testing
|
||||||
ARG DOCKER_VERSION=27.5.0
|
ARG DOCKER_VERSION=28.0.0
|
||||||
|
ARG DOCKER_VERSION_ALT_27=27.5.1
|
||||||
ARG DOCKER_VERSION_ALT_26=26.1.3
|
ARG DOCKER_VERSION_ALT_26=26.1.3
|
||||||
ARG DOCKER_CLI_VERSION=${DOCKER_VERSION}
|
ARG DOCKER_CLI_VERSION=${DOCKER_VERSION}
|
||||||
ARG GOTESTSUM_VERSION=v1.12.0
|
ARG GOTESTSUM_VERSION=v1.12.0
|
||||||
ARG REGISTRY_VERSION=2.8.3
|
ARG REGISTRY_VERSION=2.8.3
|
||||||
ARG BUILDKIT_VERSION=v0.19.0-rc2
|
ARG BUILDKIT_VERSION=v0.20.1
|
||||||
ARG UNDOCK_VERSION=0.9.0
|
ARG UNDOCK_VERSION=0.9.0
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golatest
|
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golatest
|
||||||
FROM moby/moby-bin:$DOCKER_VERSION AS docker-engine
|
FROM moby/moby-bin:$DOCKER_VERSION AS docker-engine
|
||||||
FROM dockereng/cli-bin:$DOCKER_CLI_VERSION AS docker-cli
|
FROM dockereng/cli-bin:$DOCKER_CLI_VERSION AS docker-cli
|
||||||
FROM moby/moby-bin:$DOCKER_VERSION_ALT_26 AS docker-engine-alt
|
FROM moby/moby-bin:$DOCKER_VERSION_ALT_27 AS docker-engine-alt27
|
||||||
FROM dockereng/cli-bin:$DOCKER_VERSION_ALT_26 AS docker-cli-alt
|
FROM moby/moby-bin:$DOCKER_VERSION_ALT_26 AS docker-engine-alt26
|
||||||
|
FROM dockereng/cli-bin:$DOCKER_VERSION_ALT_27 AS docker-cli-alt27
|
||||||
|
FROM dockereng/cli-bin:$DOCKER_VERSION_ALT_26 AS docker-cli-alt26
|
||||||
FROM registry:$REGISTRY_VERSION AS registry
|
FROM registry:$REGISTRY_VERSION AS registry
|
||||||
FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit
|
FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit
|
||||||
FROM crazymax/undock:$UNDOCK_VERSION AS undock
|
FROM crazymax/undock:$UNDOCK_VERSION AS undock
|
||||||
@@ -102,6 +105,7 @@ COPY --link --from=buildx-build /usr/bin/docker-buildx /buildx
|
|||||||
FROM binaries-unix AS binaries-darwin
|
FROM binaries-unix AS binaries-darwin
|
||||||
FROM binaries-unix AS binaries-freebsd
|
FROM binaries-unix AS binaries-freebsd
|
||||||
FROM binaries-unix AS binaries-linux
|
FROM binaries-unix AS binaries-linux
|
||||||
|
FROM binaries-unix AS binaries-netbsd
|
||||||
FROM binaries-unix AS binaries-openbsd
|
FROM binaries-unix AS binaries-openbsd
|
||||||
|
|
||||||
FROM scratch AS binaries-windows
|
FROM scratch AS binaries-windows
|
||||||
@@ -127,13 +131,15 @@ COPY --link --from=gotestsum /out /usr/bin/
|
|||||||
COPY --link --from=registry /bin/registry /usr/bin/
|
COPY --link --from=registry /bin/registry /usr/bin/
|
||||||
COPY --link --from=docker-engine / /usr/bin/
|
COPY --link --from=docker-engine / /usr/bin/
|
||||||
COPY --link --from=docker-cli / /usr/bin/
|
COPY --link --from=docker-cli / /usr/bin/
|
||||||
COPY --link --from=docker-engine-alt / /opt/docker-alt-26/
|
COPY --link --from=docker-engine-alt27 / /opt/docker-alt-27/
|
||||||
COPY --link --from=docker-cli-alt / /opt/docker-alt-26/
|
COPY --link --from=docker-engine-alt26 / /opt/docker-alt-26/
|
||||||
|
COPY --link --from=docker-cli-alt27 / /opt/docker-alt-27/
|
||||||
|
COPY --link --from=docker-cli-alt26 / /opt/docker-alt-26/
|
||||||
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
|
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
|
||||||
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
|
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
|
||||||
COPY --link --from=undock /usr/local/bin/undock /usr/bin/
|
COPY --link --from=undock /usr/local/bin/undock /usr/bin/
|
||||||
COPY --link --from=binaries /buildx /usr/bin/
|
COPY --link --from=binaries /buildx /usr/bin/
|
||||||
ENV TEST_DOCKER_EXTRA="docker@26.1=/opt/docker-alt-26"
|
ENV TEST_DOCKER_EXTRA="docker@27.5=/opt/docker-alt-27,docker@26.1=/opt/docker-alt-26"
|
||||||
|
|
||||||
FROM integration-test-base AS integration-test
|
FROM integration-test-base AS integration-test
|
||||||
COPY . .
|
COPY . .
|
||||||
|
134
bake/bake.go
134
bake/bake.go
@@ -27,9 +27,7 @@ import (
|
|||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
"github.com/moby/buildkit/session/auth/authprovider"
|
"github.com/moby/buildkit/session/auth/authprovider"
|
||||||
"github.com/moby/buildkit/util/entitlements"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/tonistiigi/go-csvvalue"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
"github.com/zclconf/go-cty/cty/convert"
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
)
|
)
|
||||||
@@ -47,6 +45,7 @@ type File struct {
|
|||||||
type Override struct {
|
type Override struct {
|
||||||
Value string
|
Value string
|
||||||
ArrValue []string
|
ArrValue []string
|
||||||
|
Append bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultFilenames() []string {
|
func defaultFilenames() []string {
|
||||||
@@ -487,10 +486,8 @@ func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[st
|
|||||||
if target == name {
|
if target == name {
|
||||||
return errors.Errorf("target %s cannot link to itself", target)
|
return errors.Errorf("target %s cannot link to itself", target)
|
||||||
}
|
}
|
||||||
for _, v := range visited {
|
if slices.Contains(visited, target) {
|
||||||
if v == target {
|
return errors.Errorf("infinite loop from %s to %s", name, target)
|
||||||
return errors.Errorf("infinite loop from %s to %s", name, target)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t2, ok := m[target]
|
t2, ok := m[target]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -530,9 +527,12 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
|
|||||||
m := map[string]map[string]Override{}
|
m := map[string]map[string]Override{}
|
||||||
for _, v := range v {
|
for _, v := range v {
|
||||||
parts := strings.SplitN(v, "=", 2)
|
parts := strings.SplitN(v, "=", 2)
|
||||||
keys := strings.SplitN(parts[0], ".", 3)
|
|
||||||
|
skey := strings.TrimSuffix(parts[0], "+")
|
||||||
|
appendTo := strings.HasSuffix(parts[0], "+")
|
||||||
|
keys := strings.SplitN(skey, ".", 3)
|
||||||
if len(keys) < 2 {
|
if len(keys) < 2 {
|
||||||
return nil, errors.Errorf("invalid override key %s, expected target.name", parts[0])
|
return nil, errors.Errorf("invalid override key %s, expected target.name", skey)
|
||||||
}
|
}
|
||||||
|
|
||||||
pattern := keys[0]
|
pattern := keys[0]
|
||||||
@@ -545,8 +545,7 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
kk := strings.SplitN(parts[0], ".", 2)
|
okey := strings.Join(keys[1:], ".")
|
||||||
|
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
t, ok := m[name]
|
t, ok := m[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -554,14 +553,15 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
|
|||||||
m[name] = t
|
m[name] = t
|
||||||
}
|
}
|
||||||
|
|
||||||
o := t[kk[1]]
|
override := t[okey]
|
||||||
|
|
||||||
// IMPORTANT: if you add more fields here, do not forget to update
|
// IMPORTANT: if you add more fields here, do not forget to update
|
||||||
// docs/bake-reference.md and https://docs.docker.com/build/bake/overrides/
|
// docs/reference/buildx_bake.md (--set) and https://docs.docker.com/build/bake/overrides/
|
||||||
switch keys[1] {
|
switch keys[1] {
|
||||||
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network":
|
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network", "annotations":
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
o.ArrValue = append(o.ArrValue, parts[1])
|
override.Append = appendTo
|
||||||
|
override.ArrValue = append(override.ArrValue, parts[1])
|
||||||
}
|
}
|
||||||
case "args":
|
case "args":
|
||||||
if len(keys) != 3 {
|
if len(keys) != 3 {
|
||||||
@@ -572,7 +572,7 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
o.Value = v
|
override.Value = v
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case "contexts":
|
case "contexts":
|
||||||
@@ -582,11 +582,11 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
|
|||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
o.Value = parts[1]
|
override.Value = parts[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t[kk[1]] = o
|
t[okey] = override
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
@@ -898,13 +898,21 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
|
|||||||
}
|
}
|
||||||
t.Labels[keys[1]] = &value
|
t.Labels[keys[1]] = &value
|
||||||
case "tags":
|
case "tags":
|
||||||
t.Tags = o.ArrValue
|
if o.Append {
|
||||||
|
t.Tags = append(t.Tags, o.ArrValue...)
|
||||||
|
} else {
|
||||||
|
t.Tags = o.ArrValue
|
||||||
|
}
|
||||||
case "cache-from":
|
case "cache-from":
|
||||||
cacheFrom, err := parseCacheArrValues(o.ArrValue)
|
cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.CacheFrom = cacheFrom
|
if o.Append {
|
||||||
|
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
|
||||||
|
} else {
|
||||||
|
t.CacheFrom = cacheFrom
|
||||||
|
}
|
||||||
for _, c := range t.CacheFrom {
|
for _, c := range t.CacheFrom {
|
||||||
if c.Type == "local" {
|
if c.Type == "local" {
|
||||||
if v, ok := c.Attrs["src"]; ok {
|
if v, ok := c.Attrs["src"]; ok {
|
||||||
@@ -913,11 +921,15 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "cache-to":
|
case "cache-to":
|
||||||
cacheTo, err := parseCacheArrValues(o.ArrValue)
|
cacheTo, err := buildflags.ParseCacheEntry(o.ArrValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.CacheTo = cacheTo
|
if o.Append {
|
||||||
|
t.CacheTo = t.CacheTo.Merge(cacheTo)
|
||||||
|
} else {
|
||||||
|
t.CacheTo = cacheTo
|
||||||
|
}
|
||||||
for _, c := range t.CacheTo {
|
for _, c := range t.CacheTo {
|
||||||
if c.Type == "local" {
|
if c.Type == "local" {
|
||||||
if v, ok := c.Attrs["dest"]; ok {
|
if v, ok := c.Attrs["dest"]; ok {
|
||||||
@@ -934,7 +946,11 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "invalid value for outputs")
|
return errors.Wrap(err, "invalid value for outputs")
|
||||||
}
|
}
|
||||||
t.Secrets = secrets
|
if o.Append {
|
||||||
|
t.Secrets = t.Secrets.Merge(secrets)
|
||||||
|
} else {
|
||||||
|
t.Secrets = secrets
|
||||||
|
}
|
||||||
for _, s := range t.Secrets {
|
for _, s := range t.Secrets {
|
||||||
if s.FilePath != "" {
|
if s.FilePath != "" {
|
||||||
ent.FSRead = append(ent.FSRead, s.FilePath)
|
ent.FSRead = append(ent.FSRead, s.FilePath)
|
||||||
@@ -945,18 +961,30 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "invalid value for outputs")
|
return errors.Wrap(err, "invalid value for outputs")
|
||||||
}
|
}
|
||||||
t.SSH = ssh
|
if o.Append {
|
||||||
|
t.SSH = t.SSH.Merge(ssh)
|
||||||
|
} else {
|
||||||
|
t.SSH = ssh
|
||||||
|
}
|
||||||
for _, s := range t.SSH {
|
for _, s := range t.SSH {
|
||||||
ent.FSRead = append(ent.FSRead, s.Paths...)
|
ent.FSRead = append(ent.FSRead, s.Paths...)
|
||||||
}
|
}
|
||||||
case "platform":
|
case "platform":
|
||||||
t.Platforms = o.ArrValue
|
if o.Append {
|
||||||
|
t.Platforms = append(t.Platforms, o.ArrValue...)
|
||||||
|
} else {
|
||||||
|
t.Platforms = o.ArrValue
|
||||||
|
}
|
||||||
case "output":
|
case "output":
|
||||||
outputs, err := parseArrValue[buildflags.ExportEntry](o.ArrValue)
|
outputs, err := parseArrValue[buildflags.ExportEntry](o.ArrValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "invalid value for outputs")
|
return errors.Wrap(err, "invalid value for outputs")
|
||||||
}
|
}
|
||||||
t.Outputs = outputs
|
if o.Append {
|
||||||
|
t.Outputs = t.Outputs.Merge(outputs)
|
||||||
|
} else {
|
||||||
|
t.Outputs = outputs
|
||||||
|
}
|
||||||
for _, o := range t.Outputs {
|
for _, o := range t.Outputs {
|
||||||
if o.Destination != "" {
|
if o.Destination != "" {
|
||||||
ent.FSWrite = append(ent.FSWrite, o.Destination)
|
ent.FSWrite = append(ent.FSWrite, o.Destination)
|
||||||
@@ -986,11 +1014,19 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
|
|||||||
}
|
}
|
||||||
t.NoCache = &noCache
|
t.NoCache = &noCache
|
||||||
case "no-cache-filter":
|
case "no-cache-filter":
|
||||||
t.NoCacheFilter = o.ArrValue
|
if o.Append {
|
||||||
|
t.NoCacheFilter = append(t.NoCacheFilter, o.ArrValue...)
|
||||||
|
} else {
|
||||||
|
t.NoCacheFilter = o.ArrValue
|
||||||
|
}
|
||||||
case "shm-size":
|
case "shm-size":
|
||||||
t.ShmSize = &value
|
t.ShmSize = &value
|
||||||
case "ulimits":
|
case "ulimits":
|
||||||
t.Ulimits = o.ArrValue
|
if o.Append {
|
||||||
|
t.Ulimits = append(t.Ulimits, o.ArrValue...)
|
||||||
|
} else {
|
||||||
|
t.Ulimits = o.ArrValue
|
||||||
|
}
|
||||||
case "network":
|
case "network":
|
||||||
t.NetworkMode = &value
|
t.NetworkMode = &value
|
||||||
case "pull":
|
case "pull":
|
||||||
@@ -1131,7 +1167,9 @@ func (t *Target) GetName(ectx *hcl.EvalContext, block *hcl.Block, loadDeps func(
|
|||||||
func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) {
|
func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) {
|
||||||
// make sure local credentials are loaded multiple times for different targets
|
// make sure local credentials are loaded multiple times for different targets
|
||||||
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
|
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
|
||||||
authProvider := authprovider.NewDockerAuthProvider(dockerConfig, nil)
|
authProvider := authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
|
||||||
|
ConfigFile: dockerConfig,
|
||||||
|
})
|
||||||
|
|
||||||
m2 := make(map[string]build.Options, len(m))
|
m2 := make(map[string]build.Options, len(m))
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
@@ -1433,9 +1471,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||||||
}
|
}
|
||||||
bo.Ulimits = ulimits
|
bo.Ulimits = ulimits
|
||||||
|
|
||||||
for _, ent := range t.Entitlements {
|
bo.Allow = append(bo.Allow, t.Entitlements...)
|
||||||
bo.Allow = append(bo.Allow, entitlements.Entitlement(ent))
|
|
||||||
}
|
|
||||||
|
|
||||||
return bo, nil
|
return bo, nil
|
||||||
}
|
}
|
||||||
@@ -1585,37 +1621,3 @@ func parseArrValue[T any, PT arrValue[T]](s []string) ([]*T, error) {
|
|||||||
}
|
}
|
||||||
return outputs, nil
|
return outputs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCacheArrValues(s []string) (buildflags.CacheOptions, error) {
|
|
||||||
var outs buildflags.CacheOptions
|
|
||||||
for _, in := range s {
|
|
||||||
if in == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.Contains(in, "=") {
|
|
||||||
// This is ref only format. Each field in the CSV is its own entry.
|
|
||||||
fields, err := csvvalue.Fields(in, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, field := range fields {
|
|
||||||
out := buildflags.CacheOptionsEntry{}
|
|
||||||
if err := out.UnmarshalText([]byte(field)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
outs = append(outs, &out)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normal entry.
|
|
||||||
out := buildflags.CacheOptionsEntry{}
|
|
||||||
if err := out.UnmarshalText([]byte(in)); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
outs = append(outs, &out)
|
|
||||||
}
|
|
||||||
return outs, nil
|
|
||||||
}
|
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/buildx/util/buildflags"
|
||||||
"github.com/moby/buildkit/util/entitlements"
|
"github.com/moby/buildkit/util/entitlements"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -33,6 +34,18 @@ target "webapp" {
|
|||||||
args = {
|
args = {
|
||||||
VAR_BOTH = "webapp"
|
VAR_BOTH = "webapp"
|
||||||
}
|
}
|
||||||
|
annotations = [
|
||||||
|
"index,manifest:org.opencontainers.image.authors=dvdksn"
|
||||||
|
]
|
||||||
|
attest = [
|
||||||
|
"type=provenance,mode=max"
|
||||||
|
]
|
||||||
|
platforms = [
|
||||||
|
"linux/amd64"
|
||||||
|
]
|
||||||
|
secret = [
|
||||||
|
"id=FOO,env=FOO"
|
||||||
|
]
|
||||||
inherits = ["webDEP"]
|
inherits = ["webDEP"]
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
@@ -114,6 +127,31 @@ target "webapp" {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("AnnotationsOverrides", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.annotations=index,manifest:org.opencontainers.image.vendor=docker"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{"index,manifest:org.opencontainers.image.authors=dvdksn", "index,manifest:org.opencontainers.image.vendor=docker"}, m["webapp"].Annotations)
|
||||||
|
require.Equal(t, 1, len(g))
|
||||||
|
require.Equal(t, []string{"webapp"}, g["default"].Targets)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("AttestOverride", func(t *testing.T) {
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.attest=type=sbom"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, m["webapp"].Attest, 2)
|
||||||
|
require.Equal(t, "provenance", m["webapp"].Attest[0].Type)
|
||||||
|
require.Equal(t, "sbom", m["webapp"].Attest[1].Type)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("AttestAppend", func(t *testing.T) {
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.attest+=type=sbom"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, m["webapp"].Attest, 2)
|
||||||
|
require.Equal(t, "provenance", m["webapp"].Attest[0].Type)
|
||||||
|
require.Equal(t, "sbom", m["webapp"].Attest[1].Type)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("ContextOverride", func(t *testing.T) {
|
t.Run("ContextOverride", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
_, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil, &EntitlementConf{})
|
_, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil, &EntitlementConf{})
|
||||||
@@ -135,6 +173,49 @@ target "webapp" {
|
|||||||
require.Equal(t, []string{"webapp"}, g["default"].Targets)
|
require.Equal(t, []string{"webapp"}, g["default"].Targets)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("PlatformOverride", func(t *testing.T) {
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform=linux/arm64"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{"linux/arm64"}, m["webapp"].Platforms)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PlatformAppend", func(t *testing.T) {
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, m["webapp"].Platforms)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PlatformAppendMulti", func(t *testing.T) {
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64", "webapp.platform+=linux/riscv64"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{"linux/amd64", "linux/arm64", "linux/riscv64"}, m["webapp"].Platforms)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PlatformAppendMultiLastOverride", func(t *testing.T) {
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.platform+=linux/arm64", "webapp.platform=linux/riscv64"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []string{"linux/arm64", "linux/riscv64"}, m["webapp"].Platforms)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SecretsOverride", func(t *testing.T) {
|
||||||
|
t.Setenv("FOO", "foo")
|
||||||
|
t.Setenv("BAR", "bar")
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets=id=BAR,env=BAR"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, m["webapp"].Secrets, 1)
|
||||||
|
require.Equal(t, "BAR", m["webapp"].Secrets[0].ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("SecretsAppend", func(t *testing.T) {
|
||||||
|
t.Setenv("FOO", "foo")
|
||||||
|
t.Setenv("BAR", "bar")
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.secrets+=id=BAR,env=BAR"}, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, m["webapp"].Secrets, 2)
|
||||||
|
require.Equal(t, "FOO", m["webapp"].Secrets[0].ID)
|
||||||
|
require.Equal(t, "BAR", m["webapp"].Secrets[1].ID)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("ShmSizeOverride", func(t *testing.T) {
|
t.Run("ShmSizeOverride", func(t *testing.T) {
|
||||||
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil, &EntitlementConf{})
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil, &EntitlementConf{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -1759,6 +1840,27 @@ func TestAnnotations(t *testing.T) {
|
|||||||
require.Equal(t, "bar", bo["app"].Exports[0].Attrs["annotation-manifest[linux/amd64].foo"])
|
require.Equal(t, "bar", bo["app"].Exports[0].Attrs["annotation-manifest[linux/amd64].foo"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRefOnlyCacheOptions(t *testing.T) {
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
|
output = ["type=image,name=foo"]
|
||||||
|
cache-from = ["ref1,ref2"]
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
ctx := context.TODO()
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil, &EntitlementConf{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, m, 1)
|
||||||
|
require.Contains(t, m, "app")
|
||||||
|
require.Equal(t, buildflags.CacheOptions{
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "ref1"}},
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "ref2"}},
|
||||||
|
}, m["app"].CacheFrom)
|
||||||
|
}
|
||||||
|
|
||||||
func TestHCLEntitlements(t *testing.T) {
|
func TestHCLEntitlements(t *testing.T) {
|
||||||
fp := File{
|
fp := File{
|
||||||
Name: "docker-bake.hcl",
|
Name: "docker-bake.hcl",
|
||||||
@@ -1784,8 +1886,8 @@ func TestHCLEntitlements(t *testing.T) {
|
|||||||
require.Equal(t, "network.host", m["app"].Entitlements[1])
|
require.Equal(t, "network.host", m["app"].Entitlements[1])
|
||||||
|
|
||||||
require.Len(t, bo["app"].Allow, 2)
|
require.Len(t, bo["app"].Allow, 2)
|
||||||
require.Equal(t, entitlements.EntitlementSecurityInsecure, bo["app"].Allow[0])
|
require.Equal(t, entitlements.EntitlementSecurityInsecure.String(), bo["app"].Allow[0])
|
||||||
require.Equal(t, entitlements.EntitlementNetworkHost, bo["app"].Allow[1])
|
require.Equal(t, entitlements.EntitlementNetworkHost.String(), bo["app"].Allow[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEntitlementsForNetHostCompose(t *testing.T) {
|
func TestEntitlementsForNetHostCompose(t *testing.T) {
|
||||||
@@ -1824,7 +1926,7 @@ func TestEntitlementsForNetHostCompose(t *testing.T) {
|
|||||||
require.Equal(t, "host", *m["app"].NetworkMode)
|
require.Equal(t, "host", *m["app"].NetworkMode)
|
||||||
|
|
||||||
require.Len(t, bo["app"].Allow, 1)
|
require.Len(t, bo["app"].Allow, 1)
|
||||||
require.Equal(t, entitlements.EntitlementNetworkHost, bo["app"].Allow[0])
|
require.Equal(t, entitlements.EntitlementNetworkHost.String(), bo["app"].Allow[0])
|
||||||
require.Equal(t, "host", bo["app"].NetworkMode)
|
require.Equal(t, "host", bo["app"].NetworkMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1855,7 +1957,7 @@ func TestEntitlementsForNetHost(t *testing.T) {
|
|||||||
require.Equal(t, "host", *m["app"].NetworkMode)
|
require.Equal(t, "host", *m["app"].NetworkMode)
|
||||||
|
|
||||||
require.Len(t, bo["app"].Allow, 1)
|
require.Len(t, bo["app"].Allow, 1)
|
||||||
require.Equal(t, entitlements.EntitlementNetworkHost, bo["app"].Allow[0])
|
require.Equal(t, entitlements.EntitlementNetworkHost.String(), bo["app"].Allow[0])
|
||||||
require.Equal(t, "host", bo["app"].NetworkMode)
|
require.Equal(t, "host", bo["app"].NetworkMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -145,12 +145,12 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
|||||||
labels[k] = &v
|
labels[k] = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheFrom, err := parseCacheArrValues(s.Build.CacheFrom)
|
cacheFrom, err := buildflags.ParseCacheEntry(s.Build.CacheFrom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheTo, err := parseCacheArrValues(s.Build.CacheTo)
|
cacheTo, err := buildflags.ParseCacheEntry(s.Build.CacheTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ type (
|
|||||||
stringArray []string
|
stringArray []string
|
||||||
)
|
)
|
||||||
|
|
||||||
func (sa *stringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
func (sa *stringArray) UnmarshalYAML(unmarshal func(any) error) error {
|
||||||
var multi []string
|
var multi []string
|
||||||
err := unmarshal(&multi)
|
err := unmarshal(&multi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -332,7 +332,7 @@ func (sa *stringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||||||
|
|
||||||
// composeExtTarget converts Compose build extension x-bake to bake Target
|
// composeExtTarget converts Compose build extension x-bake to bake Target
|
||||||
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension
|
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension
|
||||||
func (t *Target) composeExtTarget(exts map[string]interface{}) error {
|
func (t *Target) composeExtTarget(exts map[string]any) error {
|
||||||
var xb xbake
|
var xb xbake
|
||||||
|
|
||||||
ext, ok := exts["x-bake"]
|
ext, ok := exts["x-bake"]
|
||||||
@@ -349,14 +349,14 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
|
|||||||
t.Tags = dedupSlice(append(t.Tags, xb.Tags...))
|
t.Tags = dedupSlice(append(t.Tags, xb.Tags...))
|
||||||
}
|
}
|
||||||
if len(xb.CacheFrom) > 0 {
|
if len(xb.CacheFrom) > 0 {
|
||||||
cacheFrom, err := parseCacheArrValues(xb.CacheFrom)
|
cacheFrom, err := buildflags.ParseCacheEntry(xb.CacheFrom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
|
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
|
||||||
}
|
}
|
||||||
if len(xb.CacheTo) > 0 {
|
if len(xb.CacheTo) > 0 {
|
||||||
cacheTo, err := parseCacheArrValues(xb.CacheTo)
|
cacheTo, err := buildflags.ParseCacheEntry(xb.CacheTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/moby/buildkit/util/entitlements"
|
"github.com/moby/buildkit/util/entitlements"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/tonistiigi/go-csvvalue"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EntitlementKey string
|
type EntitlementKey string
|
||||||
@@ -27,6 +28,7 @@ type EntitlementKey string
|
|||||||
const (
|
const (
|
||||||
EntitlementKeyNetworkHost EntitlementKey = "network.host"
|
EntitlementKeyNetworkHost EntitlementKey = "network.host"
|
||||||
EntitlementKeySecurityInsecure EntitlementKey = "security.insecure"
|
EntitlementKeySecurityInsecure EntitlementKey = "security.insecure"
|
||||||
|
EntitlementKeyDevice EntitlementKey = "device"
|
||||||
EntitlementKeyFSRead EntitlementKey = "fs.read"
|
EntitlementKeyFSRead EntitlementKey = "fs.read"
|
||||||
EntitlementKeyFSWrite EntitlementKey = "fs.write"
|
EntitlementKeyFSWrite EntitlementKey = "fs.write"
|
||||||
EntitlementKeyFS EntitlementKey = "fs"
|
EntitlementKeyFS EntitlementKey = "fs"
|
||||||
@@ -39,6 +41,7 @@ const (
|
|||||||
type EntitlementConf struct {
|
type EntitlementConf struct {
|
||||||
NetworkHost bool
|
NetworkHost bool
|
||||||
SecurityInsecure bool
|
SecurityInsecure bool
|
||||||
|
Devices *EntitlementsDevicesConf
|
||||||
FSRead []string
|
FSRead []string
|
||||||
FSWrite []string
|
FSWrite []string
|
||||||
ImagePush []string
|
ImagePush []string
|
||||||
@@ -46,6 +49,11 @@ type EntitlementConf struct {
|
|||||||
SSH bool
|
SSH bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EntitlementsDevicesConf struct {
|
||||||
|
All bool
|
||||||
|
Devices map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
func ParseEntitlements(in []string) (EntitlementConf, error) {
|
func ParseEntitlements(in []string) (EntitlementConf, error) {
|
||||||
var conf EntitlementConf
|
var conf EntitlementConf
|
||||||
for _, e := range in {
|
for _, e := range in {
|
||||||
@@ -59,6 +67,22 @@ func ParseEntitlements(in []string) (EntitlementConf, error) {
|
|||||||
default:
|
default:
|
||||||
k, v, _ := strings.Cut(e, "=")
|
k, v, _ := strings.Cut(e, "=")
|
||||||
switch k {
|
switch k {
|
||||||
|
case string(EntitlementKeyDevice):
|
||||||
|
if v == "" {
|
||||||
|
conf.Devices = &EntitlementsDevicesConf{All: true}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields, err := csvvalue.Fields(v, nil)
|
||||||
|
if err != nil {
|
||||||
|
return EntitlementConf{}, errors.Wrapf(err, "failed to parse device entitlement %q", v)
|
||||||
|
}
|
||||||
|
if conf.Devices == nil {
|
||||||
|
conf.Devices = &EntitlementsDevicesConf{}
|
||||||
|
}
|
||||||
|
if conf.Devices.Devices == nil {
|
||||||
|
conf.Devices.Devices = make(map[string]struct{}, 0)
|
||||||
|
}
|
||||||
|
conf.Devices.Devices[fields[0]] = struct{}{}
|
||||||
case string(EntitlementKeyFSRead):
|
case string(EntitlementKeyFSRead):
|
||||||
conf.FSRead = append(conf.FSRead, v)
|
conf.FSRead = append(conf.FSRead, v)
|
||||||
case string(EntitlementKeyFSWrite):
|
case string(EntitlementKeyFSWrite):
|
||||||
@@ -95,12 +119,34 @@ func (c EntitlementConf) Validate(m map[string]build.Options) (EntitlementConf,
|
|||||||
|
|
||||||
func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) error {
|
func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) error {
|
||||||
for _, e := range bo.Allow {
|
for _, e := range bo.Allow {
|
||||||
|
k, rest, _ := strings.Cut(e, "=")
|
||||||
|
switch k {
|
||||||
|
case entitlements.EntitlementDevice.String():
|
||||||
|
if rest == "" {
|
||||||
|
if c.Devices == nil || !c.Devices.All {
|
||||||
|
expected.Devices = &EntitlementsDevicesConf{All: true}
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields, err := csvvalue.Fields(rest, nil)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to parse device entitlement %q", rest)
|
||||||
|
}
|
||||||
|
if expected.Devices == nil {
|
||||||
|
expected.Devices = &EntitlementsDevicesConf{}
|
||||||
|
}
|
||||||
|
if expected.Devices.Devices == nil {
|
||||||
|
expected.Devices.Devices = make(map[string]struct{}, 0)
|
||||||
|
}
|
||||||
|
expected.Devices.Devices[fields[0]] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
switch e {
|
switch e {
|
||||||
case entitlements.EntitlementNetworkHost:
|
case entitlements.EntitlementNetworkHost.String():
|
||||||
if !c.NetworkHost {
|
if !c.NetworkHost {
|
||||||
expected.NetworkHost = true
|
expected.NetworkHost = true
|
||||||
}
|
}
|
||||||
case entitlements.EntitlementSecurityInsecure:
|
case entitlements.EntitlementSecurityInsecure.String():
|
||||||
if !c.SecurityInsecure {
|
if !c.SecurityInsecure {
|
||||||
expected.SecurityInsecure = true
|
expected.SecurityInsecure = true
|
||||||
}
|
}
|
||||||
@@ -187,6 +233,18 @@ func (c EntitlementConf) Prompt(ctx context.Context, isRemote bool, out io.Write
|
|||||||
flags = append(flags, string(EntitlementKeySecurityInsecure))
|
flags = append(flags, string(EntitlementKeySecurityInsecure))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Devices != nil {
|
||||||
|
if c.Devices.All {
|
||||||
|
msgs = append(msgs, " - Access to CDI devices")
|
||||||
|
flags = append(flags, string(EntitlementKeyDevice))
|
||||||
|
} else {
|
||||||
|
for d := range c.Devices.Devices {
|
||||||
|
msgs = append(msgs, fmt.Sprintf(" - Access to device %s", d))
|
||||||
|
flags = append(flags, string(EntitlementKeyDevice)+"="+d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c.SSH {
|
if c.SSH {
|
||||||
msgsFS = append(msgsFS, " - Forwarding default SSH agent socket")
|
msgsFS = append(msgsFS, " - Forwarding default SSH agent socket")
|
||||||
flagsFS = append(flagsFS, string(EntitlementKeySSH))
|
flagsFS = append(flagsFS, string(EntitlementKeySSH))
|
||||||
@@ -248,7 +306,7 @@ func (c EntitlementConf) Prompt(ctx context.Context, isRemote bool, out io.Write
|
|||||||
fmt.Fprintf(out, "\nPass %q to grant requested privileges.\n", strings.Join(slices.Concat(flags, flagsFS), " "))
|
fmt.Fprintf(out, "\nPass %q to grant requested privileges.\n", strings.Join(slices.Concat(flags, flagsFS), " "))
|
||||||
}
|
}
|
||||||
|
|
||||||
args := append([]string(nil), os.Args...)
|
args := slices.Clone(os.Args)
|
||||||
if v, ok := os.LookupEnv("DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"); ok && v != "" {
|
if v, ok := os.LookupEnv("DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND"); ok && v != "" {
|
||||||
args[0] = v
|
args[0] = v
|
||||||
}
|
}
|
||||||
|
@@ -208,8 +208,8 @@ func TestValidateEntitlements(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "NetworkHostMissing",
|
name: "NetworkHostMissing",
|
||||||
opt: build.Options{
|
opt: build.Options{
|
||||||
Allow: []entitlements.Entitlement{
|
Allow: []string{
|
||||||
entitlements.EntitlementNetworkHost,
|
entitlements.EntitlementNetworkHost.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: EntitlementConf{
|
expected: EntitlementConf{
|
||||||
@@ -223,8 +223,8 @@ func TestValidateEntitlements(t *testing.T) {
|
|||||||
NetworkHost: true,
|
NetworkHost: true,
|
||||||
},
|
},
|
||||||
opt: build.Options{
|
opt: build.Options{
|
||||||
Allow: []entitlements.Entitlement{
|
Allow: []string{
|
||||||
entitlements.EntitlementNetworkHost,
|
entitlements.EntitlementNetworkHost.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: EntitlementConf{
|
expected: EntitlementConf{
|
||||||
@@ -234,9 +234,9 @@ func TestValidateEntitlements(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "SecurityAndNetworkHostMissing",
|
name: "SecurityAndNetworkHostMissing",
|
||||||
opt: build.Options{
|
opt: build.Options{
|
||||||
Allow: []entitlements.Entitlement{
|
Allow: []string{
|
||||||
entitlements.EntitlementNetworkHost,
|
entitlements.EntitlementNetworkHost.String(),
|
||||||
entitlements.EntitlementSecurityInsecure,
|
entitlements.EntitlementSecurityInsecure.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: EntitlementConf{
|
expected: EntitlementConf{
|
||||||
@@ -251,9 +251,9 @@ func TestValidateEntitlements(t *testing.T) {
|
|||||||
NetworkHost: true,
|
NetworkHost: true,
|
||||||
},
|
},
|
||||||
opt: build.Options{
|
opt: build.Options{
|
||||||
Allow: []entitlements.Entitlement{
|
Allow: []string{
|
||||||
entitlements.EntitlementNetworkHost,
|
entitlements.EntitlementNetworkHost.String(),
|
||||||
entitlements.EntitlementSecurityInsecure,
|
entitlements.EntitlementSecurityInsecure.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: EntitlementConf{
|
expected: EntitlementConf{
|
||||||
|
@@ -2,8 +2,10 @@ package bake
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
hcl "github.com/hashicorp/hcl/v2"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -606,7 +608,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
|
|||||||
target "app" {
|
target "app" {
|
||||||
attest = [
|
attest = [
|
||||||
{ type = "provenance", mode = "max" },
|
{ type = "provenance", mode = "max" },
|
||||||
"type=sbom,disabled=true",
|
"type=sbom,disabled=true,generator=foo,\"ENV1=bar,baz\",ENV2=hello",
|
||||||
]
|
]
|
||||||
|
|
||||||
cache-from = [
|
cache-from = [
|
||||||
@@ -639,7 +641,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 1, len(c.Targets))
|
require.Equal(t, 1, len(c.Targets))
|
||||||
require.Equal(t, []string{"type=provenance,mode=max", "type=sbom,disabled=true"}, stringify(c.Targets[0].Attest))
|
require.Equal(t, []string{"type=provenance,mode=max", "type=sbom,disabled=true,\"ENV1=bar,baz\",ENV2=hello,generator=foo"}, stringify(c.Targets[0].Attest))
|
||||||
require.Equal(t, []string{"type=local,dest=../out", "type=oci,dest=../out.tar"}, stringify(c.Targets[0].Outputs))
|
require.Equal(t, []string{"type=local,dest=../out", "type=oci,dest=../out.tar"}, stringify(c.Targets[0].Outputs))
|
||||||
require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(c.Targets[0].CacheFrom))
|
require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(c.Targets[0].CacheFrom))
|
||||||
require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[0].CacheTo))
|
require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[0].CacheTo))
|
||||||
@@ -647,7 +649,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
|
|||||||
require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH))
|
require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHCLAttrsCapsuleTypeVars(t *testing.T) {
|
func TestHCLAttrsCapsuleType_ObjectVars(t *testing.T) {
|
||||||
dt := []byte(`
|
dt := []byte(`
|
||||||
variable "foo" {
|
variable "foo" {
|
||||||
default = "bar"
|
default = "bar"
|
||||||
@@ -716,6 +718,52 @@ func TestHCLAttrsCapsuleTypeVars(t *testing.T) {
|
|||||||
require.Equal(t, []string{"id=oci,src=/local/secret"}, stringify(web.Secrets))
|
require.Equal(t, []string{"id=oci,src=/local/secret"}, stringify(web.Secrets))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHCLAttrsCapsuleType_MissingVars(t *testing.T) {
|
||||||
|
dt := []byte(`
|
||||||
|
target "app" {
|
||||||
|
attest = [
|
||||||
|
"type=sbom,disabled=${SBOM}",
|
||||||
|
]
|
||||||
|
|
||||||
|
cache-from = [
|
||||||
|
{ type = "registry", ref = "user/app:${FOO1}" },
|
||||||
|
"type=local,src=path/to/cache:${FOO2}",
|
||||||
|
]
|
||||||
|
|
||||||
|
cache-to = [
|
||||||
|
{ type = "local", dest = "path/to/${BAR}" },
|
||||||
|
]
|
||||||
|
|
||||||
|
output = [
|
||||||
|
{ type = "oci", dest = "../${OUTPUT}.tar" },
|
||||||
|
]
|
||||||
|
|
||||||
|
secret = [
|
||||||
|
{ id = "mysecret", src = "/local/${SECRET}" },
|
||||||
|
]
|
||||||
|
|
||||||
|
ssh = [
|
||||||
|
{ id = "key", paths = ["path/to/${SSH_KEY}"] },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
_, err := ParseFile(dt, "docker-bake.hcl")
|
||||||
|
require.ErrorAs(t, err, &diags)
|
||||||
|
|
||||||
|
re := regexp.MustCompile(`There is no variable named "([\w\d_]+)"`)
|
||||||
|
var actual []string
|
||||||
|
for _, diag := range diags {
|
||||||
|
if m := re.FindStringSubmatch(diag.Error()); m != nil {
|
||||||
|
actual = append(actual, m[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.ElementsMatch(t,
|
||||||
|
[]string{"SBOM", "FOO1", "FOO2", "BAR", "OUTPUT", "SECRET", "SSH_KEY"},
|
||||||
|
actual)
|
||||||
|
}
|
||||||
|
|
||||||
func TestHCLMultiFileAttrs(t *testing.T) {
|
func TestHCLMultiFileAttrs(t *testing.T) {
|
||||||
dt := []byte(`
|
dt := []byte(`
|
||||||
variable "FOO" {
|
variable "FOO" {
|
||||||
@@ -1597,7 +1645,7 @@ func TestHCLIndexOfFunc(t *testing.T) {
|
|||||||
require.Empty(t, c.Targets[1].Tags[1])
|
require.Empty(t, c.Targets[1].Tags[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func ptrstr(s interface{}) *string {
|
func ptrstr(s any) *string {
|
||||||
var n *string
|
var n *string
|
||||||
if reflect.ValueOf(s).Kind() == reflect.String {
|
if reflect.ValueOf(s).Kind() == reflect.String {
|
||||||
ss := s.(string)
|
ss := s.(string)
|
||||||
|
@@ -15,11 +15,11 @@ import (
|
|||||||
|
|
||||||
// DecodeOptions allows customizing sections of the decoding process.
|
// DecodeOptions allows customizing sections of the decoding process.
|
||||||
type DecodeOptions struct {
|
type DecodeOptions struct {
|
||||||
ImpliedType func(gv interface{}) (cty.Type, error)
|
ImpliedType func(gv any) (cty.Type, error)
|
||||||
Convert func(in cty.Value, want cty.Type) (cty.Value, error)
|
Convert func(in cty.Value, want cty.Type) (cty.Value, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o DecodeOptions) DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
func (o DecodeOptions) DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
|
||||||
o = o.withDefaults()
|
o = o.withDefaults()
|
||||||
|
|
||||||
rv := reflect.ValueOf(val)
|
rv := reflect.ValueOf(val)
|
||||||
@@ -46,7 +46,7 @@ func (o DecodeOptions) DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val inter
|
|||||||
// are returned then the given value may have been partially-populated but
|
// are returned then the given value may have been partially-populated but
|
||||||
// may still be accessed by a careful caller for static analysis and editor
|
// may still be accessed by a careful caller for static analysis and editor
|
||||||
// integration use-cases.
|
// integration use-cases.
|
||||||
func DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
func DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
|
||||||
return DecodeOptions{}.DecodeBody(body, ctx, val)
|
return DecodeOptions{}.DecodeBody(body, ctx, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +282,7 @@ func (o DecodeOptions) decodeBlockToValue(block *hcl.Block, ctx *hcl.EvalContext
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o DecodeOptions) DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
func (o DecodeOptions) DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
|
||||||
o = o.withDefaults()
|
o = o.withDefaults()
|
||||||
|
|
||||||
srcVal, diags := expr.Value(ctx)
|
srcVal, diags := expr.Value(ctx)
|
||||||
@@ -332,7 +332,7 @@ func (o DecodeOptions) DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContex
|
|||||||
// are returned then the given value may have been partially-populated but
|
// are returned then the given value may have been partially-populated but
|
||||||
// may still be accessed by a careful caller for static analysis and editor
|
// may still be accessed by a careful caller for static analysis and editor
|
||||||
// integration use-cases.
|
// integration use-cases.
|
||||||
func DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
func DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
|
||||||
return DecodeOptions{}.DecodeExpression(expr, ctx, val)
|
return DecodeOptions{}.DecodeExpression(expr, ctx, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,8 +16,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDecodeBody(t *testing.T) {
|
func TestDecodeBody(t *testing.T) {
|
||||||
deepEquals := func(other interface{}) func(v interface{}) bool {
|
deepEquals := func(other any) func(v any) bool {
|
||||||
return func(v interface{}) bool {
|
return func(v any) bool {
|
||||||
return reflect.DeepEqual(v, other)
|
return reflect.DeepEqual(v, other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,19 +45,19 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Body map[string]interface{}
|
Body map[string]any
|
||||||
Target func() interface{}
|
Target func() any
|
||||||
Check func(v interface{}) bool
|
Check func(v any) bool
|
||||||
DiagCount int
|
DiagCount int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
map[string]interface{}{},
|
map[string]any{},
|
||||||
makeInstantiateType(struct{}{}),
|
makeInstantiateType(struct{}{}),
|
||||||
deepEquals(struct{}{}),
|
deepEquals(struct{}{}),
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{},
|
map[string]any{},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Name string `hcl:"name"`
|
Name string `hcl:"name"`
|
||||||
}{}),
|
}{}),
|
||||||
@@ -67,7 +67,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
1, // name is required
|
1, // name is required
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{},
|
map[string]any{},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Name *string `hcl:"name"`
|
Name *string `hcl:"name"`
|
||||||
}{}),
|
}{}),
|
||||||
@@ -77,7 +77,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
}, // name nil
|
}, // name nil
|
||||||
{
|
{
|
||||||
map[string]interface{}{},
|
map[string]any{},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Name string `hcl:"name,optional"`
|
Name string `hcl:"name,optional"`
|
||||||
}{}),
|
}{}),
|
||||||
@@ -87,9 +87,9 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
}, // name optional
|
}, // name optional
|
||||||
{
|
{
|
||||||
map[string]interface{}{},
|
map[string]any{},
|
||||||
makeInstantiateType(withNameExpression{}),
|
makeInstantiateType(withNameExpression{}),
|
||||||
func(v interface{}) bool {
|
func(v any) bool {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -109,11 +109,11 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
},
|
},
|
||||||
makeInstantiateType(withNameExpression{}),
|
makeInstantiateType(withNameExpression{}),
|
||||||
func(v interface{}) bool {
|
func(v any) bool {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -133,7 +133,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
@@ -145,7 +145,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"age": 23,
|
"age": 23,
|
||||||
},
|
},
|
||||||
@@ -158,7 +158,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
1, // Extraneous "age" property
|
1, // Extraneous "age" property
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"age": 50,
|
"age": 50,
|
||||||
},
|
},
|
||||||
@@ -166,7 +166,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
Name string `hcl:"name"`
|
Name string `hcl:"name"`
|
||||||
Attrs hcl.Attributes `hcl:",remain"`
|
Attrs hcl.Attributes `hcl:",remain"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
got := gotI.(struct {
|
got := gotI.(struct {
|
||||||
Name string `hcl:"name"`
|
Name string `hcl:"name"`
|
||||||
Attrs hcl.Attributes `hcl:",remain"`
|
Attrs hcl.Attributes `hcl:",remain"`
|
||||||
@@ -176,7 +176,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"age": 50,
|
"age": 50,
|
||||||
},
|
},
|
||||||
@@ -184,7 +184,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
Name string `hcl:"name"`
|
Name string `hcl:"name"`
|
||||||
Remain hcl.Body `hcl:",remain"`
|
Remain hcl.Body `hcl:",remain"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
got := gotI.(struct {
|
got := gotI.(struct {
|
||||||
Name string `hcl:"name"`
|
Name string `hcl:"name"`
|
||||||
Remain hcl.Body `hcl:",remain"`
|
Remain hcl.Body `hcl:",remain"`
|
||||||
@@ -197,7 +197,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"living": true,
|
"living": true,
|
||||||
},
|
},
|
||||||
@@ -217,7 +217,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"age": 50,
|
"age": 50,
|
||||||
},
|
},
|
||||||
@@ -226,7 +226,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
Body hcl.Body `hcl:",body"`
|
Body hcl.Body `hcl:",body"`
|
||||||
Remain hcl.Body `hcl:",remain"`
|
Remain hcl.Body `hcl:",remain"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
got := gotI.(struct {
|
got := gotI.(struct {
|
||||||
Name string `hcl:"name"`
|
Name string `hcl:"name"`
|
||||||
Body hcl.Body `hcl:",body"`
|
Body hcl.Body `hcl:",body"`
|
||||||
@@ -241,76 +241,76 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": map[string]interface{}{},
|
"noodle": map[string]any{},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle struct{} `hcl:"noodle,block"`
|
Noodle struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
// Generating no diagnostics is good enough for this one.
|
// Generating no diagnostics is good enough for this one.
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{{}},
|
"noodle": []map[string]any{{}},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle struct{} `hcl:"noodle,block"`
|
Noodle struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
// Generating no diagnostics is good enough for this one.
|
// Generating no diagnostics is good enough for this one.
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{{}, {}},
|
"noodle": []map[string]any{{}, {}},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle struct{} `hcl:"noodle,block"`
|
Noodle struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
// Generating one diagnostic is good enough for this one.
|
// Generating one diagnostic is good enough for this one.
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{},
|
map[string]any{},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle struct{} `hcl:"noodle,block"`
|
Noodle struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
// Generating one diagnostic is good enough for this one.
|
// Generating one diagnostic is good enough for this one.
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{},
|
"noodle": []map[string]any{},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle struct{} `hcl:"noodle,block"`
|
Noodle struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
// Generating one diagnostic is good enough for this one.
|
// Generating one diagnostic is good enough for this one.
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": map[string]interface{}{},
|
"noodle": map[string]any{},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle *struct{} `hcl:"noodle,block"`
|
Noodle *struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
return gotI.(struct {
|
return gotI.(struct {
|
||||||
Noodle *struct{} `hcl:"noodle,block"`
|
Noodle *struct{} `hcl:"noodle,block"`
|
||||||
}).Noodle != nil
|
}).Noodle != nil
|
||||||
@@ -318,13 +318,13 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{{}},
|
"noodle": []map[string]any{{}},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle *struct{} `hcl:"noodle,block"`
|
Noodle *struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
return gotI.(struct {
|
return gotI.(struct {
|
||||||
Noodle *struct{} `hcl:"noodle,block"`
|
Noodle *struct{} `hcl:"noodle,block"`
|
||||||
}).Noodle != nil
|
}).Noodle != nil
|
||||||
@@ -332,13 +332,13 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{},
|
"noodle": []map[string]any{},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle *struct{} `hcl:"noodle,block"`
|
Noodle *struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
return gotI.(struct {
|
return gotI.(struct {
|
||||||
Noodle *struct{} `hcl:"noodle,block"`
|
Noodle *struct{} `hcl:"noodle,block"`
|
||||||
}).Noodle == nil
|
}).Noodle == nil
|
||||||
@@ -346,26 +346,26 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{{}, {}},
|
"noodle": []map[string]any{{}, {}},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle *struct{} `hcl:"noodle,block"`
|
Noodle *struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
// Generating one diagnostic is good enough for this one.
|
// Generating one diagnostic is good enough for this one.
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{},
|
"noodle": []map[string]any{},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle []struct{} `hcl:"noodle,block"`
|
Noodle []struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
noodle := gotI.(struct {
|
noodle := gotI.(struct {
|
||||||
Noodle []struct{} `hcl:"noodle,block"`
|
Noodle []struct{} `hcl:"noodle,block"`
|
||||||
}).Noodle
|
}).Noodle
|
||||||
@@ -374,13 +374,13 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{{}},
|
"noodle": []map[string]any{{}},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle []struct{} `hcl:"noodle,block"`
|
Noodle []struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
noodle := gotI.(struct {
|
noodle := gotI.(struct {
|
||||||
Noodle []struct{} `hcl:"noodle,block"`
|
Noodle []struct{} `hcl:"noodle,block"`
|
||||||
}).Noodle
|
}).Noodle
|
||||||
@@ -389,13 +389,13 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": []map[string]interface{}{{}, {}},
|
"noodle": []map[string]any{{}, {}},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle []struct{} `hcl:"noodle,block"`
|
Noodle []struct{} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
noodle := gotI.(struct {
|
noodle := gotI.(struct {
|
||||||
Noodle []struct{} `hcl:"noodle,block"`
|
Noodle []struct{} `hcl:"noodle,block"`
|
||||||
}).Noodle
|
}).Noodle
|
||||||
@@ -404,15 +404,15 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": map[string]interface{}{},
|
"noodle": map[string]any{},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
Noodle struct {
|
Noodle struct {
|
||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
} `hcl:"noodle,block"`
|
} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
//nolint:misspell
|
//nolint:misspell
|
||||||
// Generating two diagnostics is good enough for this one.
|
// Generating two diagnostics is good enough for this one.
|
||||||
// (one for the missing noodle block and the other for
|
// (one for the missing noodle block and the other for
|
||||||
@@ -423,9 +423,9 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
2,
|
2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": map[string]interface{}{
|
"noodle": map[string]any{
|
||||||
"foo_foo": map[string]interface{}{},
|
"foo_foo": map[string]any{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
@@ -433,7 +433,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
} `hcl:"noodle,block"`
|
} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
noodle := gotI.(struct {
|
noodle := gotI.(struct {
|
||||||
Noodle struct {
|
Noodle struct {
|
||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
@@ -444,10 +444,10 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": map[string]interface{}{
|
"noodle": map[string]any{
|
||||||
"foo_foo": map[string]interface{}{},
|
"foo_foo": map[string]any{},
|
||||||
"bar_baz": map[string]interface{}{},
|
"bar_baz": map[string]any{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
@@ -455,17 +455,17 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
} `hcl:"noodle,block"`
|
} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
// One diagnostic is enough for this one.
|
// One diagnostic is enough for this one.
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
1,
|
1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": map[string]interface{}{
|
"noodle": map[string]any{
|
||||||
"foo_foo": map[string]interface{}{},
|
"foo_foo": map[string]any{},
|
||||||
"bar_baz": map[string]interface{}{},
|
"bar_baz": map[string]any{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
makeInstantiateType(struct {
|
makeInstantiateType(struct {
|
||||||
@@ -473,7 +473,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
} `hcl:"noodle,block"`
|
} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
noodles := gotI.(struct {
|
noodles := gotI.(struct {
|
||||||
Noodles []struct {
|
Noodles []struct {
|
||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
@@ -484,9 +484,9 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"noodle": map[string]interface{}{
|
"noodle": map[string]any{
|
||||||
"foo_foo": map[string]interface{}{
|
"foo_foo": map[string]any{
|
||||||
"type": "rice",
|
"type": "rice",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -497,7 +497,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
Type string `hcl:"type"`
|
Type string `hcl:"type"`
|
||||||
} `hcl:"noodle,block"`
|
} `hcl:"noodle,block"`
|
||||||
}{}),
|
}{}),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
noodle := gotI.(struct {
|
noodle := gotI.(struct {
|
||||||
Noodle struct {
|
Noodle struct {
|
||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
@@ -510,7 +510,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"age": 34,
|
"age": 34,
|
||||||
},
|
},
|
||||||
@@ -522,31 +522,31 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"age": 89,
|
"age": 89,
|
||||||
},
|
},
|
||||||
makeInstantiateType(map[string]*hcl.Attribute(nil)),
|
makeInstantiateType(map[string]*hcl.Attribute(nil)),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
got := gotI.(map[string]*hcl.Attribute)
|
got := gotI.(map[string]*hcl.Attribute)
|
||||||
return len(got) == 2 && got["name"] != nil && got["age"] != nil
|
return len(got) == 2 && got["name"] != nil && got["age"] != nil
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"age": 13,
|
"age": 13,
|
||||||
},
|
},
|
||||||
makeInstantiateType(map[string]hcl.Expression(nil)),
|
makeInstantiateType(map[string]hcl.Expression(nil)),
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
got := gotI.(map[string]hcl.Expression)
|
got := gotI.(map[string]hcl.Expression)
|
||||||
return len(got) == 2 && got["name"] != nil && got["age"] != nil
|
return len(got) == 2 && got["name"] != nil && got["age"] != nil
|
||||||
},
|
},
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"name": "Ermintrude",
|
"name": "Ermintrude",
|
||||||
"living": true,
|
"living": true,
|
||||||
},
|
},
|
||||||
@@ -559,10 +559,10 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Retain "nested" block while decoding
|
// Retain "nested" block while decoding
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"plain": "foo",
|
"plain": "foo",
|
||||||
},
|
},
|
||||||
func() interface{} {
|
func() any {
|
||||||
return &withNestedBlock{
|
return &withNestedBlock{
|
||||||
Plain: "bar",
|
Plain: "bar",
|
||||||
Nested: &withTwoAttributes{
|
Nested: &withTwoAttributes{
|
||||||
@@ -570,7 +570,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
foo := gotI.(withNestedBlock)
|
foo := gotI.(withNestedBlock)
|
||||||
return foo.Plain == "foo" && foo.Nested != nil && foo.Nested.A == "bar"
|
return foo.Plain == "foo" && foo.Nested != nil && foo.Nested.A == "bar"
|
||||||
},
|
},
|
||||||
@@ -578,19 +578,19 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Retain values in "nested" block while decoding
|
// Retain values in "nested" block while decoding
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"nested": map[string]interface{}{
|
"nested": map[string]any{
|
||||||
"a": "foo",
|
"a": "foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
func() interface{} {
|
func() any {
|
||||||
return &withNestedBlock{
|
return &withNestedBlock{
|
||||||
Nested: &withTwoAttributes{
|
Nested: &withTwoAttributes{
|
||||||
B: "bar",
|
B: "bar",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
foo := gotI.(withNestedBlock)
|
foo := gotI.(withNestedBlock)
|
||||||
return foo.Nested.A == "foo" && foo.Nested.B == "bar"
|
return foo.Nested.A == "foo" && foo.Nested.B == "bar"
|
||||||
},
|
},
|
||||||
@@ -598,14 +598,14 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Retain values in "nested" block list while decoding
|
// Retain values in "nested" block list while decoding
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"nested": []map[string]interface{}{
|
"nested": []map[string]any{
|
||||||
{
|
{
|
||||||
"a": "foo",
|
"a": "foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
func() interface{} {
|
func() any {
|
||||||
return &withListofNestedBlocks{
|
return &withListofNestedBlocks{
|
||||||
Nested: []*withTwoAttributes{
|
Nested: []*withTwoAttributes{
|
||||||
{
|
{
|
||||||
@@ -614,7 +614,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
n := gotI.(withListofNestedBlocks)
|
n := gotI.(withListofNestedBlocks)
|
||||||
return n.Nested[0].A == "foo" && n.Nested[0].B == "bar"
|
return n.Nested[0].A == "foo" && n.Nested[0].B == "bar"
|
||||||
},
|
},
|
||||||
@@ -622,14 +622,14 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Remove additional elements from the list while decoding nested blocks
|
// Remove additional elements from the list while decoding nested blocks
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"nested": []map[string]interface{}{
|
"nested": []map[string]any{
|
||||||
{
|
{
|
||||||
"a": "foo",
|
"a": "foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
func() interface{} {
|
func() any {
|
||||||
return &withListofNestedBlocks{
|
return &withListofNestedBlocks{
|
||||||
Nested: []*withTwoAttributes{
|
Nested: []*withTwoAttributes{
|
||||||
{
|
{
|
||||||
@@ -641,7 +641,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
n := gotI.(withListofNestedBlocks)
|
n := gotI.(withListofNestedBlocks)
|
||||||
return len(n.Nested) == 1
|
return len(n.Nested) == 1
|
||||||
},
|
},
|
||||||
@@ -649,8 +649,8 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Make sure decoding value slices works the same as pointer slices.
|
// Make sure decoding value slices works the same as pointer slices.
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"nested": []map[string]interface{}{
|
"nested": []map[string]any{
|
||||||
{
|
{
|
||||||
"b": "bar",
|
"b": "bar",
|
||||||
},
|
},
|
||||||
@@ -659,7 +659,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
func() interface{} {
|
func() any {
|
||||||
return &withListofNestedBlocksNoPointers{
|
return &withListofNestedBlocksNoPointers{
|
||||||
Nested: []withTwoAttributes{
|
Nested: []withTwoAttributes{
|
||||||
{
|
{
|
||||||
@@ -668,7 +668,7 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
func(gotI interface{}) bool {
|
func(gotI any) bool {
|
||||||
n := gotI.(withListofNestedBlocksNoPointers)
|
n := gotI.(withListofNestedBlocksNoPointers)
|
||||||
return n.Nested[0].B == "bar" && len(n.Nested) == 2
|
return n.Nested[0].B == "bar" && len(n.Nested) == 2
|
||||||
},
|
},
|
||||||
@@ -710,8 +710,8 @@ func TestDecodeBody(t *testing.T) {
|
|||||||
func TestDecodeExpression(t *testing.T) {
|
func TestDecodeExpression(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Value cty.Value
|
Value cty.Value
|
||||||
Target interface{}
|
Target any
|
||||||
Want interface{}
|
Want any
|
||||||
DiagCount int
|
DiagCount int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -799,8 +799,8 @@ func (e *fixedExpression) Variables() []hcl.Traversal {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeInstantiateType(target interface{}) func() interface{} {
|
func makeInstantiateType(target any) func() any {
|
||||||
return func() interface{} {
|
return func() any {
|
||||||
return reflect.New(reflect.TypeOf(target)).Interface()
|
return reflect.New(reflect.TypeOf(target)).Interface()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -34,9 +34,9 @@ import (
|
|||||||
// The layout of the resulting HCL source is derived from the ordering of
|
// The layout of the resulting HCL source is derived from the ordering of
|
||||||
// the struct fields, with blank lines around nested blocks of different types.
|
// the struct fields, with blank lines around nested blocks of different types.
|
||||||
// Fields representing attributes should usually precede those representing
|
// Fields representing attributes should usually precede those representing
|
||||||
// blocks so that the attributes can group togather in the result. For more
|
// blocks so that the attributes can group together in the result. For more
|
||||||
// control, use the hclwrite API directly.
|
// control, use the hclwrite API directly.
|
||||||
func EncodeIntoBody(val interface{}, dst *hclwrite.Body) {
|
func EncodeIntoBody(val any, dst *hclwrite.Body) {
|
||||||
rv := reflect.ValueOf(val)
|
rv := reflect.ValueOf(val)
|
||||||
ty := rv.Type()
|
ty := rv.Type()
|
||||||
if ty.Kind() == reflect.Ptr {
|
if ty.Kind() == reflect.Ptr {
|
||||||
@@ -60,7 +60,7 @@ func EncodeIntoBody(val interface{}, dst *hclwrite.Body) {
|
|||||||
//
|
//
|
||||||
// This function has the same constraints as EncodeIntoBody and will panic
|
// This function has the same constraints as EncodeIntoBody and will panic
|
||||||
// if they are violated.
|
// if they are violated.
|
||||||
func EncodeAsBlock(val interface{}, blockType string) *hclwrite.Block {
|
func EncodeAsBlock(val any, blockType string) *hclwrite.Block {
|
||||||
rv := reflect.ValueOf(val)
|
rv := reflect.ValueOf(val)
|
||||||
ty := rv.Type()
|
ty := rv.Type()
|
||||||
if ty.Kind() == reflect.Ptr {
|
if ty.Kind() == reflect.Ptr {
|
||||||
@@ -158,7 +158,7 @@ func populateBody(rv reflect.Value, ty reflect.Type, tags *fieldTags, dst *hclwr
|
|||||||
|
|
||||||
if isSeq {
|
if isSeq {
|
||||||
l := fieldVal.Len()
|
l := fieldVal.Len()
|
||||||
for i := 0; i < l; i++ {
|
for i := range l {
|
||||||
elemVal := fieldVal.Index(i)
|
elemVal := fieldVal.Index(i)
|
||||||
if !elemVal.IsValid() {
|
if !elemVal.IsValid() {
|
||||||
continue // ignore (elem value is nil pointer)
|
continue // ignore (elem value is nil pointer)
|
||||||
|
@@ -22,7 +22,7 @@ import (
|
|||||||
// This uses the tags on the fields of the struct to discover how each
|
// This uses the tags on the fields of the struct to discover how each
|
||||||
// field's value should be expressed within configuration. If an invalid
|
// field's value should be expressed within configuration. If an invalid
|
||||||
// mapping is attempted, this function will panic.
|
// mapping is attempted, this function will panic.
|
||||||
func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) {
|
func ImpliedBodySchema(val any) (schema *hcl.BodySchema, partial bool) {
|
||||||
ty := reflect.TypeOf(val)
|
ty := reflect.TypeOf(val)
|
||||||
|
|
||||||
if ty.Kind() == reflect.Ptr {
|
if ty.Kind() == reflect.Ptr {
|
||||||
@@ -134,7 +134,7 @@ func getFieldTags(ty reflect.Type) *fieldTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ct := ty.NumField()
|
ct := ty.NumField()
|
||||||
for i := 0; i < ct; i++ {
|
for i := range ct {
|
||||||
field := ty.Field(i)
|
field := ty.Field(i)
|
||||||
tag := field.Tag.Get("hcl")
|
tag := field.Tag.Get("hcl")
|
||||||
if tag == "" {
|
if tag == "" {
|
||||||
|
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
func TestImpliedBodySchema(t *testing.T) {
|
func TestImpliedBodySchema(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
val interface{}
|
val any
|
||||||
wantSchema *hcl.BodySchema
|
wantSchema *hcl.BodySchema
|
||||||
wantPartial bool
|
wantPartial bool
|
||||||
}{
|
}{
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -589,7 +590,7 @@ type ParseMeta struct {
|
|||||||
AllVariables []*Variable
|
AllVariables []*Variable
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) {
|
func Parse(b hcl.Body, opt Opt, val any) (*ParseMeta, hcl.Diagnostics) {
|
||||||
reserved := map[string]struct{}{}
|
reserved := map[string]struct{}{}
|
||||||
schema, _ := gohcl.ImpliedBodySchema(val)
|
schema, _ := gohcl.ImpliedBodySchema(val)
|
||||||
|
|
||||||
@@ -763,7 +764,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) {
|
|||||||
types := map[string]field{}
|
types := map[string]field{}
|
||||||
renamed := map[string]map[string][]string{}
|
renamed := map[string]map[string][]string{}
|
||||||
vt := reflect.ValueOf(val).Elem().Type()
|
vt := reflect.ValueOf(val).Elem().Type()
|
||||||
for i := 0; i < vt.NumField(); i++ {
|
for i := range vt.NumField() {
|
||||||
tags := strings.Split(vt.Field(i).Tag.Get("hcl"), ",")
|
tags := strings.Split(vt.Field(i).Tag.Get("hcl"), ",")
|
||||||
|
|
||||||
p.blockTypes[tags[0]] = vt.Field(i).Type.Elem().Elem()
|
p.blockTypes[tags[0]] = vt.Field(i).Type.Elem().Elem()
|
||||||
@@ -831,7 +832,7 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) {
|
|||||||
oldValue, exists := t.values[lblName]
|
oldValue, exists := t.values[lblName]
|
||||||
if !exists && lblExists {
|
if !exists && lblExists {
|
||||||
if v.Elem().Field(t.idx).Type().Kind() == reflect.Slice {
|
if v.Elem().Field(t.idx).Type().Kind() == reflect.Slice {
|
||||||
for i := 0; i < v.Elem().Field(t.idx).Len(); i++ {
|
for i := range v.Elem().Field(t.idx).Len() {
|
||||||
if lblName == v.Elem().Field(t.idx).Index(i).Elem().Field(lblIndex).String() {
|
if lblName == v.Elem().Field(t.idx).Index(i).Elem().Field(lblIndex).String() {
|
||||||
exists = true
|
exists = true
|
||||||
oldValue = value{Value: v.Elem().Field(t.idx).Index(i), idx: i}
|
oldValue = value{Value: v.Elem().Field(t.idx).Index(i), idx: i}
|
||||||
@@ -898,7 +899,7 @@ func wrapErrorDiagnostic(message string, err error, subject *hcl.Range, context
|
|||||||
|
|
||||||
func setName(v reflect.Value, name string) {
|
func setName(v reflect.Value, name string) {
|
||||||
numFields := v.Elem().Type().NumField()
|
numFields := v.Elem().Type().NumField()
|
||||||
for i := 0; i < numFields; i++ {
|
for i := range numFields {
|
||||||
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
|
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
|
||||||
for _, t := range parts[1:] {
|
for _, t := range parts[1:] {
|
||||||
if t == "label" {
|
if t == "label" {
|
||||||
@@ -910,12 +911,10 @@ func setName(v reflect.Value, name string) {
|
|||||||
|
|
||||||
func getName(v reflect.Value) (string, bool) {
|
func getName(v reflect.Value) (string, bool) {
|
||||||
numFields := v.Elem().Type().NumField()
|
numFields := v.Elem().Type().NumField()
|
||||||
for i := 0; i < numFields; i++ {
|
for i := range numFields {
|
||||||
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
|
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
|
||||||
for _, t := range parts[1:] {
|
if slices.Contains(parts[1:], "label") {
|
||||||
if t == "label" {
|
return v.Elem().Field(i).String(), true
|
||||||
return v.Elem().Field(i).String(), true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
@@ -923,12 +922,10 @@ func getName(v reflect.Value) (string, bool) {
|
|||||||
|
|
||||||
func getNameIndex(v reflect.Value) (int, bool) {
|
func getNameIndex(v reflect.Value) (int, bool) {
|
||||||
numFields := v.Elem().Type().NumField()
|
numFields := v.Elem().Type().NumField()
|
||||||
for i := 0; i < numFields; i++ {
|
for i := range numFields {
|
||||||
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
|
parts := strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",")
|
||||||
for _, t := range parts[1:] {
|
if slices.Contains(parts[1:], "label") {
|
||||||
if t == "label" {
|
return i, true
|
||||||
return i, true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, false
|
return 0, false
|
||||||
@@ -988,7 +985,7 @@ func key(ks ...any) uint64 {
|
|||||||
return hash.Sum64()
|
return hash.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
func decodeBody(body hcl.Body, ctx *hcl.EvalContext, val any) hcl.Diagnostics {
|
||||||
dec := gohcl.DecodeOptions{ImpliedType: ImpliedType}
|
dec := gohcl.DecodeOptions{ImpliedType: ImpliedType}
|
||||||
return dec.DecodeBody(body, ctx, val)
|
return dec.DecodeBody(body, ctx, val)
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ import (
|
|||||||
// In particular, ImpliedType will never use capsule types in its returned
|
// In particular, ImpliedType will never use capsule types in its returned
|
||||||
// type, because it cannot know the capsule types supported by the calling
|
// type, because it cannot know the capsule types supported by the calling
|
||||||
// program.
|
// program.
|
||||||
func ImpliedType(gv interface{}) (cty.Type, error) {
|
func ImpliedType(gv any) (cty.Type, error) {
|
||||||
rt := reflect.TypeOf(gv)
|
rt := reflect.TypeOf(gv)
|
||||||
var path cty.Path
|
var path cty.Path
|
||||||
return impliedType(rt, path)
|
return impliedType(rt, path)
|
||||||
@@ -148,7 +148,7 @@ func structTagIndices(st reflect.Type) map[string]int {
|
|||||||
ct := st.NumField()
|
ct := st.NumField()
|
||||||
ret := make(map[string]int, ct)
|
ret := make(map[string]int, ct)
|
||||||
|
|
||||||
for i := 0; i < ct; i++ {
|
for i := range ct {
|
||||||
field := st.Field(i)
|
field := st.Field(i)
|
||||||
attrName := field.Tag.Get("cty")
|
attrName := field.Tag.Get("cty")
|
||||||
if attrName != "" {
|
if attrName != "" {
|
||||||
|
@@ -40,7 +40,6 @@ import (
|
|||||||
"github.com/moby/buildkit/solver/errdefs"
|
"github.com/moby/buildkit/solver/errdefs"
|
||||||
"github.com/moby/buildkit/solver/pb"
|
"github.com/moby/buildkit/solver/pb"
|
||||||
spb "github.com/moby/buildkit/sourcepolicy/pb"
|
spb "github.com/moby/buildkit/sourcepolicy/pb"
|
||||||
"github.com/moby/buildkit/util/entitlements"
|
|
||||||
"github.com/moby/buildkit/util/progress/progresswriter"
|
"github.com/moby/buildkit/util/progress/progresswriter"
|
||||||
"github.com/moby/buildkit/util/tracing"
|
"github.com/moby/buildkit/util/tracing"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
@@ -63,7 +62,7 @@ type Options struct {
|
|||||||
Inputs Inputs
|
Inputs Inputs
|
||||||
|
|
||||||
Ref string
|
Ref string
|
||||||
Allow []entitlements.Entitlement
|
Allow []string
|
||||||
Attests map[string]*string
|
Attests map[string]*string
|
||||||
BuildArgs map[string]string
|
BuildArgs map[string]string
|
||||||
CacheFrom []client.CacheOptionsEntry
|
CacheFrom []client.CacheOptionsEntry
|
||||||
@@ -540,7 +539,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||||||
node := dp.Node().Driver
|
node := dp.Node().Driver
|
||||||
if node.IsMobyDriver() {
|
if node.IsMobyDriver() {
|
||||||
for _, e := range so.Exports {
|
for _, e := range so.Exports {
|
||||||
if e.Type == "moby" && e.Attrs["push"] != "" {
|
if e.Type == "moby" && e.Attrs["push"] != "" && !node.Features(ctx)[driver.DirectPush] {
|
||||||
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
|
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
|
||||||
pushNames = e.Attrs["name"]
|
pushNames = e.Attrs["name"]
|
||||||
if pushNames == "" {
|
if pushNames == "" {
|
||||||
@@ -623,7 +622,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
|
|||||||
// This is fallback for some very old buildkit versions.
|
// This is fallback for some very old buildkit versions.
|
||||||
// Note that the mediatype isn't really correct as most of the time it is image manifest and
|
// Note that the mediatype isn't really correct as most of the time it is image manifest and
|
||||||
// not manifest list but actually both are handled because for Docker mediatypes the
|
// not manifest list but actually both are handled because for Docker mediatypes the
|
||||||
// mediatype value in the Accpet header does not seem to matter.
|
// mediatype value in the Accept header does not seem to matter.
|
||||||
s, ok = r.ExporterResponse[exptypes.ExporterImageDigestKey]
|
s, ok = r.ExporterResponse[exptypes.ExporterImageDigestKey]
|
||||||
if ok {
|
if ok {
|
||||||
descs = append(descs, specs.Descriptor{
|
descs = append(descs, specs.Descriptor{
|
||||||
@@ -835,7 +834,7 @@ func remoteDigestWithMoby(ctx context.Context, d *driver.DriverHandle, name stri
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
img, _, err := api.ImageInspectWithRaw(ctx, name)
|
img, err := api.ImageInspect(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
stderrors "errors"
|
stderrors "errors"
|
||||||
"net"
|
"net"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"github.com/containerd/platforms"
|
"github.com/containerd/platforms"
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
@@ -37,15 +38,7 @@ func Dial(ctx context.Context, nodes []builder.Node, pw progress.Writer, platfor
|
|||||||
for _, ls := range resolved {
|
for _, ls := range resolved {
|
||||||
for _, rn := range ls {
|
for _, rn := range ls {
|
||||||
if platform != nil {
|
if platform != nil {
|
||||||
p := *platform
|
if !slices.ContainsFunc(rn.platforms, platforms.Only(*platform).Match) {
|
||||||
var found bool
|
|
||||||
for _, pp := range rn.platforms {
|
|
||||||
if platforms.Only(p).Match(pp) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@ package build
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containerd/platforms"
|
"github.com/containerd/platforms"
|
||||||
@@ -221,7 +222,7 @@ func (r *nodeResolver) get(p specs.Platform, matcher matchMaker, additionalPlatf
|
|||||||
for i, node := range r.nodes {
|
for i, node := range r.nodes {
|
||||||
platforms := node.Platforms
|
platforms := node.Platforms
|
||||||
if additionalPlatforms != nil {
|
if additionalPlatforms != nil {
|
||||||
platforms = append([]specs.Platform{}, platforms...)
|
platforms = slices.Clone(platforms)
|
||||||
platforms = append(platforms, additionalPlatforms(i, node)...)
|
platforms = append(platforms, additionalPlatforms(i, node)...)
|
||||||
}
|
}
|
||||||
for _, p2 := range platforms {
|
for _, p2 := range platforms {
|
||||||
|
@@ -318,7 +318,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
|
|||||||
switch opt.NetworkMode {
|
switch opt.NetworkMode {
|
||||||
case "host":
|
case "host":
|
||||||
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
|
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
|
||||||
so.AllowedEntitlements = append(so.AllowedEntitlements, entitlements.EntitlementNetworkHost)
|
so.AllowedEntitlements = append(so.AllowedEntitlements, entitlements.EntitlementNetworkHost.String())
|
||||||
case "none":
|
case "none":
|
||||||
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
|
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
|
||||||
case "", "default":
|
case "", "default":
|
||||||
|
@@ -28,11 +28,11 @@ func TestSyncMultiReaderParallel(t *testing.T) {
|
|||||||
|
|
||||||
readers := make([]io.ReadCloser, numReaders)
|
readers := make([]io.ReadCloser, numReaders)
|
||||||
|
|
||||||
for i := 0; i < numReaders; i++ {
|
for i := range numReaders {
|
||||||
readers[i] = mr.NewReadCloser()
|
readers[i] = mr.NewReadCloser()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < numReaders; i++ {
|
for i := range numReaders {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func(readerId int) {
|
go func(readerId int) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -199,7 +200,7 @@ func (b *Builder) Boot(ctx context.Context) (bool, error) {
|
|||||||
err = err1
|
err = err1
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && len(errCh) == len(toBoot) {
|
if err == nil && len(errCh) > 0 {
|
||||||
return false, <-errCh
|
return false, <-errCh
|
||||||
}
|
}
|
||||||
return true, err
|
return true, err
|
||||||
@@ -656,13 +657,7 @@ func parseBuildkitdFlags(inp string, driver string, driverOpts map[string]string
|
|||||||
flags.StringArrayVar(&allowInsecureEntitlements, "allow-insecure-entitlement", nil, "")
|
flags.StringArrayVar(&allowInsecureEntitlements, "allow-insecure-entitlement", nil, "")
|
||||||
_ = flags.Parse(res)
|
_ = flags.Parse(res)
|
||||||
|
|
||||||
var hasNetworkHostEntitlement bool
|
hasNetworkHostEntitlement := slices.Contains(allowInsecureEntitlements, "network.host")
|
||||||
for _, e := range allowInsecureEntitlements {
|
|
||||||
if e == "network.host" {
|
|
||||||
hasNetworkHostEntitlement = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasNetworkHostEntitlementInConf bool
|
var hasNetworkHostEntitlementInConf bool
|
||||||
if buildkitdConfigFile != "" {
|
if buildkitdConfigFile != "" {
|
||||||
@@ -671,11 +666,8 @@ func parseBuildkitdFlags(inp string, driver string, driverOpts map[string]string
|
|||||||
return nil, err
|
return nil, err
|
||||||
} else if btoml != nil {
|
} else if btoml != nil {
|
||||||
if ies := btoml.GetArray("insecure-entitlements"); ies != nil {
|
if ies := btoml.GetArray("insecure-entitlements"); ies != nil {
|
||||||
for _, e := range ies.([]string) {
|
if slices.Contains(ies.([]string), "network.host") {
|
||||||
if e == "network.host" {
|
hasNetworkHostEntitlementInConf = true
|
||||||
hasNetworkHostEntitlementInConf = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,10 +32,11 @@ type Node struct {
|
|||||||
Err error
|
Err error
|
||||||
|
|
||||||
// worker settings
|
// worker settings
|
||||||
IDs []string
|
IDs []string
|
||||||
Platforms []ocispecs.Platform
|
Platforms []ocispecs.Platform
|
||||||
GCPolicy []client.PruneInfo
|
GCPolicy []client.PruneInfo
|
||||||
Labels map[string]string
|
Labels map[string]string
|
||||||
|
CDIDevices []client.CDIDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns nodes for this builder.
|
// Nodes returns nodes for this builder.
|
||||||
@@ -168,7 +169,7 @@ func (b *Builder) LoadNodes(ctx context.Context, opts ...LoadNodesOption) (_ []N
|
|||||||
// dynamic nodes are used in Kubernetes driver.
|
// dynamic nodes are used in Kubernetes driver.
|
||||||
// Kubernetes' pods are dynamically mapped to BuildKit Nodes.
|
// Kubernetes' pods are dynamically mapped to BuildKit Nodes.
|
||||||
if di.DriverInfo != nil && len(di.DriverInfo.DynamicNodes) > 0 {
|
if di.DriverInfo != nil && len(di.DriverInfo.DynamicNodes) > 0 {
|
||||||
for i := 0; i < len(di.DriverInfo.DynamicNodes); i++ {
|
for i := range di.DriverInfo.DynamicNodes {
|
||||||
diClone := di
|
diClone := di
|
||||||
if pl := di.DriverInfo.DynamicNodes[i].Platforms; len(pl) > 0 {
|
if pl := di.DriverInfo.DynamicNodes[i].Platforms; len(pl) > 0 {
|
||||||
diClone.Platforms = pl
|
diClone.Platforms = pl
|
||||||
@@ -259,6 +260,7 @@ func (n *Node) loadData(ctx context.Context, clientOpt ...client.ClientOpt) erro
|
|||||||
n.GCPolicy = w.GCPolicy
|
n.GCPolicy = w.GCPolicy
|
||||||
n.Labels = w.Labels
|
n.Labels = w.Labels
|
||||||
}
|
}
|
||||||
|
n.CDIDevices = w.CDIDevices
|
||||||
}
|
}
|
||||||
sort.Strings(n.IDs)
|
sort.Strings(n.IDs)
|
||||||
n.Platforms = platformutil.Dedupe(n.Platforms)
|
n.Platforms = platformutil.Dedupe(n.Platforms)
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/docker/buildx/commands"
|
"github.com/docker/buildx/commands"
|
||||||
controllererrors "github.com/docker/buildx/controller/errdefs"
|
controllererrors "github.com/docker/buildx/controller/errdefs"
|
||||||
@@ -41,7 +42,8 @@ func runStandalone(cmd *command.DockerCli) error {
|
|||||||
}
|
}
|
||||||
defer flushMetrics(cmd)
|
defer flushMetrics(cmd)
|
||||||
|
|
||||||
rootCmd := commands.NewRootCmd(os.Args[0], false, cmd)
|
executable := os.Args[0]
|
||||||
|
rootCmd := commands.NewRootCmd(filepath.Base(executable), false, cmd)
|
||||||
return rootCmd.Execute()
|
return rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -66,7 +66,11 @@ type bakeOptions struct {
|
|||||||
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
||||||
mp := dockerCli.MeterProvider()
|
mp := dockerCli.MeterProvider()
|
||||||
|
|
||||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
ctx, end, err := tracing.TraceCurrentCommand(ctx, append([]string{"bake"}, targets...),
|
||||||
|
attribute.String("builder", in.builder),
|
||||||
|
attribute.StringSlice("targets", targets),
|
||||||
|
attribute.StringSlice("files", in.files),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -271,8 +275,10 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := exp.Prompt(ctx, url != "", &syncWriter{w: dockerCli.Err(), wait: printer.Wait}); err != nil {
|
if progressMode != progressui.RawJSONMode {
|
||||||
return err
|
if err := exp.Prompt(ctx, url != "", &syncWriter{w: dockerCli.Err(), wait: printer.Wait}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if printer.IsDone() {
|
if printer.IsDone() {
|
||||||
// init new printer as old one was stopped to show the prompt
|
// init new printer as old one was stopped to show the prompt
|
||||||
@@ -281,7 +287,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := saveLocalStateGroup(dockerCli, in, targets, bo, overrides, def); err != nil {
|
if err := saveLocalStateGroup(dockerCli, in, targets, bo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +309,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||||||
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
|
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
|
||||||
}
|
}
|
||||||
if len(in.metadataFile) > 0 {
|
if len(in.metadataFile) > 0 {
|
||||||
dt := make(map[string]interface{})
|
dt := make(map[string]any)
|
||||||
for t, r := range resp {
|
for t, r := range resp {
|
||||||
dt[t] = decodeExporterResponse(r.ExporterResponse)
|
dt[t] = decodeExporterResponse(r.ExporterResponse)
|
||||||
}
|
}
|
||||||
@@ -486,7 +492,14 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string, bo map[string]build.Options, overrides []string, def any) error {
|
func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string, bo map[string]build.Options) error {
|
||||||
|
l, err := localstate.New(confutil.NewConfig(dockerCli))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer l.MigrateIfNeeded()
|
||||||
|
|
||||||
prm := confutil.MetadataProvenance()
|
prm := confutil.MetadataProvenance()
|
||||||
if len(in.metadataFile) == 0 {
|
if len(in.metadataFile) == 0 {
|
||||||
prm = confutil.MetadataProvenanceModeDisabled
|
prm = confutil.MetadataProvenanceModeDisabled
|
||||||
@@ -506,19 +519,10 @@ func saveLocalStateGroup(dockerCli command.Cli, in bakeOptions, targets []string
|
|||||||
if len(refs) == 0 {
|
if len(refs) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
l, err := localstate.New(confutil.NewConfig(dockerCli))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dtdef, err := json.MarshalIndent(def, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return l.SaveGroup(groupRef, localstate.StateGroup{
|
return l.SaveGroup(groupRef, localstate.StateGroup{
|
||||||
Definition: dtdef,
|
Refs: refs,
|
||||||
Targets: targets,
|
Targets: targets,
|
||||||
Inputs: overrides,
|
|
||||||
Refs: refs,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -41,7 +42,7 @@ import (
|
|||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
dockeropts "github.com/docker/cli/opts"
|
dockeropts "github.com/docker/cli/opts"
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
"github.com/docker/docker/pkg/atomicwriter"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||||
"github.com/moby/buildkit/frontend/subrequests"
|
"github.com/moby/buildkit/frontend/subrequests"
|
||||||
@@ -156,7 +157,7 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
inAttests := append([]string{}, o.attests...)
|
inAttests := slices.Clone(o.attests)
|
||||||
if o.provenance != "" {
|
if o.provenance != "" {
|
||||||
inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", o.provenance))
|
inAttests = append(inAttests, buildflags.CanonicalizeAttest("provenance", o.provenance))
|
||||||
}
|
}
|
||||||
@@ -183,14 +184,17 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.CacheFrom, err = buildflags.ParseCacheEntry(o.cacheFrom)
|
cacheFrom, err := buildflags.ParseCacheEntry(o.cacheFrom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts.CacheTo, err = buildflags.ParseCacheEntry(o.cacheTo)
|
opts.CacheFrom = cacheFrom.ToPB()
|
||||||
|
|
||||||
|
cacheTo, err := buildflags.ParseCacheEntry(o.cacheTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
opts.CacheTo = cacheTo.ToPB()
|
||||||
|
|
||||||
opts.Secrets, err = buildflags.ParseSecretSpecs(o.secrets)
|
opts.Secrets, err = buildflags.ParseSecretSpecs(o.secrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -282,7 +286,11 @@ func (o *buildOptionsHash) String() string {
|
|||||||
func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) (err error) {
|
func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) (err error) {
|
||||||
mp := dockerCli.MeterProvider()
|
mp := dockerCli.MeterProvider()
|
||||||
|
|
||||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
|
ctx, end, err := tracing.TraceCurrentCommand(ctx, []string{"build", options.contextPath},
|
||||||
|
attribute.String("builder", options.builder),
|
||||||
|
attribute.String("context", options.contextPath),
|
||||||
|
attribute.String("dockerfile", options.dockerfileName),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -463,7 +471,7 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
var be *controllererrors.BuildError
|
var be *controllererrors.BuildError
|
||||||
if errors.As(err, &be) {
|
if errors.As(err, &be) {
|
||||||
ref = be.Ref
|
ref = be.SessionID
|
||||||
retErr = err
|
retErr = err
|
||||||
// We can proceed to monitor
|
// We can proceed to monitor
|
||||||
} else {
|
} else {
|
||||||
@@ -590,7 +598,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
|
|||||||
|
|
||||||
flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`)
|
flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`)
|
||||||
|
|
||||||
flags.StringSliceVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
|
flags.StringArrayVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
|
||||||
|
|
||||||
flags.StringArrayVarP(&options.annotations, "annotation", "", []string{}, "Add annotation to the image")
|
flags.StringArrayVarP(&options.annotations, "annotation", "", []string{}, "Add annotation to the image")
|
||||||
|
|
||||||
@@ -737,15 +745,15 @@ func checkWarnedFlags(f *pflag.Flag) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeMetadataFile(filename string, dt interface{}) error {
|
func writeMetadataFile(filename string, dt any) error {
|
||||||
b, err := json.MarshalIndent(dt, "", " ")
|
b, err := json.MarshalIndent(dt, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ioutils.AtomicWriteFile(filename, b, 0644)
|
return atomicwriter.WriteFile(filename, b, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeExporterResponse(exporterResponse map[string]string) map[string]interface{} {
|
func decodeExporterResponse(exporterResponse map[string]string) map[string]any {
|
||||||
decFunc := func(k, v string) ([]byte, error) {
|
decFunc := func(k, v string) ([]byte, error) {
|
||||||
if k == "result.json" {
|
if k == "result.json" {
|
||||||
// result.json is part of metadata response for subrequests which
|
// result.json is part of metadata response for subrequests which
|
||||||
@@ -754,16 +762,16 @@ func decodeExporterResponse(exporterResponse map[string]string) map[string]inter
|
|||||||
}
|
}
|
||||||
return base64.StdEncoding.DecodeString(v)
|
return base64.StdEncoding.DecodeString(v)
|
||||||
}
|
}
|
||||||
out := make(map[string]interface{})
|
out := make(map[string]any)
|
||||||
for k, v := range exporterResponse {
|
for k, v := range exporterResponse {
|
||||||
dt, err := decFunc(k, v)
|
dt, err := decFunc(k, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out[k] = v
|
out[k] = v
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var raw map[string]interface{}
|
var raw map[string]any
|
||||||
if err = json.Unmarshal(dt, &raw); err != nil || len(raw) == 0 {
|
if err = json.Unmarshal(dt, &raw); err != nil || len(raw) == 0 {
|
||||||
var rawList []map[string]interface{}
|
var rawList []map[string]any
|
||||||
if err = json.Unmarshal(dt, &rawList); err != nil || len(rawList) == 0 {
|
if err = json.Unmarshal(dt, &rawList); err != nil || len(rawList) == 0 {
|
||||||
out[k] = v
|
out[k] = v
|
||||||
continue
|
continue
|
||||||
|
@@ -124,7 +124,7 @@ func duCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func printKV(w io.Writer, k string, v interface{}) {
|
func printKV(w io.Writer, k string, v any) {
|
||||||
fmt.Fprintf(w, "%s:\t%v\n", k, v)
|
fmt.Fprintf(w, "%s:\t%v\n", k, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
135
commands/history/import.go
Normal file
135
commands/history/import.go
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
remoteutil "github.com/docker/buildx/driver/remote/util"
|
||||||
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
|
"github.com/docker/buildx/util/desktop"
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/pkg/browser"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
type importOptions struct {
|
||||||
|
file []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func runImport(ctx context.Context, dockerCli command.Cli, opts importOptions) error {
|
||||||
|
sock, err := desktop.BuildServerAddr()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
network, addr, ok := strings.Cut(sock, "://")
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.Errorf("invalid endpoint address: %s", sock)
|
||||||
|
}
|
||||||
|
return remoteutil.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: tr,
|
||||||
|
}
|
||||||
|
|
||||||
|
var urls []string
|
||||||
|
|
||||||
|
if len(opts.file) == 0 {
|
||||||
|
u, err := importFrom(ctx, client, os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
urls = append(urls, u...)
|
||||||
|
} else {
|
||||||
|
for _, fn := range opts.file {
|
||||||
|
var f *os.File
|
||||||
|
var rdr io.Reader = os.Stdin
|
||||||
|
if fn != "-" {
|
||||||
|
f, err = os.Open(fn)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to open file %s", fn)
|
||||||
|
}
|
||||||
|
rdr = f
|
||||||
|
}
|
||||||
|
u, err := importFrom(ctx, client, rdr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
urls = append(urls, u...)
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(urls) == 0 {
|
||||||
|
return errors.New("no build records found in the bundle")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, url := range urls {
|
||||||
|
fmt.Fprintln(dockerCli.Err(), url)
|
||||||
|
if i == 0 {
|
||||||
|
err = browser.OpenURL(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func importFrom(ctx context.Context, c *http.Client, rdr io.Reader) ([]string, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://docker-desktop/upload", rdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to create request")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to send request, check if Docker Desktop is running")
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
return nil, errors.Errorf("failed to import build: %s", string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
var refs []string
|
||||||
|
dec := json.NewDecoder(resp.Body)
|
||||||
|
if err := dec.Decode(&refs); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to decode response")
|
||||||
|
}
|
||||||
|
|
||||||
|
var urls []string
|
||||||
|
for _, ref := range refs {
|
||||||
|
urls = append(urls, desktop.BuildURL(fmt.Sprintf(".imported/_/%s", ref)))
|
||||||
|
}
|
||||||
|
return urls, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func importCmd(dockerCli command.Cli, _ RootOptions) *cobra.Command {
|
||||||
|
var options importOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "import [OPTIONS] < bundle.dockerbuild",
|
||||||
|
Short: "Import a build into Docker Desktop",
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runImport(cmd.Context(), dockerCli, options)
|
||||||
|
},
|
||||||
|
ValidArgsFunction: completion.Disable,
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.StringArrayVarP(&options.file, "file", "f", nil, "Import from a file path")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/containerd/v2/core/content"
|
"github.com/containerd/containerd/v2/core/content"
|
||||||
@@ -25,6 +26,7 @@ import (
|
|||||||
"github.com/docker/buildx/util/confutil"
|
"github.com/docker/buildx/util/confutil"
|
||||||
"github.com/docker/buildx/util/desktop"
|
"github.com/docker/buildx/util/desktop"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"github.com/docker/cli/cli/debug"
|
"github.com/docker/cli/cli/debug"
|
||||||
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
|
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
|
||||||
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||||
@@ -45,9 +47,114 @@ import (
|
|||||||
proto "google.golang.org/protobuf/proto"
|
proto "google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type statusT string
|
||||||
|
|
||||||
|
const (
|
||||||
|
statusComplete statusT = "completed"
|
||||||
|
statusRunning statusT = "running"
|
||||||
|
statusError statusT = "failed"
|
||||||
|
statusCanceled statusT = "canceled"
|
||||||
|
)
|
||||||
|
|
||||||
type inspectOptions struct {
|
type inspectOptions struct {
|
||||||
builder string
|
builder string
|
||||||
ref string
|
ref string
|
||||||
|
format string
|
||||||
|
}
|
||||||
|
|
||||||
|
type inspectOutput struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Ref string
|
||||||
|
|
||||||
|
Context string `json:",omitempty"`
|
||||||
|
Dockerfile string `json:",omitempty"`
|
||||||
|
VCSRepository string `json:",omitempty"`
|
||||||
|
VCSRevision string `json:",omitempty"`
|
||||||
|
Target string `json:",omitempty"`
|
||||||
|
Platform []string `json:",omitempty"`
|
||||||
|
KeepGitDir bool `json:",omitempty"`
|
||||||
|
|
||||||
|
NamedContexts []keyValueOutput `json:",omitempty"`
|
||||||
|
|
||||||
|
StartedAt *time.Time `json:",omitempty"`
|
||||||
|
CompletedAt *time.Time `json:",omitempty"`
|
||||||
|
Duration time.Duration `json:",omitempty"`
|
||||||
|
Status statusT `json:",omitempty"`
|
||||||
|
Error *errorOutput `json:",omitempty"`
|
||||||
|
|
||||||
|
NumCompletedSteps int32
|
||||||
|
NumTotalSteps int32
|
||||||
|
NumCachedSteps int32
|
||||||
|
|
||||||
|
BuildArgs []keyValueOutput `json:",omitempty"`
|
||||||
|
Labels []keyValueOutput `json:",omitempty"`
|
||||||
|
|
||||||
|
Config configOutput `json:",omitempty"`
|
||||||
|
|
||||||
|
Materials []materialOutput `json:",omitempty"`
|
||||||
|
Attachments []attachmentOutput `json:",omitempty"`
|
||||||
|
|
||||||
|
Errors []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type configOutput struct {
|
||||||
|
Network string `json:",omitempty"`
|
||||||
|
ExtraHosts []string `json:",omitempty"`
|
||||||
|
Hostname string `json:",omitempty"`
|
||||||
|
CgroupParent string `json:",omitempty"`
|
||||||
|
ImageResolveMode string `json:",omitempty"`
|
||||||
|
MultiPlatform bool `json:",omitempty"`
|
||||||
|
NoCache bool `json:",omitempty"`
|
||||||
|
NoCacheFilter []string `json:",omitempty"`
|
||||||
|
|
||||||
|
ShmSize string `json:",omitempty"`
|
||||||
|
Ulimit string `json:",omitempty"`
|
||||||
|
CacheMountNS string `json:",omitempty"`
|
||||||
|
DockerfileCheckConfig string `json:",omitempty"`
|
||||||
|
SourceDateEpoch string `json:",omitempty"`
|
||||||
|
SandboxHostname string `json:",omitempty"`
|
||||||
|
|
||||||
|
RestRaw []keyValueOutput `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type materialOutput struct {
|
||||||
|
URI string `json:",omitempty"`
|
||||||
|
Digests []string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type attachmentOutput struct {
|
||||||
|
Digest string `json:",omitempty"`
|
||||||
|
Platform string `json:",omitempty"`
|
||||||
|
Type string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorOutput struct {
|
||||||
|
Code int `json:",omitempty"`
|
||||||
|
Message string `json:",omitempty"`
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Logs []string `json:",omitempty"`
|
||||||
|
Sources []byte `json:",omitempty"`
|
||||||
|
Stack []byte `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyValueOutput struct {
|
||||||
|
Name string `json:",omitempty"`
|
||||||
|
Value string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAttr[T any](attrs map[string]string, k string, dest *T, f func(v string) (T, bool)) {
|
||||||
|
if sv, ok := attrs[k]; ok {
|
||||||
|
if f != nil {
|
||||||
|
v, ok := f(sv)
|
||||||
|
if ok {
|
||||||
|
*dest = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if d, ok := any(dest).(*string); ok {
|
||||||
|
*d = sv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete(attrs, k)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
|
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) error {
|
||||||
@@ -66,7 +173,7 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -78,13 +185,26 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
|
|||||||
return errors.Errorf("no record found for ref %q", opts.ref)
|
return errors.Errorf("no record found for ref %q", opts.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ref == "" {
|
rec := &recs[0]
|
||||||
slices.SortFunc(recs, func(a, b historyRecord) int {
|
c, err := rec.node.Driver.Client(ctx)
|
||||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
if err != nil {
|
||||||
})
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rec := &recs[0]
|
store := proxy.NewContentStore(c.ContentClient())
|
||||||
|
|
||||||
|
var defaultPlatform string
|
||||||
|
workers, err := c.ListWorkers(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to list workers")
|
||||||
|
}
|
||||||
|
workers0:
|
||||||
|
for _, w := range workers {
|
||||||
|
for _, p := range w.Platforms {
|
||||||
|
defaultPlatform = platforms.FormatAll(platforms.Normalize(p))
|
||||||
|
break workers0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ls, err := localstate.New(confutil.NewConfig(dockerCli))
|
ls, err := localstate.New(confutil.NewConfig(dockerCli))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -92,22 +212,10 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
|
|||||||
}
|
}
|
||||||
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
|
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
|
||||||
|
|
||||||
tw := tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
|
||||||
|
|
||||||
attrs := rec.FrontendAttrs
|
attrs := rec.FrontendAttrs
|
||||||
delete(attrs, "frontend.caps")
|
delete(attrs, "frontend.caps")
|
||||||
|
|
||||||
writeAttr := func(k, name string, f func(v string) (string, bool)) {
|
var out inspectOutput
|
||||||
if v, ok := attrs[k]; ok {
|
|
||||||
if f != nil {
|
|
||||||
v, ok = f(v)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
fmt.Fprintf(tw, "%s:\t%s\n", name, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(attrs, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
var context string
|
var context string
|
||||||
var dockerfile string
|
var dockerfile string
|
||||||
@@ -146,131 +254,171 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
|
|||||||
}
|
}
|
||||||
delete(attrs, "filename")
|
delete(attrs, "filename")
|
||||||
|
|
||||||
if context != "" {
|
out.Name = buildName(rec.FrontendAttrs, st)
|
||||||
fmt.Fprintf(tw, "Context:\t%s\n", context)
|
out.Ref = rec.Ref
|
||||||
}
|
|
||||||
if dockerfile != "" {
|
out.Context = context
|
||||||
fmt.Fprintf(tw, "Dockerfile:\t%s\n", dockerfile)
|
out.Dockerfile = dockerfile
|
||||||
}
|
|
||||||
if _, ok := attrs["context"]; !ok {
|
if _, ok := attrs["context"]; !ok {
|
||||||
if src, ok := attrs["vcs:source"]; ok {
|
if src, ok := attrs["vcs:source"]; ok {
|
||||||
fmt.Fprintf(tw, "VCS Repository:\t%s\n", src)
|
out.VCSRepository = src
|
||||||
}
|
}
|
||||||
if rev, ok := attrs["vcs:revision"]; ok {
|
if rev, ok := attrs["vcs:revision"]; ok {
|
||||||
fmt.Fprintf(tw, "VCS Revision:\t%s\n", rev)
|
out.VCSRevision = rev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeAttr("target", "Target", nil)
|
readAttr(attrs, "target", &out.Target, nil)
|
||||||
writeAttr("platform", "Platform", func(v string) (string, bool) {
|
|
||||||
return tryParseValue(v, func(v string) (string, error) {
|
readAttr(attrs, "platform", &out.Platform, func(v string) ([]string, bool) {
|
||||||
|
return tryParseValue(v, &out.Errors, func(v string) ([]string, error) {
|
||||||
var pp []string
|
var pp []string
|
||||||
for _, v := range strings.Split(v, ",") {
|
for _, v := range strings.Split(v, ",") {
|
||||||
p, err := platforms.Parse(v)
|
p, err := platforms.Parse(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
pp = append(pp, platforms.FormatAll(platforms.Normalize(p)))
|
pp = append(pp, platforms.FormatAll(platforms.Normalize(p)))
|
||||||
}
|
}
|
||||||
return strings.Join(pp, ", "), nil
|
if len(pp) == 0 {
|
||||||
}), true
|
pp = append(pp, defaultPlatform)
|
||||||
})
|
|
||||||
writeAttr("build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR", "Keep Git Dir", func(v string) (string, bool) {
|
|
||||||
return tryParseValue(v, func(v string) (string, error) {
|
|
||||||
b, err := strconv.ParseBool(v)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
return strconv.FormatBool(b), nil
|
return pp, nil
|
||||||
}), true
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
tw.Flush()
|
readAttr(attrs, "build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR", &out.KeepGitDir, func(v string) (bool, bool) {
|
||||||
|
return tryParseValue(v, &out.Errors, strconv.ParseBool)
|
||||||
|
})
|
||||||
|
|
||||||
fmt.Fprintln(dockerCli.Out())
|
out.NamedContexts = readKeyValues(attrs, "context:")
|
||||||
|
|
||||||
printTable(dockerCli.Out(), attrs, "context:", "Named Context")
|
if rec.CreatedAt != nil {
|
||||||
|
tm := rec.CreatedAt.AsTime().Local()
|
||||||
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
out.StartedAt = &tm
|
||||||
|
|
||||||
fmt.Fprintf(tw, "Started:\t%s\n", rec.CreatedAt.AsTime().Local().Format("2006-01-02 15:04:05"))
|
|
||||||
var duration time.Duration
|
|
||||||
var statusStr string
|
|
||||||
if rec.CompletedAt != nil {
|
|
||||||
duration = rec.CompletedAt.AsTime().Sub(rec.CreatedAt.AsTime())
|
|
||||||
} else {
|
|
||||||
duration = rec.currentTimestamp.Sub(rec.CreatedAt.AsTime())
|
|
||||||
statusStr = " (running)"
|
|
||||||
}
|
}
|
||||||
fmt.Fprintf(tw, "Duration:\t%s%s\n", formatDuration(duration), statusStr)
|
out.Status = statusRunning
|
||||||
if rec.Error != nil {
|
|
||||||
if codes.Code(rec.Error.Code) == codes.Canceled {
|
if rec.CompletedAt != nil {
|
||||||
fmt.Fprintf(tw, "Status:\tCanceled\n")
|
tm := rec.CompletedAt.AsTime().Local()
|
||||||
} else {
|
out.CompletedAt = &tm
|
||||||
fmt.Fprintf(tw, "Error:\t%s %s\n", codes.Code(rec.Error.Code).String(), rec.Error.Message)
|
out.Status = statusComplete
|
||||||
|
}
|
||||||
|
|
||||||
|
if rec.Error != nil || rec.ExternalError != nil {
|
||||||
|
out.Error = &errorOutput{}
|
||||||
|
if rec.Error != nil {
|
||||||
|
if codes.Code(rec.Error.Code) == codes.Canceled {
|
||||||
|
out.Status = statusCanceled
|
||||||
|
} else {
|
||||||
|
out.Status = statusError
|
||||||
|
}
|
||||||
|
out.Error.Code = int(codes.Code(rec.Error.Code))
|
||||||
|
out.Error.Message = rec.Error.Message
|
||||||
|
}
|
||||||
|
if rec.ExternalError != nil {
|
||||||
|
dt, err := content.ReadBlob(ctx, store, ociDesc(rec.ExternalError))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to read external error %s", rec.ExternalError.Digest)
|
||||||
|
}
|
||||||
|
var st spb.Status
|
||||||
|
if err := proto.Unmarshal(dt, &st); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to unmarshal external error %s", rec.ExternalError.Digest)
|
||||||
|
}
|
||||||
|
retErr := grpcerrors.FromGRPC(status.ErrorProto(&st))
|
||||||
|
var errsources bytes.Buffer
|
||||||
|
for _, s := range errdefs.Sources(retErr) {
|
||||||
|
s.Print(&errsources)
|
||||||
|
errsources.WriteString("\n")
|
||||||
|
}
|
||||||
|
out.Error.Sources = errsources.Bytes()
|
||||||
|
var ve *errdefs.VertexError
|
||||||
|
if errors.As(retErr, &ve) {
|
||||||
|
dgst, err := digest.Parse(ve.Vertex.Digest)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to parse vertex digest %s", ve.Vertex.Digest)
|
||||||
|
}
|
||||||
|
name, logs, err := loadVertexLogs(ctx, c, rec.Ref, dgst, 16)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to load vertex logs %s", dgst)
|
||||||
|
}
|
||||||
|
out.Error.Name = name
|
||||||
|
out.Error.Logs = logs
|
||||||
|
}
|
||||||
|
out.Error.Stack = fmt.Appendf(nil, "%+v", stack.Formatter(retErr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(tw, "Build Steps:\t%d/%d (%.0f%% cached)\n", rec.NumCompletedSteps, rec.NumTotalSteps, float64(rec.NumCachedSteps)/float64(rec.NumTotalSteps)*100)
|
|
||||||
tw.Flush()
|
|
||||||
|
|
||||||
fmt.Fprintln(dockerCli.Out())
|
if out.StartedAt != nil {
|
||||||
|
if out.CompletedAt != nil {
|
||||||
|
out.Duration = out.CompletedAt.Sub(*out.StartedAt)
|
||||||
|
} else {
|
||||||
|
out.Duration = rec.currentTimestamp.Sub(*out.StartedAt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
out.NumCompletedSteps = rec.NumCompletedSteps
|
||||||
|
out.NumTotalSteps = rec.NumTotalSteps
|
||||||
|
out.NumCachedSteps = rec.NumCachedSteps
|
||||||
|
|
||||||
writeAttr("force-network-mode", "Network", nil)
|
out.BuildArgs = readKeyValues(attrs, "build-arg:")
|
||||||
writeAttr("hostname", "Hostname", nil)
|
out.Labels = readKeyValues(attrs, "label:")
|
||||||
writeAttr("add-hosts", "Extra Hosts", func(v string) (string, bool) {
|
|
||||||
return tryParseValue(v, func(v string) (string, error) {
|
readAttr(attrs, "force-network-mode", &out.Config.Network, nil)
|
||||||
|
readAttr(attrs, "hostname", &out.Config.Hostname, nil)
|
||||||
|
readAttr(attrs, "cgroup-parent", &out.Config.CgroupParent, nil)
|
||||||
|
readAttr(attrs, "image-resolve-mode", &out.Config.ImageResolveMode, nil)
|
||||||
|
readAttr(attrs, "build-arg:BUILDKIT_MULTI_PLATFORM", &out.Config.MultiPlatform, func(v string) (bool, bool) {
|
||||||
|
return tryParseValue(v, &out.Errors, strconv.ParseBool)
|
||||||
|
})
|
||||||
|
readAttr(attrs, "multi-platform", &out.Config.MultiPlatform, func(v string) (bool, bool) {
|
||||||
|
return tryParseValue(v, &out.Errors, strconv.ParseBool)
|
||||||
|
})
|
||||||
|
readAttr(attrs, "no-cache", &out.Config.NoCache, func(v string) (bool, bool) {
|
||||||
|
if v == "" {
|
||||||
|
return true, true
|
||||||
|
}
|
||||||
|
return false, false
|
||||||
|
})
|
||||||
|
readAttr(attrs, "no-cache", &out.Config.NoCacheFilter, func(v string) ([]string, bool) {
|
||||||
|
if v == "" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return strings.Split(v, ","), true
|
||||||
|
})
|
||||||
|
|
||||||
|
readAttr(attrs, "add-hosts", &out.Config.ExtraHosts, func(v string) ([]string, bool) {
|
||||||
|
return tryParseValue(v, &out.Errors, func(v string) ([]string, error) {
|
||||||
fields, err := csvvalue.Fields(v, nil)
|
fields, err := csvvalue.Fields(v, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
return strings.Join(fields, ", "), nil
|
return fields, nil
|
||||||
}), true
|
})
|
||||||
})
|
})
|
||||||
writeAttr("cgroup-parent", "Cgroup Parent", nil)
|
|
||||||
writeAttr("image-resolve-mode", "Image Resolve Mode", nil)
|
|
||||||
writeAttr("multi-platform", "Force Multi-Platform", nil)
|
|
||||||
writeAttr("build-arg:BUILDKIT_MULTI_PLATFORM", "Force Multi-Platform", nil)
|
|
||||||
writeAttr("no-cache", "Disable Cache", func(v string) (string, bool) {
|
|
||||||
if v == "" {
|
|
||||||
return "true", true
|
|
||||||
}
|
|
||||||
return v, true
|
|
||||||
})
|
|
||||||
writeAttr("shm-size", "Shm Size", nil)
|
|
||||||
writeAttr("ulimit", "Resource Limits", nil)
|
|
||||||
writeAttr("build-arg:BUILDKIT_CACHE_MOUNT_NS", "Cache Mount Namespace", nil)
|
|
||||||
writeAttr("build-arg:BUILDKIT_DOCKERFILE_CHECK", "Dockerfile Check Config", nil)
|
|
||||||
writeAttr("build-arg:SOURCE_DATE_EPOCH", "Source Date Epoch", nil)
|
|
||||||
writeAttr("build-arg:SANDBOX_HOSTNAME", "Sandbox Hostname", nil)
|
|
||||||
|
|
||||||
var unusedAttrs []string
|
readAttr(attrs, "shm-size", &out.Config.ShmSize, nil)
|
||||||
|
readAttr(attrs, "ulimit", &out.Config.Ulimit, nil)
|
||||||
|
readAttr(attrs, "build-arg:BUILDKIT_CACHE_MOUNT_NS", &out.Config.CacheMountNS, nil)
|
||||||
|
readAttr(attrs, "build-arg:BUILDKIT_DOCKERFILE_CHECK", &out.Config.DockerfileCheckConfig, nil)
|
||||||
|
readAttr(attrs, "build-arg:SOURCE_DATE_EPOCH", &out.Config.SourceDateEpoch, nil)
|
||||||
|
readAttr(attrs, "build-arg:SANDBOX_HOSTNAME", &out.Config.SandboxHostname, nil)
|
||||||
|
|
||||||
|
var unusedAttrs []keyValueOutput
|
||||||
for k := range attrs {
|
for k := range attrs {
|
||||||
if strings.HasPrefix(k, "vcs:") || strings.HasPrefix(k, "build-arg:") || strings.HasPrefix(k, "label:") || strings.HasPrefix(k, "context:") || strings.HasPrefix(k, "attest:") {
|
if strings.HasPrefix(k, "vcs:") || strings.HasPrefix(k, "build-arg:") || strings.HasPrefix(k, "label:") || strings.HasPrefix(k, "context:") || strings.HasPrefix(k, "attest:") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
unusedAttrs = append(unusedAttrs, k)
|
unusedAttrs = append(unusedAttrs, keyValueOutput{
|
||||||
|
Name: k,
|
||||||
|
Value: attrs[k],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
slices.Sort(unusedAttrs)
|
slices.SortFunc(unusedAttrs, func(a, b keyValueOutput) int {
|
||||||
|
return cmp.Compare(a.Name, b.Name)
|
||||||
for _, k := range unusedAttrs {
|
})
|
||||||
fmt.Fprintf(tw, "%s:\t%s\n", k, attrs[k])
|
out.Config.RestRaw = unusedAttrs
|
||||||
}
|
|
||||||
|
|
||||||
tw.Flush()
|
|
||||||
|
|
||||||
fmt.Fprintln(dockerCli.Out())
|
|
||||||
|
|
||||||
printTable(dockerCli.Out(), attrs, "build-arg:", "Build Arg")
|
|
||||||
printTable(dockerCli.Out(), attrs, "label:", "Label")
|
|
||||||
|
|
||||||
c, err := rec.node.Driver.Client(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
store := proxy.NewContentStore(c.ContentClient())
|
|
||||||
|
|
||||||
attachments, err := allAttachments(ctx, store, *rec)
|
attachments, err := allAttachments(ctx, store, *rec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -282,81 +430,209 @@ func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions)
|
|||||||
})
|
})
|
||||||
if provIndex != -1 {
|
if provIndex != -1 {
|
||||||
prov := attachments[provIndex]
|
prov := attachments[provIndex]
|
||||||
|
|
||||||
dt, err := content.ReadBlob(ctx, store, prov.descr)
|
dt, err := content.ReadBlob(ctx, store, prov.descr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("failed to read provenance %s: %v", prov.descr.Digest, err)
|
return errors.Errorf("failed to read provenance %s: %v", prov.descr.Digest, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pred provenancetypes.ProvenancePredicate
|
var pred provenancetypes.ProvenancePredicate
|
||||||
if err := json.Unmarshal(dt, &pred); err != nil {
|
if err := json.Unmarshal(dt, &pred); err != nil {
|
||||||
return errors.Errorf("failed to unmarshal provenance %s: %v", prov.descr.Digest, err)
|
return errors.Errorf("failed to unmarshal provenance %s: %v", prov.descr.Digest, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(dockerCli.Out(), "Materials:")
|
|
||||||
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
|
||||||
fmt.Fprintf(tw, "URI\tDIGEST\n")
|
|
||||||
for _, m := range pred.Materials {
|
for _, m := range pred.Materials {
|
||||||
fmt.Fprintf(tw, "%s\t%s\n", m.URI, strings.Join(digestSetToDigests(m.Digest), ", "))
|
out.Materials = append(out.Materials, materialOutput{
|
||||||
|
URI: m.URI,
|
||||||
|
Digests: digestSetToDigests(m.Digest),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
tw.Flush()
|
|
||||||
fmt.Fprintln(dockerCli.Out())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(attachments) > 0 {
|
if len(attachments) > 0 {
|
||||||
fmt.Fprintf(tw, "Attachments:\n")
|
|
||||||
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
|
||||||
fmt.Fprintf(tw, "DIGEST\tPLATFORM\tTYPE\n")
|
|
||||||
for _, a := range attachments {
|
for _, a := range attachments {
|
||||||
p := ""
|
p := ""
|
||||||
if a.platform != nil {
|
if a.platform != nil {
|
||||||
p = platforms.FormatAll(*a.platform)
|
p = platforms.FormatAll(*a.platform)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(tw, "%s\t%s\t%s\n", a.descr.Digest, p, descrType(a.descr))
|
out.Attachments = append(out.Attachments, attachmentOutput{
|
||||||
|
Digest: a.descr.Digest.String(),
|
||||||
|
Platform: p,
|
||||||
|
Type: descrType(a.descr),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.format == formatter.JSONFormatKey {
|
||||||
|
enc := json.NewEncoder(dockerCli.Out())
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
return enc.Encode(out)
|
||||||
|
} else if opts.format != formatter.PrettyFormatKey {
|
||||||
|
tmpl, err := template.New("inspect").Parse(opts.format)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to parse format template")
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&buf, out); err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to execute format template")
|
||||||
|
}
|
||||||
|
fmt.Fprintln(dockerCli.Out(), buf.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tw := tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||||
|
|
||||||
|
if out.Name != "" {
|
||||||
|
fmt.Fprintf(tw, "Name:\t%s\n", out.Name)
|
||||||
|
}
|
||||||
|
if opts.ref == "" && out.Ref != "" {
|
||||||
|
fmt.Fprintf(tw, "Ref:\t%s\n", out.Ref)
|
||||||
|
}
|
||||||
|
if out.Context != "" {
|
||||||
|
fmt.Fprintf(tw, "Context:\t%s\n", out.Context)
|
||||||
|
}
|
||||||
|
if out.Dockerfile != "" {
|
||||||
|
fmt.Fprintf(tw, "Dockerfile:\t%s\n", out.Dockerfile)
|
||||||
|
}
|
||||||
|
if out.VCSRepository != "" {
|
||||||
|
fmt.Fprintf(tw, "VCS Repository:\t%s\n", out.VCSRepository)
|
||||||
|
}
|
||||||
|
if out.VCSRevision != "" {
|
||||||
|
fmt.Fprintf(tw, "VCS Revision:\t%s\n", out.VCSRevision)
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.Target != "" {
|
||||||
|
fmt.Fprintf(tw, "Target:\t%s\n", out.Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(out.Platform) > 0 {
|
||||||
|
fmt.Fprintf(tw, "Platforms:\t%s\n", strings.Join(out.Platform, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.KeepGitDir {
|
||||||
|
fmt.Fprintf(tw, "Keep Git Dir:\t%s\n", strconv.FormatBool(out.KeepGitDir))
|
||||||
|
}
|
||||||
|
|
||||||
|
tw.Flush()
|
||||||
|
|
||||||
|
fmt.Fprintln(dockerCli.Out())
|
||||||
|
|
||||||
|
printTable(dockerCli.Out(), out.NamedContexts, "Named Context")
|
||||||
|
|
||||||
|
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||||
|
|
||||||
|
fmt.Fprintf(tw, "Started:\t%s\n", out.StartedAt.Format("2006-01-02 15:04:05"))
|
||||||
|
var statusStr string
|
||||||
|
if out.Status == statusRunning {
|
||||||
|
statusStr = " (running)"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(tw, "Duration:\t%s%s\n", formatDuration(out.Duration), statusStr)
|
||||||
|
|
||||||
|
if out.Status == statusError {
|
||||||
|
fmt.Fprintf(tw, "Error:\t%s %s\n", codes.Code(rec.Error.Code).String(), rec.Error.Message)
|
||||||
|
} else if out.Status == statusCanceled {
|
||||||
|
fmt.Fprintf(tw, "Status:\tCanceled\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(tw, "Build Steps:\t%d/%d (%.0f%% cached)\n", out.NumCompletedSteps, out.NumTotalSteps, float64(out.NumCachedSteps)/float64(out.NumTotalSteps)*100)
|
||||||
|
tw.Flush()
|
||||||
|
|
||||||
|
fmt.Fprintln(dockerCli.Out())
|
||||||
|
|
||||||
|
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||||
|
|
||||||
|
if out.Config.Network != "" {
|
||||||
|
fmt.Fprintf(tw, "Network:\t%s\n", out.Config.Network)
|
||||||
|
}
|
||||||
|
if out.Config.Hostname != "" {
|
||||||
|
fmt.Fprintf(tw, "Hostname:\t%s\n", out.Config.Hostname)
|
||||||
|
}
|
||||||
|
if len(out.Config.ExtraHosts) > 0 {
|
||||||
|
fmt.Fprintf(tw, "Extra Hosts:\t%s\n", strings.Join(out.Config.ExtraHosts, ", "))
|
||||||
|
}
|
||||||
|
if out.Config.CgroupParent != "" {
|
||||||
|
fmt.Fprintf(tw, "Cgroup Parent:\t%s\n", out.Config.CgroupParent)
|
||||||
|
}
|
||||||
|
if out.Config.ImageResolveMode != "" {
|
||||||
|
fmt.Fprintf(tw, "Image Resolve Mode:\t%s\n", out.Config.ImageResolveMode)
|
||||||
|
}
|
||||||
|
if out.Config.MultiPlatform {
|
||||||
|
fmt.Fprintf(tw, "Multi-Platform:\t%s\n", strconv.FormatBool(out.Config.MultiPlatform))
|
||||||
|
}
|
||||||
|
if out.Config.NoCache {
|
||||||
|
fmt.Fprintf(tw, "No Cache:\t%s\n", strconv.FormatBool(out.Config.NoCache))
|
||||||
|
}
|
||||||
|
if len(out.Config.NoCacheFilter) > 0 {
|
||||||
|
fmt.Fprintf(tw, "No Cache Filter:\t%s\n", strings.Join(out.Config.NoCacheFilter, ", "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.Config.ShmSize != "" {
|
||||||
|
fmt.Fprintf(tw, "Shm Size:\t%s\n", out.Config.ShmSize)
|
||||||
|
}
|
||||||
|
if out.Config.Ulimit != "" {
|
||||||
|
fmt.Fprintf(tw, "Resource Limits:\t%s\n", out.Config.Ulimit)
|
||||||
|
}
|
||||||
|
if out.Config.CacheMountNS != "" {
|
||||||
|
fmt.Fprintf(tw, "Cache Mount Namespace:\t%s\n", out.Config.CacheMountNS)
|
||||||
|
}
|
||||||
|
if out.Config.DockerfileCheckConfig != "" {
|
||||||
|
fmt.Fprintf(tw, "Dockerfile Check Config:\t%s\n", out.Config.DockerfileCheckConfig)
|
||||||
|
}
|
||||||
|
if out.Config.SourceDateEpoch != "" {
|
||||||
|
fmt.Fprintf(tw, "Source Date Epoch:\t%s\n", out.Config.SourceDateEpoch)
|
||||||
|
}
|
||||||
|
if out.Config.SandboxHostname != "" {
|
||||||
|
fmt.Fprintf(tw, "Sandbox Hostname:\t%s\n", out.Config.SandboxHostname)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kv := range out.Config.RestRaw {
|
||||||
|
fmt.Fprintf(tw, "%s:\t%s\n", kv.Name, kv.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
tw.Flush()
|
||||||
|
|
||||||
|
fmt.Fprintln(dockerCli.Out())
|
||||||
|
|
||||||
|
printTable(dockerCli.Out(), out.BuildArgs, "Build Arg")
|
||||||
|
printTable(dockerCli.Out(), out.Labels, "Label")
|
||||||
|
|
||||||
|
if len(out.Materials) > 0 {
|
||||||
|
fmt.Fprintln(dockerCli.Out(), "Materials:")
|
||||||
|
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||||
|
fmt.Fprintf(tw, "URI\tDIGEST\n")
|
||||||
|
for _, m := range out.Materials {
|
||||||
|
fmt.Fprintf(tw, "%s\t%s\n", m.URI, strings.Join(m.Digests, ", "))
|
||||||
}
|
}
|
||||||
tw.Flush()
|
tw.Flush()
|
||||||
fmt.Fprintln(dockerCli.Out())
|
fmt.Fprintln(dockerCli.Out())
|
||||||
}
|
}
|
||||||
|
|
||||||
if rec.ExternalError != nil {
|
if len(out.Attachments) > 0 {
|
||||||
dt, err := content.ReadBlob(ctx, store, ociDesc(rec.ExternalError))
|
fmt.Fprintf(tw, "Attachments:\n")
|
||||||
if err != nil {
|
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||||
return errors.Wrapf(err, "failed to read external error %s", rec.ExternalError.Digest)
|
fmt.Fprintf(tw, "DIGEST\tPLATFORM\tTYPE\n")
|
||||||
}
|
for _, a := range out.Attachments {
|
||||||
var st spb.Status
|
fmt.Fprintf(tw, "%s\t%s\t%s\n", a.Digest, a.Platform, a.Type)
|
||||||
if err := proto.Unmarshal(dt, &st); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to unmarshal external error %s", rec.ExternalError.Digest)
|
|
||||||
}
|
|
||||||
retErr := grpcerrors.FromGRPC(status.ErrorProto(&st))
|
|
||||||
for _, s := range errdefs.Sources(retErr) {
|
|
||||||
s.Print(dockerCli.Out())
|
|
||||||
}
|
}
|
||||||
|
tw.Flush()
|
||||||
fmt.Fprintln(dockerCli.Out())
|
fmt.Fprintln(dockerCli.Out())
|
||||||
|
}
|
||||||
|
|
||||||
var ve *errdefs.VertexError
|
if out.Error != nil {
|
||||||
if errors.As(retErr, &ve) {
|
if out.Error.Sources != nil {
|
||||||
dgst, err := digest.Parse(ve.Vertex.Digest)
|
fmt.Fprint(dockerCli.Out(), string(out.Error.Sources))
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to parse vertex digest %s", ve.Vertex.Digest)
|
|
||||||
}
|
|
||||||
name, logs, err := loadVertexLogs(ctx, c, rec.Ref, dgst, 16)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to load vertex logs %s", dgst)
|
|
||||||
}
|
|
||||||
if len(logs) > 0 {
|
|
||||||
fmt.Fprintln(dockerCli.Out(), "Logs:")
|
|
||||||
fmt.Fprintf(dockerCli.Out(), "> => %s:\n", name)
|
|
||||||
for _, l := range logs {
|
|
||||||
fmt.Fprintln(dockerCli.Out(), "> "+l)
|
|
||||||
}
|
|
||||||
fmt.Fprintln(dockerCli.Out())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if len(out.Error.Logs) > 0 {
|
||||||
if debug.IsEnabled() {
|
fmt.Fprintln(dockerCli.Out(), "Logs:")
|
||||||
fmt.Fprintf(dockerCli.Out(), "\n%+v\n", stack.Formatter(retErr))
|
fmt.Fprintf(dockerCli.Out(), "> => %s:\n", out.Error.Name)
|
||||||
} else if len(stack.Traces(retErr)) > 0 {
|
for _, l := range out.Error.Logs {
|
||||||
fmt.Fprintf(dockerCli.Out(), "Enable --debug to see stack traces for error\n")
|
fmt.Fprintln(dockerCli.Out(), "> "+l)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(dockerCli.Out())
|
||||||
|
}
|
||||||
|
if len(out.Error.Stack) > 0 {
|
||||||
|
if debug.IsEnabled() {
|
||||||
|
fmt.Fprintf(dockerCli.Out(), "\n%s\n", out.Error.Stack)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(dockerCli.Out(), "Enable --debug to see stack traces for error\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,7 +664,8 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
|||||||
attachmentCmd(dockerCli, rootOpts),
|
attachmentCmd(dockerCli, rootOpts),
|
||||||
)
|
)
|
||||||
|
|
||||||
// flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
|
flags.StringVar(&options.format, "format", formatter.PrettyFormatKey, "Format the output")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@@ -565,36 +842,48 @@ func descrType(desc ocispecs.Descriptor) string {
|
|||||||
return desc.MediaType
|
return desc.MediaType
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryParseValue(s string, f func(string) (string, error)) string {
|
func tryParseValue[T any](s string, errs *[]string, f func(string) (T, error)) (T, bool) {
|
||||||
v, err := f(s)
|
v, err := f(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprintf("%s (%v)", s, err)
|
errStr := fmt.Sprintf("failed to parse %s: (%v)", s, err)
|
||||||
|
*errs = append(*errs, errStr)
|
||||||
}
|
}
|
||||||
return v
|
return v, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func printTable(w io.Writer, attrs map[string]string, prefix, title string) {
|
func printTable(w io.Writer, kvs []keyValueOutput, title string) {
|
||||||
var keys []string
|
if len(kvs) == 0 {
|
||||||
for k := range attrs {
|
|
||||||
if strings.HasPrefix(k, prefix) {
|
|
||||||
keys = append(keys, strings.TrimPrefix(k, prefix))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slices.Sort(keys)
|
|
||||||
|
|
||||||
if len(keys) == 0 {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||||
fmt.Fprintf(tw, "%s\tVALUE\n", strings.ToUpper(title))
|
fmt.Fprintf(tw, "%s\tVALUE\n", strings.ToUpper(title))
|
||||||
for _, k := range keys {
|
for _, k := range kvs {
|
||||||
fmt.Fprintf(tw, "%s\t%s\n", k, attrs[prefix+k])
|
fmt.Fprintf(tw, "%s\t%s\n", k.Name, k.Value)
|
||||||
}
|
}
|
||||||
tw.Flush()
|
tw.Flush()
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readKeyValues(attrs map[string]string, prefix string) []keyValueOutput {
|
||||||
|
var out []keyValueOutput
|
||||||
|
for k, v := range attrs {
|
||||||
|
if strings.HasPrefix(k, prefix) {
|
||||||
|
out = append(out, keyValueOutput{
|
||||||
|
Name: strings.TrimPrefix(k, prefix),
|
||||||
|
Value: v,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(out) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
slices.SortFunc(out, func(a, b keyValueOutput) int {
|
||||||
|
return cmp.Compare(a.Name, b.Name)
|
||||||
|
})
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func digestSetToDigests(ds slsa.DigestSet) []string {
|
func digestSetToDigests(ds slsa.DigestSet) []string {
|
||||||
var out []string
|
var out []string
|
||||||
for k, v := range ds {
|
for k, v := range ds {
|
||||||
|
@@ -3,7 +3,6 @@ package history
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/v2/core/content/proxy"
|
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||||
"github.com/containerd/platforms"
|
"github.com/containerd/platforms"
|
||||||
@@ -42,7 +41,7 @@ func runAttachment(ctx context.Context, dockerCli command.Cli, opts attachmentOp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -54,12 +53,6 @@ func runAttachment(ctx context.Context, dockerCli command.Cli, opts attachmentOp
|
|||||||
return errors.Errorf("no record found for ref %q", opts.ref)
|
return errors.Errorf("no record found for ref %q", opts.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ref == "" {
|
|
||||||
slices.SortFunc(recs, func(a, b historyRecord) int {
|
|
||||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
rec := &recs[0]
|
rec := &recs[0]
|
||||||
|
|
||||||
c, err := rec.node.Driver.Client(ctx)
|
c, err := rec.node.Driver.Client(ctx)
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
@@ -39,7 +38,7 @@ func runLogs(ctx context.Context, dockerCli command.Cli, opts logsOptions) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -51,12 +50,6 @@ func runLogs(ctx context.Context, dockerCli command.Cli, opts logsOptions) error
|
|||||||
return errors.Errorf("no record found for ref %q", opts.ref)
|
return errors.Errorf("no record found for ref %q", opts.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ref == "" {
|
|
||||||
slices.SortFunc(recs, func(a, b historyRecord) int {
|
|
||||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
rec := &recs[0]
|
rec := &recs[0]
|
||||||
c, err := rec.node.Driver.Client(ctx)
|
c, err := rec.node.Driver.Client(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
@@ -18,7 +19,6 @@ import (
|
|||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/docker/cli/cli/command/formatter"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -56,7 +56,7 @@ func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := queryRecords(ctx, "", nodes)
|
out, err := queryRecords(ctx, "", nodes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ type lsContext struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *lsContext) MarshalJSON() ([]byte, error) {
|
func (c *lsContext) MarshalJSON() ([]byte, error) {
|
||||||
m := map[string]interface{}{
|
m := map[string]any{
|
||||||
"ref": c.FullRef(),
|
"ref": c.FullRef(),
|
||||||
"name": c.Name(),
|
"name": c.Name(),
|
||||||
"status": c.Status(),
|
"status": c.Status(),
|
||||||
|
@@ -3,7 +3,6 @@ package history
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
@@ -35,7 +34,7 @@ func runOpen(ctx context.Context, dockerCli command.Cli, opts openOptions) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
recs, err := queryRecords(ctx, opts.ref, nodes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -47,12 +46,6 @@ func runOpen(ctx context.Context, dockerCli command.Cli, opts openOptions) error
|
|||||||
return errors.Errorf("no record found for ref %q", opts.ref)
|
return errors.Errorf("no record found for ref %q", opts.ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ref == "" {
|
|
||||||
slices.SortFunc(recs, func(a, b historyRecord) int {
|
|
||||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
rec := &recs[0]
|
rec := &recs[0]
|
||||||
|
|
||||||
url := desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref))
|
url := desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref))
|
||||||
|
@@ -24,6 +24,8 @@ func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *c
|
|||||||
logsCmd(dockerCli, opts),
|
logsCmd(dockerCli, opts),
|
||||||
inspectCmd(dockerCli, opts),
|
inspectCmd(dockerCli, opts),
|
||||||
openCmd(dockerCli, opts),
|
openCmd(dockerCli, opts),
|
||||||
|
traceCmd(dockerCli, opts),
|
||||||
|
importCmd(dockerCli, opts),
|
||||||
)
|
)
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
228
commands/history/trace.go
Normal file
228
commands/history/trace.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/console"
|
||||||
|
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||||
|
"github.com/docker/buildx/builder"
|
||||||
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
|
"github.com/docker/buildx/util/otelutil"
|
||||||
|
"github.com/docker/buildx/util/otelutil/jaeger"
|
||||||
|
"github.com/docker/cli/cli/command"
|
||||||
|
controlapi "github.com/moby/buildkit/api/services/control"
|
||||||
|
"github.com/opencontainers/go-digest"
|
||||||
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
"github.com/pkg/browser"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
jaegerui "github.com/tonistiigi/jaeger-ui-rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type traceOptions struct {
|
||||||
|
builder string
|
||||||
|
ref string
|
||||||
|
addr string
|
||||||
|
compare string
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadTrace(ctx context.Context, ref string, nodes []builder.Node) (string, []byte, error) {
|
||||||
|
recs, err := queryRecords(ctx, ref, nodes, &queryOptions{
|
||||||
|
CompletedOnly: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(recs) == 0 {
|
||||||
|
if ref == "" {
|
||||||
|
return "", nil, errors.New("no records found")
|
||||||
|
}
|
||||||
|
return "", nil, errors.Errorf("no record found for ref %q", ref)
|
||||||
|
}
|
||||||
|
rec := &recs[0]
|
||||||
|
|
||||||
|
if rec.CompletedAt == nil {
|
||||||
|
return "", nil, errors.Errorf("build %q is not completed, only completed builds can be traced", rec.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rec.Trace == nil {
|
||||||
|
// build is complete but no trace yet. try to finalize the trace
|
||||||
|
time.Sleep(1 * time.Second) // give some extra time for last parts of trace to be written
|
||||||
|
|
||||||
|
c, err := rec.node.Driver.Client(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
_, err = c.ControlClient().UpdateBuildHistory(ctx, &controlapi.UpdateBuildHistoryRequest{
|
||||||
|
Ref: rec.Ref,
|
||||||
|
Finalize: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
recs, err := queryRecords(ctx, rec.Ref, []builder.Node{*rec.node}, &queryOptions{
|
||||||
|
CompletedOnly: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(recs) == 0 {
|
||||||
|
return "", nil, errors.Errorf("build record %q was deleted", rec.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
rec = &recs[0]
|
||||||
|
if rec.Trace == nil {
|
||||||
|
return "", nil, errors.Errorf("build record %q is missing a trace", rec.Ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := rec.node.Driver.Client(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
store := proxy.NewContentStore(c.ContentClient())
|
||||||
|
|
||||||
|
ra, err := store.ReaderAt(ctx, ocispecs.Descriptor{
|
||||||
|
Digest: digest.Digest(rec.Trace.Digest),
|
||||||
|
MediaType: rec.Trace.MediaType,
|
||||||
|
Size: rec.Trace.Size,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
spans, err := otelutil.ParseSpanStubs(io.NewSectionReader(ra, 0, ra.Size()))
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper := struct {
|
||||||
|
Data []jaeger.Trace `json:"data"`
|
||||||
|
}{
|
||||||
|
Data: spans.JaegerData().Data,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wrapper.Data) == 0 {
|
||||||
|
return "", nil, errors.New("no trace data")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
enc := json.NewEncoder(buf)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
if err := enc.Encode(wrapper); err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(wrapper.Data[0].TraceID), buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTrace(ctx context.Context, dockerCli command.Cli, opts traceOptions) error {
|
||||||
|
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes, err := b.LoadNodes(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, node := range nodes {
|
||||||
|
if node.Err != nil {
|
||||||
|
return node.Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID, data, err := loadTrace(ctx, opts.ref, nodes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
srv := jaegerui.NewServer(jaegerui.Config{})
|
||||||
|
if err := srv.AddTrace(traceID, bytes.NewReader(data)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
url := "/trace/" + traceID
|
||||||
|
|
||||||
|
if opts.compare != "" {
|
||||||
|
traceIDcomp, data, err := loadTrace(ctx, opts.compare, nodes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "failed to load trace for %s", opts.compare)
|
||||||
|
}
|
||||||
|
if err := srv.AddTrace(traceIDcomp, bytes.NewReader(data)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
url = "/trace/" + traceIDcomp + "..." + traceID
|
||||||
|
}
|
||||||
|
|
||||||
|
var term bool
|
||||||
|
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
||||||
|
term = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !term && opts.compare == "" {
|
||||||
|
fmt.Fprintln(dockerCli.Out(), string(data))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp", opts.addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
browser.OpenURL(url)
|
||||||
|
}()
|
||||||
|
|
||||||
|
url = "http://" + ln.Addr().String() + url
|
||||||
|
fmt.Fprintf(dockerCli.Err(), "Trace available at %s\n", url)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
ln.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = srv.Serve(ln)
|
||||||
|
if err != nil {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||||
|
var options traceOptions
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "trace [OPTIONS] [REF]",
|
||||||
|
Short: "Show the OpenTelemetry trace of a build record",
|
||||||
|
Args: cobra.MaximumNArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) > 0 {
|
||||||
|
options.ref = args[0]
|
||||||
|
}
|
||||||
|
options.builder = *rootOpts.Builder
|
||||||
|
return runTrace(cmd.Context(), dockerCli, options)
|
||||||
|
},
|
||||||
|
ValidArgsFunction: completion.Disable,
|
||||||
|
}
|
||||||
|
|
||||||
|
flags := cmd.Flags()
|
||||||
|
flags.StringVar(&options.addr, "addr", "127.0.0.1:0", "Address to bind the UI server")
|
||||||
|
flags.StringVar(&options.compare, "compare", "", "Compare with another build reference")
|
||||||
|
|
||||||
|
return cmd
|
||||||
|
}
|
@@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -106,10 +108,24 @@ type historyRecord struct {
|
|||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryRecords(ctx context.Context, ref string, nodes []builder.Node) ([]historyRecord, error) {
|
type queryOptions struct {
|
||||||
|
CompletedOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryRecords(ctx context.Context, ref string, nodes []builder.Node, opts *queryOptions) ([]historyRecord, error) {
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
var out []historyRecord
|
var out []historyRecord
|
||||||
|
|
||||||
|
var offset *int
|
||||||
|
if strings.HasPrefix(ref, "^") {
|
||||||
|
off, err := strconv.Atoi(ref[1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "invalid offset %q", ref)
|
||||||
|
}
|
||||||
|
offset = &off
|
||||||
|
ref = ""
|
||||||
|
}
|
||||||
|
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
node := node
|
node := node
|
||||||
@@ -153,6 +169,10 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node) ([]hist
|
|||||||
if he.Type == controlapi.BuildHistoryEventType_DELETED || he.Record == nil {
|
if he.Type == controlapi.BuildHistoryEventType_DELETED || he.Record == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if opts != nil && opts.CompletedOnly && he.Type != controlapi.BuildHistoryEventType_COMPLETE {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
records = append(records, historyRecord{
|
records = append(records, historyRecord{
|
||||||
BuildHistoryRecord: he.Record,
|
BuildHistoryRecord: he.Record,
|
||||||
currentTimestamp: ts,
|
currentTimestamp: ts,
|
||||||
@@ -169,6 +189,27 @@ func queryRecords(ctx context.Context, ref string, nodes []builder.Node) ([]hist
|
|||||||
if err := eg.Wait(); err != nil {
|
if err := eg.Wait(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(out, func(a, b historyRecord) int {
|
||||||
|
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
||||||
|
})
|
||||||
|
|
||||||
|
if offset != nil {
|
||||||
|
var filtered []historyRecord
|
||||||
|
for _, r := range out {
|
||||||
|
if *offset > 0 {
|
||||||
|
*offset--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filtered = append(filtered, r)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if *offset > 0 {
|
||||||
|
return nil, errors.Errorf("no completed build found with offset %d", *offset)
|
||||||
|
}
|
||||||
|
out = filtered
|
||||||
|
}
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -194,7 +194,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
|||||||
}
|
}
|
||||||
s := s
|
s := s
|
||||||
eg2.Go(func() error {
|
eg2.Go(func() error {
|
||||||
sub.Log(1, []byte(fmt.Sprintf("copying %s from %s to %s\n", s.Desc.Digest.String(), s.Ref.String(), t.String())))
|
sub.Log(1, fmt.Appendf(nil, "copying %s from %s to %s\n", s.Desc.Digest.String(), s.Ref.String(), t.String()))
|
||||||
return r.Copy(ctx, s, t)
|
return r.Copy(ctx, s, t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
|||||||
if err := eg2.Wait(); err != nil {
|
if err := eg2.Wait(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sub.Log(1, []byte(fmt.Sprintf("pushing %s to %s\n", desc.Digest.String(), t.String())))
|
sub.Log(1, fmt.Appendf(nil, "pushing %s to %s\n", desc.Digest.String(), t.String()))
|
||||||
return r.Push(ctx, t, desc, dt)
|
return r.Push(ctx, t, desc, dt)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@@ -115,6 +115,25 @@ func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) e
|
|||||||
fmt.Fprintf(w, "\t%s:\t%s\n", k, v)
|
fmt.Fprintf(w, "\t%s:\t%s\n", k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(nodes[i].CDIDevices) > 0 {
|
||||||
|
fmt.Fprintf(w, "Devices:\n")
|
||||||
|
for _, dev := range nodes[i].CDIDevices {
|
||||||
|
fmt.Fprintf(w, "\tName:\t%s\n", dev.Name)
|
||||||
|
if dev.OnDemand {
|
||||||
|
fmt.Fprintf(w, "\tOn-Demand:\t%v\n", dev.OnDemand)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "\tAutomatically allowed:\t%v\n", dev.AutoAllow)
|
||||||
|
}
|
||||||
|
if len(dev.Annotations) > 0 {
|
||||||
|
fmt.Fprintf(w, "\tAnnotations:\n")
|
||||||
|
for k, v := range dev.Annotations {
|
||||||
|
fmt.Fprintf(w, "\t\t%s:\t%s\n", k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for ri, rule := range nodes[i].GCPolicy {
|
for ri, rule := range nodes[i].GCPolicy {
|
||||||
fmt.Fprintf(w, "GC Policy rule#%d:\n", ri)
|
fmt.Fprintf(w, "GC Policy rule#%d:\n", ri)
|
||||||
fmt.Fprintf(w, "\tAll:\t%v\n", rule.All)
|
fmt.Fprintf(w, "\tAll:\t%v\n", rule.All)
|
||||||
|
@@ -159,6 +159,9 @@ func lsPrint(dockerCli command.Cli, current *store.NodeGroup, builders []*builde
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if ctx.Format.IsJSON() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, n := range b.Nodes() {
|
for _, n := range b.Nodes() {
|
||||||
if n.Err != nil {
|
if n.Err != nil {
|
||||||
if ctx.Format.IsTable() {
|
if ctx.Format.IsTable() {
|
||||||
|
@@ -75,7 +75,9 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *controllerapi.Buil
|
|||||||
opts.Platforms = platforms
|
opts.Platforms = platforms
|
||||||
|
|
||||||
dockerConfig := dockerCli.ConfigFile()
|
dockerConfig := dockerCli.ConfigFile()
|
||||||
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(dockerConfig, nil))
|
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(authprovider.DockerAuthProviderConfig{
|
||||||
|
ConfigFile: dockerConfig,
|
||||||
|
}))
|
||||||
|
|
||||||
secrets, err := controllerapi.CreateSecrets(in.Secrets)
|
secrets, err := controllerapi.CreateSecrets(in.Secrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -13,8 +13,8 @@ import (
|
|||||||
type BuildxController interface {
|
type BuildxController interface {
|
||||||
Build(ctx context.Context, options *controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (ref string, resp *client.SolveResponse, inputs *build.Inputs, err error)
|
Build(ctx context.Context, options *controllerapi.BuildOptions, in io.ReadCloser, progress progress.Writer) (ref string, resp *client.SolveResponse, inputs *build.Inputs, err error)
|
||||||
// Invoke starts an IO session into the specified process.
|
// Invoke starts an IO session into the specified process.
|
||||||
// If pid doesn't matche to any running processes, it starts a new process with the specified config.
|
// If pid doesn't match to any running processes, it starts a new process with the specified config.
|
||||||
// If there is no container running or InvokeConfig.Rollback is speicfied, the process will start in a newly created container.
|
// If there is no container running or InvokeConfig.Rollback is specified, the process will start in a newly created container.
|
||||||
// NOTE: If needed, in the future, we can split this API into three APIs (NewContainer, NewProcess and Attach).
|
// NOTE: If needed, in the future, we can split this API into three APIs (NewContainer, NewProcess and Attach).
|
||||||
Invoke(ctx context.Context, ref, pid string, options *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
|
Invoke(ctx context.Context, ref, pid string, options *controllerapi.InvokeConfig, ioIn io.ReadCloser, ioOut io.WriteCloser, ioErr io.WriteCloser) error
|
||||||
Kill(ctx context.Context) error
|
Kill(ctx context.Context) error
|
||||||
|
@@ -24,11 +24,11 @@ func (w *writer) Write(status *client.SolveStatus) {
|
|||||||
|
|
||||||
func (w *writer) WriteBuildRef(target string, ref string) {}
|
func (w *writer) WriteBuildRef(target string, ref string) {}
|
||||||
|
|
||||||
func (w *writer) ValidateLogSource(digest.Digest, interface{}) bool {
|
func (w *writer) ValidateLogSource(digest.Digest, any) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *writer) ClearLogSource(interface{}) {}
|
func (w *writer) ClearLogSource(any) {}
|
||||||
|
|
||||||
func ToControlStatus(s *client.SolveStatus) *StatusResponse {
|
func ToControlStatus(s *client.SolveStatus) *StatusResponse {
|
||||||
resp := StatusResponse{}
|
resp := StatusResponse{}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package pb
|
package pb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
|
|
||||||
"github.com/moby/buildkit/session"
|
"github.com/moby/buildkit/session"
|
||||||
"github.com/moby/buildkit/session/sshforward/sshprovider"
|
"github.com/moby/buildkit/session/sshforward/sshprovider"
|
||||||
)
|
)
|
||||||
@@ -10,7 +12,7 @@ func CreateSSH(ssh []*SSH) (session.Attachable, error) {
|
|||||||
for _, ssh := range ssh {
|
for _, ssh := range ssh {
|
||||||
cfg := sshprovider.AgentConfig{
|
cfg := sshprovider.AgentConfig{
|
||||||
ID: ssh.ID,
|
ID: ssh.ID,
|
||||||
Paths: append([]string{}, ssh.Paths...),
|
Paths: slices.Clone(ssh.Paths),
|
||||||
}
|
}
|
||||||
configs = append(configs, cfg)
|
configs = append(configs, cfg)
|
||||||
}
|
}
|
||||||
|
@@ -39,7 +39,7 @@ func (p *Process) Done() <-chan error {
|
|||||||
return p.errCh
|
return p.errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager manages a set of proceses.
|
// Manager manages a set of processes.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
container atomic.Value
|
container atomic.Value
|
||||||
processes sync.Map
|
processes sync.Map
|
||||||
|
@@ -140,7 +140,7 @@ func serveCmd(dockerCli command.Cli) *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pidF := filepath.Join(root, defaultPIDFilename)
|
pidF := filepath.Join(root, defaultPIDFilename)
|
||||||
if err := os.WriteFile(pidF, []byte(fmt.Sprintf("%d", os.Getpid())), 0600); err != nil {
|
if err := os.WriteFile(pidF, fmt.Appendf(nil, "%d", os.Getpid()), 0600); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@@ -48,6 +48,8 @@ target "lint" {
|
|||||||
"linux/s390x",
|
"linux/s390x",
|
||||||
"linux/ppc64le",
|
"linux/ppc64le",
|
||||||
"linux/riscv64",
|
"linux/riscv64",
|
||||||
|
"netbsd/amd64",
|
||||||
|
"netbsd/arm64",
|
||||||
"openbsd/amd64",
|
"openbsd/amd64",
|
||||||
"openbsd/arm64",
|
"openbsd/arm64",
|
||||||
"windows/amd64",
|
"windows/amd64",
|
||||||
@@ -167,6 +169,8 @@ target "binaries-cross" {
|
|||||||
"linux/ppc64le",
|
"linux/ppc64le",
|
||||||
"linux/riscv64",
|
"linux/riscv64",
|
||||||
"linux/s390x",
|
"linux/s390x",
|
||||||
|
"netbsd/amd64",
|
||||||
|
"netbsd/arm64",
|
||||||
"openbsd/amd64",
|
"openbsd/amd64",
|
||||||
"openbsd/arm64",
|
"openbsd/arm64",
|
||||||
"windows/amd64",
|
"windows/amd64",
|
||||||
|
@@ -221,8 +221,10 @@ The following table shows the complete list of attributes that you can assign to
|
|||||||
| [`attest`](#targetattest) | List | Build attestations |
|
| [`attest`](#targetattest) | List | Build attestations |
|
||||||
| [`cache-from`](#targetcache-from) | List | External cache sources |
|
| [`cache-from`](#targetcache-from) | List | External cache sources |
|
||||||
| [`cache-to`](#targetcache-to) | List | External cache destinations |
|
| [`cache-to`](#targetcache-to) | List | External cache destinations |
|
||||||
|
| [`call`](#targetcall) | String | Specify the frontend method to call for the target. |
|
||||||
| [`context`](#targetcontext) | String | Set of files located in the specified path or URL |
|
| [`context`](#targetcontext) | String | Set of files located in the specified path or URL |
|
||||||
| [`contexts`](#targetcontexts) | Map | Additional build contexts |
|
| [`contexts`](#targetcontexts) | Map | Additional build contexts |
|
||||||
|
| [`description`](#targetdescription) | String | Description of a target |
|
||||||
| [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string |
|
| [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string |
|
||||||
| [`dockerfile`](#targetdockerfile) | String | Dockerfile location |
|
| [`dockerfile`](#targetdockerfile) | String | Dockerfile location |
|
||||||
| [`inherits`](#targetinherits) | List | Inherit attributes from other targets |
|
| [`inherits`](#targetinherits) | List | Inherit attributes from other targets |
|
||||||
@@ -283,19 +285,11 @@ The key takes a list of annotations, in the format of `KEY=VALUE`.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
target "default" {
|
target "default" {
|
||||||
output = ["type=image,name=foo"]
|
output = [{ type = "image", name = "foo" }]
|
||||||
annotations = ["org.opencontainers.image.authors=dvdksn"]
|
annotations = ["org.opencontainers.image.authors=dvdksn"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
is the same as
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
target "default" {
|
|
||||||
output = ["type=image,name=foo,annotation.org.opencontainers.image.authors=dvdksn"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, the annotation is added to image manifests. You can configure the
|
By default, the annotation is added to image manifests. You can configure the
|
||||||
level of the annotations by adding a prefix to the annotation, containing a
|
level of the annotations by adding a prefix to the annotation, containing a
|
||||||
comma-separated list of all the levels that you want to annotate. The following
|
comma-separated list of all the levels that you want to annotate. The following
|
||||||
@@ -303,7 +297,7 @@ example adds annotations to both the image index and manifests.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
target "default" {
|
target "default" {
|
||||||
output = ["type=image,name=foo"]
|
output = [{ type = "image", name = "foo" }]
|
||||||
annotations = ["index,manifest:org.opencontainers.image.authors=dvdksn"]
|
annotations = ["index,manifest:org.opencontainers.image.authors=dvdksn"]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -319,8 +313,13 @@ This attribute accepts the long-form CSV version of attestation parameters.
|
|||||||
```hcl
|
```hcl
|
||||||
target "default" {
|
target "default" {
|
||||||
attest = [
|
attest = [
|
||||||
"type=provenance,mode=min",
|
{
|
||||||
"type=sbom"
|
type = "provenance",
|
||||||
|
mode = "max",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "sbom",
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -336,8 +335,15 @@ This takes a list value, so you can specify multiple cache sources.
|
|||||||
```hcl
|
```hcl
|
||||||
target "app" {
|
target "app" {
|
||||||
cache-from = [
|
cache-from = [
|
||||||
"type=s3,region=eu-west-1,bucket=mybucket",
|
{
|
||||||
"user/repo:cache",
|
type = "s3",
|
||||||
|
region = "eu-west-1",
|
||||||
|
bucket = "mybucket"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "registry",
|
||||||
|
ref = "user/repo:cache"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -353,8 +359,14 @@ This takes a list value, so you can specify multiple cache export targets.
|
|||||||
```hcl
|
```hcl
|
||||||
target "app" {
|
target "app" {
|
||||||
cache-to = [
|
cache-to = [
|
||||||
"type=s3,region=eu-west-1,bucket=mybucket",
|
{
|
||||||
"type=inline"
|
type = "s3",
|
||||||
|
region = "eu-west-1",
|
||||||
|
bucket = "mybucket"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type = "inline",
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -371,6 +383,13 @@ target "app" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Supported values are:
|
||||||
|
|
||||||
|
- `build` builds the target (default)
|
||||||
|
- `check`: evaluates [build checks](https://docs.docker.com/build/checks/) for the target
|
||||||
|
- `outline`: displays the target's build arguments and their default values if available
|
||||||
|
- `targets`: lists all Bake targets in the loaded definition, along with its [description](#targetdescription).
|
||||||
|
|
||||||
For more information about frontend methods, refer to the CLI reference for
|
For more information about frontend methods, refer to the CLI reference for
|
||||||
[`docker buildx build --call`](https://docs.docker.com/reference/cli/docker/buildx/build/#call).
|
[`docker buildx build --call`](https://docs.docker.com/reference/cli/docker/buildx/build/#call).
|
||||||
|
|
||||||
@@ -481,6 +500,25 @@ FROM baseapp
|
|||||||
RUN echo "Hello world"
|
RUN echo "Hello world"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `target.description`
|
||||||
|
|
||||||
|
Defines a human-readable description for the target, clarifying its purpose or
|
||||||
|
functionality.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
target "lint" {
|
||||||
|
description = "Runs golangci-lint to detect style errors"
|
||||||
|
args = {
|
||||||
|
GOLANGCI_LINT_VERSION = null
|
||||||
|
}
|
||||||
|
dockerfile = "lint.Dockerfile"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This attribute is useful when combined with the `docker buildx bake --list=targets`
|
||||||
|
option, providing a more informative output when listing the available build
|
||||||
|
targets in a Bake file.
|
||||||
|
|
||||||
### `target.dockerfile-inline`
|
### `target.dockerfile-inline`
|
||||||
|
|
||||||
Uses the string value as an inline Dockerfile for the build target.
|
Uses the string value as an inline Dockerfile for the build target.
|
||||||
@@ -835,7 +873,7 @@ The following example configures the target to use a cache-only output,
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
target "default" {
|
target "default" {
|
||||||
output = ["type=cacheonly"]
|
output = [{ type = "cacheonly" }]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -875,8 +913,8 @@ variable "HOME" {
|
|||||||
|
|
||||||
target "default" {
|
target "default" {
|
||||||
secret = [
|
secret = [
|
||||||
"type=env,id=KUBECONFIG",
|
{ type = "env", id = "KUBECONFIG" },
|
||||||
"type=file,id=aws,src=${HOME}/.aws/credentials"
|
{ type = "file", id = "aws", src = "${HOME}/.aws/credentials" },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -920,7 +958,7 @@ This can be useful if you need to access private repositories during a build.
|
|||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
target "default" {
|
target "default" {
|
||||||
ssh = ["default"]
|
ssh = [{ id = "default" }]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ Build from a file
|
|||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:------------------------------------|:--------------|:--------|:-------------------------------------------------------------------------------------------------------------|
|
|:------------------------------------|:--------------|:--------|:-------------------------------------------------------------------------------------------------------------|
|
||||||
| `--allow` | `stringArray` | | Allow build to access specified resources |
|
| [`--allow`](#allow) | `stringArray` | | Allow build to access specified resources |
|
||||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
||||||
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
||||||
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
|
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
|
||||||
@@ -51,6 +51,80 @@ guide for introduction to writing bake files.
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
### <a name="allow"></a> Allow extra privileged entitlement (--allow)
|
||||||
|
|
||||||
|
```text
|
||||||
|
--allow=ENTITLEMENT[=VALUE]
|
||||||
|
```
|
||||||
|
|
||||||
|
Entitlements are designed to provide controlled access to privileged
|
||||||
|
operations. By default, Buildx and BuildKit operates with restricted
|
||||||
|
permissions to protect users and their systems from unintended side effects or
|
||||||
|
security risks. The `--allow` flag explicitly grants access to additional
|
||||||
|
entitlements, making it clear when a build or bake operation requires elevated
|
||||||
|
privileges.
|
||||||
|
|
||||||
|
In addition to BuildKit's `network.host` and `security.insecure` entitlements
|
||||||
|
(see [`docker buildx build --allow`](https://docs.docker.com/reference/cli/docker/buildx/build/#allow),
|
||||||
|
Bake supports file system entitlements that grant granular control over file
|
||||||
|
system access. These are particularly useful when working with builds that need
|
||||||
|
access to files outside the default working directory.
|
||||||
|
|
||||||
|
Bake supports the following filesystem entitlements:
|
||||||
|
|
||||||
|
- `--allow fs=<path|*>` - Grant read and write access to files outside of the
|
||||||
|
working directory.
|
||||||
|
- `--allow fs.read=<path|*>` - Grant read access to files outside of the
|
||||||
|
working directory.
|
||||||
|
- `--allow fs.write=<path|*>` - Grant write access to files outside of the
|
||||||
|
working directory.
|
||||||
|
|
||||||
|
The `fs` entitlements take a path value (relative or absolute) to a directory
|
||||||
|
on the filesystem. Alternatively, you can pass a wildcard (`*`) to allow Bake
|
||||||
|
to access the entire filesystem.
|
||||||
|
|
||||||
|
### Example: fs.read
|
||||||
|
|
||||||
|
Given the following Bake configuration, Bake would need to access the parent
|
||||||
|
directory, relative to the Bake file.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
target "app" {
|
||||||
|
context = "../src"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming `docker buildx bake app` is executed in the same directory as the
|
||||||
|
`docker-bake.hcl` file, you would need to explicitly allow Bake to read from
|
||||||
|
the `../src` directory. In this case, the following invocations all work:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker buildx bake --allow fs.read=* app
|
||||||
|
$ docker buildx bake --allow fs.read=../src app
|
||||||
|
$ docker buildx bake --allow fs=* app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: fs.write
|
||||||
|
|
||||||
|
The following `docker-bake.hcl` file requires write access to the `/tmp`
|
||||||
|
directory.
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
target "app" {
|
||||||
|
output = "/tmp"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Assuming `docker buildx bake app` is executed outside of the `/tmp` directory,
|
||||||
|
you would need to allow the `fs.write` entitlement, either by specifying the
|
||||||
|
path or using a wildcard:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker buildx bake --allow fs=/tmp app
|
||||||
|
$ docker buildx bake --allow fs.write=/tmp app
|
||||||
|
$ docker buildx bake --allow fs.write=* app
|
||||||
|
```
|
||||||
|
|
||||||
### <a name="builder"></a> Override the configured builder instance (--builder)
|
### <a name="builder"></a> Override the configured builder instance (--builder)
|
||||||
|
|
||||||
Same as [`buildx --builder`](buildx.md#builder).
|
Same as [`buildx --builder`](buildx.md#builder).
|
||||||
@@ -273,18 +347,22 @@ is defined in https://golang.org/pkg/path/#Match.
|
|||||||
```console
|
```console
|
||||||
$ docker buildx bake --set target.args.mybuildarg=value
|
$ docker buildx bake --set target.args.mybuildarg=value
|
||||||
$ docker buildx bake --set target.platform=linux/arm64
|
$ docker buildx bake --set target.platform=linux/arm64
|
||||||
$ docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo'
|
$ docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo'
|
||||||
$ docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets
|
$ docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets
|
||||||
$ docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo'
|
$ docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo'
|
||||||
|
$ docker buildx bake --set target.platform+=linux/arm64 # appends 'linux/arm64' to the platform list
|
||||||
```
|
```
|
||||||
|
|
||||||
You can override the following fields:
|
You can override the following fields:
|
||||||
|
|
||||||
|
* `annotations`
|
||||||
|
* `attest`
|
||||||
* `args`
|
* `args`
|
||||||
* `cache-from`
|
* `cache-from`
|
||||||
* `cache-to`
|
* `cache-to`
|
||||||
* `context`
|
* `context`
|
||||||
* `dockerfile`
|
* `dockerfile`
|
||||||
|
* `entitlements`
|
||||||
* `labels`
|
* `labels`
|
||||||
* `load`
|
* `load`
|
||||||
* `no-cache`
|
* `no-cache`
|
||||||
@@ -297,3 +375,20 @@ You can override the following fields:
|
|||||||
* `ssh`
|
* `ssh`
|
||||||
* `tags`
|
* `tags`
|
||||||
* `target`
|
* `target`
|
||||||
|
|
||||||
|
You can append using `+=` operator for the following fields:
|
||||||
|
|
||||||
|
* `annotations`¹
|
||||||
|
* `attest`¹
|
||||||
|
* `cache-from`
|
||||||
|
* `cache-to`
|
||||||
|
* `entitlements`¹
|
||||||
|
* `no-cache-filter`
|
||||||
|
* `output`
|
||||||
|
* `platform`
|
||||||
|
* `secrets`
|
||||||
|
* `ssh`
|
||||||
|
* `tags`
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> ¹ These fields already append by default.
|
||||||
|
@@ -16,7 +16,7 @@ Start a build
|
|||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:----------------------------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
|
|:----------------------------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
|
||||||
| [`--add-host`](#add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
|
| [`--add-host`](#add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
|
||||||
| [`--allow`](#allow) | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
|
| [`--allow`](#allow) | `stringArray` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
|
||||||
| [`--annotation`](#annotation) | `stringArray` | | Add annotation to the image |
|
| [`--annotation`](#annotation) | `stringArray` | | Add annotation to the image |
|
||||||
| [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
| [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
||||||
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
|
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
|
||||||
|
@@ -12,7 +12,7 @@ Start a build
|
|||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:--------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
|
|:--------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
|
||||||
| `--add-host` | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
|
| `--add-host` | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
|
||||||
| `--allow` | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
|
| `--allow` | `stringArray` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
|
||||||
| `--annotation` | `stringArray` | | Add annotation to the image |
|
| `--annotation` | `stringArray` | | Add annotation to the image |
|
||||||
| `--attest` | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
| `--attest` | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
||||||
| `--build-arg` | `stringArray` | | Set build-time variables |
|
| `--build-arg` | `stringArray` | | Set build-time variables |
|
||||||
|
@@ -5,13 +5,15 @@ Commands to work on build records
|
|||||||
|
|
||||||
### Subcommands
|
### Subcommands
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
|:---------------------------------------|:-------------------------------|
|
|:---------------------------------------|:-----------------------------------------------|
|
||||||
| [`inspect`](buildx_history_inspect.md) | Inspect a build |
|
| [`import`](buildx_history_import.md) | Import a build into Docker Desktop |
|
||||||
| [`logs`](buildx_history_logs.md) | Print the logs of a build |
|
| [`inspect`](buildx_history_inspect.md) | Inspect a build |
|
||||||
| [`ls`](buildx_history_ls.md) | List build records |
|
| [`logs`](buildx_history_logs.md) | Print the logs of a build |
|
||||||
| [`open`](buildx_history_open.md) | Open a build in Docker Desktop |
|
| [`ls`](buildx_history_ls.md) | List build records |
|
||||||
| [`rm`](buildx_history_rm.md) | Remove build records |
|
| [`open`](buildx_history_open.md) | Open a build in Docker Desktop |
|
||||||
|
| [`rm`](buildx_history_rm.md) | Remove build records |
|
||||||
|
| [`trace`](buildx_history_trace.md) | Show the OpenTelemetry trace of a build record |
|
||||||
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
16
docs/reference/buildx_history_import.md
Normal file
16
docs/reference/buildx_history_import.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# docker buildx history import
|
||||||
|
|
||||||
|
<!---MARKER_GEN_START-->
|
||||||
|
Import a build into Docker Desktop
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
|:----------------|:--------------|:--------|:-----------------------------------------|
|
||||||
|
| `--builder` | `string` | | Override the configured builder instance |
|
||||||
|
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||||
|
| `-f`, `--file` | `stringArray` | | Import from a file path |
|
||||||
|
|
||||||
|
|
||||||
|
<!---MARKER_GEN_END-->
|
||||||
|
|
@@ -12,11 +12,106 @@ Inspect a build
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
|:----------------------|:---------|:---------|:-----------------------------------------|
|
||||||
| `--builder` | `string` | | Override the configured builder instance |
|
| `--builder` | `string` | | Override the configured builder instance |
|
||||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||||
|
| [`--format`](#format) | `string` | `pretty` | Format the output |
|
||||||
|
|
||||||
|
|
||||||
<!---MARKER_GEN_END-->
|
<!---MARKER_GEN_END-->
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### <a name="format"></a> Format the output (--format)
|
||||||
|
|
||||||
|
The formatting options (`--format`) pretty-prints the output to `pretty` (default),
|
||||||
|
`json` or using a Go template.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker buildx history inspect
|
||||||
|
Name: buildx (binaries)
|
||||||
|
Context: .
|
||||||
|
Dockerfile: Dockerfile
|
||||||
|
VCS Repository: https://github.com/crazy-max/buildx.git
|
||||||
|
VCS Revision: f15eaa1ee324ffbbab29605600d27a84cab86361
|
||||||
|
Target: binaries
|
||||||
|
Platforms: linux/amd64
|
||||||
|
Keep Git Dir: true
|
||||||
|
|
||||||
|
Started: 2025-02-07 11:56:24
|
||||||
|
Duration: 1m 1s
|
||||||
|
Build Steps: 16/16 (25% cached)
|
||||||
|
|
||||||
|
Image Resolve Mode: local
|
||||||
|
|
||||||
|
Materials:
|
||||||
|
URI DIGEST
|
||||||
|
pkg:docker/docker/dockerfile@1 sha256:93bfd3b68c109427185cd78b4779fc82b484b0b7618e36d0f104d4d801e66d25
|
||||||
|
pkg:docker/golang@1.23-alpine3.21?platform=linux%2Famd64 sha256:2c49857f2295e89b23b28386e57e018a86620a8fede5003900f2d138ba9c4037
|
||||||
|
pkg:docker/tonistiigi/xx@1.6.1?platform=linux%2Famd64 sha256:923441d7c25f1e2eb5789f82d987693c47b8ed987c4ab3b075d6ed2b5d6779a3
|
||||||
|
|
||||||
|
Attachments:
|
||||||
|
DIGEST PLATFORM TYPE
|
||||||
|
sha256:217329d2af959d4f02e3a96dcbe62bf100cab1feb8006a047ddfe51a5397f7e3 https://slsa.dev/provenance/v0.2
|
||||||
|
|
||||||
|
Print build logs: docker buildx history logs g9808bwrjrlkbhdamxklx660b
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker buildx history inspect --format json
|
||||||
|
{
|
||||||
|
"Name": "buildx (binaries)",
|
||||||
|
"Ref": "5w7vkqfi0rf59hw4hnmn627r9",
|
||||||
|
"Context": ".",
|
||||||
|
"Dockerfile": "Dockerfile",
|
||||||
|
"VCSRepository": "https://github.com/crazy-max/buildx.git",
|
||||||
|
"VCSRevision": "f15eaa1ee324ffbbab29605600d27a84cab86361",
|
||||||
|
"Target": "binaries",
|
||||||
|
"Platform": [
|
||||||
|
"linux/amd64"
|
||||||
|
],
|
||||||
|
"KeepGitDir": true,
|
||||||
|
"StartedAt": "2025-02-07T12:01:05.75807272+01:00",
|
||||||
|
"CompletedAt": "2025-02-07T12:02:07.991778875+01:00",
|
||||||
|
"Duration": 62233706155,
|
||||||
|
"Status": "completed",
|
||||||
|
"NumCompletedSteps": 16,
|
||||||
|
"NumTotalSteps": 16,
|
||||||
|
"NumCachedSteps": 4,
|
||||||
|
"Config": {
|
||||||
|
"ImageResolveMode": "local"
|
||||||
|
},
|
||||||
|
"Materials": [
|
||||||
|
{
|
||||||
|
"URI": "pkg:docker/docker/dockerfile@1",
|
||||||
|
"Digests": [
|
||||||
|
"sha256:93bfd3b68c109427185cd78b4779fc82b484b0b7618e36d0f104d4d801e66d25"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"URI": "pkg:docker/golang@1.23-alpine3.21?platform=linux%2Famd64",
|
||||||
|
"Digests": [
|
||||||
|
"sha256:2c49857f2295e89b23b28386e57e018a86620a8fede5003900f2d138ba9c4037"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"URI": "pkg:docker/tonistiigi/xx@1.6.1?platform=linux%2Famd64",
|
||||||
|
"Digests": [
|
||||||
|
"sha256:923441d7c25f1e2eb5789f82d987693c47b8ed987c4ab3b075d6ed2b5d6779a3"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Attachments": [
|
||||||
|
{
|
||||||
|
"Digest": "sha256:450fdd2e6b868fecd69e9891c2c404ba461aa38a47663b4805edeb8d2baf80b1",
|
||||||
|
"Type": "https://slsa.dev/provenance/v0.2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ docker buildx history inspect --format "{{.Name}}: {{.VCSRepository}} ({{.VCSRevision}})"
|
||||||
|
buildx (binaries): https://github.com/crazy-max/buildx.git (f15eaa1ee324ffbbab29605600d27a84cab86361)
|
||||||
|
```
|
||||||
|
17
docs/reference/buildx_history_trace.md
Normal file
17
docs/reference/buildx_history_trace.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# docker buildx history trace
|
||||||
|
|
||||||
|
<!---MARKER_GEN_START-->
|
||||||
|
Show the OpenTelemetry trace of a build record
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
| Name | Type | Default | Description |
|
||||||
|
|:----------------|:---------|:--------------|:-----------------------------------------|
|
||||||
|
| `--addr` | `string` | `127.0.0.1:0` | Address to bind the UI server |
|
||||||
|
| `--builder` | `string` | | Override the configured builder instance |
|
||||||
|
| `--compare` | `string` | | Compare with another build reference |
|
||||||
|
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||||
|
|
||||||
|
|
||||||
|
<!---MARKER_GEN_END-->
|
||||||
|
|
@@ -23,10 +23,10 @@ import (
|
|||||||
"github.com/docker/docker/api/types/mount"
|
"github.com/docker/docker/api/types/mount"
|
||||||
"github.com/docker/docker/api/types/network"
|
"github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/docker/api/types/system"
|
"github.com/docker/docker/api/types/system"
|
||||||
dockerclient "github.com/docker/docker/client"
|
|
||||||
"github.com/docker/docker/errdefs"
|
"github.com/docker/docker/errdefs"
|
||||||
dockerarchive "github.com/docker/docker/pkg/archive"
|
dockerarchive "github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/idtools"
|
"github.com/docker/docker/pkg/idtools"
|
||||||
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
"github.com/docker/docker/pkg/stdcopy"
|
"github.com/docker/docker/pkg/stdcopy"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -56,6 +56,7 @@ type Driver struct {
|
|||||||
restartPolicy container.RestartPolicy
|
restartPolicy container.RestartPolicy
|
||||||
env []string
|
env []string
|
||||||
defaultLoad bool
|
defaultLoad bool
|
||||||
|
gpus []container.DeviceRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Driver) IsMobyDriver() bool {
|
func (d *Driver) IsMobyDriver() bool {
|
||||||
@@ -70,7 +71,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
|||||||
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
|
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
|
||||||
_, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
_, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if dockerclient.IsErrNotFound(err) {
|
if errdefs.IsNotFound(err) {
|
||||||
return d.create(ctx, sub)
|
return d.create(ctx, sub)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -95,19 +96,20 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rc, err := d.DockerAPI.ImageCreate(ctx, imageName, image.CreateOptions{
|
resp, err := d.DockerAPI.ImageCreate(ctx, imageName, image.CreateOptions{
|
||||||
RegistryAuth: ra,
|
RegistryAuth: ra,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = io.Copy(io.Discard, rc)
|
defer resp.Close()
|
||||||
return err
|
return jsonmessage.DisplayJSONMessagesStream(resp, io.Discard, 0, false, nil)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
// image pulling failed, check if it exists in local image store.
|
// image pulling failed, check if it exists in local image store.
|
||||||
// if not, return pulling error. otherwise log it.
|
// if not, return pulling error. otherwise log it.
|
||||||
_, _, errInspect := d.DockerAPI.ImageInspectWithRaw(ctx, imageName)
|
_, errInspect := d.DockerAPI.ImageInspect(ctx, imageName)
|
||||||
if errInspect != nil {
|
found := errInspect == nil
|
||||||
|
if !found {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
l.Wrap("pulling failed, using local image "+imageName, func() error { return nil })
|
l.Wrap("pulling failed, using local image "+imageName, func() error { return nil })
|
||||||
@@ -157,6 +159,9 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
|
|||||||
if d.cpusetMems != "" {
|
if d.cpusetMems != "" {
|
||||||
hc.Resources.CpusetMems = d.cpusetMems
|
hc.Resources.CpusetMems = d.cpusetMems
|
||||||
}
|
}
|
||||||
|
if len(d.gpus) > 0 && d.hasGPUCapability(ctx, cfg.Image, d.gpus) {
|
||||||
|
hc.Resources.DeviceRequests = d.gpus
|
||||||
|
}
|
||||||
if info, err := d.DockerAPI.Info(ctx); err == nil {
|
if info, err := d.DockerAPI.Info(ctx); err == nil {
|
||||||
if info.CgroupDriver == "cgroupfs" {
|
if info.CgroupDriver == "cgroupfs" {
|
||||||
// Place all buildkit containers inside this cgroup by default so limits can be attached
|
// Place all buildkit containers inside this cgroup by default so limits can be attached
|
||||||
@@ -306,7 +311,7 @@ func (d *Driver) start(ctx context.Context) error {
|
|||||||
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
|
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
|
||||||
ctn, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
ctn, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if dockerclient.IsErrNotFound(err) {
|
if errdefs.IsNotFound(err) {
|
||||||
return &driver.Info{
|
return &driver.Info{
|
||||||
Status: driver.Inactive,
|
Status: driver.Inactive,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -419,6 +424,7 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
|
|||||||
driver.DockerExporter: true,
|
driver.DockerExporter: true,
|
||||||
driver.CacheExport: true,
|
driver.CacheExport: true,
|
||||||
driver.MultiPlatform: true,
|
driver.MultiPlatform: true,
|
||||||
|
driver.DirectPush: true,
|
||||||
driver.DefaultLoad: d.defaultLoad,
|
driver.DefaultLoad: d.defaultLoad,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,6 +433,31 @@ func (d *Driver) HostGatewayIP(ctx context.Context) (net.IP, error) {
|
|||||||
return nil, errors.New("host-gateway is not supported by the docker-container driver")
|
return nil, errors.New("host-gateway is not supported by the docker-container driver")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasGPUCapability checks if docker daemon has GPU capability. We need to run
|
||||||
|
// a dummy container with GPU device to check if the daemon has this capability
|
||||||
|
// because there is no API to check it yet.
|
||||||
|
func (d *Driver) hasGPUCapability(ctx context.Context, image string, gpus []container.DeviceRequest) bool {
|
||||||
|
cfg := &container.Config{
|
||||||
|
Image: image,
|
||||||
|
Entrypoint: []string{"/bin/true"},
|
||||||
|
}
|
||||||
|
hc := &container.HostConfig{
|
||||||
|
NetworkMode: container.NetworkMode(container.IPCModeNone),
|
||||||
|
AutoRemove: true,
|
||||||
|
Resources: container.Resources{
|
||||||
|
DeviceRequests: gpus,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, nil, "")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := d.DockerAPI.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func demuxConn(c net.Conn) net.Conn {
|
func demuxConn(c net.Conn) net.Conn {
|
||||||
pr, pw := io.Pipe()
|
pr, pw := io.Pipe()
|
||||||
// TODO: rewrite parser with Reader() to avoid goroutine switch
|
// TODO: rewrite parser with Reader() to avoid goroutine switch
|
||||||
|
@@ -51,6 +51,12 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
|
|||||||
InitConfig: cfg,
|
InitConfig: cfg,
|
||||||
restartPolicy: rp,
|
restartPolicy: rp,
|
||||||
}
|
}
|
||||||
|
var gpus dockeropts.GpuOpts
|
||||||
|
if err := gpus.Set("all"); err == nil {
|
||||||
|
if v := gpus.Value(); len(v) > 0 {
|
||||||
|
d.gpus = v
|
||||||
|
}
|
||||||
|
}
|
||||||
for k, v := range cfg.DriverOpts {
|
for k, v := range cfg.DriverOpts {
|
||||||
switch {
|
switch {
|
||||||
case k == "network":
|
case k == "network":
|
||||||
|
@@ -93,6 +93,7 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
|
|||||||
driver.DockerExporter: useContainerdSnapshotter,
|
driver.DockerExporter: useContainerdSnapshotter,
|
||||||
driver.CacheExport: useContainerdSnapshotter,
|
driver.CacheExport: useContainerdSnapshotter,
|
||||||
driver.MultiPlatform: useContainerdSnapshotter,
|
driver.MultiPlatform: useContainerdSnapshotter,
|
||||||
|
driver.DirectPush: useContainerdSnapshotter,
|
||||||
driver.DefaultLoad: true,
|
driver.DefaultLoad: true,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -7,5 +7,6 @@ const DockerExporter Feature = "Docker exporter"
|
|||||||
|
|
||||||
const CacheExport Feature = "Cache export"
|
const CacheExport Feature = "Cache export"
|
||||||
const MultiPlatform Feature = "Multi-platform build"
|
const MultiPlatform Feature = "Multi-platform build"
|
||||||
|
const DirectPush Feature = "Direct push"
|
||||||
|
|
||||||
const DefaultLoad Feature = "Automatically load images to the Docker Engine image store"
|
const DefaultLoad Feature = "Automatically load images to the Docker Engine image store"
|
||||||
|
@@ -35,10 +35,10 @@ func testEndpoint(server, defaultNamespace string, ca, cert, key []byte, skipTLS
|
|||||||
}
|
}
|
||||||
|
|
||||||
var testStoreCfg = store.NewConfig(
|
var testStoreCfg = store.NewConfig(
|
||||||
func() interface{} {
|
func() any {
|
||||||
return &map[string]interface{}{}
|
return &map[string]any{}
|
||||||
},
|
},
|
||||||
store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }),
|
store.EndpointTypeGetter(KubernetesEndpoint, func() any { return &EndpointMeta{} }),
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSaveLoadContexts(t *testing.T) {
|
func TestSaveLoadContexts(t *testing.T) {
|
||||||
@@ -197,7 +197,7 @@ func checkClientConfig(t *testing.T, ep Endpoint, server, namespace string, ca,
|
|||||||
|
|
||||||
func save(s store.Writer, ep Endpoint, name string) error {
|
func save(s store.Writer, ep Endpoint, name string) error {
|
||||||
meta := store.Metadata{
|
meta := store.Metadata{
|
||||||
Endpoints: map[string]interface{}{
|
Endpoints: map[string]any{
|
||||||
KubernetesEndpoint: ep.EndpointMeta,
|
KubernetesEndpoint: ep.EndpointMeta,
|
||||||
},
|
},
|
||||||
Name: name,
|
Name: name,
|
||||||
|
@@ -43,7 +43,7 @@ type Endpoint struct {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
command.RegisterDefaultStoreEndpoints(
|
command.RegisterDefaultStoreEndpoints(
|
||||||
store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }),
|
store.EndpointTypeGetter(KubernetesEndpoint, func() any { return &EndpointMeta{} }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ func (c *Endpoint) KubernetesConfig() clientcmd.ClientConfig {
|
|||||||
|
|
||||||
// ResolveDefault returns endpoint metadata for the default Kubernetes
|
// ResolveDefault returns endpoint metadata for the default Kubernetes
|
||||||
// endpoint, which is derived from the env-based kubeconfig.
|
// endpoint, which is derived from the env-based kubeconfig.
|
||||||
func (c *EndpointMeta) ResolveDefault() (interface{}, *store.EndpointTLSData, error) {
|
func (c *EndpointMeta) ResolveDefault() (any, *store.EndpointTLSData, error) {
|
||||||
kubeconfig := os.Getenv("KUBECONFIG")
|
kubeconfig := os.Getenv("KUBECONFIG")
|
||||||
if kubeconfig == "" {
|
if kubeconfig == "" {
|
||||||
kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
|
kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
|
||||||
|
@@ -238,6 +238,7 @@ func (d *Driver) Features(_ context.Context) map[driver.Feature]bool {
|
|||||||
driver.DockerExporter: d.DockerAPI != nil,
|
driver.DockerExporter: d.DockerAPI != nil,
|
||||||
driver.CacheExport: true,
|
driver.CacheExport: true,
|
||||||
driver.MultiPlatform: true, // Untested (needs multiple Driver instances)
|
driver.MultiPlatform: true, // Untested (needs multiple Driver instances)
|
||||||
|
driver.DirectPush: true,
|
||||||
driver.DefaultLoad: d.defaultLoad,
|
driver.DefaultLoad: d.defaultLoad,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,7 @@ func ListRunningPods(ctx context.Context, client clientcorev1.PodInterface, depl
|
|||||||
for i := range podList.Items {
|
for i := range podList.Items {
|
||||||
pod := &podList.Items[i]
|
pod := &podList.Items[i]
|
||||||
if pod.Status.Phase == corev1.PodRunning {
|
if pod.Status.Phase == corev1.PodRunning {
|
||||||
logrus.Debugf("pod runnning: %q", pod.Name)
|
logrus.Debugf("pod running: %q", pod.Name)
|
||||||
runningPods = append(runningPods, pod)
|
runningPods = append(runningPods, pod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ func GenerateNodeName(builderName string, txn *store.Txn) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var name string
|
var name string
|
||||||
for i := 0; i < 6; i++ {
|
for range 6 {
|
||||||
name, err = randomName()
|
name, err = randomName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@@ -164,6 +164,7 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
|
|||||||
driver.DockerExporter: true,
|
driver.DockerExporter: true,
|
||||||
driver.CacheExport: true,
|
driver.CacheExport: true,
|
||||||
driver.MultiPlatform: true,
|
driver.MultiPlatform: true,
|
||||||
|
driver.DirectPush: true,
|
||||||
driver.DefaultLoad: d.defaultLoad,
|
driver.DefaultLoad: d.defaultLoad,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
go.mod
28
go.mod
@@ -6,9 +6,9 @@ require (
|
|||||||
github.com/Masterminds/semver/v3 v3.2.1
|
github.com/Masterminds/semver/v3 v3.2.1
|
||||||
github.com/Microsoft/go-winio v0.6.2
|
github.com/Microsoft/go-winio v0.6.2
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
||||||
github.com/compose-spec/compose-go/v2 v2.4.7
|
github.com/compose-spec/compose-go/v2 v2.4.8
|
||||||
github.com/containerd/console v1.0.4
|
github.com/containerd/console v1.0.4
|
||||||
github.com/containerd/containerd/v2 v2.0.2
|
github.com/containerd/containerd/v2 v2.0.3
|
||||||
github.com/containerd/continuity v0.4.5
|
github.com/containerd/continuity v0.4.5
|
||||||
github.com/containerd/errdefs v1.0.0
|
github.com/containerd/errdefs v1.0.0
|
||||||
github.com/containerd/log v0.1.0
|
github.com/containerd/log v0.1.0
|
||||||
@@ -17,9 +17,9 @@ require (
|
|||||||
github.com/creack/pty v1.1.24
|
github.com/creack/pty v1.1.24
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/distribution/reference v0.6.0
|
github.com/distribution/reference v0.6.0
|
||||||
github.com/docker/cli v27.5.0+incompatible
|
github.com/docker/cli v28.0.1+incompatible
|
||||||
github.com/docker/cli-docs-tool v0.9.0
|
github.com/docker/cli-docs-tool v0.9.0
|
||||||
github.com/docker/docker v27.5.0+incompatible
|
github.com/docker/docker v28.0.1+incompatible
|
||||||
github.com/docker/go-units v0.5.0
|
github.com/docker/go-units v0.5.0
|
||||||
github.com/gofrs/flock v0.12.1
|
github.com/gofrs/flock v0.12.1
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
@@ -29,7 +29,7 @@ require (
|
|||||||
github.com/hashicorp/hcl/v2 v2.23.0
|
github.com/hashicorp/hcl/v2 v2.23.0
|
||||||
github.com/in-toto/in-toto-golang v0.5.0
|
github.com/in-toto/in-toto-golang v0.5.0
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/moby/buildkit v0.19.0-rc3
|
github.com/moby/buildkit v0.20.1
|
||||||
github.com/moby/sys/mountinfo v0.7.2
|
github.com/moby/sys/mountinfo v0.7.2
|
||||||
github.com/moby/sys/signal v0.7.1
|
github.com/moby/sys/signal v0.7.1
|
||||||
github.com/morikuni/aec v1.0.0
|
github.com/morikuni/aec v1.0.0
|
||||||
@@ -46,19 +46,20 @@ require (
|
|||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a
|
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a
|
||||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
|
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
|
||||||
|
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6
|
||||||
github.com/zclconf/go-cty v1.16.0
|
github.com/zclconf/go-cty v1.16.0
|
||||||
go.opentelemetry.io/otel v1.31.0
|
go.opentelemetry.io/otel v1.31.0
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
|
||||||
go.opentelemetry.io/otel/metric v1.31.0
|
go.opentelemetry.io/otel/metric v1.31.0
|
||||||
go.opentelemetry.io/otel/sdk v1.31.0
|
go.opentelemetry.io/otel/sdk v1.31.0
|
||||||
go.opentelemetry.io/otel/trace v1.31.0
|
go.opentelemetry.io/otel/trace v1.31.0
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0
|
golang.org/x/mod v0.22.0
|
||||||
golang.org/x/mod v0.21.0
|
|
||||||
golang.org/x/sync v0.10.0
|
golang.org/x/sync v0.10.0
|
||||||
golang.org/x/sys v0.28.0
|
golang.org/x/sys v0.29.0
|
||||||
golang.org/x/term v0.27.0
|
golang.org/x/term v0.27.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.21.0
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38
|
||||||
google.golang.org/grpc v1.68.1
|
google.golang.org/grpc v1.69.4
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||||
google.golang.org/protobuf v1.35.2
|
google.golang.org/protobuf v1.35.2
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@@ -69,7 +70,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
|
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
|
||||||
github.com/agext/levenshtein v1.2.3 // indirect
|
github.com/agext/levenshtein v1.2.3 // indirect
|
||||||
github.com/apparentlymart/go-cidr v1.0.1 // indirect
|
github.com/apparentlymart/go-cidr v1.0.1 // indirect
|
||||||
@@ -136,7 +137,7 @@ require (
|
|||||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||||
github.com/moby/sys/user v0.3.0 // indirect
|
github.com/moby/sys/user v0.3.0 // indirect
|
||||||
github.com/moby/sys/userns v0.1.0 // indirect
|
github.com/moby/sys/userns v0.1.0 // indirect
|
||||||
github.com/moby/term v0.5.0 // indirect
|
github.com/moby/term v0.5.2 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
@@ -169,11 +170,12 @@ require (
|
|||||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
|
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.31.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.33.0 // indirect
|
||||||
golang.org/x/oauth2 v0.23.0 // indirect
|
golang.org/x/oauth2 v0.23.0 // indirect
|
||||||
golang.org/x/time v0.6.0 // indirect
|
golang.org/x/time v0.6.0 // indirect
|
||||||
golang.org/x/tools v0.25.0 // indirect
|
golang.org/x/tools v0.27.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
k8s.io/klog/v2 v2.130.1 // indirect
|
||||||
|
70
go.sum
70
go.sum
@@ -2,8 +2,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af
|
|||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
|
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M=
|
||||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
@@ -77,16 +77,16 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
|
|||||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||||
github.com/compose-spec/compose-go/v2 v2.4.7 h1:WNpz5bIbKG+G+w9pfu72B1ZXr+Og9jez8TMEo8ecXPk=
|
github.com/compose-spec/compose-go/v2 v2.4.8 h1:7Myl8wDRl/4mRz77S+eyDJymGGEHu0diQdGSSeyq90A=
|
||||||
github.com/compose-spec/compose-go/v2 v2.4.7/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
github.com/compose-spec/compose-go/v2 v2.4.8/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||||
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
|
||||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
|
||||||
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
|
||||||
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||||
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
|
||||||
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
|
||||||
github.com/containerd/containerd/v2 v2.0.2 h1:GmH/tRBlTvrXOLwSpWE2vNAm8+MqI6nmxKpKBNKY8Wc=
|
github.com/containerd/containerd/v2 v2.0.3 h1:zBKgwgZsuu+LPCMzCLgA4sC4MiZzZ59ZT31XkmiISQM=
|
||||||
github.com/containerd/containerd/v2 v2.0.2/go.mod h1:wIqEvQ/6cyPFUGJ5yMFanspPabMLor+bF865OHvNTTI=
|
github.com/containerd/containerd/v2 v2.0.3/go.mod h1:5j9QUUaV/cy9ZeAx4S+8n9ffpf+iYnEj4jiExgcbuLY=
|
||||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||||
@@ -122,15 +122,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM=
|
github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs=
|
||||||
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
|
github.com/docker/cli-docs-tool v0.9.0 h1:CVwQbE+ZziwlPqrJ7LRyUF6GvCA+6gj7MTCsayaK9t0=
|
||||||
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
|
github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3Tr1ipoypjAUtc=
|
||||||
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.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
|
github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||||
github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v28.0.1+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=
|
||||||
@@ -152,8 +152,6 @@ github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a
|
|||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
|
||||||
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
|
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
|
||||||
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||||
@@ -297,8 +295,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/z
|
|||||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/moby/buildkit v0.19.0-rc3 h1:9PJv9iECP3fqy/4v8m5WvdBTYg3diDp/ImsfPBy2xG0=
|
github.com/moby/buildkit v0.20.1 h1:sT0ZXhhNo5rVbMcYfgttma3TdUHfO5JjFA0UAL8p9fY=
|
||||||
github.com/moby/buildkit v0.19.0-rc3/go.mod h1:WiHBFTgWV8eB1AmPxIWsAlKjUACAwm3X/14xOV4VWew=
|
github.com/moby/buildkit v0.20.1/go.mod h1:Rq9nB/fJImdk6QeM0niKtOHJqwKeYMrK847hTTDVuA4=
|
||||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||||
@@ -317,8 +315,8 @@ github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
|||||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@@ -351,8 +349,6 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ
|
|||||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0=
|
|
||||||
github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI=
|
|
||||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||||
@@ -437,8 +433,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
|
||||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
|
||||||
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c=
|
||||||
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw=
|
||||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
|
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 h1:eUk79E1w8yMtXeHSzjKorxuC8qJOnyXQnLaJehxpJaI=
|
||||||
@@ -447,6 +441,8 @@ github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXW
|
|||||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw=
|
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw=
|
||||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
|
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 h1:7I5c2Ig/5FgqkYOh/N87NzoyI9U15qUPXhDD8uCupv8=
|
||||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
||||||
|
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6 h1:RT/a0RvdX84iwtOrUK45+wjcNpaG+hS7n7XFYqj4axg=
|
||||||
|
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250211190051-7d4944a45bb6/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54=
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0=
|
||||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk=
|
||||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
||||||
@@ -490,6 +486,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy
|
|||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64=
|
||||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||||
@@ -512,12 +510,12 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
@@ -549,8 +547,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
@@ -565,20 +563,20 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
|
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||||
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
|
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA=
|
||||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||||
@@ -628,7 +626,3 @@ sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+s
|
|||||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||||
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
|
|
||||||
tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y=
|
|
||||||
tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA=
|
|
||||||
tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws=
|
|
||||||
|
@@ -9,10 +9,13 @@ Vagrant.configure("2") do |config|
|
|||||||
|
|
||||||
config.vm.provision "init", type: "shell", run: "once" do |sh|
|
config.vm.provision "init", type: "shell", run: "once" do |sh|
|
||||||
sh.inline = <<~SHELL
|
sh.inline = <<~SHELL
|
||||||
|
set -x
|
||||||
pkg bootstrap
|
pkg bootstrap
|
||||||
pkg install -y go123 git
|
pkg install -y git
|
||||||
ln -s /usr/local/bin/go123 /usr/local/bin/go
|
|
||||||
go install gotest.tools/gotestsum@#{ENV['GOTESTSUM_VERSION']}
|
fetch https://go.dev/dl/go#{ENV['GO_VERSION']}.freebsd-amd64.tar.gz
|
||||||
|
tar -C /usr/local -xzf go#{ENV['GO_VERSION']}.freebsd-amd64.tar.gz
|
||||||
|
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||||
SHELL
|
SHELL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
32
hack/Vagrantfile.netbsd
Normal file
32
hack/Vagrantfile.netbsd
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- mode: ruby -*-
|
||||||
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.vm.box = "generic/netbsd9"
|
||||||
|
config.vm.boot_timeout = 900
|
||||||
|
config.vm.synced_folder ".", "/vagrant", type: "rsync"
|
||||||
|
config.ssh.keep_alive = true
|
||||||
|
|
||||||
|
config.vm.provision "init", type: "shell", run: "once" do |sh|
|
||||||
|
sh.inline = <<~SHELL
|
||||||
|
set -x
|
||||||
|
mkdir -p /var/tmp
|
||||||
|
chmod 1777 /var/tmp
|
||||||
|
|
||||||
|
pkgin -y install git mozilla-rootcerts
|
||||||
|
mozilla-rootcerts install
|
||||||
|
|
||||||
|
ftp https://go.dev/dl/go#{ENV['GO_VERSION']}.netbsd-amd64.tar.gz
|
||||||
|
tar -C /var/tmp -xzf go#{ENV['GO_VERSION']}.netbsd-amd64.tar.gz
|
||||||
|
|
||||||
|
cat << 'EOF' > /usr/bin/go-wrapper
|
||||||
|
#!/bin/sh
|
||||||
|
export TMPDIR="/var/tmp"
|
||||||
|
exec /var/tmp/go/bin/go "$@"
|
||||||
|
EOF
|
||||||
|
chmod +x /usr/bin/go-wrapper
|
||||||
|
|
||||||
|
ln -s /usr/bin/go-wrapper /usr/bin/go
|
||||||
|
SHELL
|
||||||
|
end
|
||||||
|
end
|
@@ -10,12 +10,12 @@ Vagrant.configure("2") do |config|
|
|||||||
|
|
||||||
config.vm.provision "init", type: "shell", run: "once" do |sh|
|
config.vm.provision "init", type: "shell", run: "once" do |sh|
|
||||||
sh.inline = <<~SHELL
|
sh.inline = <<~SHELL
|
||||||
|
set -x
|
||||||
pkg_add -x git
|
pkg_add -x git
|
||||||
|
|
||||||
ftp https://go.dev/dl/go1.23.3.openbsd-amd64.tar.gz
|
ftp https://go.dev/dl/go#{ENV['GO_VERSION']}.openbsd-amd64.tar.gz
|
||||||
tar -C /usr/local -xzf go1.23.3.openbsd-amd64.tar.gz
|
tar -C /usr/local -xzf go#{ENV['GO_VERSION']}.openbsd-amd64.tar.gz
|
||||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||||
go install gotest.tools/gotestsum@#{ENV['GOTESTSUM_VERSION']}
|
|
||||||
SHELL
|
SHELL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@@ -5,9 +5,10 @@ ARG ALPINE_VERSION=3.21
|
|||||||
ARG XX_VERSION=1.6.1
|
ARG XX_VERSION=1.6.1
|
||||||
|
|
||||||
ARG GOLANGCI_LINT_VERSION=1.62.0
|
ARG GOLANGCI_LINT_VERSION=1.62.0
|
||||||
ARG GOPLS_VERSION=v0.26.0
|
# v0.31 requires go1.24
|
||||||
|
ARG GOPLS_VERSION=v0.30.0
|
||||||
# disabled: deprecated unusedvariable simplifyrange
|
# disabled: deprecated unusedvariable simplifyrange
|
||||||
ARG GOPLS_ANALYZERS="embeddirective fillreturns infertypeargs nonewvars norangeoverfunc noresultvalues simplifycompositelit simplifyslice undeclaredname unusedparams useany"
|
ARG GOPLS_ANALYZERS="embeddirective fillreturns hostport infertypeargs modernize nonewvars noresultvalues simplifycompositelit simplifyslice unusedparams yield"
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||||
|
|
||||||
|
12
hack/test
12
hack/test
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
set -eu -o pipefail
|
set -eu -o pipefail
|
||||||
|
|
||||||
|
: "${GITHUB_ACTIONS=}"
|
||||||
|
|
||||||
: "${BUILDX_CMD=docker buildx}"
|
: "${BUILDX_CMD=docker buildx}"
|
||||||
|
|
||||||
: "${TEST_COVERAGE=}"
|
: "${TEST_COVERAGE=}"
|
||||||
@@ -37,7 +39,15 @@ if [ "$TEST_COVERAGE" = "1" ]; then
|
|||||||
export GO_TEST_COVERPROFILE="/testreports/coverage-report$TEST_REPORT_SUFFIX.txt"
|
export GO_TEST_COVERPROFILE="/testreports/coverage-report$TEST_REPORT_SUFFIX.txt"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cid=$(docker create --rm --privileged \
|
dockerConfigMount=""
|
||||||
|
if [ "$GITHUB_ACTIONS" = "true" ]; then
|
||||||
|
dockerConfigPath="$HOME/.docker/config.json"
|
||||||
|
if [ -f "$dockerConfigPath" ]; then
|
||||||
|
dockerConfigMount="-v $dockerConfigPath:/root/.docker/config.json:ro"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cid=$(docker create --rm --privileged $dockerConfigMount \
|
||||||
-v /tmp $testReportsVol \
|
-v /tmp $testReportsVol \
|
||||||
--volumes-from=$cacheVolume \
|
--volumes-from=$cacheVolume \
|
||||||
-e GITHUB_REF \
|
-e GITHUB_REF \
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/buildx/util/confutil"
|
"github.com/docker/buildx/util/confutil"
|
||||||
@@ -14,6 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
version = 2
|
||||||
refsDir = "refs"
|
refsDir = "refs"
|
||||||
groupDir = "__group__"
|
groupDir = "__group__"
|
||||||
)
|
)
|
||||||
@@ -31,12 +33,8 @@ type State struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type StateGroup struct {
|
type StateGroup struct {
|
||||||
// Definition is the raw representation of the group (bake definition)
|
|
||||||
Definition []byte
|
|
||||||
// Targets are the targets invoked
|
// Targets are the targets invoked
|
||||||
Targets []string `json:",omitempty"`
|
Targets []string `json:",omitempty"`
|
||||||
// Inputs are the user inputs (bake overrides)
|
|
||||||
Inputs []string `json:",omitempty"`
|
|
||||||
// Refs are used to track all the refs that belong to the same group
|
// Refs are used to track all the refs that belong to the same group
|
||||||
Refs []string
|
Refs []string
|
||||||
}
|
}
|
||||||
@@ -52,9 +50,7 @@ func New(cfg *confutil.Config) (*LocalState, error) {
|
|||||||
if err := cfg.MkdirAll(refsDir, 0700); err != nil {
|
if err := cfg.MkdirAll(refsDir, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &LocalState{
|
return &LocalState{cfg: cfg}, nil
|
||||||
cfg: cfg,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LocalState) ReadRef(builderName, nodeName, id string) (*State, error) {
|
func (ls *LocalState) ReadRef(builderName, nodeName, id string) (*State, error) {
|
||||||
@@ -87,8 +83,12 @@ func (ls *LocalState) SaveRef(builderName, nodeName, id string, st State) error
|
|||||||
return ls.cfg.AtomicWriteFile(filepath.Join(refDir, id), dt, 0644)
|
return ls.cfg.AtomicWriteFile(filepath.Join(refDir, id), dt, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ls *LocalState) GroupDir() string {
|
||||||
|
return filepath.Join(ls.cfg.Dir(), refsDir, groupDir)
|
||||||
|
}
|
||||||
|
|
||||||
func (ls *LocalState) ReadGroup(id string) (*StateGroup, error) {
|
func (ls *LocalState) ReadGroup(id string) (*StateGroup, error) {
|
||||||
dt, err := os.ReadFile(filepath.Join(ls.cfg.Dir(), refsDir, groupDir, id))
|
dt, err := os.ReadFile(filepath.Join(ls.GroupDir(), id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ func (ls *LocalState) removeGroup(id string) error {
|
|||||||
if id == "" {
|
if id == "" {
|
||||||
return errors.Errorf("group ref empty")
|
return errors.Errorf("group ref empty")
|
||||||
}
|
}
|
||||||
f := filepath.Join(ls.cfg.Dir(), refsDir, groupDir, id)
|
f := filepath.Join(ls.GroupDir(), id)
|
||||||
if _, err := os.Lstat(f); err != nil {
|
if _, err := os.Lstat(f); err != nil {
|
||||||
if !os.IsNotExist(err) {
|
if !os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
@@ -230,3 +230,16 @@ func (ls *LocalState) validate(builderName, nodeName, id string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ls *LocalState) readVersion() int {
|
||||||
|
if vdt, err := os.ReadFile(filepath.Join(ls.cfg.Dir(), refsDir, "version")); err == nil {
|
||||||
|
if v, err := strconv.Atoi(string(vdt)); err == nil {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LocalState) writeVersion(version int) error {
|
||||||
|
return ls.cfg.AtomicWriteFile(filepath.Join(refsDir, "version"), []byte(strconv.Itoa(version)), 0600)
|
||||||
|
}
|
||||||
|
@@ -68,10 +68,8 @@ var (
|
|||||||
|
|
||||||
testStateGroupID = "kvqs0sgly2rmitz84r25u9qd0"
|
testStateGroupID = "kvqs0sgly2rmitz84r25u9qd0"
|
||||||
testStateGroup = StateGroup{
|
testStateGroup = StateGroup{
|
||||||
Definition: []byte(`{"group":{"default":{"targets":["pre-checkin"]},"pre-checkin":{"targets":["vendor-update","format","build"]}},"target":{"build":{"context":".","dockerfile":"dev.Dockerfile","target":"build-update","platforms":["linux/amd64"],"output":["."]},"format":{"context":".","dockerfile":"dev.Dockerfile","target":"format-update","platforms":["linux/amd64"],"output":["."]},"vendor-update":{"context":".","dockerfile":"dev.Dockerfile","target":"vendor-update","platforms":["linux/amd64"],"output":["."]}}}`),
|
Targets: []string{"pre-checkin"},
|
||||||
Targets: []string{"pre-checkin"},
|
Refs: []string{"builder/builder0/hx2qf1w11qvz1x3k471c5i8xw", "builder/builder0/968zj0g03jmlx0s8qslnvh6rl", "builder/builder0/naf44f9i1710lf7y12lv5hb1z"},
|
||||||
Inputs: []string{"*.platform=linux/amd64"},
|
|
||||||
Refs: []string{"builder/builder0/hx2qf1w11qvz1x3k471c5i8xw", "builder/builder0/968zj0g03jmlx0s8qslnvh6rl", "builder/builder0/naf44f9i1710lf7y12lv5hb1z"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
testStateGroupRef1ID = "hx2qf1w11qvz1x3k471c5i8xw"
|
testStateGroupRef1ID = "hx2qf1w11qvz1x3k471c5i8xw"
|
||||||
|
56
localstate/migrate.go
Normal file
56
localstate/migrate.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package localstate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ls *LocalState) MigrateIfNeeded() error {
|
||||||
|
currentVersion := ls.readVersion()
|
||||||
|
if currentVersion == version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
migrations := map[int]func(*LocalState) error{
|
||||||
|
2: (*LocalState).migration2,
|
||||||
|
}
|
||||||
|
for v := currentVersion + 1; v <= version; v++ {
|
||||||
|
migration, found := migrations[v]
|
||||||
|
if !found {
|
||||||
|
return errors.Errorf("localstate migration v%d not found", v)
|
||||||
|
}
|
||||||
|
if err := migration(ls); err != nil {
|
||||||
|
return errors.Wrapf(err, "localstate migration v%d failed", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ls.writeVersion(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LocalState) migration2() error {
|
||||||
|
return filepath.Walk(ls.GroupDir(), func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
dt, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var stg StateGroup
|
||||||
|
if err := json.Unmarshal(dt, &stg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mdt, err := json.Marshal(stg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(path, mdt, 0600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"github.com/docker/buildx/monitor/types"
|
"github.com/docker/buildx/monitor/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -50,14 +51,7 @@ func (cm *AttachCmd) Exec(ctx context.Context, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("failed to get the list of sessions: %v", err)
|
return errors.Errorf("failed to get the list of sessions: %v", err)
|
||||||
}
|
}
|
||||||
found := false
|
if !slices.Contains(refs, ref) {
|
||||||
for _, s := range refs {
|
|
||||||
if s == ref {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return errors.Errorf("unknown ID: %q", ref)
|
return errors.Errorf("unknown ID: %q", ref)
|
||||||
}
|
}
|
||||||
cm.m.Detach() // Finish existing attach
|
cm.m.Detach() // Finish existing attach
|
||||||
|
@@ -66,7 +66,7 @@ func (cm *ReloadCmd) Exec(ctx context.Context, args []string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
var be *controllererrors.BuildError
|
var be *controllererrors.BuildError
|
||||||
if errors.As(err, &be) {
|
if errors.As(err, &be) {
|
||||||
ref = be.Ref
|
ref = be.SessionID
|
||||||
resultUpdated = true
|
resultUpdated = true
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("failed to reload: %v\n", err)
|
fmt.Printf("failed to reload: %v\n", err)
|
||||||
|
@@ -2,6 +2,7 @@ package store
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/platforms"
|
"github.com/containerd/platforms"
|
||||||
@@ -44,7 +45,7 @@ func (ng *NodeGroup) Leave(name string) error {
|
|||||||
if len(ng.Nodes) == 1 {
|
if len(ng.Nodes) == 1 {
|
||||||
return errors.Errorf("can not leave last node, do you want to rm instance instead?")
|
return errors.Errorf("can not leave last node, do you want to rm instance instead?")
|
||||||
}
|
}
|
||||||
ng.Nodes = append(ng.Nodes[:i], ng.Nodes[i+1:]...)
|
ng.Nodes = slices.Delete(ng.Nodes, i, i+1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,7 +39,7 @@ func ValidateName(s string) (string, error) {
|
|||||||
|
|
||||||
func GenerateName(txn *Txn) (string, error) {
|
func GenerateName(txn *Txn) (string, error) {
|
||||||
var name string
|
var name string
|
||||||
for i := 0; i < 6; i++ {
|
for i := range 6 {
|
||||||
name = namesgenerator.GetRandomName(i)
|
name = namesgenerator.GetRandomName(i)
|
||||||
if _, err := txn.NodeGroupByName(name); err != nil {
|
if _, err := txn.NodeGroupByName(name); err != nil {
|
||||||
if !os.IsNotExist(errors.Cause(err)) {
|
if !os.IsNotExist(errors.Cause(err)) {
|
||||||
|
@@ -38,6 +38,7 @@ func bakeCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
|
|||||||
var bakeTests = []func(t *testing.T, sb integration.Sandbox){
|
var bakeTests = []func(t *testing.T, sb integration.Sandbox){
|
||||||
testBakePrint,
|
testBakePrint,
|
||||||
testBakePrintSensitive,
|
testBakePrintSensitive,
|
||||||
|
testBakePrintOverrideEmpty,
|
||||||
testBakeLocal,
|
testBakeLocal,
|
||||||
testBakeLocalMulti,
|
testBakeLocalMulti,
|
||||||
testBakeRemote,
|
testBakeRemote,
|
||||||
@@ -286,6 +287,47 @@ RUN echo "Hello ${HELLO}"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testBakePrintOverrideEmpty(t *testing.T, sb integration.Sandbox) {
|
||||||
|
dockerfile := []byte(`
|
||||||
|
FROM scratch
|
||||||
|
COPY foo /foo
|
||||||
|
`)
|
||||||
|
bakefile := []byte(`
|
||||||
|
target "default" {
|
||||||
|
cache-to = ["type=gha,mode=min,scope=integration-tests"]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
dir := tmpdir(
|
||||||
|
t,
|
||||||
|
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
||||||
|
fstest.CreateFile("Dockerfile", dockerfile, 0600),
|
||||||
|
fstest.CreateFile("foo", []byte("foo"), 0600),
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--print", "--set", "*.cache-to="))
|
||||||
|
stdout := bytes.Buffer{}
|
||||||
|
stderr := bytes.Buffer{}
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
require.NoError(t, cmd.Run(), stdout.String(), stderr.String())
|
||||||
|
|
||||||
|
require.JSONEq(t, `{
|
||||||
|
"group": {
|
||||||
|
"default": {
|
||||||
|
"targets": [
|
||||||
|
"default"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"default": {
|
||||||
|
"context": ".",
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`, stdout.String())
|
||||||
|
}
|
||||||
|
|
||||||
func testBakeLocal(t *testing.T, sb integration.Sandbox) {
|
func testBakeLocal(t *testing.T, sb integration.Sandbox) {
|
||||||
dockerfile := []byte(`
|
dockerfile := []byte(`
|
||||||
FROM scratch
|
FROM scratch
|
||||||
@@ -871,6 +913,7 @@ target "default" {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBakeSetNonExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
|
func testBakeSetNonExistingOutsideNoParallel(t *testing.T, sb integration.Sandbox) {
|
||||||
for _, ent := range []bool{true, false} {
|
for _, ent := range []bool{true, false} {
|
||||||
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
|
t.Run(fmt.Sprintf("ent=%v", ent), func(t *testing.T) {
|
||||||
@@ -973,11 +1016,11 @@ FROM scratch
|
|||||||
COPY foo /foo
|
COPY foo /foo
|
||||||
`)
|
`)
|
||||||
destDir := t.TempDir()
|
destDir := t.TempDir()
|
||||||
bakefile := []byte(fmt.Sprintf(`
|
bakefile := fmt.Appendf(nil, `
|
||||||
target "default" {
|
target "default" {
|
||||||
output = ["type=local,dest=%s/not/exists"]
|
output = ["type=local,dest=%s/not/exists"]
|
||||||
}
|
}
|
||||||
`, destDir))
|
`, destDir)
|
||||||
dir := tmpdir(
|
dir := tmpdir(
|
||||||
t,
|
t,
|
||||||
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
||||||
@@ -1007,11 +1050,11 @@ FROM scratch
|
|||||||
COPY foo /foo
|
COPY foo /foo
|
||||||
`)
|
`)
|
||||||
destDir := t.TempDir()
|
destDir := t.TempDir()
|
||||||
bakefile := []byte(fmt.Sprintf(`
|
bakefile := fmt.Appendf(nil, `
|
||||||
target "default" {
|
target "default" {
|
||||||
output = ["type=local,dest=%s"]
|
output = ["type=local,dest=%s"]
|
||||||
}
|
}
|
||||||
`, destDir))
|
`, destDir)
|
||||||
dir := tmpdir(
|
dir := tmpdir(
|
||||||
t,
|
t,
|
||||||
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
||||||
@@ -1108,11 +1151,11 @@ COPY Dockerfile /foo
|
|||||||
keyDir := t.TempDir()
|
keyDir := t.TempDir()
|
||||||
err := writeTempPrivateKey(filepath.Join(keyDir, "id_rsa"))
|
err := writeTempPrivateKey(filepath.Join(keyDir, "id_rsa"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
bakefile := []byte(fmt.Sprintf(`
|
bakefile := fmt.Appendf(nil, `
|
||||||
target "default" {
|
target "default" {
|
||||||
ssh = ["key=%s"]
|
ssh = ["key=%s"]
|
||||||
}
|
}
|
||||||
`, filepath.Join(keyDir, "id_rsa")))
|
`, filepath.Join(keyDir, "id_rsa"))
|
||||||
dir := tmpdir(
|
dir := tmpdir(
|
||||||
t,
|
t,
|
||||||
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
|
||||||
@@ -1271,8 +1314,8 @@ target "default" {
|
|||||||
|
|
||||||
type mdT struct {
|
type mdT struct {
|
||||||
Default struct {
|
Default struct {
|
||||||
BuildRef string `json:"buildx.build.ref"`
|
BuildRef string `json:"buildx.build.ref"`
|
||||||
BuildProvenance map[string]interface{} `json:"buildx.build.provenance"`
|
BuildProvenance map[string]any `json:"buildx.build.provenance"`
|
||||||
} `json:"default"`
|
} `json:"default"`
|
||||||
}
|
}
|
||||||
var md mdT
|
var md mdT
|
||||||
|
@@ -804,8 +804,8 @@ func buildMetadataProvenance(t *testing.T, sb integration.Sandbox, metadataMode
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
type mdT struct {
|
type mdT struct {
|
||||||
BuildRef string `json:"buildx.build.ref"`
|
BuildRef string `json:"buildx.build.ref"`
|
||||||
BuildProvenance map[string]interface{} `json:"buildx.build.provenance"`
|
BuildProvenance map[string]any `json:"buildx.build.provenance"`
|
||||||
}
|
}
|
||||||
var md mdT
|
var md mdT
|
||||||
err = json.Unmarshal(dt, &md)
|
err = json.Unmarshal(dt, &md)
|
||||||
|
@@ -50,7 +50,7 @@ func withDir(dir string) cmdOpt {
|
|||||||
|
|
||||||
func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
|
func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
|
||||||
cmd := exec.Command("buildx")
|
cmd := exec.Command("buildx")
|
||||||
cmd.Env = append([]string{}, os.Environ()...)
|
cmd.Env = os.Environ()
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(cmd)
|
opt(cmd)
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
|
|||||||
|
|
||||||
func dockerCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
|
func dockerCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
|
||||||
cmd := exec.Command("docker")
|
cmd := exec.Command("docker")
|
||||||
cmd.Env = append([]string{}, os.Environ()...)
|
cmd.Env = os.Environ()
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(cmd)
|
opt(cmd)
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ func skipNoCompatBuildKit(t *testing.T, sb integration.Sandbox, constraint strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ptrstr(s interface{}) *string {
|
func ptrstr(s any) *string {
|
||||||
var n *string
|
var n *string
|
||||||
if reflect.ValueOf(s).Kind() == reflect.String {
|
if reflect.ValueOf(s).Kind() == reflect.String {
|
||||||
ss := s.(string)
|
ss := s.(string)
|
||||||
|
@@ -45,7 +45,7 @@ func testRmMulti(t *testing.T, sb integration.Sandbox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var builderNames []string
|
var builderNames []string
|
||||||
for i := 0; i < 3; i++ {
|
for range 3 {
|
||||||
out, err := createCmd(sb, withArgs("--driver", "docker-container"))
|
out, err := createCmd(sb, withArgs("--driver", "docker-container"))
|
||||||
require.NoError(t, err, out)
|
require.NoError(t, err, out)
|
||||||
builderName := strings.TrimSpace(out)
|
builderName := strings.TrimSpace(out)
|
||||||
|
@@ -2,6 +2,7 @@ package workers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/moby/buildkit/util/testutil/integration"
|
"github.com/moby/buildkit/util/testutil/integration"
|
||||||
@@ -49,23 +50,14 @@ func (s *backend) ExtraEnv() []string {
|
|||||||
|
|
||||||
func (s backend) Supports(feature string) bool {
|
func (s backend) Supports(feature string) bool {
|
||||||
if enabledFeatures := os.Getenv("BUILDKIT_TEST_ENABLE_FEATURES"); enabledFeatures != "" {
|
if enabledFeatures := os.Getenv("BUILDKIT_TEST_ENABLE_FEATURES"); enabledFeatures != "" {
|
||||||
for _, enabledFeature := range strings.Split(enabledFeatures, ",") {
|
if slices.Contains(strings.Split(enabledFeatures, ","), feature) {
|
||||||
if feature == enabledFeature {
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if disabledFeatures := os.Getenv("BUILDKIT_TEST_DISABLE_FEATURES"); disabledFeatures != "" {
|
if disabledFeatures := os.Getenv("BUILDKIT_TEST_DISABLE_FEATURES"); disabledFeatures != "" {
|
||||||
for _, disabledFeature := range strings.Split(disabledFeatures, ",") {
|
if slices.Contains(strings.Split(disabledFeatures, ","), feature) {
|
||||||
if feature == disabledFeature {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, unsupportedFeature := range s.unsupportedFeatures {
|
|
||||||
if feature == unsupportedFeature {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return !slices.Contains(s.unsupportedFeatures, feature)
|
||||||
}
|
}
|
||||||
|
@@ -90,8 +90,8 @@ func (a *Attest) ToPB() *controllerapi.Attest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Attest) MarshalJSON() ([]byte, error) {
|
func (a *Attest) MarshalJSON() ([]byte, error) {
|
||||||
m := make(map[string]interface{}, len(a.Attrs)+2)
|
m := make(map[string]any, len(a.Attrs)+2)
|
||||||
for k, v := range m {
|
for k, v := range a.Attrs {
|
||||||
m[k] = v
|
m[k] = v
|
||||||
}
|
}
|
||||||
m["type"] = a.Type
|
m["type"] = a.Type
|
||||||
@@ -102,7 +102,7 @@ func (a *Attest) MarshalJSON() ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Attest) UnmarshalJSON(data []byte) error {
|
func (a *Attest) UnmarshalJSON(data []byte) error {
|
||||||
var m map[string]interface{}
|
var m map[string]any
|
||||||
if err := json.Unmarshal(data, &m); err != nil {
|
if err := json.Unmarshal(data, &m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -148,9 +148,8 @@ func (a *Attest) UnmarshalText(text []byte) error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return errors.Errorf("invalid value %s", field)
|
return errors.Errorf("invalid value %s", field)
|
||||||
}
|
}
|
||||||
key = strings.TrimSpace(strings.ToLower(key))
|
|
||||||
|
|
||||||
switch key {
|
switch strings.TrimSpace(strings.ToLower(key)) {
|
||||||
case "type":
|
case "type":
|
||||||
a.Type = value
|
a.Type = value
|
||||||
case "disabled":
|
case "disabled":
|
||||||
|
@@ -22,18 +22,19 @@ func (e *Attests) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Attests) fromCtyValue(in cty.Value, p cty.Path) error {
|
func (e *Attests) fromCtyValue(in cty.Value, p cty.Path) (retErr error) {
|
||||||
*e = make([]*Attest, 0, in.LengthInt())
|
*e = make([]*Attest, 0, in.LengthInt())
|
||||||
for elem := in.ElementIterator(); elem.Next(); {
|
|
||||||
_, value := elem.Element()
|
|
||||||
|
|
||||||
|
yield := func(value cty.Value) bool {
|
||||||
entry := &Attest{}
|
entry := &Attest{}
|
||||||
if err := entry.FromCtyValue(value, p); err != nil {
|
if retErr = entry.FromCtyValue(value, p); retErr != nil {
|
||||||
return err
|
return false
|
||||||
}
|
}
|
||||||
*e = append(*e, entry)
|
*e = append(*e, entry)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return nil
|
eachElement(in)(yield)
|
||||||
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Attests) ToCtyValue() cty.Value {
|
func (e Attests) ToCtyValue() cty.Value {
|
||||||
@@ -64,6 +65,10 @@ func (e *Attest) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
e.Attrs = map[string]string{}
|
e.Attrs = map[string]string{}
|
||||||
for it := conv.ElementIterator(); it.Next(); {
|
for it := conv.ElementIterator(); it.Next(); {
|
||||||
k, v := it.Element()
|
k, v := it.Element()
|
||||||
|
if !v.IsKnown() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch key := k.AsString(); key {
|
switch key := k.AsString(); key {
|
||||||
case "type":
|
case "type":
|
||||||
e.Type = v.AsString()
|
e.Type = v.AsString()
|
||||||
|
117
util/buildflags/attests_test.go
Normal file
117
util/buildflags/attests_test.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package buildflags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAttests(t *testing.T) {
|
||||||
|
t.Run("MarshalJSON", func(t *testing.T) {
|
||||||
|
attests := Attests{
|
||||||
|
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
|
||||||
|
{Type: "sbom", Disabled: true},
|
||||||
|
{Type: "sbom", Attrs: map[string]string{
|
||||||
|
"generator": "scanner",
|
||||||
|
"ENV1": `"foo,bar"`,
|
||||||
|
"Env2": "hello",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `[{"type":"provenance","mode":"max"},{"type":"sbom","disabled":true},{"ENV1":"\"foo,bar\"","Env2":"hello","generator":"scanner","type":"sbom"}]`
|
||||||
|
actual, err := json.Marshal(attests)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, expected, string(actual))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("UnmarshalJSON", func(t *testing.T) {
|
||||||
|
in := `[{"type":"provenance","mode":"max"},{"type":"sbom","disabled":true},{"ENV1":"\"foo,bar\"","Env2":"hello","generator":"scanner","type":"sbom"}]`
|
||||||
|
|
||||||
|
var actual Attests
|
||||||
|
err := json.Unmarshal([]byte(in), &actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := Attests{
|
||||||
|
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
|
||||||
|
{Type: "sbom", Disabled: true, Attrs: map[string]string{}},
|
||||||
|
{Type: "sbom", Disabled: false, Attrs: map[string]string{
|
||||||
|
"generator": "scanner",
|
||||||
|
"ENV1": `"foo,bar"`,
|
||||||
|
"Env2": "hello",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FromCtyValue", func(t *testing.T) {
|
||||||
|
in := cty.TupleVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("provenance"),
|
||||||
|
"mode": cty.StringVal("max"),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("sbom"),
|
||||||
|
"generator": cty.StringVal("scan"),
|
||||||
|
"ENV1": cty.StringVal(`foo,bar`),
|
||||||
|
"Env2": cty.StringVal(`hello`),
|
||||||
|
}),
|
||||||
|
cty.StringVal("type=sbom,disabled=true"),
|
||||||
|
cty.StringVal(`type=sbom,generator=scan,"FOO=bar,baz",Hello=World`),
|
||||||
|
})
|
||||||
|
|
||||||
|
var actual Attests
|
||||||
|
err := actual.FromCtyValue(in, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := Attests{
|
||||||
|
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
|
||||||
|
{Type: "sbom", Attrs: map[string]string{
|
||||||
|
"generator": "scan",
|
||||||
|
"ENV1": "foo,bar",
|
||||||
|
"Env2": "hello",
|
||||||
|
}},
|
||||||
|
{Type: "sbom", Disabled: true, Attrs: map[string]string{}},
|
||||||
|
{Type: "sbom", Attrs: map[string]string{
|
||||||
|
"generator": "scan",
|
||||||
|
"FOO": "bar,baz",
|
||||||
|
"Hello": "World",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ToCtyValue", func(t *testing.T) {
|
||||||
|
attests := Attests{
|
||||||
|
{Type: "provenance", Attrs: map[string]string{"mode": "max"}},
|
||||||
|
{Type: "sbom", Disabled: true},
|
||||||
|
{Type: "sbom", Attrs: map[string]string{
|
||||||
|
"generator": "scan",
|
||||||
|
"ENV1": `"foo,bar"`,
|
||||||
|
"Env2": "hello",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := attests.ToCtyValue()
|
||||||
|
expected := cty.ListVal([]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("provenance"),
|
||||||
|
"mode": cty.StringVal("max"),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("sbom"),
|
||||||
|
"disabled": cty.StringVal("true"),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("sbom"),
|
||||||
|
"generator": cty.StringVal("scan"),
|
||||||
|
"ENV1": cty.StringVal(`"foo,bar"`),
|
||||||
|
"Env2": cty.StringVal("hello"),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
result := actual.Equals(expected)
|
||||||
|
require.True(t, result.True())
|
||||||
|
})
|
||||||
|
}
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"maps"
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||||
@@ -149,7 +150,7 @@ func (e *CacheOptionsEntry) UnmarshalText(text []byte) error {
|
|||||||
return e.validate(text)
|
return e.validate(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *CacheOptionsEntry) validate(gv interface{}) error {
|
func (e *CacheOptionsEntry) validate(gv any) error {
|
||||||
if e.Type == "" {
|
if e.Type == "" {
|
||||||
var text []byte
|
var text []byte
|
||||||
switch gv := gv.(type) {
|
switch gv := gv.(type) {
|
||||||
@@ -167,34 +168,73 @@ func (e *CacheOptionsEntry) validate(gv interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseCacheEntry(in []string) ([]*controllerapi.CacheOptionsEntry, error) {
|
func ParseCacheEntry(in []string) (CacheOptions, error) {
|
||||||
if len(in) == 0 {
|
if len(in) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := make(CacheOptions, 0, len(in))
|
opts := make(CacheOptions, 0, len(in))
|
||||||
for _, in := range in {
|
for _, in := range in {
|
||||||
|
if in == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(in, "=") {
|
||||||
|
// This is ref only format. Each field in the CSV is its own entry.
|
||||||
|
fields, err := csvvalue.Fields(in, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
opt := CacheOptionsEntry{}
|
||||||
|
if err := opt.UnmarshalText([]byte(field)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
opts = append(opts, &opt)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
var out CacheOptionsEntry
|
var out CacheOptionsEntry
|
||||||
if err := out.UnmarshalText([]byte(in)); err != nil {
|
if err := out.UnmarshalText([]byte(in)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
opts = append(opts, &out)
|
opts = append(opts, &out)
|
||||||
}
|
}
|
||||||
return opts.ToPB(), nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addGithubToken(ci *controllerapi.CacheOptionsEntry) {
|
func addGithubToken(ci *controllerapi.CacheOptionsEntry) {
|
||||||
if ci.Type != "gha" {
|
if ci.Type != "gha" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
version, ok := ci.Attrs["version"]
|
||||||
|
if !ok {
|
||||||
|
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L19
|
||||||
|
if v, ok := os.LookupEnv("ACTIONS_CACHE_SERVICE_V2"); ok {
|
||||||
|
if b, err := strconv.ParseBool(v); err == nil && b {
|
||||||
|
version = "2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if _, ok := ci.Attrs["token"]; !ok {
|
if _, ok := ci.Attrs["token"]; !ok {
|
||||||
if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok {
|
if v, ok := os.LookupEnv("ACTIONS_RUNTIME_TOKEN"); ok {
|
||||||
ci.Attrs["token"] = v
|
ci.Attrs["token"] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if _, ok := ci.Attrs["url_v2"]; !ok && version == "2" {
|
||||||
|
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L34-L35
|
||||||
|
if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
|
||||||
|
ci.Attrs["url_v2"] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
if _, ok := ci.Attrs["url"]; !ok {
|
if _, ok := ci.Attrs["url"]; !ok {
|
||||||
|
// https://github.com/actions/toolkit/blob/2b08dc18f261b9fdd978b70279b85cbef81af8bc/packages/cache/src/internal/config.ts#L28-L33
|
||||||
if v, ok := os.LookupEnv("ACTIONS_CACHE_URL"); ok {
|
if v, ok := os.LookupEnv("ACTIONS_CACHE_URL"); ok {
|
||||||
ci.Attrs["url"] = v
|
ci.Attrs["url"] = v
|
||||||
|
} else if v, ok := os.LookupEnv("ACTIONS_RESULTS_URL"); ok {
|
||||||
|
ci.Attrs["url"] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,5 +274,5 @@ func isActive(pb *controllerapi.CacheOptionsEntry) bool {
|
|||||||
if pb.Type != "gha" {
|
if pb.Type != "gha" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return pb.Attrs["token"] != "" && pb.Attrs["url"] != ""
|
return pb.Attrs["token"] != "" && (pb.Attrs["url"] != "" || pb.Attrs["url_v2"] != "")
|
||||||
}
|
}
|
||||||
|
@@ -21,22 +21,30 @@ func (o *CacheOptions) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *CacheOptions) fromCtyValue(in cty.Value, p cty.Path) error {
|
func (o *CacheOptions) fromCtyValue(in cty.Value, p cty.Path) (retErr error) {
|
||||||
*o = make([]*CacheOptionsEntry, 0, in.LengthInt())
|
*o = make([]*CacheOptionsEntry, 0, in.LengthInt())
|
||||||
for elem := in.ElementIterator(); elem.Next(); {
|
|
||||||
_, value := elem.Element()
|
|
||||||
|
|
||||||
if isEmpty(value) {
|
yield := func(value cty.Value) bool {
|
||||||
continue
|
// Special handling for a string type to handle ref only format.
|
||||||
|
if value.Type() == cty.String {
|
||||||
|
var entries CacheOptions
|
||||||
|
entries, retErr = ParseCacheEntry([]string{value.AsString()})
|
||||||
|
if retErr != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
*o = append(*o, entries...)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := &CacheOptionsEntry{}
|
entry := &CacheOptionsEntry{}
|
||||||
if err := entry.FromCtyValue(value, p); err != nil {
|
if retErr = entry.FromCtyValue(value, p); retErr != nil {
|
||||||
return err
|
return false
|
||||||
}
|
}
|
||||||
*o = append(*o, entry)
|
*o = append(*o, entry)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return nil
|
eachElement(in)(yield)
|
||||||
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o CacheOptions) ToCtyValue() cty.Value {
|
func (o CacheOptions) ToCtyValue() cty.Value {
|
||||||
@@ -52,13 +60,6 @@ func (o CacheOptions) ToCtyValue() cty.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *CacheOptionsEntry) FromCtyValue(in cty.Value, p cty.Path) error {
|
func (o *CacheOptionsEntry) FromCtyValue(in cty.Value, p cty.Path) error {
|
||||||
if in.Type() == cty.String {
|
|
||||||
if err := o.UnmarshalText([]byte(in.AsString())); err != nil {
|
|
||||||
return p.NewError(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
conv, err := convert.Convert(in, cty.Map(cty.String))
|
conv, err := convert.Convert(in, cty.Map(cty.String))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
package buildflags
|
package buildflags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/buildx/controller/pb"
|
"github.com/docker/buildx/controller/pb"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCacheOptions_DerivedVars(t *testing.T) {
|
func TestCacheOptions_DerivedVars(t *testing.T) {
|
||||||
@@ -35,5 +37,84 @@ func TestCacheOptions_DerivedVars(t *testing.T) {
|
|||||||
"session_token": "not_a_mitm_attack",
|
"session_token": "not_a_mitm_attack",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, cacheFrom)
|
}, cacheFrom.ToPB())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheOptions(t *testing.T) {
|
||||||
|
t.Run("MarshalJSON", func(t *testing.T) {
|
||||||
|
cache := CacheOptions{
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "user/app:cache"}},
|
||||||
|
{Type: "local", Attrs: map[string]string{"src": "path/to/cache"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `[{"type":"registry","ref":"user/app:cache"},{"type":"local","src":"path/to/cache"}]`
|
||||||
|
actual, err := json.Marshal(cache)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, expected, string(actual))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("UnmarshalJSON", func(t *testing.T) {
|
||||||
|
in := `[{"type":"registry","ref":"user/app:cache"},{"type":"local","src":"path/to/cache"}]`
|
||||||
|
|
||||||
|
var actual CacheOptions
|
||||||
|
err := json.Unmarshal([]byte(in), &actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := CacheOptions{
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "user/app:cache"}},
|
||||||
|
{Type: "local", Attrs: map[string]string{"src": "path/to/cache"}},
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FromCtyValue", func(t *testing.T) {
|
||||||
|
in := cty.TupleVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("registry"),
|
||||||
|
"ref": cty.StringVal("user/app:cache"),
|
||||||
|
}),
|
||||||
|
cty.StringVal("type=local,src=path/to/cache"),
|
||||||
|
})
|
||||||
|
|
||||||
|
var actual CacheOptions
|
||||||
|
err := actual.FromCtyValue(in, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := CacheOptions{
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "user/app:cache"}},
|
||||||
|
{Type: "local", Attrs: map[string]string{"src": "path/to/cache"}},
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ToCtyValue", func(t *testing.T) {
|
||||||
|
attests := CacheOptions{
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "user/app:cache"}},
|
||||||
|
{Type: "local", Attrs: map[string]string{"src": "path/to/cache"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := attests.ToCtyValue()
|
||||||
|
expected := cty.ListVal([]cty.Value{
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("registry"),
|
||||||
|
"ref": cty.StringVal("user/app:cache"),
|
||||||
|
}),
|
||||||
|
cty.MapVal(map[string]cty.Value{
|
||||||
|
"type": cty.StringVal("local"),
|
||||||
|
"src": cty.StringVal("path/to/cache"),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
result := actual.Equals(expected)
|
||||||
|
require.True(t, result.True())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCacheOptions_RefOnlyFormat(t *testing.T) {
|
||||||
|
opts, err := ParseCacheEntry([]string{"ref1", "ref2"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, CacheOptions{
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "ref1"}},
|
||||||
|
{Type: "registry", Attrs: map[string]string{"ref": "ref2"}},
|
||||||
|
}, opts)
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,20 @@
|
|||||||
package buildflags
|
package buildflags
|
||||||
|
|
||||||
import "github.com/moby/buildkit/util/entitlements"
|
import (
|
||||||
|
"github.com/moby/buildkit/util/entitlements"
|
||||||
|
)
|
||||||
|
|
||||||
func ParseEntitlements(in []string) ([]entitlements.Entitlement, error) {
|
func ParseEntitlements(in []string) ([]string, error) {
|
||||||
out := make([]entitlements.Entitlement, 0, len(in))
|
out := make([]string, 0, len(in))
|
||||||
for _, v := range in {
|
for _, v := range in {
|
||||||
if v == "" {
|
if v == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
e, err := entitlements.Parse(v)
|
if _, _, err := entitlements.Parse(v); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
out = append(out, e)
|
out = append(out, v)
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package buildflags
|
package buildflags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/csv"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"maps"
|
"maps"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -259,9 +260,18 @@ func (w *csvBuilder) Write(key, value string) {
|
|||||||
if w.sb.Len() > 0 {
|
if w.sb.Len() > 0 {
|
||||||
w.sb.WriteByte(',')
|
w.sb.WriteByte(',')
|
||||||
}
|
}
|
||||||
w.sb.WriteString(key)
|
|
||||||
w.sb.WriteByte('=')
|
pair := key + "=" + value
|
||||||
w.sb.WriteString(value)
|
if strings.ContainsRune(pair, ',') || strings.ContainsRune(pair, '"') {
|
||||||
|
var attr strings.Builder
|
||||||
|
writer := csv.NewWriter(&attr)
|
||||||
|
writer.Write([]string{pair})
|
||||||
|
writer.Flush()
|
||||||
|
// Strips the extra newline added by the csv writer
|
||||||
|
pair = strings.TrimSpace(attr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
w.sb.WriteString(pair)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *csvBuilder) WriteAttributes(attrs map[string]string) {
|
func (w *csvBuilder) WriteAttributes(attrs map[string]string) {
|
||||||
|
@@ -21,22 +21,19 @@ func (e *Exports) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Exports) fromCtyValue(in cty.Value, p cty.Path) error {
|
func (e *Exports) fromCtyValue(in cty.Value, p cty.Path) (retErr error) {
|
||||||
*e = make([]*ExportEntry, 0, in.LengthInt())
|
*e = make([]*ExportEntry, 0, in.LengthInt())
|
||||||
for elem := in.ElementIterator(); elem.Next(); {
|
|
||||||
_, value := elem.Element()
|
|
||||||
|
|
||||||
if isEmpty(value) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
yield := func(value cty.Value) bool {
|
||||||
entry := &ExportEntry{}
|
entry := &ExportEntry{}
|
||||||
if err := entry.FromCtyValue(value, p); err != nil {
|
if retErr = entry.FromCtyValue(value, p); retErr != nil {
|
||||||
return err
|
return false
|
||||||
}
|
}
|
||||||
*e = append(*e, entry)
|
*e = append(*e, entry)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return nil
|
eachElement(in)(yield)
|
||||||
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e Exports) ToCtyValue() cty.Value {
|
func (e Exports) ToCtyValue() cty.Value {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package buildflags
|
package buildflags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
controllerapi "github.com/docker/buildx/controller/pb"
|
controllerapi "github.com/docker/buildx/controller/pb"
|
||||||
@@ -26,7 +27,7 @@ func (s Secrets) Normalize() Secrets {
|
|||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return removeDupes(s)
|
return removeSecretDupes(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Secrets) ToPB() []*controllerapi.Secret {
|
func (s Secrets) ToPB() []*controllerapi.Secret {
|
||||||
@@ -73,6 +74,22 @@ func (s *Secret) ToPB() *controllerapi.Secret {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Secret) UnmarshalJSON(data []byte) error {
|
||||||
|
var v struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
FilePath string `json:"src,omitempty"`
|
||||||
|
Env string `json:"env,omitempty"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ID = v.ID
|
||||||
|
s.FilePath = v.FilePath
|
||||||
|
s.Env = v.Env
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Secret) UnmarshalText(text []byte) error {
|
func (s *Secret) UnmarshalText(text []byte) error {
|
||||||
value := string(text)
|
value := string(text)
|
||||||
fields, err := csvvalue.Fields(value, nil)
|
fields, err := csvvalue.Fields(value, nil)
|
||||||
@@ -138,3 +155,17 @@ func parseSecret(value string) (*controllerapi.Secret, error) {
|
|||||||
}
|
}
|
||||||
return s.ToPB(), nil
|
return s.ToPB(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeSecretDupes(s []*Secret) []*Secret {
|
||||||
|
var res []*Secret
|
||||||
|
m := map[string]int{}
|
||||||
|
for _, sec := range s {
|
||||||
|
if i, ok := m[sec.ID]; ok {
|
||||||
|
res[i] = sec
|
||||||
|
} else {
|
||||||
|
m[sec.ID] = len(res)
|
||||||
|
res = append(res, sec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@@ -28,22 +28,19 @@ func (s *Secrets) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Secrets) fromCtyValue(in cty.Value, p cty.Path) error {
|
func (s *Secrets) fromCtyValue(in cty.Value, p cty.Path) (retErr error) {
|
||||||
*s = make([]*Secret, 0, in.LengthInt())
|
*s = make([]*Secret, 0, in.LengthInt())
|
||||||
for elem := in.ElementIterator(); elem.Next(); {
|
|
||||||
_, value := elem.Element()
|
|
||||||
|
|
||||||
if isEmpty(value) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
yield := func(value cty.Value) bool {
|
||||||
entry := &Secret{}
|
entry := &Secret{}
|
||||||
if err := entry.FromCtyValue(value, p); err != nil {
|
if retErr = entry.FromCtyValue(value, p); retErr != nil {
|
||||||
return err
|
return false
|
||||||
}
|
}
|
||||||
*s = append(*s, entry)
|
*s = append(*s, entry)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return nil
|
eachElement(in)(yield)
|
||||||
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Secrets) ToCtyValue() cty.Value {
|
func (s Secrets) ToCtyValue() cty.Value {
|
||||||
@@ -71,13 +68,13 @@ func (e *Secret) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if id := conv.GetAttr("id"); !id.IsNull() {
|
if id := conv.GetAttr("id"); !id.IsNull() && id.IsKnown() {
|
||||||
e.ID = id.AsString()
|
e.ID = id.AsString()
|
||||||
}
|
}
|
||||||
if src := conv.GetAttr("src"); !src.IsNull() {
|
if src := conv.GetAttr("src"); !src.IsNull() && src.IsKnown() {
|
||||||
e.FilePath = src.AsString()
|
e.FilePath = src.AsString()
|
||||||
}
|
}
|
||||||
if env := conv.GetAttr("env"); !env.IsNull() {
|
if env := conv.GetAttr("env"); !env.IsNull() && env.IsKnown() {
|
||||||
e.Env = env.AsString()
|
e.Env = env.AsString()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
97
util/buildflags/secrets_test.go
Normal file
97
util/buildflags/secrets_test.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package buildflags
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSecrets(t *testing.T) {
|
||||||
|
t.Run("MarshalJSON", func(t *testing.T) {
|
||||||
|
secrets := Secrets{
|
||||||
|
{ID: "mysecret", FilePath: "/local/secret"},
|
||||||
|
{ID: "mysecret2", Env: "TOKEN"},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `[{"id":"mysecret","src":"/local/secret"},{"id":"mysecret2","env":"TOKEN"}]`
|
||||||
|
actual, err := json.Marshal(secrets)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, expected, string(actual))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("UnmarshalJSON", func(t *testing.T) {
|
||||||
|
in := `[{"id":"mysecret","src":"/local/secret"},{"id":"mysecret2","env":"TOKEN"}]`
|
||||||
|
|
||||||
|
var actual Secrets
|
||||||
|
err := json.Unmarshal([]byte(in), &actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := Secrets{
|
||||||
|
{ID: "mysecret", FilePath: "/local/secret"},
|
||||||
|
{ID: "mysecret2", Env: "TOKEN"},
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("FromCtyValue", func(t *testing.T) {
|
||||||
|
in := cty.TupleVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("mysecret"),
|
||||||
|
"src": cty.StringVal("/local/secret"),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("mysecret2"),
|
||||||
|
"env": cty.StringVal("TOKEN"),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
var actual Secrets
|
||||||
|
err := actual.FromCtyValue(in, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := Secrets{
|
||||||
|
{ID: "mysecret", FilePath: "/local/secret"},
|
||||||
|
{ID: "mysecret2", Env: "TOKEN"},
|
||||||
|
}
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ToCtyValue", func(t *testing.T) {
|
||||||
|
secrets := Secrets{
|
||||||
|
{ID: "mysecret", FilePath: "/local/secret"},
|
||||||
|
{ID: "mysecret2", Env: "TOKEN"},
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := secrets.ToCtyValue()
|
||||||
|
expected := cty.ListVal([]cty.Value{
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("mysecret"),
|
||||||
|
"src": cty.StringVal("/local/secret"),
|
||||||
|
"env": cty.StringVal(""),
|
||||||
|
}),
|
||||||
|
cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("mysecret2"),
|
||||||
|
"src": cty.StringVal(""),
|
||||||
|
"env": cty.StringVal("TOKEN"),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
result := actual.Equals(expected)
|
||||||
|
require.True(t, result.True())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("RemoveDupes", func(t *testing.T) {
|
||||||
|
secrets := Secrets{
|
||||||
|
{ID: "mysecret", Env: "FOO"},
|
||||||
|
{ID: "mysecret", Env: "BAR"},
|
||||||
|
{ID: "mysecret2", Env: "BAZ"},
|
||||||
|
}.Normalize()
|
||||||
|
|
||||||
|
expected := `[{"id":"mysecret","env":"BAR"},{"id":"mysecret2","env":"BAZ"}]`
|
||||||
|
actual, err := json.Marshal(secrets)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, expected, string(actual))
|
||||||
|
})
|
||||||
|
}
|
@@ -2,6 +2,7 @@ package buildflags
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"encoding/json"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ func (s SSHKeys) Normalize() SSHKeys {
|
|||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return removeDupes(s)
|
return removeSSHDupes(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SSHKeys) ToPB() []*controllerapi.SSH {
|
func (s SSHKeys) ToPB() []*controllerapi.SSH {
|
||||||
@@ -76,6 +77,20 @@ func (s *SSH) ToPB() *controllerapi.SSH {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SSH) UnmarshalJSON(data []byte) error {
|
||||||
|
var v struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Paths []string `json:"paths,omitempty"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(data, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.ID = v.ID
|
||||||
|
s.Paths = v.Paths
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SSH) UnmarshalText(text []byte) error {
|
func (s *SSH) UnmarshalText(text []byte) error {
|
||||||
parts := strings.SplitN(string(text), "=", 2)
|
parts := strings.SplitN(string(text), "=", 2)
|
||||||
|
|
||||||
@@ -116,3 +131,17 @@ func IsGitSSH(repo string) bool {
|
|||||||
}
|
}
|
||||||
return url.Scheme == gitutil.SSHProtocol
|
return url.Scheme == gitutil.SSHProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func removeSSHDupes(s []*SSH) []*SSH {
|
||||||
|
var res []*SSH
|
||||||
|
m := map[string]int{}
|
||||||
|
for _, ssh := range s {
|
||||||
|
if i, ok := m[ssh.ID]; ok {
|
||||||
|
res[i] = ssh
|
||||||
|
} else {
|
||||||
|
m[ssh.ID] = len(res)
|
||||||
|
res = append(res, ssh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@@ -28,22 +28,19 @@ func (s *SSHKeys) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
return p.NewErrorf("%s", convert.MismatchMessage(got, want))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SSHKeys) fromCtyValue(in cty.Value, p cty.Path) error {
|
func (s *SSHKeys) fromCtyValue(in cty.Value, p cty.Path) (retErr error) {
|
||||||
*s = make([]*SSH, 0, in.LengthInt())
|
*s = make([]*SSH, 0, in.LengthInt())
|
||||||
for elem := in.ElementIterator(); elem.Next(); {
|
|
||||||
_, value := elem.Element()
|
|
||||||
|
|
||||||
if isEmpty(value) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
yield := func(value cty.Value) bool {
|
||||||
entry := &SSH{}
|
entry := &SSH{}
|
||||||
if err := entry.FromCtyValue(value, p); err != nil {
|
if retErr = entry.FromCtyValue(value, p); retErr != nil {
|
||||||
return err
|
return false
|
||||||
}
|
}
|
||||||
*s = append(*s, entry)
|
*s = append(*s, entry)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
return nil
|
eachElement(in)(yield)
|
||||||
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s SSHKeys) ToCtyValue() cty.Value {
|
func (s SSHKeys) ToCtyValue() cty.Value {
|
||||||
@@ -71,10 +68,10 @@ func (e *SSH) FromCtyValue(in cty.Value, p cty.Path) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if id := conv.GetAttr("id"); !id.IsNull() {
|
if id := conv.GetAttr("id"); !id.IsNull() && id.IsKnown() {
|
||||||
e.ID = id.AsString()
|
e.ID = id.AsString()
|
||||||
}
|
}
|
||||||
if paths := conv.GetAttr("paths"); !paths.IsNull() {
|
if paths := conv.GetAttr("paths"); !paths.IsNull() && paths.IsKnown() {
|
||||||
if err := gocty.FromCtyValue(paths, &e.Paths); err != nil {
|
if err := gocty.FromCtyValue(paths, &e.Paths); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user