mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-08-15 08:15:55 +08:00
Compare commits
202 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
245093b99a | ||
![]() |
e2ed15f0c9 | ||
![]() |
fd442f8e10 | ||
![]() |
1002e6fb42 | ||
![]() |
d5ad869033 | ||
![]() |
bd7090b981 | ||
![]() |
24f3a1df80 | ||
![]() |
8e30c4669c | ||
![]() |
cf74356afc | ||
![]() |
c1d3955fbe | ||
![]() |
d0b63e60e2 | ||
![]() |
e141c8fa71 | ||
![]() |
2ee156236b | ||
![]() |
1335264c9d | ||
![]() |
e74185aa6d | ||
![]() |
0224773102 | ||
![]() |
8c27b5c545 | ||
![]() |
f7594d484b | ||
![]() |
f118749cdc | ||
![]() |
0d92ad713c | ||
![]() |
a18ff4d5ef | ||
![]() |
b035a04aaa | ||
![]() |
6220e0aae8 | ||
![]() |
d9abc78e8f | ||
![]() |
3313026961 | ||
![]() |
06912aa24c | ||
![]() |
cde0e9814d | ||
![]() |
2e6e146087 | ||
![]() |
af3cbe6cec | ||
![]() |
1ef9e67cbb | ||
![]() |
75204426bd | ||
![]() |
b73f58a90b | ||
![]() |
6f5486e718 | ||
![]() |
3fa0c3d122 | ||
![]() |
b0b902de41 | ||
![]() |
77d632e0c5 | ||
![]() |
6a12543db3 | ||
![]() |
4027b60fa0 | ||
![]() |
dda8df3b06 | ||
![]() |
d54a110b3c | ||
![]() |
44fa243d58 | ||
![]() |
630066bfc5 | ||
![]() |
026ac2313c | ||
![]() |
45fc5ed3b3 | ||
![]() |
1eb167a767 | ||
![]() |
45d2ec69f1 | ||
![]() |
793ec7f3b2 | ||
![]() |
6cb62dddf2 | ||
![]() |
66ecb53fa7 | ||
![]() |
26026810fe | ||
![]() |
d3830e0a6e | ||
![]() |
8c2759f6ae | ||
![]() |
8a472c6c9d | ||
![]() |
b98653d8fe | ||
![]() |
807d15ff9d | ||
![]() |
ac636fd2d8 | ||
![]() |
769d22fb30 | ||
![]() |
e36535e137 | ||
![]() |
ada44e82ea | ||
![]() |
16edf5d4aa | ||
![]() |
11c85b2369 | ||
![]() |
41215835cf | ||
![]() |
a41fc81796 | ||
![]() |
5f057bdee7 | ||
![]() |
883806524a | ||
![]() |
38b71998f5 | ||
![]() |
07db2be2f0 | ||
![]() |
f3f5e760b3 | ||
![]() |
e762d3dbca | ||
![]() |
4ecbb018f2 | ||
![]() |
a8f4699c5e | ||
![]() |
7cf12fce98 | ||
![]() |
07190d20da | ||
![]() |
c79368c199 | ||
![]() |
f47d12e692 | ||
![]() |
0fc204915a | ||
![]() |
3a0eeeacd5 | ||
![]() |
e6ce3917d3 | ||
![]() |
e085ed8c5c | ||
![]() |
b83c3e239e | ||
![]() |
a90d5794ee | ||
![]() |
c571b9d730 | ||
![]() |
af53930206 | ||
![]() |
c4a2db8f0c | ||
![]() |
206bd6c3a2 | ||
![]() |
5c169dd878 | ||
![]() |
875e717361 | ||
![]() |
72c3d4a237 | ||
![]() |
ce46297960 | ||
![]() |
e8389c8a02 | ||
![]() |
804ee66f13 | ||
![]() |
5c5bc510ac | ||
![]() |
0dfc4a1019 | ||
![]() |
1e992b295c | ||
![]() |
4f81bcb5c8 | ||
![]() |
3771fe2034 | ||
![]() |
5dd4ae0335 | ||
![]() |
567361d494 | ||
![]() |
21b1be1667 | ||
![]() |
876e003685 | ||
![]() |
a53ed0a354 | ||
![]() |
737da6959d | ||
![]() |
6befa70cc8 | ||
![]() |
2d051bde96 | ||
![]() |
63985b591b | ||
![]() |
695200c81a | ||
![]() |
828c1dbf98 | ||
![]() |
f321d4ac95 | ||
![]() |
0d13bf6606 | ||
![]() |
3e3242cfdd | ||
![]() |
f9e2d07b30 | ||
![]() |
c281e18892 | ||
![]() |
98d4cb1eb3 | ||
![]() |
70f2fb6442 | ||
![]() |
fdac6d5fe7 | ||
![]() |
d4eca07af8 | ||
![]() |
95e77da0fa | ||
![]() |
6810a7c69c | ||
![]() |
dd596d6542 | ||
![]() |
c6e403ad7f | ||
![]() |
d6d713aac6 | ||
![]() |
f148976e6e | ||
![]() |
8f70196de1 | ||
![]() |
e196855bed | ||
![]() |
71c7889719 | ||
![]() |
a3418e0178 | ||
![]() |
6a1cf78879 | ||
![]() |
ec1f712328 | ||
![]() |
5ce6597c07 | ||
![]() |
9c75071793 | ||
![]() |
d612139b19 | ||
![]() |
42f7898c53 | ||
![]() |
3148c098a2 | ||
![]() |
f95d574f94 | ||
![]() |
60822781be | ||
![]() |
4c83475703 | ||
![]() |
17eff25fe5 | ||
![]() |
9c8ffb77d6 | ||
![]() |
13a426fca6 | ||
![]() |
1a039115bc | ||
![]() |
07d58782b8 | ||
![]() |
3ccbb88e6a | ||
![]() |
a34c641bc4 | ||
![]() |
f10be074b4 | ||
![]() |
9f429965c0 | ||
![]() |
f3929447d7 | ||
![]() |
615f4f6759 | ||
![]() |
9a7b028bab | ||
![]() |
1af4f05ba4 | ||
![]() |
4b5d78db9b | ||
![]() |
d2c512a95b | ||
![]() |
5937ba0e00 | ||
![]() |
21fb026aa3 | ||
![]() |
bc45641086 | ||
![]() |
96689e5d05 | ||
![]() |
50a8f11f0f | ||
![]() |
11cf38bd97 | ||
![]() |
300d56b3ff | ||
![]() |
e04da86aca | ||
![]() |
9f1fc99018 | ||
![]() |
26bbddb5d6 | ||
![]() |
58fd190c31 | ||
![]() |
e7a53fb829 | ||
![]() |
c0fd64f4f8 | ||
![]() |
0c629335ac | ||
![]() |
f216b71ad2 | ||
![]() |
debe8c0187 | ||
![]() |
a69d857b8a | ||
![]() |
a6ef9db84d | ||
![]() |
9c27be752c | ||
![]() |
82a65d4f9b | ||
![]() |
8647f408ac | ||
![]() |
e51cdcac50 | ||
![]() |
55a544d976 | ||
![]() |
3b943bd4ba | ||
![]() |
502bb51a3b | ||
![]() |
48977780ad | ||
![]() |
e540bb03a4 | ||
![]() |
919c52395d | ||
![]() |
7f01c63be7 | ||
![]() |
076d2f19d5 | ||
![]() |
3c3150b8d3 | ||
![]() |
b03d8c52e1 | ||
![]() |
e67ccb080b | ||
![]() |
dab02c347e | ||
![]() |
6caa151e98 | ||
![]() |
be6d8326a8 | ||
![]() |
7855f8324b | ||
![]() |
850e5330ad | ||
![]() |
b7ea25eb59 | ||
![]() |
8cdeac54ab | ||
![]() |
752c70a06c | ||
![]() |
83dd969dc1 | ||
![]() |
a5bb117ff0 | ||
![]() |
735b7f68fe | ||
![]() |
bcac44f658 | ||
![]() |
d46595eed8 | ||
![]() |
62407927fa | ||
![]() |
c7b0a84c6a | ||
![]() |
1aac809c63 | ||
![]() |
ca502cc9a5 | ||
![]() |
8056a3dc7c |
85
.github/CONTRIBUTING.md
vendored
85
.github/CONTRIBUTING.md
vendored
@@ -188,6 +188,89 @@ To generate new vendored files with go modules run:
|
||||
$ make vendor
|
||||
```
|
||||
|
||||
### Generate profiling data
|
||||
|
||||
You can configure Buildx to generate [`pprof`](https://github.com/google/pprof)
|
||||
memory and CPU profiles to analyze and optimize your builds. These profiles are
|
||||
useful for identifying performance bottlenecks, detecting memory
|
||||
inefficiencies, and ensuring the program (Buildx) runs efficiently.
|
||||
|
||||
The following environment variables control whether Buildx generates profiling
|
||||
data for builds:
|
||||
|
||||
```console
|
||||
$ export BUILDX_CPU_PROFILE=buildx_cpu.prof
|
||||
$ export BUILDX_MEM_PROFILE=buildx_mem.prof
|
||||
```
|
||||
|
||||
When set, Buildx emits profiling samples for the builds to the location
|
||||
specified by the environment variable.
|
||||
|
||||
To analyze and visualize profiling samples, you need `pprof` from the Go
|
||||
toolchain, and (optionally) GraphViz for visualization in a graphical format.
|
||||
|
||||
To inspect profiling data with `pprof`:
|
||||
|
||||
1. Build a local binary of Buildx from source.
|
||||
|
||||
```console
|
||||
$ docker buildx bake
|
||||
```
|
||||
|
||||
The binary gets exported to `./bin/build/buildx`.
|
||||
|
||||
2. Run a build and with the environment variables set to generate profiling data.
|
||||
|
||||
```console
|
||||
$ export BUILDX_CPU_PROFILE=buildx_cpu.prof
|
||||
$ export BUILDX_MEM_PROFILE=buildx_mem.prof
|
||||
$ ./bin/build/buildx bake
|
||||
```
|
||||
|
||||
This creates `buildx_cpu.prof` and `buildx_mem.prof` for the build.
|
||||
|
||||
3. Start `pprof` and specify the filename of the profile that you want to
|
||||
analyze.
|
||||
|
||||
```console
|
||||
$ go tool pprof buildx_cpu.prof
|
||||
```
|
||||
|
||||
This opens the `pprof` interactive console. From here, you can inspect the
|
||||
profiling sample using various commands. For example, use `top 10` command
|
||||
to view the top 10 most time-consuming entries.
|
||||
|
||||
```plaintext
|
||||
(pprof) top 10
|
||||
Showing nodes accounting for 3.04s, 91.02% of 3.34s total
|
||||
Dropped 123 nodes (cum <= 0.02s)
|
||||
Showing top 10 nodes out of 159
|
||||
flat flat% sum% cum cum%
|
||||
1.14s 34.13% 34.13% 1.14s 34.13% syscall.syscall
|
||||
0.91s 27.25% 61.38% 0.91s 27.25% runtime.kevent
|
||||
0.35s 10.48% 71.86% 0.35s 10.48% runtime.pthread_cond_wait
|
||||
0.22s 6.59% 78.44% 0.22s 6.59% runtime.pthread_cond_signal
|
||||
0.15s 4.49% 82.93% 0.15s 4.49% runtime.usleep
|
||||
0.10s 2.99% 85.93% 0.10s 2.99% runtime.memclrNoHeapPointers
|
||||
0.10s 2.99% 88.92% 0.10s 2.99% runtime.memmove
|
||||
0.03s 0.9% 89.82% 0.03s 0.9% runtime.madvise
|
||||
0.02s 0.6% 90.42% 0.02s 0.6% runtime.(*mspan).typePointersOfUnchecked
|
||||
0.02s 0.6% 91.02% 0.02s 0.6% runtime.pcvalue
|
||||
```
|
||||
|
||||
To view the call graph in a GUI, run `go tool pprof -http=:8081 <sample>`.
|
||||
|
||||
> [!NOTE]
|
||||
> Requires [GraphViz](https://www.graphviz.org/) to be installed.
|
||||
|
||||
```console
|
||||
$ go tool pprof -http=:8081 buildx_cpu.prof
|
||||
Serving web UI on http://127.0.0.1:8081
|
||||
http://127.0.0.1:8081
|
||||
```
|
||||
|
||||
For more information about using `pprof` and how to interpret the call graph,
|
||||
refer to the [`pprof` README](https://github.com/google/pprof/blob/main/doc/README.md).
|
||||
|
||||
### Conventions
|
||||
|
||||
@@ -343,4 +426,4 @@ The rules:
|
||||
|
||||
If you are having trouble getting into the mood of idiomatic Go, we recommend
|
||||
reading through [Effective Go](https://golang.org/doc/effective_go.html). The
|
||||
[Go Blog](https://blog.golang.org) is also a great resource.
|
||||
[Go Blog](https://blog.golang.org) is also a great resource.
|
||||
|
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@@ -96,6 +96,11 @@ area/hack:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'hack/**'
|
||||
|
||||
# Add 'area/history' label to changes in history command
|
||||
area/history:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: 'commands/history/**'
|
||||
|
||||
# Add 'area/tests' label to changes in test files
|
||||
area/tests:
|
||||
- changed-files:
|
||||
|
123
.github/workflows/build.yml
vendored
123
.github/workflows/build.yml
vendored
@@ -28,15 +28,15 @@ on:
|
||||
- 'docs/**'
|
||||
|
||||
env:
|
||||
BUILDX_VERSION: "latest"
|
||||
BUILDKIT_IMAGE: "moby/buildkit:latest"
|
||||
SETUP_BUILDX_VERSION: "edge"
|
||||
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
|
||||
SCOUT_VERSION: "1.11.0"
|
||||
REPO_SLUG: "docker/buildx-bin"
|
||||
DESTDIR: "./bin"
|
||||
TEST_CACHE_SCOPE: "test"
|
||||
TESTFLAGS: "-v --parallel=6 --timeout=30m"
|
||||
GOTESTSUM_FORMAT: "standard-verbose"
|
||||
GO_VERSION: "1.22"
|
||||
GO_VERSION: "1.23"
|
||||
GOTESTSUM_VERSION: "v1.9.0" # same as one in Dockerfile
|
||||
|
||||
jobs:
|
||||
@@ -54,9 +54,9 @@ jobs:
|
||||
- master
|
||||
- latest
|
||||
- buildx-stable-1
|
||||
- v0.15.2
|
||||
- v0.14.1
|
||||
- v0.13.2
|
||||
- v0.19.0-rc2
|
||||
- v0.18.2
|
||||
- v0.17.2
|
||||
worker:
|
||||
- docker-container
|
||||
- remote
|
||||
@@ -76,6 +76,16 @@ jobs:
|
||||
- worker: docker+containerd # same as docker, but with containerd snapshotter
|
||||
pkg: ./tests
|
||||
mode: experimental
|
||||
- worker: "docker@26.1"
|
||||
pkg: ./tests
|
||||
- worker: "docker+containerd@26.1" # same as docker, but with containerd snapshotter
|
||||
pkg: ./tests
|
||||
- worker: "docker@26.1"
|
||||
pkg: ./tests
|
||||
mode: experimental
|
||||
- worker: "docker+containerd@26.1" # same as docker, but with containerd snapshotter
|
||||
pkg: ./tests
|
||||
mode: experimental
|
||||
steps:
|
||||
-
|
||||
name: Prepare
|
||||
@@ -86,7 +96,7 @@ jobs:
|
||||
fi
|
||||
testFlags="--run=//worker=$(echo "${{ matrix.worker }}" | sed 's/\+/\\+/g')$"
|
||||
case "${{ matrix.worker }}" in
|
||||
docker | docker+containerd)
|
||||
docker | docker+containerd | docker@* | docker+containerd@*)
|
||||
echo "TESTFLAGS=${{ env.TESTFLAGS_DOCKER }} $testFlags" >> $GITHUB_ENV
|
||||
;;
|
||||
*)
|
||||
@@ -111,13 +121,14 @@ jobs:
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: ${{ env.BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.BUILDKIT_IMAGE }}
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Build test image
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: .
|
||||
targets: integration-test
|
||||
set: |
|
||||
*.output=type=docker,name=${{ env.TEST_IMAGE_ID }}
|
||||
@@ -131,7 +142,7 @@ jobs:
|
||||
-
|
||||
name: Send to Codecov
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
directory: ./bin/testreports
|
||||
flags: integration
|
||||
@@ -158,7 +169,7 @@ jobs:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-24.04
|
||||
- macos-12
|
||||
- macos-14
|
||||
- windows-2022
|
||||
env:
|
||||
SKIP_INTEGRATION_TESTS: 1
|
||||
@@ -203,7 +214,7 @@ jobs:
|
||||
-
|
||||
name: Send to Codecov
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
directory: ${{ env.TESTREPORTS_DIR }}
|
||||
env_vars: RUNNER_OS
|
||||
@@ -224,6 +235,63 @@ jobs:
|
||||
name: test-reports-${{ env.TESTREPORTS_NAME }}
|
||||
path: ${{ env.TESTREPORTS_BASEDIR }}
|
||||
|
||||
test-bsd-unit:
|
||||
runs-on: ubuntu-22.04
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- freebsd
|
||||
- openbsd
|
||||
steps:
|
||||
-
|
||||
name: Prepare
|
||||
run: |
|
||||
echo "VAGRANT_FILE=hack/Vagrantfile.${{ matrix.os }}" >> $GITHUB_ENV
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Cache Vagrant boxes
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.vagrant.d/boxes
|
||||
key: ${{ runner.os }}-vagrant-${{ matrix.os }}-${{ hashFiles(env.VAGRANT_FILE) }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vagrant-${{ matrix.os }}-
|
||||
-
|
||||
name: Install vagrant
|
||||
run: |
|
||||
set -x
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libvirt-daemon libvirt-daemon-system vagrant vagrant-libvirt ruby-libvirt
|
||||
sudo systemctl enable --now libvirtd
|
||||
sudo chmod a+rw /var/run/libvirt/libvirt-sock
|
||||
vagrant plugin install vagrant-libvirt
|
||||
vagrant --version
|
||||
-
|
||||
name: Set up vagrant
|
||||
run: |
|
||||
ln -sf ${{ env.VAGRANT_FILE }} Vagrantfile
|
||||
vagrant up --no-tty
|
||||
-
|
||||
name: Test
|
||||
run: |
|
||||
vagrant ssh -- "cd /vagrant; SKIP_INTEGRATION_TESTS=1 go test -mod=vendor -coverprofile=coverage.txt -covermode=atomic ${{ env.TESTFLAGS }} ./..."
|
||||
vagrant ssh -c "sudo cat /vagrant/coverage.txt" > coverage.txt
|
||||
-
|
||||
name: Upload coverage
|
||||
if: always()
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: ./coverage.txt
|
||||
env_vars: RUNNER_OS
|
||||
flags: unit,${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
env:
|
||||
RUNNER_OS: ${{ matrix.os }}
|
||||
|
||||
govulncheck:
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
@@ -232,19 +300,16 @@ jobs:
|
||||
# required to write sarif report
|
||||
security-events: write
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: ${{ env.BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.BUILDKIT_IMAGE }}
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Run
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
targets: govulncheck
|
||||
env:
|
||||
@@ -298,8 +363,8 @@ jobs:
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: ${{ env.BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.BUILDKIT_IMAGE }}
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Build
|
||||
@@ -324,9 +389,6 @@ jobs:
|
||||
- test-unit
|
||||
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }}
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -334,8 +396,8 @@ jobs:
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: ${{ env.BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.BUILDKIT_IMAGE }}
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Docker meta
|
||||
@@ -358,11 +420,11 @@ jobs:
|
||||
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}
|
||||
-
|
||||
name: Build and push image
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
files: |
|
||||
./docker-bake.hcl
|
||||
${{ steps.meta.outputs.bake-file }}
|
||||
cwd://${{ steps.meta.outputs.bake-file }}
|
||||
targets: image-cross
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
sbom: true
|
||||
@@ -381,9 +443,6 @@ jobs:
|
||||
needs:
|
||||
- bin-image
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
@@ -438,7 +497,7 @@ jobs:
|
||||
-
|
||||
name: GitHub Release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8
|
||||
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -17,7 +17,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.22"
|
||||
GO_VERSION: "1.23"
|
||||
|
||||
jobs:
|
||||
codeql:
|
||||
|
12
.github/workflows/docs-release.yml
vendored
12
.github/workflows/docs-release.yml
vendored
@@ -19,6 +19,10 @@ on:
|
||||
types:
|
||||
- released
|
||||
|
||||
env:
|
||||
SETUP_BUILDX_VERSION: "edge"
|
||||
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
|
||||
|
||||
jobs:
|
||||
open-pr:
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -46,9 +50,13 @@ jobs:
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Generate yaml
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: ${{ github.server_url }}/${{ github.repository }}.git#${{ env.RELEASE_NAME }}
|
||||
targets: update-docs
|
||||
@@ -69,7 +77,7 @@ jobs:
|
||||
VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }}
|
||||
-
|
||||
name: Create PR on docs repo
|
||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
||||
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6
|
||||
with:
|
||||
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
||||
push-to-fork: docker-tools-robot/docker.github.io
|
||||
|
15
.github/workflows/docs-upstream.yml
vendored
15
.github/workflows/docs-upstream.yml
vendored
@@ -29,21 +29,24 @@ on:
|
||||
- '.github/workflows/docs-upstream.yml'
|
||||
- 'docs/**'
|
||||
|
||||
env:
|
||||
SETUP_BUILDX_VERSION: "edge"
|
||||
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
|
||||
|
||||
jobs:
|
||||
docs-yaml:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: latest
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Build reference YAML docs
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
targets: update-docs
|
||||
provenance: false
|
||||
@@ -62,7 +65,7 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
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:
|
||||
- docs-yaml
|
||||
with:
|
||||
|
82
.github/workflows/e2e.yml
vendored
82
.github/workflows/e2e.yml
vendored
@@ -26,6 +26,8 @@ on:
|
||||
- 'docs/**'
|
||||
|
||||
env:
|
||||
SETUP_BUILDX_VERSION: "edge"
|
||||
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
|
||||
DESTDIR: "./bin"
|
||||
K3S_VERSION: "v1.21.2-k3s1"
|
||||
|
||||
@@ -33,16 +35,16 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: latest
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
targets: binaries
|
||||
set: |
|
||||
@@ -175,3 +177,75 @@ jobs:
|
||||
DRIVER_OPT: ${{ matrix.driver-opt }}
|
||||
ENDPOINT: ${{ matrix.endpoint }}
|
||||
PLATFORMS: ${{ matrix.platforms }}
|
||||
|
||||
bake:
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- build
|
||||
env:
|
||||
DOCKER_BUILD_CHECKS_ANNOTATIONS: false
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
-
|
||||
# https://github.com/docker/bake-action/blob/v5.11.0/.github/workflows/ci.yml#L227-L237
|
||||
source: "https://github.com/docker/bake-action.git#v5.11.0:test/go"
|
||||
overrides: |
|
||||
*.output=/tmp/bake-build
|
||||
-
|
||||
# https://github.com/tonistiigi/xx/blob/2fc85604e7280bfb3f626569bd4c5413c43eb4af/.github/workflows/ld.yml#L90-L98
|
||||
source: "https://github.com/tonistiigi/xx.git#2fc85604e7280bfb3f626569bd4c5413c43eb4af"
|
||||
targets: |
|
||||
ld64-static-tgz
|
||||
overrides: |
|
||||
ld64-static-tgz.output=type=local,dest=./dist
|
||||
ld64-static-tgz.platform=linux/amd64
|
||||
ld64-static-tgz.cache-from=type=gha,scope=xx-ld64-static-tgz
|
||||
ld64-static-tgz.cache-to=type=gha,scope=xx-ld64-static-tgz
|
||||
-
|
||||
# https://github.com/moby/buildkit-bench/blob/54c194011c4fc99a94aa75d4b3d4f3ffd4c4ce27/docker-bake.hcl#L154-L160
|
||||
source: "https://github.com/moby/buildkit-bench.git#54c194011c4fc99a94aa75d4b3d4f3ffd4c4ce27"
|
||||
targets: |
|
||||
tests-buildkit
|
||||
envs: |
|
||||
BUILDKIT_REFS=v0.18.2
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Environment variables
|
||||
if: matrix.envs != ''
|
||||
run: |
|
||||
for l in "${{ matrix.envs }}"; do
|
||||
echo "${l?}" >> $GITHUB_ENV
|
||||
done
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Install buildx
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: binary
|
||||
path: /home/runner/.docker/cli-plugins
|
||||
-
|
||||
name: Fix perms and check
|
||||
run: |
|
||||
chmod +x /home/runner/.docker/cli-plugins/docker-buildx
|
||||
docker buildx version
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Build
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
source: ${{ matrix.source }}
|
||||
targets: ${{ matrix.targets }}
|
||||
set: ${{ matrix.overrides }}
|
||||
|
13
.github/workflows/validate.yml
vendored
13
.github/workflows/validate.yml
vendored
@@ -25,6 +25,10 @@ on:
|
||||
paths-ignore:
|
||||
- '.github/releases.json'
|
||||
|
||||
env:
|
||||
SETUP_BUILDX_VERSION: "edge"
|
||||
SETUP_BUILDKIT_IMAGE: "moby/buildkit:latest"
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-24.04
|
||||
@@ -90,17 +94,16 @@ jobs:
|
||||
if [ "$GITHUB_REPOSITORY" = "docker/buildx" ]; then
|
||||
echo "GOLANGCI_LINT_MULTIPLATFORM=1" >> $GITHUB_ENV
|
||||
fi
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: latest
|
||||
version: ${{ env.SETUP_BUILDX_VERSION }}
|
||||
driver-opts: image=${{ env.SETUP_BUILDKIT_IMAGE }}
|
||||
buildkitd-flags: --debug
|
||||
-
|
||||
name: Validate
|
||||
uses: docker/bake-action@v5
|
||||
uses: docker/bake-action@v6
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
set: |
|
||||
|
@@ -1,26 +1,52 @@
|
||||
run:
|
||||
timeout: 30m
|
||||
|
||||
modules-download-mode: vendor
|
||||
# default uses Go version from the go.mod file, fallback on the env var
|
||||
# `GOVERSION`, fallback on 1.17: https://golangci-lint.run/usage/configuration/#run-configuration
|
||||
go: "1.23"
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- gofmt
|
||||
- govet
|
||||
- bodyclose
|
||||
- depguard
|
||||
- forbidigo
|
||||
- gocritic
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- makezero
|
||||
- misspell
|
||||
- unused
|
||||
- noctx
|
||||
- nolintlint
|
||||
- revive
|
||||
- staticcheck
|
||||
- testifylint
|
||||
- typecheck
|
||||
- nolintlint
|
||||
- gosec
|
||||
- forbidigo
|
||||
- unused
|
||||
- whitespace
|
||||
disable-all: true
|
||||
|
||||
linters-settings:
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- "ifElseChain"
|
||||
- "assignOp"
|
||||
- "appendAssign"
|
||||
- "singleCaseSwitch"
|
||||
- "exitAfterDefer" # FIXME
|
||||
importas:
|
||||
alias:
|
||||
# Enforce alias to prevent it accidentally being used instead of
|
||||
# buildkit errdefs package (or vice-versa).
|
||||
- pkg: "github.com/containerd/errdefs"
|
||||
alias: "cerrdefs"
|
||||
- pkg: "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
alias: "ocispecs"
|
||||
- pkg: "github.com/opencontainers/go-digest"
|
||||
alias: "digest"
|
||||
govet:
|
||||
enable:
|
||||
- nilness
|
||||
@@ -43,14 +69,27 @@ linters-settings:
|
||||
desc: The io/ioutil package has been deprecated.
|
||||
forbidigo:
|
||||
forbid:
|
||||
- '^context\.WithCancel(# use context\.WithCancelCause instead)?$'
|
||||
- '^context\.WithDeadline(# use context\.WithDeadline instead)?$'
|
||||
- '^context\.WithTimeout(# use context\.WithTimeoutCause instead)?$'
|
||||
- '^ctx\.Err(# use context\.Cause instead)?$'
|
||||
- '^fmt\.Errorf(# use errors\.Errorf instead)?$'
|
||||
- '^platforms\.DefaultString(# use platforms\.Format(platforms\.DefaultSpec()) instead\.)?$'
|
||||
gosec:
|
||||
excludes:
|
||||
- G204 # Audit use of command execution
|
||||
- G402 # TLS MinVersion too low
|
||||
- G115 # integer overflow conversion (TODO: verify these)
|
||||
config:
|
||||
G306: "0644"
|
||||
testifylint:
|
||||
disable:
|
||||
# disable rules that reduce the test condition
|
||||
- "empty"
|
||||
- "bool-compare"
|
||||
- "len"
|
||||
- "negative-positive"
|
||||
|
||||
|
||||
issues:
|
||||
exclude-files:
|
||||
|
30
Dockerfile
30
Dockerfile
@@ -1,20 +1,24 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.22
|
||||
ARG XX_VERSION=1.5.0
|
||||
ARG GO_VERSION=1.23
|
||||
ARG ALPINE_VERSION=3.21
|
||||
ARG XX_VERSION=1.6.1
|
||||
|
||||
# for testing
|
||||
ARG DOCKER_VERSION=27.2.1
|
||||
ARG DOCKER_VERSION=27.5.0
|
||||
ARG DOCKER_VERSION_ALT_26=26.1.3
|
||||
ARG DOCKER_CLI_VERSION=${DOCKER_VERSION}
|
||||
ARG GOTESTSUM_VERSION=v1.9.0
|
||||
ARG REGISTRY_VERSION=2.8.0
|
||||
ARG BUILDKIT_VERSION=v0.16.0
|
||||
ARG UNDOCK_VERSION=0.7.0
|
||||
ARG GOTESTSUM_VERSION=v1.12.0
|
||||
ARG REGISTRY_VERSION=2.8.3
|
||||
ARG BUILDKIT_VERSION=v0.19.0-rc2
|
||||
ARG UNDOCK_VERSION=0.9.0
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS golatest
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golatest
|
||||
FROM moby/moby-bin:$DOCKER_VERSION AS docker-engine
|
||||
FROM dockereng/cli-bin:$DOCKER_CLI_VERSION AS docker-cli
|
||||
FROM moby/moby-bin:$DOCKER_VERSION_ALT_26 AS docker-engine-alt
|
||||
FROM dockereng/cli-bin:$DOCKER_VERSION_ALT_26 AS docker-cli-alt
|
||||
FROM registry:$REGISTRY_VERSION AS registry
|
||||
FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit
|
||||
FROM crazymax/undock:$UNDOCK_VERSION AS undock
|
||||
@@ -77,6 +81,7 @@ RUN --mount=type=bind,target=. \
|
||||
set -e
|
||||
xx-go --wrap
|
||||
DESTDIR=/usr/bin VERSION=$(cat /buildx-version/version) REVISION=$(cat /buildx-version/revision) GO_EXTRA_LDFLAGS="-s -w" ./hack/build
|
||||
file /usr/bin/docker-buildx
|
||||
xx-verify --static /usr/bin/docker-buildx
|
||||
EOT
|
||||
|
||||
@@ -95,7 +100,9 @@ FROM scratch AS binaries-unix
|
||||
COPY --link --from=buildx-build /usr/bin/docker-buildx /buildx
|
||||
|
||||
FROM binaries-unix AS binaries-darwin
|
||||
FROM binaries-unix AS binaries-freebsd
|
||||
FROM binaries-unix AS binaries-linux
|
||||
FROM binaries-unix AS binaries-openbsd
|
||||
|
||||
FROM scratch AS binaries-windows
|
||||
COPY --link --from=buildx-build /usr/bin/docker-buildx /buildx.exe
|
||||
@@ -120,16 +127,19 @@ COPY --link --from=gotestsum /out /usr/bin/
|
||||
COPY --link --from=registry /bin/registry /usr/bin/
|
||||
COPY --link --from=docker-engine / /usr/bin/
|
||||
COPY --link --from=docker-cli / /usr/bin/
|
||||
COPY --link --from=docker-engine-alt / /opt/docker-alt-26/
|
||||
COPY --link --from=docker-cli-alt / /opt/docker-alt-26/
|
||||
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
|
||||
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
|
||||
COPY --link --from=undock /usr/local/bin/undock /usr/bin/
|
||||
COPY --link --from=binaries /buildx /usr/bin/
|
||||
ENV TEST_DOCKER_EXTRA="docker@26.1=/opt/docker-alt-26"
|
||||
|
||||
FROM integration-test-base AS integration-test
|
||||
COPY . .
|
||||
|
||||
# Release
|
||||
FROM --platform=$BUILDPLATFORM alpine AS releaser
|
||||
FROM --platform=$BUILDPLATFORM alpine:${ALPINE_VERSION} AS releaser
|
||||
WORKDIR /work
|
||||
ARG TARGETPLATFORM
|
||||
RUN --mount=from=binaries \
|
||||
@@ -144,7 +154,7 @@ COPY --from=releaser /out/ /
|
||||
|
||||
# Shell
|
||||
FROM docker:$DOCKER_VERSION AS dockerd-release
|
||||
FROM alpine AS shell
|
||||
FROM alpine:${ALPINE_VERSION} AS shell
|
||||
RUN apk add --no-cache iptables tmux git vim less openssh
|
||||
RUN mkdir -p /usr/local/lib/docker/cli-plugins && ln -s /usr/local/bin/buildx /usr/local/lib/docker/cli-plugins/docker-buildx
|
||||
COPY ./hack/demo-env/entrypoint.sh /usr/local/bin
|
||||
|
526
bake/bake.go
526
bake/bake.go
@@ -2,6 +2,7 @@ package bake
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
@@ -28,7 +29,6 @@ import (
|
||||
"github.com/moby/buildkit/session/auth/authprovider"
|
||||
"github.com/moby/buildkit/util/entitlements"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
)
|
||||
@@ -53,8 +53,8 @@ func defaultFilenames() []string {
|
||||
names = append(names, composecli.DefaultFileNames...)
|
||||
names = append(names, []string{
|
||||
"docker-bake.json",
|
||||
"docker-bake.override.json",
|
||||
"docker-bake.hcl",
|
||||
"docker-bake.override.json",
|
||||
"docker-bake.override.hcl",
|
||||
}...)
|
||||
return names
|
||||
@@ -193,7 +193,7 @@ func ListTargets(files []File) ([]string, error) {
|
||||
return dedupSlice(targets), nil
|
||||
}
|
||||
|
||||
func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string) (map[string]*Target, map[string]*Group, error) {
|
||||
func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string, ent *EntitlementConf) (map[string]*Target, map[string]*Group, error) {
|
||||
c, _, err := ParseFiles(files, defaults)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -207,23 +207,24 @@ func ReadTargets(ctx context.Context, files []File, targets, overrides []string,
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
m := map[string]*Target{}
|
||||
n := map[string]*Group{}
|
||||
|
||||
targetsMap := map[string]*Target{}
|
||||
groupsMap := map[string]*Group{}
|
||||
for _, target := range targets {
|
||||
ts, gs := c.ResolveGroup(target)
|
||||
for _, tname := range ts {
|
||||
t, err := c.ResolveTarget(tname, o)
|
||||
t, err := c.ResolveTarget(tname, o, ent)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if t != nil {
|
||||
m[tname] = t
|
||||
targetsMap[tname] = t
|
||||
}
|
||||
}
|
||||
for _, gname := range gs {
|
||||
for _, group := range c.Groups {
|
||||
if group.Name == gname {
|
||||
n[gname] = group
|
||||
groupsMap[gname] = group
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -231,25 +232,26 @@ func ReadTargets(ctx context.Context, files []File, targets, overrides []string,
|
||||
}
|
||||
|
||||
for _, target := range targets {
|
||||
if target == "default" {
|
||||
if _, ok := groupsMap["default"]; ok && target == "default" {
|
||||
continue
|
||||
}
|
||||
if _, ok := n["default"]; !ok {
|
||||
n["default"] = &Group{Name: "default"}
|
||||
if _, ok := groupsMap["default"]; !ok {
|
||||
groupsMap["default"] = &Group{Name: "default"}
|
||||
}
|
||||
n["default"].Targets = append(n["default"].Targets, target)
|
||||
groupsMap["default"].Targets = append(groupsMap["default"].Targets, target)
|
||||
}
|
||||
if g, ok := n["default"]; ok {
|
||||
if g, ok := groupsMap["default"]; ok {
|
||||
g.Targets = dedupSlice(g.Targets)
|
||||
sort.Strings(g.Targets)
|
||||
}
|
||||
|
||||
for name, t := range m {
|
||||
if err := c.loadLinks(name, t, m, o, nil); err != nil {
|
||||
for name, t := range targetsMap {
|
||||
if err := c.loadLinks(name, t, targetsMap, o, nil, ent); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return m, n, nil
|
||||
return targetsMap, groupsMap, nil
|
||||
}
|
||||
|
||||
func dedupSlice(s []string) []string {
|
||||
@@ -476,7 +478,7 @@ func (c Config) expandTargets(pattern string) ([]string, error) {
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[string]map[string]Override, visited []string) error {
|
||||
func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[string]map[string]Override, visited []string, ent *EntitlementConf) error {
|
||||
visited = append(visited, name)
|
||||
for _, v := range t.Contexts {
|
||||
if strings.HasPrefix(v, "target:") {
|
||||
@@ -492,15 +494,17 @@ func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[st
|
||||
t2, ok := m[target]
|
||||
if !ok {
|
||||
var err error
|
||||
t2, err = c.ResolveTarget(target, o)
|
||||
t2, err = c.ResolveTarget(target, o, ent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t2.Outputs = []string{"type=cacheonly"}
|
||||
t2.Outputs = []*buildflags.ExportEntry{
|
||||
{Type: "cacheonly"},
|
||||
}
|
||||
t2.linked = true
|
||||
m[target] = t2
|
||||
}
|
||||
if err := c.loadLinks(target, t2, m, o, visited); err != nil {
|
||||
if err := c.loadLinks(target, t2, m, o, visited, ent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -512,8 +516,8 @@ func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[st
|
||||
}
|
||||
|
||||
if len(t.Platforms) > 1 && len(t2.Platforms) > 1 {
|
||||
if !sliceEqual(t.Platforms, t2.Platforms) {
|
||||
return errors.Errorf("target %s can't be used by %s because it is defined for different platforms %v and %v", target, name, t2.Platforms, t.Platforms)
|
||||
if !isSubset(t.Platforms, t2.Platforms) {
|
||||
return errors.Errorf("target %s can't be used by %s because its platforms %v are not a subset of %v", target, name, t.Platforms, t2.Platforms)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,6 +555,8 @@ func (c Config) newOverrides(v []string) (map[string]map[string]Override, error)
|
||||
|
||||
o := t[kk[1]]
|
||||
|
||||
// IMPORTANT: if you add more fields here, do not forget to update
|
||||
// docs/bake-reference.md and https://docs.docker.com/build/bake/overrides/
|
||||
switch keys[1] {
|
||||
case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh", "attest", "entitlements", "network":
|
||||
if len(parts) == 2 {
|
||||
@@ -627,8 +633,8 @@ func (c Config) group(name string, visited map[string]visit) ([]string, []string
|
||||
return targets, groups
|
||||
}
|
||||
|
||||
func (c Config) ResolveTarget(name string, overrides map[string]map[string]Override) (*Target, error) {
|
||||
t, err := c.target(name, map[string]*Target{}, overrides)
|
||||
func (c Config) ResolveTarget(name string, overrides map[string]map[string]Override, ent *EntitlementConf) (*Target, error) {
|
||||
t, err := c.target(name, map[string]*Target{}, overrides, ent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -644,7 +650,7 @@ func (c Config) ResolveTarget(name string, overrides map[string]map[string]Overr
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (c Config) target(name string, visited map[string]*Target, overrides map[string]map[string]Override) (*Target, error) {
|
||||
func (c Config) target(name string, visited map[string]*Target, overrides map[string]map[string]Override, ent *EntitlementConf) (*Target, error) {
|
||||
if t, ok := visited[name]; ok {
|
||||
return t, nil
|
||||
}
|
||||
@@ -661,7 +667,7 @@ func (c Config) target(name string, visited map[string]*Target, overrides map[st
|
||||
}
|
||||
tt := &Target{}
|
||||
for _, name := range t.Inherits {
|
||||
t, err := c.target(name, visited, overrides)
|
||||
t, err := c.target(name, visited, overrides, ent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -673,7 +679,7 @@ func (c Config) target(name string, visited map[string]*Target, overrides map[st
|
||||
m.Merge(tt)
|
||||
m.Merge(t)
|
||||
tt = m
|
||||
if err := tt.AddOverrides(overrides[name]); err != nil {
|
||||
if err := tt.AddOverrides(overrides[name], ent); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tt.normalize()
|
||||
@@ -695,59 +701,61 @@ type Target struct {
|
||||
// Inherits is the only field that cannot be overridden with --set
|
||||
Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional" cty:"inherits"`
|
||||
|
||||
Annotations []string `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
|
||||
Attest []string `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
|
||||
Context *string `json:"context,omitempty" hcl:"context,optional" cty:"context"`
|
||||
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
|
||||
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional" cty:"dockerfile"`
|
||||
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional" cty:"dockerfile-inline"`
|
||||
Args map[string]*string `json:"args,omitempty" hcl:"args,optional" cty:"args"`
|
||||
Labels map[string]*string `json:"labels,omitempty" hcl:"labels,optional" cty:"labels"`
|
||||
Tags []string `json:"tags,omitempty" hcl:"tags,optional" cty:"tags"`
|
||||
CacheFrom []string `json:"cache-from,omitempty" hcl:"cache-from,optional" cty:"cache-from"`
|
||||
CacheTo []string `json:"cache-to,omitempty" hcl:"cache-to,optional" cty:"cache-to"`
|
||||
Target *string `json:"target,omitempty" hcl:"target,optional" cty:"target"`
|
||||
Secrets []string `json:"secret,omitempty" hcl:"secret,optional" cty:"secret"`
|
||||
SSH []string `json:"ssh,omitempty" hcl:"ssh,optional" cty:"ssh"`
|
||||
Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional" cty:"platforms"`
|
||||
Outputs []string `json:"output,omitempty" hcl:"output,optional" cty:"output"`
|
||||
Pull *bool `json:"pull,omitempty" hcl:"pull,optional" cty:"pull"`
|
||||
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
|
||||
NetworkMode *string `json:"network,omitempty" hcl:"network,optional" cty:"network"`
|
||||
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"`
|
||||
ShmSize *string `json:"shm-size,omitempty" hcl:"shm-size,optional"`
|
||||
Ulimits []string `json:"ulimits,omitempty" hcl:"ulimits,optional"`
|
||||
Call *string `json:"call,omitempty" hcl:"call,optional" cty:"call"`
|
||||
Entitlements []string `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"`
|
||||
Annotations []string `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
|
||||
Attest buildflags.Attests `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
|
||||
Context *string `json:"context,omitempty" hcl:"context,optional" cty:"context"`
|
||||
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
|
||||
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional" cty:"dockerfile"`
|
||||
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional" cty:"dockerfile-inline"`
|
||||
Args map[string]*string `json:"args,omitempty" hcl:"args,optional" cty:"args"`
|
||||
Labels map[string]*string `json:"labels,omitempty" hcl:"labels,optional" cty:"labels"`
|
||||
Tags []string `json:"tags,omitempty" hcl:"tags,optional" cty:"tags"`
|
||||
CacheFrom buildflags.CacheOptions `json:"cache-from,omitempty" hcl:"cache-from,optional" cty:"cache-from"`
|
||||
CacheTo buildflags.CacheOptions `json:"cache-to,omitempty" hcl:"cache-to,optional" cty:"cache-to"`
|
||||
Target *string `json:"target,omitempty" hcl:"target,optional" cty:"target"`
|
||||
Secrets buildflags.Secrets `json:"secret,omitempty" hcl:"secret,optional" cty:"secret"`
|
||||
SSH buildflags.SSHKeys `json:"ssh,omitempty" hcl:"ssh,optional" cty:"ssh"`
|
||||
Platforms []string `json:"platforms,omitempty" hcl:"platforms,optional" cty:"platforms"`
|
||||
Outputs buildflags.Exports `json:"output,omitempty" hcl:"output,optional" cty:"output"`
|
||||
Pull *bool `json:"pull,omitempty" hcl:"pull,optional" cty:"pull"`
|
||||
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
|
||||
NetworkMode *string `json:"network,omitempty" hcl:"network,optional" cty:"network"`
|
||||
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"`
|
||||
ShmSize *string `json:"shm-size,omitempty" hcl:"shm-size,optional" cty:"shm-size"`
|
||||
Ulimits []string `json:"ulimits,omitempty" hcl:"ulimits,optional" cty:"ulimits"`
|
||||
Call *string `json:"call,omitempty" hcl:"call,optional" cty:"call"`
|
||||
Entitlements []string `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"`
|
||||
// IMPORTANT: if you add more fields here, do not forget to update newOverrides/AddOverrides and docs/bake-reference.md.
|
||||
|
||||
// linked is a private field to mark a target used as a linked one
|
||||
linked bool
|
||||
}
|
||||
|
||||
var _ hclparser.WithEvalContexts = &Target{}
|
||||
var _ hclparser.WithGetName = &Target{}
|
||||
var _ hclparser.WithEvalContexts = &Group{}
|
||||
var _ hclparser.WithGetName = &Group{}
|
||||
var (
|
||||
_ hclparser.WithEvalContexts = &Target{}
|
||||
_ hclparser.WithGetName = &Target{}
|
||||
_ hclparser.WithEvalContexts = &Group{}
|
||||
_ hclparser.WithGetName = &Group{}
|
||||
)
|
||||
|
||||
func (t *Target) normalize() {
|
||||
t.Annotations = removeDupes(t.Annotations)
|
||||
t.Attest = removeAttestDupes(t.Attest)
|
||||
t.Tags = removeDupes(t.Tags)
|
||||
t.Secrets = removeDupes(t.Secrets)
|
||||
t.SSH = removeDupes(t.SSH)
|
||||
t.Platforms = removeDupes(t.Platforms)
|
||||
t.CacheFrom = removeDupes(t.CacheFrom)
|
||||
t.CacheTo = removeDupes(t.CacheTo)
|
||||
t.Outputs = removeDupes(t.Outputs)
|
||||
t.NoCacheFilter = removeDupes(t.NoCacheFilter)
|
||||
t.Ulimits = removeDupes(t.Ulimits)
|
||||
t.Annotations = removeDupesStr(t.Annotations)
|
||||
t.Attest = t.Attest.Normalize()
|
||||
t.Tags = removeDupesStr(t.Tags)
|
||||
t.Secrets = t.Secrets.Normalize()
|
||||
t.SSH = t.SSH.Normalize()
|
||||
t.Platforms = removeDupesStr(t.Platforms)
|
||||
t.CacheFrom = t.CacheFrom.Normalize()
|
||||
t.CacheTo = t.CacheTo.Normalize()
|
||||
t.Outputs = t.Outputs.Normalize()
|
||||
t.NoCacheFilter = removeDupesStr(t.NoCacheFilter)
|
||||
t.Ulimits = removeDupesStr(t.Ulimits)
|
||||
|
||||
if t.NetworkMode != nil && *t.NetworkMode == "host" {
|
||||
t.Entitlements = append(t.Entitlements, "network.host")
|
||||
}
|
||||
|
||||
t.Entitlements = removeDupes(t.Entitlements)
|
||||
t.Entitlements = removeDupesStr(t.Entitlements)
|
||||
|
||||
for k, v := range t.Contexts {
|
||||
if v == "" {
|
||||
@@ -806,20 +814,19 @@ func (t *Target) Merge(t2 *Target) {
|
||||
t.Annotations = append(t.Annotations, t2.Annotations...)
|
||||
}
|
||||
if t2.Attest != nil { // merge
|
||||
t.Attest = append(t.Attest, t2.Attest...)
|
||||
t.Attest = removeAttestDupes(t.Attest)
|
||||
t.Attest = t.Attest.Merge(t2.Attest)
|
||||
}
|
||||
if t2.Secrets != nil { // merge
|
||||
t.Secrets = append(t.Secrets, t2.Secrets...)
|
||||
t.Secrets = t.Secrets.Merge(t2.Secrets)
|
||||
}
|
||||
if t2.SSH != nil { // merge
|
||||
t.SSH = append(t.SSH, t2.SSH...)
|
||||
t.SSH = t.SSH.Merge(t2.SSH)
|
||||
}
|
||||
if t2.Platforms != nil { // no merge
|
||||
t.Platforms = t2.Platforms
|
||||
}
|
||||
if t2.CacheFrom != nil { // merge
|
||||
t.CacheFrom = append(t.CacheFrom, t2.CacheFrom...)
|
||||
t.CacheFrom = t.CacheFrom.Merge(t2.CacheFrom)
|
||||
}
|
||||
if t2.CacheTo != nil { // no merge
|
||||
t.CacheTo = t2.CacheTo
|
||||
@@ -854,7 +861,9 @@ func (t *Target) Merge(t2 *Target) {
|
||||
t.Inherits = append(t.Inherits, t2.Inherits...)
|
||||
}
|
||||
|
||||
func (t *Target) AddOverrides(overrides map[string]Override) error {
|
||||
func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementConf) error {
|
||||
// IMPORTANT: if you add more fields here, do not forget to update
|
||||
// docs/bake-reference.md and https://docs.docker.com/build/bake/overrides/
|
||||
for key, o := range overrides {
|
||||
value := o.Value
|
||||
keys := strings.SplitN(key, ".", 2)
|
||||
@@ -865,7 +874,7 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
||||
t.Dockerfile = &value
|
||||
case "args":
|
||||
if len(keys) != 2 {
|
||||
return errors.Errorf("args require name")
|
||||
return errors.Errorf("invalid format for args, expecting args.<name>=<value>")
|
||||
}
|
||||
if t.Args == nil {
|
||||
t.Args = map[string]*string{}
|
||||
@@ -873,7 +882,7 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
||||
t.Args[keys[1]] = &value
|
||||
case "contexts":
|
||||
if len(keys) != 2 {
|
||||
return errors.Errorf("contexts require name")
|
||||
return errors.Errorf("invalid format for contexts, expecting contexts.<name>=<value>")
|
||||
}
|
||||
if t.Contexts == nil {
|
||||
t.Contexts = map[string]string{}
|
||||
@@ -881,7 +890,7 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
||||
t.Contexts[keys[1]] = value
|
||||
case "labels":
|
||||
if len(keys) != 2 {
|
||||
return errors.Errorf("labels require name")
|
||||
return errors.Errorf("invalid format for labels, expecting labels.<name>=<value>")
|
||||
}
|
||||
if t.Labels == nil {
|
||||
t.Labels = map[string]*string{}
|
||||
@@ -890,27 +899,85 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
||||
case "tags":
|
||||
t.Tags = o.ArrValue
|
||||
case "cache-from":
|
||||
t.CacheFrom = o.ArrValue
|
||||
cacheFrom, err := buildflags.ParseCacheEntry(o.ArrValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.CacheFrom = cacheFrom
|
||||
for _, c := range t.CacheFrom {
|
||||
if c.Type == "local" {
|
||||
if v, ok := c.Attrs["src"]; ok {
|
||||
ent.FSRead = append(ent.FSRead, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
case "cache-to":
|
||||
t.CacheTo = o.ArrValue
|
||||
cacheTo, err := buildflags.ParseCacheEntry(o.ArrValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.CacheTo = cacheTo
|
||||
for _, c := range t.CacheTo {
|
||||
if c.Type == "local" {
|
||||
if v, ok := c.Attrs["dest"]; ok {
|
||||
ent.FSWrite = append(ent.FSWrite, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
case "target":
|
||||
t.Target = &value
|
||||
case "call":
|
||||
t.Call = &value
|
||||
case "secrets":
|
||||
t.Secrets = o.ArrValue
|
||||
secrets, err := parseArrValue[buildflags.Secret](o.ArrValue)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid value for outputs")
|
||||
}
|
||||
t.Secrets = secrets
|
||||
for _, s := range t.Secrets {
|
||||
if s.FilePath != "" {
|
||||
ent.FSRead = append(ent.FSRead, s.FilePath)
|
||||
}
|
||||
}
|
||||
case "ssh":
|
||||
t.SSH = o.ArrValue
|
||||
ssh, err := parseArrValue[buildflags.SSH](o.ArrValue)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid value for outputs")
|
||||
}
|
||||
t.SSH = ssh
|
||||
for _, s := range t.SSH {
|
||||
ent.FSRead = append(ent.FSRead, s.Paths...)
|
||||
}
|
||||
case "platform":
|
||||
t.Platforms = o.ArrValue
|
||||
case "output":
|
||||
t.Outputs = o.ArrValue
|
||||
outputs, err := parseArrValue[buildflags.ExportEntry](o.ArrValue)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid value for outputs")
|
||||
}
|
||||
t.Outputs = outputs
|
||||
for _, o := range t.Outputs {
|
||||
if o.Destination != "" {
|
||||
ent.FSWrite = append(ent.FSWrite, o.Destination)
|
||||
}
|
||||
}
|
||||
case "entitlements":
|
||||
t.Entitlements = append(t.Entitlements, o.ArrValue...)
|
||||
for _, v := range o.ArrValue {
|
||||
if v == string(EntitlementKeyNetworkHost) {
|
||||
ent.NetworkHost = true
|
||||
} else if v == string(EntitlementKeySecurityInsecure) {
|
||||
ent.SecurityInsecure = true
|
||||
}
|
||||
}
|
||||
case "annotations":
|
||||
t.Annotations = append(t.Annotations, o.ArrValue...)
|
||||
case "attest":
|
||||
t.Attest = append(t.Attest, o.ArrValue...)
|
||||
attest, err := parseArrValue[buildflags.Attest](o.ArrValue)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "invalid value for attest")
|
||||
}
|
||||
t.Attest = t.Attest.Merge(attest)
|
||||
case "no-cache":
|
||||
noCache, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
@@ -1115,62 +1182,44 @@ func updateContext(t *build.Inputs, inp *Input) {
|
||||
t.ContextState = &st
|
||||
}
|
||||
|
||||
// validateContextsEntitlements is a basic check to ensure contexts do not
|
||||
// escape local directories when loaded from remote sources. This is to be
|
||||
// replaced with proper entitlements support in the future.
|
||||
func validateContextsEntitlements(t build.Inputs, inp *Input) error {
|
||||
if inp == nil || inp.State == nil {
|
||||
return nil
|
||||
func isRemoteContext(t build.Inputs, inp *Input) bool {
|
||||
if build.IsRemoteURL(t.ContextPath) {
|
||||
return true
|
||||
}
|
||||
if v, ok := os.LookupEnv("BAKE_ALLOW_REMOTE_FS_ACCESS"); ok {
|
||||
if vv, _ := strconv.ParseBool(v); vv {
|
||||
return nil
|
||||
}
|
||||
if inp != nil && build.IsRemoteURL(inp.URL) && !strings.HasPrefix(t.ContextPath, "cwd://") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func collectLocalPaths(t build.Inputs) []string {
|
||||
var out []string
|
||||
if t.ContextState == nil {
|
||||
if err := checkPath(t.ContextPath); err != nil {
|
||||
return err
|
||||
if v, ok := isLocalPath(t.ContextPath); ok {
|
||||
out = append(out, v)
|
||||
}
|
||||
if v, ok := isLocalPath(t.DockerfilePath); ok {
|
||||
out = append(out, v)
|
||||
}
|
||||
} else if strings.HasPrefix(t.ContextPath, "cwd://") {
|
||||
out = append(out, strings.TrimPrefix(t.ContextPath, "cwd://"))
|
||||
}
|
||||
for _, v := range t.NamedContexts {
|
||||
if v.State != nil {
|
||||
continue
|
||||
}
|
||||
if err := checkPath(v.Path); err != nil {
|
||||
return err
|
||||
if v, ok := isLocalPath(v.Path); ok {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return out
|
||||
}
|
||||
|
||||
func checkPath(p string) error {
|
||||
func isLocalPath(p string) (string, bool) {
|
||||
if build.IsRemoteURL(p) || strings.HasPrefix(p, "target:") || strings.HasPrefix(p, "docker-image:") {
|
||||
return nil
|
||||
return "", false
|
||||
}
|
||||
p, err := filepath.EvalSymlinks(p)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, err := filepath.Rel(wd, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parts := strings.Split(rel, string(os.PathSeparator))
|
||||
if parts[0] == ".." {
|
||||
return errors.Errorf("path %s is outside of the working directory, please set BAKE_ALLOW_REMOTE_FS_ACCESS=1", p)
|
||||
}
|
||||
return nil
|
||||
return strings.TrimPrefix(p, "cwd://"), true
|
||||
}
|
||||
|
||||
func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
||||
@@ -1210,9 +1259,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
||||
// it's not outside the working directory and then resolve it to an
|
||||
// absolute path.
|
||||
bi.DockerfilePath = path.Clean(strings.TrimPrefix(bi.DockerfilePath, "cwd://"))
|
||||
if err := checkPath(bi.DockerfilePath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var err error
|
||||
bi.DockerfilePath, err = filepath.Abs(bi.DockerfilePath)
|
||||
if err != nil {
|
||||
@@ -1249,10 +1295,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateContextsEntitlements(bi, inp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.Context = &bi.ContextPath
|
||||
|
||||
args := map[string]string{}
|
||||
@@ -1309,24 +1351,35 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
||||
}
|
||||
bo.Platforms = platforms
|
||||
|
||||
secrets, err := buildflags.ParseSecretSpecs(t.Secrets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
secrets := t.Secrets
|
||||
if isRemoteContext(bi, inp) {
|
||||
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
|
||||
secrets = append(secrets, &buildflags.Secret{
|
||||
ID: llb.GitAuthTokenKey,
|
||||
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
|
||||
})
|
||||
}
|
||||
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
|
||||
secrets = append(secrets, &buildflags.Secret{
|
||||
ID: llb.GitAuthHeaderKey,
|
||||
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
|
||||
})
|
||||
}
|
||||
}
|
||||
secretAttachment, err := controllerapi.CreateSecrets(secrets)
|
||||
secrets = secrets.Normalize()
|
||||
bo.SecretSpecs = secrets.ToPB()
|
||||
secretAttachment, err := controllerapi.CreateSecrets(bo.SecretSpecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bo.Session = append(bo.Session, secretAttachment)
|
||||
|
||||
sshSpecs, err := buildflags.ParseSSHSpecs(t.SSH)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
bo.SSHSpecs = t.SSH.ToPB()
|
||||
if len(bo.SSHSpecs) == 0 && buildflags.IsGitSSH(bi.ContextPath) || (inp != nil && buildflags.IsGitSSH(inp.URL)) {
|
||||
bo.SSHSpecs = []*controllerapi.SSH{{ID: "default"}}
|
||||
}
|
||||
if len(sshSpecs) == 0 && (buildflags.IsGitSSH(bi.ContextPath) || (inp != nil && buildflags.IsGitSSH(inp.URL))) {
|
||||
sshSpecs = append(sshSpecs, &controllerapi.SSH{ID: "default"})
|
||||
}
|
||||
sshAttachment, err := controllerapi.CreateSSH(sshSpecs)
|
||||
|
||||
sshAttachment, err := controllerapi.CreateSSH(bo.SSHSpecs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1342,23 +1395,14 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
||||
}
|
||||
}
|
||||
|
||||
cacheImports, err := buildflags.ParseCacheEntry(t.CacheFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if t.CacheFrom != nil {
|
||||
bo.CacheFrom = controllerapi.CreateCaches(t.CacheFrom.ToPB())
|
||||
}
|
||||
if t.CacheTo != nil {
|
||||
bo.CacheTo = controllerapi.CreateCaches(t.CacheTo.ToPB())
|
||||
}
|
||||
bo.CacheFrom = controllerapi.CreateCaches(cacheImports)
|
||||
|
||||
cacheExports, err := buildflags.ParseCacheEntry(t.CacheTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bo.CacheTo = controllerapi.CreateCaches(cacheExports)
|
||||
|
||||
outputs, err := buildflags.ParseExports(t.Outputs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bo.Exports, err = controllerapi.CreateExports(outputs)
|
||||
bo.Exports, bo.ExportsLocalPathsTemporary, err = controllerapi.CreateExports(t.Outputs.ToPB())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1373,11 +1417,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
||||
}
|
||||
}
|
||||
|
||||
attests, err := buildflags.ParseAttests(t.Attest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bo.Attests = controllerapi.CreateAttestations(attests)
|
||||
bo.Attests = controllerapi.CreateAttestations(t.Attest.ToPB())
|
||||
|
||||
bo.SourcePolicy, err = build.ReadSourcePolicy()
|
||||
if err != nil {
|
||||
@@ -1403,7 +1443,7 @@ func defaultTarget() *Target {
|
||||
return &Target{}
|
||||
}
|
||||
|
||||
func removeDupes(s []string) []string {
|
||||
func removeDupesStr(s []string) []string {
|
||||
i := 0
|
||||
seen := make(map[string]struct{}, len(s))
|
||||
for _, v := range s {
|
||||
@@ -1420,106 +1460,76 @@ func removeDupes(s []string) []string {
|
||||
return s[:i]
|
||||
}
|
||||
|
||||
func removeAttestDupes(s []string) []string {
|
||||
res := []string{}
|
||||
m := map[string]int{}
|
||||
for _, v := range s {
|
||||
att, err := buildflags.ParseAttest(v)
|
||||
if err != nil {
|
||||
res = append(res, v)
|
||||
continue
|
||||
}
|
||||
|
||||
if i, ok := m[att.Type]; ok {
|
||||
res[i] = v
|
||||
} else {
|
||||
m[att.Type] = len(res)
|
||||
res = append(res, v)
|
||||
func setPushOverride(outputs []*buildflags.ExportEntry, push bool) []*buildflags.ExportEntry {
|
||||
if !push {
|
||||
// Disable push for any relevant export types
|
||||
for i := 0; i < len(outputs); {
|
||||
output := outputs[i]
|
||||
switch output.Type {
|
||||
case "registry":
|
||||
// Filter out registry output type
|
||||
outputs[i], outputs[len(outputs)-1] = outputs[len(outputs)-1], outputs[i]
|
||||
outputs = outputs[:len(outputs)-1]
|
||||
continue
|
||||
case "image":
|
||||
// Override push attribute
|
||||
output.Attrs["push"] = "false"
|
||||
}
|
||||
i++
|
||||
}
|
||||
return outputs
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func parseOutput(str string) map[string]string {
|
||||
fields, err := csvvalue.Fields(str, nil)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
res := map[string]string{}
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
if len(parts) == 2 {
|
||||
res[parts[0]] = parts[1]
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func parseOutputType(str string) string {
|
||||
if out := parseOutput(str); out != nil {
|
||||
if v, ok := out["type"]; ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func setPushOverride(outputs []string, push bool) []string {
|
||||
var out []string
|
||||
// Force push to be enabled
|
||||
setPush := true
|
||||
for _, output := range outputs {
|
||||
typ := parseOutputType(output)
|
||||
if typ == "image" || typ == "registry" {
|
||||
// no need to set push if image or registry types already defined
|
||||
if output.Type != "docker" {
|
||||
// If there is an output type that is not docker, don't set "push"
|
||||
setPush = false
|
||||
if typ == "registry" {
|
||||
if !push {
|
||||
// don't set registry output if "push" is false
|
||||
continue
|
||||
}
|
||||
// no need to set "push" attribute to true for registry
|
||||
out = append(out, output)
|
||||
continue
|
||||
}
|
||||
out = append(out, output+",push="+strconv.FormatBool(push))
|
||||
} else {
|
||||
if typ != "docker" {
|
||||
// if there is any output that is not docker, don't set "push"
|
||||
setPush = false
|
||||
}
|
||||
out = append(out, output)
|
||||
}
|
||||
|
||||
// Set push attribute for image
|
||||
if output.Type == "image" {
|
||||
output.Attrs["push"] = "true"
|
||||
}
|
||||
}
|
||||
if push && setPush {
|
||||
out = append(out, "type=image,push=true")
|
||||
|
||||
if setPush {
|
||||
// No existing output that pushes so add one
|
||||
outputs = append(outputs, &buildflags.ExportEntry{
|
||||
Type: "image",
|
||||
Attrs: map[string]string{
|
||||
"push": "true",
|
||||
},
|
||||
})
|
||||
}
|
||||
return out
|
||||
return outputs
|
||||
}
|
||||
|
||||
func setLoadOverride(outputs []string, load bool) []string {
|
||||
func setLoadOverride(outputs []*buildflags.ExportEntry, load bool) []*buildflags.ExportEntry {
|
||||
if !load {
|
||||
return outputs
|
||||
}
|
||||
setLoad := true
|
||||
|
||||
for _, output := range outputs {
|
||||
if typ := parseOutputType(output); typ == "docker" {
|
||||
if v := parseOutput(output); v != nil {
|
||||
// dest set means we want to output as tar so don't set load
|
||||
if _, ok := v["dest"]; !ok {
|
||||
setLoad = false
|
||||
break
|
||||
}
|
||||
switch output.Type {
|
||||
case "docker":
|
||||
// if dest is not set, we can reuse this entry and do not need to set load
|
||||
if output.Destination == "" {
|
||||
return outputs
|
||||
}
|
||||
} else if typ != "image" && typ != "registry" && typ != "oci" {
|
||||
case "image", "registry", "oci":
|
||||
// Ignore
|
||||
default:
|
||||
// if there is any output that is not an image, registry
|
||||
// or oci, don't set "load" similar to push override
|
||||
setLoad = false
|
||||
break
|
||||
return outputs
|
||||
}
|
||||
}
|
||||
if setLoad {
|
||||
outputs = append(outputs, "type=docker")
|
||||
}
|
||||
|
||||
outputs = append(outputs, &buildflags.ExportEntry{
|
||||
Type: "docker",
|
||||
})
|
||||
return outputs
|
||||
}
|
||||
|
||||
@@ -1537,14 +1547,9 @@ func sanitizeTargetName(target string) string {
|
||||
return strings.ReplaceAll(target, ".", "_")
|
||||
}
|
||||
|
||||
func sliceEqual(s1, s2 []string) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
sort.Strings(s1)
|
||||
sort.Strings(s2)
|
||||
for i := range s1 {
|
||||
if s1[i] != s2[i] {
|
||||
func isSubset(s1, s2 []string) bool {
|
||||
for _, item := range s1 {
|
||||
if !slices.Contains(s2, item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1558,3 +1563,24 @@ func toNamedContexts(m map[string]string) map[string]build.NamedContext {
|
||||
}
|
||||
return m2
|
||||
}
|
||||
|
||||
type arrValue[B any] interface {
|
||||
encoding.TextUnmarshaler
|
||||
*B
|
||||
}
|
||||
|
||||
func parseArrValue[T any, PT arrValue[T]](s []string) ([]*T, error) {
|
||||
outputs := make([]*T, 0, len(s))
|
||||
for _, text := range s {
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
output := new(T)
|
||||
if err := PT(output).UnmarshalText([]byte(text)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outputs = append(outputs, output)
|
||||
}
|
||||
return outputs, nil
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
101
bake/compose.go
101
bake/compose.go
@@ -5,13 +5,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/consts"
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
"github.com/compose-spec/compose-go/v2/loader"
|
||||
composetypes "github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
dockeropts "github.com/docker/cli/opts"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
@@ -102,6 +103,12 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
shmSize = &shmSizeStr
|
||||
}
|
||||
|
||||
var networkModeP *string
|
||||
if s.Build.Network != "" {
|
||||
networkMode := s.Build.Network
|
||||
networkModeP = &networkMode
|
||||
}
|
||||
|
||||
var ulimits []string
|
||||
if s.Build.Ulimits != nil {
|
||||
for n, u := range s.Build.Ulimits {
|
||||
@@ -113,14 +120,16 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
}
|
||||
}
|
||||
|
||||
var ssh []string
|
||||
var ssh []*buildflags.SSH
|
||||
for _, bkey := range s.Build.SSH {
|
||||
sshkey := composeToBuildkitSSH(bkey)
|
||||
ssh = append(ssh, sshkey)
|
||||
}
|
||||
sort.Strings(ssh)
|
||||
slices.SortFunc(ssh, func(a, b *buildflags.SSH) int {
|
||||
return a.Less(b)
|
||||
})
|
||||
|
||||
var secrets []string
|
||||
var secrets []*buildflags.Secret
|
||||
for _, bs := range s.Build.Secrets {
|
||||
secret, err := composeToBuildkitSecret(bs, cfg.Secrets[bs.Source])
|
||||
if err != nil {
|
||||
@@ -136,6 +145,16 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
labels[k] = &v
|
||||
}
|
||||
|
||||
cacheFrom, err := buildflags.ParseCacheEntry(s.Build.CacheFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cacheTo, err := buildflags.ParseCacheEntry(s.Build.CacheTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.Targets = append(g.Targets, targetName)
|
||||
t := &Target{
|
||||
Name: targetName,
|
||||
@@ -152,9 +171,9 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
val, ok := cfg.Environment[val]
|
||||
return val, ok
|
||||
})),
|
||||
CacheFrom: s.Build.CacheFrom,
|
||||
CacheTo: s.Build.CacheTo,
|
||||
NetworkMode: &s.Build.Network,
|
||||
CacheFrom: cacheFrom,
|
||||
CacheTo: cacheTo,
|
||||
NetworkMode: networkModeP,
|
||||
SSH: ssh,
|
||||
Secrets: secrets,
|
||||
ShmSize: shmSize,
|
||||
@@ -173,7 +192,6 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
||||
c.Targets = append(c.Targets, t)
|
||||
}
|
||||
c.Groups = append(c.Groups, g)
|
||||
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
@@ -292,8 +310,10 @@ type xbake struct {
|
||||
// https://github.com/docker/docs/blob/main/content/build/bake/compose-file.md#extension-field-with-x-bake
|
||||
}
|
||||
|
||||
type stringMap map[string]string
|
||||
type stringArray []string
|
||||
type (
|
||||
stringMap map[string]string
|
||||
stringArray []string
|
||||
)
|
||||
|
||||
func (sa *stringArray) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var multi []string
|
||||
@@ -329,23 +349,45 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
|
||||
t.Tags = dedupSlice(append(t.Tags, xb.Tags...))
|
||||
}
|
||||
if len(xb.CacheFrom) > 0 {
|
||||
t.CacheFrom = dedupSlice(append(t.CacheFrom, xb.CacheFrom...))
|
||||
cacheFrom, err := buildflags.ParseCacheEntry(xb.CacheFrom)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.CacheFrom = t.CacheFrom.Merge(cacheFrom)
|
||||
}
|
||||
if len(xb.CacheTo) > 0 {
|
||||
t.CacheTo = dedupSlice(append(t.CacheTo, xb.CacheTo...))
|
||||
cacheTo, err := buildflags.ParseCacheEntry(xb.CacheTo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.CacheTo = t.CacheTo.Merge(cacheTo)
|
||||
}
|
||||
if len(xb.Secrets) > 0 {
|
||||
t.Secrets = dedupSlice(append(t.Secrets, xb.Secrets...))
|
||||
secrets, err := parseArrValue[buildflags.Secret](xb.Secrets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Secrets = t.Secrets.Merge(secrets)
|
||||
}
|
||||
if len(xb.SSH) > 0 {
|
||||
t.SSH = dedupSlice(append(t.SSH, xb.SSH...))
|
||||
sort.Strings(t.SSH)
|
||||
ssh, err := parseArrValue[buildflags.SSH](xb.SSH)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.SSH = t.SSH.Merge(ssh)
|
||||
slices.SortFunc(t.SSH, func(a, b *buildflags.SSH) int {
|
||||
return a.Less(b)
|
||||
})
|
||||
}
|
||||
if len(xb.Platforms) > 0 {
|
||||
t.Platforms = dedupSlice(append(t.Platforms, xb.Platforms...))
|
||||
}
|
||||
if len(xb.Outputs) > 0 {
|
||||
t.Outputs = dedupSlice(append(t.Outputs, xb.Outputs...))
|
||||
outputs, err := parseArrValue[buildflags.ExportEntry](xb.Outputs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Outputs = t.Outputs.Merge(outputs)
|
||||
}
|
||||
if xb.Pull != nil {
|
||||
t.Pull = xb.Pull
|
||||
@@ -365,35 +407,30 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
|
||||
|
||||
// composeToBuildkitSecret converts secret from compose format to buildkit's
|
||||
// csv format.
|
||||
func composeToBuildkitSecret(inp composetypes.ServiceSecretConfig, psecret composetypes.SecretConfig) (string, error) {
|
||||
func composeToBuildkitSecret(inp composetypes.ServiceSecretConfig, psecret composetypes.SecretConfig) (*buildflags.Secret, error) {
|
||||
if psecret.External {
|
||||
return "", errors.Errorf("unsupported external secret %s", psecret.Name)
|
||||
return nil, errors.Errorf("unsupported external secret %s", psecret.Name)
|
||||
}
|
||||
|
||||
var bkattrs []string
|
||||
secret := &buildflags.Secret{}
|
||||
if inp.Source != "" {
|
||||
bkattrs = append(bkattrs, "id="+inp.Source)
|
||||
secret.ID = inp.Source
|
||||
}
|
||||
if psecret.File != "" {
|
||||
bkattrs = append(bkattrs, "src="+psecret.File)
|
||||
secret.FilePath = psecret.File
|
||||
}
|
||||
if psecret.Environment != "" {
|
||||
bkattrs = append(bkattrs, "env="+psecret.Environment)
|
||||
secret.Env = psecret.Environment
|
||||
}
|
||||
|
||||
return strings.Join(bkattrs, ","), nil
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
// composeToBuildkitSSH converts secret from compose format to buildkit's
|
||||
// csv format.
|
||||
func composeToBuildkitSSH(sshKey composetypes.SSHKey) string {
|
||||
var bkattrs []string
|
||||
|
||||
bkattrs = append(bkattrs, sshKey.ID)
|
||||
|
||||
func composeToBuildkitSSH(sshKey composetypes.SSHKey) *buildflags.SSH {
|
||||
bkssh := &buildflags.SSH{ID: sshKey.ID}
|
||||
if sshKey.Path != "" {
|
||||
bkattrs = append(bkattrs, sshKey.Path)
|
||||
bkssh.Paths = []string{sshKey.Path}
|
||||
}
|
||||
|
||||
return strings.Join(bkattrs, "=")
|
||||
return bkssh
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestParseCompose(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
db:
|
||||
build: ./db
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
cache_to:
|
||||
- type=local,dest=path/to/cache
|
||||
ssh:
|
||||
- key=path/to/key
|
||||
- key=/path/to/key
|
||||
- default
|
||||
secrets:
|
||||
- token
|
||||
@@ -74,14 +74,14 @@ secrets:
|
||||
require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
|
||||
require.Equal(t, 1, len(c.Targets[1].Args))
|
||||
require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
|
||||
require.Equal(t, []string{"type=local,src=path/to/cache"}, c.Targets[1].CacheFrom)
|
||||
require.Equal(t, []string{"type=local,dest=path/to/cache"}, c.Targets[1].CacheTo)
|
||||
require.Equal(t, []string{"type=local,src=path/to/cache"}, stringify(c.Targets[1].CacheFrom))
|
||||
require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[1].CacheTo))
|
||||
require.Equal(t, "none", *c.Targets[1].NetworkMode)
|
||||
require.Equal(t, []string{"default", "key=path/to/key"}, c.Targets[1].SSH)
|
||||
require.Equal(t, []string{"default", "key=/path/to/key"}, stringify(c.Targets[1].SSH))
|
||||
require.Equal(t, []string{
|
||||
"id=token,env=ENV_TOKEN",
|
||||
"id=aws,src=/root/.aws/credentials",
|
||||
}, c.Targets[1].Secrets)
|
||||
"id=token,env=ENV_TOKEN",
|
||||
}, stringify(c.Targets[1].Secrets))
|
||||
|
||||
require.Equal(t, "webapp2", c.Targets[2].Name)
|
||||
require.Equal(t, "dir", *c.Targets[2].Context)
|
||||
@@ -89,7 +89,7 @@ secrets:
|
||||
}
|
||||
|
||||
func TestNoBuildOutOfTreeService(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
external:
|
||||
image: "verycooldb:1337"
|
||||
@@ -103,7 +103,7 @@ services:
|
||||
}
|
||||
|
||||
func TestParseComposeTarget(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
db:
|
||||
build:
|
||||
@@ -129,7 +129,7 @@ services:
|
||||
}
|
||||
|
||||
func TestComposeBuildWithoutContext(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
db:
|
||||
build:
|
||||
@@ -153,7 +153,7 @@ services:
|
||||
}
|
||||
|
||||
func TestBuildArgEnvCompose(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
version: "3.8"
|
||||
services:
|
||||
example:
|
||||
@@ -179,7 +179,7 @@ services:
|
||||
}
|
||||
|
||||
func TestInconsistentComposeFile(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
webapp:
|
||||
entrypoint: echo 1
|
||||
@@ -190,7 +190,7 @@ services:
|
||||
}
|
||||
|
||||
func TestAdvancedNetwork(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
db:
|
||||
networks:
|
||||
@@ -215,7 +215,7 @@ networks:
|
||||
}
|
||||
|
||||
func TestTags(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
example:
|
||||
image: example
|
||||
@@ -233,7 +233,7 @@ services:
|
||||
}
|
||||
|
||||
func TestDependsOnList(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
@@ -269,7 +269,7 @@ networks:
|
||||
}
|
||||
|
||||
func TestComposeExt(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
addon:
|
||||
image: ct-addon:bar
|
||||
@@ -283,7 +283,7 @@ services:
|
||||
tags:
|
||||
- ct-addon:baz
|
||||
ssh:
|
||||
key: path/to/key
|
||||
key: /path/to/key
|
||||
args:
|
||||
CT_ECR: foo
|
||||
CT_TAG: bar
|
||||
@@ -336,23 +336,23 @@ services:
|
||||
require.Equal(t, map[string]*string{"CT_ECR": ptrstr("foo"), "CT_TAG": ptrstr("bar")}, c.Targets[0].Args)
|
||||
require.Equal(t, []string{"ct-addon:baz", "ct-addon:foo", "ct-addon:alp"}, c.Targets[0].Tags)
|
||||
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[0].Platforms)
|
||||
require.Equal(t, []string{"user/app:cache", "type=local,src=path/to/cache"}, c.Targets[0].CacheFrom)
|
||||
require.Equal(t, []string{"user/app:cache", "type=local,dest=path/to/cache"}, c.Targets[0].CacheTo)
|
||||
require.Equal(t, []string{"default", "key=path/to/key", "other=path/to/otherkey"}, c.Targets[0].SSH)
|
||||
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", "user/app:cache"}, stringify(c.Targets[0].CacheTo))
|
||||
require.Equal(t, []string{"default", "key=/path/to/key", "other=path/to/otherkey"}, stringify(c.Targets[0].SSH))
|
||||
require.Equal(t, newBool(true), c.Targets[0].Pull)
|
||||
require.Equal(t, map[string]string{"alpine": "docker-image://alpine:3.13"}, c.Targets[0].Contexts)
|
||||
require.Equal(t, []string{"ct-fake-aws:bar"}, c.Targets[1].Tags)
|
||||
require.Equal(t, []string{"id=mysecret,src=/local/secret", "id=mysecret2,src=/local/secret2"}, c.Targets[1].Secrets)
|
||||
require.Equal(t, []string{"default"}, c.Targets[1].SSH)
|
||||
require.Equal(t, []string{"id=mysecret,src=/local/secret", "id=mysecret2,src=/local/secret2"}, stringify(c.Targets[1].Secrets))
|
||||
require.Equal(t, []string{"default"}, stringify(c.Targets[1].SSH))
|
||||
require.Equal(t, []string{"linux/arm64"}, c.Targets[1].Platforms)
|
||||
require.Equal(t, []string{"type=docker"}, c.Targets[1].Outputs)
|
||||
require.Equal(t, []string{"type=docker"}, stringify(c.Targets[1].Outputs))
|
||||
require.Equal(t, newBool(true), c.Targets[1].NoCache)
|
||||
require.Equal(t, ptrstr("128MiB"), c.Targets[1].ShmSize)
|
||||
require.Equal(t, []string{"nofile=1024:1024"}, c.Targets[1].Ulimits)
|
||||
}
|
||||
|
||||
func TestComposeExtDedup(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
webapp:
|
||||
image: app:bar
|
||||
@@ -383,9 +383,9 @@ services:
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, []string{"ct-addon:foo", "ct-addon:baz"}, c.Targets[0].Tags)
|
||||
require.Equal(t, []string{"user/app:cache", "type=local,src=path/to/cache"}, c.Targets[0].CacheFrom)
|
||||
require.Equal(t, []string{"user/app:cache", "type=local,dest=path/to/cache"}, c.Targets[0].CacheTo)
|
||||
require.Equal(t, []string{"default", "key=path/to/key"}, c.Targets[0].SSH)
|
||||
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", "user/app:cache"}, stringify(c.Targets[0].CacheTo))
|
||||
require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH))
|
||||
}
|
||||
|
||||
func TestEnv(t *testing.T) {
|
||||
@@ -396,7 +396,7 @@ func TestEnv(t *testing.T) {
|
||||
_, err = envf.WriteString("FOO=bsdf -csdf\n")
|
||||
require.NoError(t, err)
|
||||
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
scratch:
|
||||
build:
|
||||
@@ -424,7 +424,7 @@ func TestDotEnv(t *testing.T) {
|
||||
err := os.WriteFile(filepath.Join(tmpdir, ".env"), []byte("FOO=bar"), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
scratch:
|
||||
build:
|
||||
@@ -443,7 +443,7 @@ services:
|
||||
}
|
||||
|
||||
func TestPorts(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
foo:
|
||||
build:
|
||||
@@ -664,7 +664,7 @@ target "default" {
|
||||
}
|
||||
|
||||
func TestComposeNullArgs(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
scratch:
|
||||
build:
|
||||
@@ -680,7 +680,7 @@ services:
|
||||
}
|
||||
|
||||
func TestDependsOn(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
foo:
|
||||
build:
|
||||
@@ -711,7 +711,7 @@ services:
|
||||
`), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
include:
|
||||
- compose-foo.yml
|
||||
|
||||
@@ -740,7 +740,7 @@ services:
|
||||
}
|
||||
|
||||
func TestDevelop(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
scratch:
|
||||
build:
|
||||
@@ -759,7 +759,7 @@ services:
|
||||
}
|
||||
|
||||
func TestCgroup(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
scratch:
|
||||
build:
|
||||
@@ -772,7 +772,7 @@ services:
|
||||
}
|
||||
|
||||
func TestProjectName(t *testing.T) {
|
||||
var dt = []byte(`
|
||||
dt := []byte(`
|
||||
services:
|
||||
scratch:
|
||||
build:
|
||||
|
@@ -2,17 +2,24 @@ package bake
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/util/osutil"
|
||||
"github.com/moby/buildkit/util/entitlements"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type EntitlementKey string
|
||||
@@ -67,10 +74,8 @@ func ParseEntitlements(in []string) (EntitlementConf, error) {
|
||||
conf.ImagePush = append(conf.ImagePush, v)
|
||||
conf.ImageLoad = append(conf.ImageLoad, v)
|
||||
default:
|
||||
return conf, errors.Errorf("uknown entitlement key %q", k)
|
||||
return conf, errors.Errorf("unknown entitlement key %q", k)
|
||||
}
|
||||
|
||||
// TODO: dedupe slices and parent paths
|
||||
}
|
||||
}
|
||||
return conf, nil
|
||||
@@ -101,10 +106,66 @@ func (c EntitlementConf) check(bo build.Options, expected *EntitlementConf) erro
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rwPaths := map[string]struct{}{}
|
||||
roPaths := map[string]struct{}{}
|
||||
|
||||
for _, p := range collectLocalPaths(bo.Inputs) {
|
||||
roPaths[p] = struct{}{}
|
||||
}
|
||||
|
||||
for _, p := range bo.ExportsLocalPathsTemporary {
|
||||
rwPaths[p] = struct{}{}
|
||||
}
|
||||
|
||||
for _, ce := range bo.CacheTo {
|
||||
if ce.Type == "local" {
|
||||
if dest, ok := ce.Attrs["dest"]; ok {
|
||||
rwPaths[dest] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ci := range bo.CacheFrom {
|
||||
if ci.Type == "local" {
|
||||
if src, ok := ci.Attrs["src"]; ok {
|
||||
roPaths[src] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, secret := range bo.SecretSpecs {
|
||||
if secret.FilePath != "" {
|
||||
roPaths[secret.FilePath] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
for _, ssh := range bo.SSHSpecs {
|
||||
for _, p := range ssh.Paths {
|
||||
roPaths[p] = struct{}{}
|
||||
}
|
||||
if len(ssh.Paths) == 0 {
|
||||
if !c.SSH {
|
||||
expected.SSH = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
expected.FSRead, err = findMissingPaths(c.FSRead, roPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expected.FSWrite, err = findMissingPaths(c.FSWrite, rwPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c EntitlementConf) Prompt(ctx context.Context, out io.Writer) error {
|
||||
func (c EntitlementConf) Prompt(ctx context.Context, isRemote bool, out io.Writer) error {
|
||||
var term bool
|
||||
if _, err := console.ConsoleFromFile(os.Stdin); err == nil {
|
||||
term = true
|
||||
@@ -113,32 +174,78 @@ func (c EntitlementConf) Prompt(ctx context.Context, out io.Writer) error {
|
||||
var msgs []string
|
||||
var flags []string
|
||||
|
||||
// these warnings are currently disabled to give users time to update
|
||||
var msgsFS []string
|
||||
var flagsFS []string
|
||||
|
||||
if c.NetworkHost {
|
||||
msgs = append(msgs, " - Running build containers that can access host network")
|
||||
flags = append(flags, "network.host")
|
||||
flags = append(flags, string(EntitlementKeyNetworkHost))
|
||||
}
|
||||
if c.SecurityInsecure {
|
||||
msgs = append(msgs, " - Running privileged containers that can make system changes")
|
||||
flags = append(flags, "security.insecure")
|
||||
flags = append(flags, string(EntitlementKeySecurityInsecure))
|
||||
}
|
||||
|
||||
if len(msgs) == 0 {
|
||||
if c.SSH {
|
||||
msgsFS = append(msgsFS, " - Forwarding default SSH agent socket")
|
||||
flagsFS = append(flagsFS, string(EntitlementKeySSH))
|
||||
}
|
||||
|
||||
roPaths, rwPaths, commonPaths := groupSamePaths(c.FSRead, c.FSWrite)
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get current working directory")
|
||||
}
|
||||
wd, err = filepath.EvalSymlinks(wd)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to evaluate working directory")
|
||||
}
|
||||
roPaths = toRelativePaths(roPaths, wd)
|
||||
rwPaths = toRelativePaths(rwPaths, wd)
|
||||
commonPaths = toRelativePaths(commonPaths, wd)
|
||||
|
||||
if len(commonPaths) > 0 {
|
||||
for _, p := range commonPaths {
|
||||
msgsFS = append(msgsFS, fmt.Sprintf(" - Read and write access to path %s", p))
|
||||
flagsFS = append(flagsFS, string(EntitlementKeyFS)+"="+p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(roPaths) > 0 {
|
||||
for _, p := range roPaths {
|
||||
msgsFS = append(msgsFS, fmt.Sprintf(" - Read access to path %s", p))
|
||||
flagsFS = append(flagsFS, string(EntitlementKeyFSRead)+"="+p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rwPaths) > 0 {
|
||||
for _, p := range rwPaths {
|
||||
msgsFS = append(msgsFS, fmt.Sprintf(" - Write access to path %s", p))
|
||||
flagsFS = append(flagsFS, string(EntitlementKeyFSWrite)+"="+p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(msgs) == 0 && len(msgsFS) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "Your build is requesting privileges for following possibly insecure capabilities:\n\n")
|
||||
for _, m := range msgs {
|
||||
for _, m := range slices.Concat(msgs, msgsFS) {
|
||||
fmt.Fprintf(out, "%s\n", m)
|
||||
}
|
||||
|
||||
for i, f := range flags {
|
||||
flags[i] = "--allow=" + f
|
||||
}
|
||||
for i, f := range flagsFS {
|
||||
flagsFS[i] = "--allow=" + f
|
||||
}
|
||||
|
||||
if term {
|
||||
fmt.Fprintf(out, "\nIn order to not see this message in the future pass %q to grant requested privileges.\n", strings.Join(flags, " "))
|
||||
fmt.Fprintf(out, "\nIn order to not see this message in the future pass %q to grant requested privileges.\n", strings.Join(slices.Concat(flags, flagsFS), " "))
|
||||
} else {
|
||||
fmt.Fprintf(out, "\nPass %q to grant requested privileges.\n", strings.Join(flags, " "))
|
||||
fmt.Fprintf(out, "\nPass %q to grant requested privileges.\n", strings.Join(slices.Concat(flags, flagsFS), " "))
|
||||
}
|
||||
|
||||
args := append([]string(nil), os.Args...)
|
||||
@@ -149,7 +256,33 @@ func (c EntitlementConf) Prompt(ctx context.Context, out io.Writer) error {
|
||||
|
||||
if idx != -1 {
|
||||
fmt.Fprintf(out, "\nYour full command with requested privileges:\n\n")
|
||||
fmt.Fprintf(out, "%s %s %s\n\n", strings.Join(args[:idx+1], " "), strings.Join(flags, " "), strings.Join(args[idx+1:], " "))
|
||||
fmt.Fprintf(out, "%s %s %s\n\n", strings.Join(args[:idx+1], " "), strings.Join(slices.Concat(flags, flagsFS), " "), strings.Join(args[idx+1:], " "))
|
||||
}
|
||||
|
||||
fsEntitlementsEnabled := true
|
||||
if isRemote {
|
||||
if v, ok := os.LookupEnv("BAKE_ALLOW_REMOTE_FS_ACCESS"); ok {
|
||||
vv, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse BAKE_ALLOW_REMOTE_FS_ACCESS value %q", v)
|
||||
}
|
||||
fsEntitlementsEnabled = !vv
|
||||
}
|
||||
}
|
||||
v, fsEntitlementsSet := os.LookupEnv("BUILDX_BAKE_ENTITLEMENTS_FS")
|
||||
if fsEntitlementsSet {
|
||||
vv, err := strconv.ParseBool(v)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to parse BUILDX_BAKE_ENTITLEMENTS_FS value %q", v)
|
||||
}
|
||||
fsEntitlementsEnabled = vv
|
||||
}
|
||||
|
||||
if !fsEntitlementsEnabled && len(msgs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if fsEntitlementsEnabled && !fsEntitlementsSet && len(msgsFS) != 0 {
|
||||
fmt.Fprintf(out, "To disable filesystem entitlements checks, you can set BUILDX_BAKE_ENTITLEMENTS_FS=0 .\n\n")
|
||||
}
|
||||
|
||||
if term {
|
||||
@@ -173,3 +306,296 @@ func (c EntitlementConf) Prompt(ctx context.Context, out io.Writer) error {
|
||||
|
||||
return errors.Errorf("additional privileges requested")
|
||||
}
|
||||
|
||||
func isParentOrEqualPath(p, parent string) bool {
|
||||
if p == parent || parent == "/" {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(p, filepath.Clean(parent+string(filepath.Separator))) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func findMissingPaths(set []string, paths map[string]struct{}) ([]string, error) {
|
||||
set, allowAny, err := evaluatePaths(set)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if allowAny {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
paths, err = evaluateToExistingPaths(paths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths, err = dedupPaths(paths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make([]string, 0, len(paths))
|
||||
loop0:
|
||||
for p := range paths {
|
||||
for _, c := range set {
|
||||
if isParentOrEqualPath(p, c) {
|
||||
continue loop0
|
||||
}
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
slices.Sort(out)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func dedupPaths(in map[string]struct{}) (map[string]struct{}, error) {
|
||||
arr := make([]string, 0, len(in))
|
||||
for p := range in {
|
||||
arr = append(arr, filepath.Clean(p))
|
||||
}
|
||||
|
||||
slices.SortFunc(arr, func(a, b string) int {
|
||||
return cmp.Compare(len(a), len(b))
|
||||
})
|
||||
|
||||
m := make(map[string]struct{}, len(arr))
|
||||
loop0:
|
||||
for _, p := range arr {
|
||||
for parent := range m {
|
||||
if strings.HasPrefix(p, parent+string(filepath.Separator)) {
|
||||
continue loop0
|
||||
}
|
||||
}
|
||||
m[p] = struct{}{}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func toRelativePaths(in []string, wd string) []string {
|
||||
out := make([]string, 0, len(in))
|
||||
for _, p := range in {
|
||||
rel, err := filepath.Rel(wd, p)
|
||||
if err == nil {
|
||||
// allow up to one level of ".." in the path
|
||||
if !strings.HasPrefix(rel, ".."+string(filepath.Separator)+"..") {
|
||||
out = append(out, rel)
|
||||
continue
|
||||
}
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func groupSamePaths(in1, in2 []string) ([]string, []string, []string) {
|
||||
if in1 == nil || in2 == nil {
|
||||
return in1, in2, nil
|
||||
}
|
||||
|
||||
slices.Sort(in1)
|
||||
slices.Sort(in2)
|
||||
|
||||
common := []string{}
|
||||
i, j := 0, 0
|
||||
|
||||
for i < len(in1) && j < len(in2) {
|
||||
switch {
|
||||
case in1[i] == in2[j]:
|
||||
common = append(common, in1[i])
|
||||
i++
|
||||
j++
|
||||
case in1[i] < in2[j]:
|
||||
i++
|
||||
default:
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
in1 = removeCommonPaths(in1, common)
|
||||
in2 = removeCommonPaths(in2, common)
|
||||
|
||||
return in1, in2, common
|
||||
}
|
||||
|
||||
func removeCommonPaths(in, common []string) []string {
|
||||
filtered := make([]string, 0, len(in))
|
||||
commonIndex := 0
|
||||
for _, path := range in {
|
||||
if commonIndex < len(common) && path == common[commonIndex] {
|
||||
commonIndex++
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, path)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func evaluatePaths(in []string) ([]string, bool, error) {
|
||||
out := make([]string, 0, len(in))
|
||||
allowAny := false
|
||||
for _, p := range in {
|
||||
if p == "*" {
|
||||
allowAny = true
|
||||
continue
|
||||
}
|
||||
v, err := filepath.Abs(p)
|
||||
if err != nil {
|
||||
logrus.Warnf("failed to evaluate entitlement path %q: %v", p, err)
|
||||
continue
|
||||
}
|
||||
v, rest, err := evaluateToExistingPath(v)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrapf(err, "failed to evaluate path %q", p)
|
||||
}
|
||||
v, err = osutil.GetLongPathName(v)
|
||||
if err != nil {
|
||||
return nil, false, errors.Wrapf(err, "failed to evaluate path %q", p)
|
||||
}
|
||||
if rest != "" {
|
||||
v = filepath.Join(v, rest)
|
||||
}
|
||||
out = append(out, v)
|
||||
}
|
||||
return out, allowAny, nil
|
||||
}
|
||||
|
||||
func evaluateToExistingPaths(in map[string]struct{}) (map[string]struct{}, error) {
|
||||
m := make(map[string]struct{}, len(in))
|
||||
for p := range in {
|
||||
v, _, err := evaluateToExistingPath(p)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to evaluate path %q", p)
|
||||
}
|
||||
v, err = osutil.GetLongPathName(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to evaluate path %q", p)
|
||||
}
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func evaluateToExistingPath(in string) (string, string, error) {
|
||||
in, err := filepath.Abs(in)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
volLen := volumeNameLen(in)
|
||||
pathSeparator := string(os.PathSeparator)
|
||||
|
||||
if volLen < len(in) && os.IsPathSeparator(in[volLen]) {
|
||||
volLen++
|
||||
}
|
||||
vol := in[:volLen]
|
||||
dest := vol
|
||||
linksWalked := 0
|
||||
var end int
|
||||
for start := volLen; start < len(in); start = end {
|
||||
for start < len(in) && os.IsPathSeparator(in[start]) {
|
||||
start++
|
||||
}
|
||||
end = start
|
||||
for end < len(in) && !os.IsPathSeparator(in[end]) {
|
||||
end++
|
||||
}
|
||||
|
||||
if end == start {
|
||||
break
|
||||
} else if in[start:end] == "." {
|
||||
continue
|
||||
} else if in[start:end] == ".." {
|
||||
var r int
|
||||
for r = len(dest) - 1; r >= volLen; r-- {
|
||||
if os.IsPathSeparator(dest[r]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if r < volLen || dest[r+1:] == ".." {
|
||||
if len(dest) > volLen {
|
||||
dest += pathSeparator
|
||||
}
|
||||
dest += ".."
|
||||
} else {
|
||||
dest = dest[:r]
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dest) > volumeNameLen(dest) && !os.IsPathSeparator(dest[len(dest)-1]) {
|
||||
dest += pathSeparator
|
||||
}
|
||||
dest += in[start:end]
|
||||
|
||||
fi, err := os.Lstat(dest)
|
||||
if err != nil {
|
||||
// If the component doesn't exist, return the last valid path
|
||||
if os.IsNotExist(err) {
|
||||
for r := len(dest) - 1; r >= volLen; r-- {
|
||||
if os.IsPathSeparator(dest[r]) {
|
||||
return dest[:r], in[start:], nil
|
||||
}
|
||||
}
|
||||
return vol, in[start:], nil
|
||||
}
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if fi.Mode()&fs.ModeSymlink == 0 {
|
||||
if !fi.Mode().IsDir() && end < len(in) {
|
||||
return "", "", syscall.ENOTDIR
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
linksWalked++
|
||||
if linksWalked > 255 {
|
||||
return "", "", errors.New("too many symlinks")
|
||||
}
|
||||
|
||||
link, err := os.Readlink(dest)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
in = link + in[end:]
|
||||
|
||||
v := volumeNameLen(link)
|
||||
if v > 0 {
|
||||
if v < len(link) && os.IsPathSeparator(link[v]) {
|
||||
v++
|
||||
}
|
||||
vol = link[:v]
|
||||
dest = vol
|
||||
end = len(vol)
|
||||
} else if len(link) > 0 && os.IsPathSeparator(link[0]) {
|
||||
dest = link[:1]
|
||||
end = 1
|
||||
vol = link[:1]
|
||||
volLen = 1
|
||||
} else {
|
||||
var r int
|
||||
for r = len(dest) - 1; r >= volLen; r-- {
|
||||
if os.IsPathSeparator(dest[r]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if r < volLen {
|
||||
dest = vol
|
||||
} else {
|
||||
dest = dest[:r]
|
||||
}
|
||||
end = 0
|
||||
}
|
||||
}
|
||||
return filepath.Clean(dest), "", nil
|
||||
}
|
||||
|
||||
func volumeNameLen(s string) int {
|
||||
return len(filepath.VolumeName(s))
|
||||
}
|
||||
|
486
bake/entitlements_test.go
Normal file
486
bake/entitlements_test.go
Normal file
@@ -0,0 +1,486 @@
|
||||
package bake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/util/osutil"
|
||||
"github.com/moby/buildkit/client/llb"
|
||||
"github.com/moby/buildkit/util/entitlements"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEvaluateToExistingPath(t *testing.T) {
|
||||
tempDir, err := osutil.GetLongPathName(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Setup temporary directory structure for testing
|
||||
existingFile := filepath.Join(tempDir, "existing_file")
|
||||
require.NoError(t, os.WriteFile(existingFile, []byte("test"), 0644))
|
||||
|
||||
existingDir := filepath.Join(tempDir, "existing_dir")
|
||||
require.NoError(t, os.Mkdir(existingDir, 0755))
|
||||
|
||||
symlinkToFile := filepath.Join(tempDir, "symlink_to_file")
|
||||
require.NoError(t, os.Symlink(existingFile, symlinkToFile))
|
||||
|
||||
symlinkToDir := filepath.Join(tempDir, "symlink_to_dir")
|
||||
require.NoError(t, os.Symlink(existingDir, symlinkToDir))
|
||||
|
||||
nonexistentPath := filepath.Join(tempDir, "nonexistent", "path", "file.txt")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
expectErr bool
|
||||
}{
|
||||
{
|
||||
name: "Existing file",
|
||||
input: existingFile,
|
||||
expected: existingFile,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "Existing directory",
|
||||
input: existingDir,
|
||||
expected: existingDir,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "Symlink to file",
|
||||
input: symlinkToFile,
|
||||
expected: existingFile,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "Symlink to directory",
|
||||
input: symlinkToDir,
|
||||
expected: existingDir,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "Non-existent path",
|
||||
input: nonexistentPath,
|
||||
expected: tempDir,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "Non-existent intermediate path",
|
||||
input: filepath.Join(tempDir, "nonexistent", "file.txt"),
|
||||
expected: tempDir,
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
name: "Root path",
|
||||
input: "/",
|
||||
expected: func() string {
|
||||
root, _ := filepath.Abs("/")
|
||||
return root
|
||||
}(),
|
||||
expectErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, _, err := evaluateToExistingPath(tt.input)
|
||||
|
||||
if tt.expectErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDedupePaths(t *testing.T) {
|
||||
wd := osutil.GetWd()
|
||||
tcases := []struct {
|
||||
in map[string]struct{}
|
||||
out map[string]struct{}
|
||||
}{
|
||||
{
|
||||
in: map[string]struct{}{
|
||||
"/a/b/c": {},
|
||||
"/a/b/d": {},
|
||||
"/a/b/e": {},
|
||||
},
|
||||
out: map[string]struct{}{
|
||||
"/a/b/c": {},
|
||||
"/a/b/d": {},
|
||||
"/a/b/e": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: map[string]struct{}{
|
||||
"/a/b/c": {},
|
||||
"/a/b/c/d": {},
|
||||
"/a/b/c/d/e": {},
|
||||
"/a/b/../b/c": {},
|
||||
},
|
||||
out: map[string]struct{}{
|
||||
"/a/b/c": {},
|
||||
},
|
||||
},
|
||||
{
|
||||
in: map[string]struct{}{
|
||||
filepath.Join(wd, "a/b/c"): {},
|
||||
filepath.Join(wd, "../aa"): {},
|
||||
filepath.Join(wd, "a/b"): {},
|
||||
filepath.Join(wd, "a/b/d"): {},
|
||||
filepath.Join(wd, "../aa/b"): {},
|
||||
filepath.Join(wd, "../../bb"): {},
|
||||
},
|
||||
out: map[string]struct{}{
|
||||
"a/b": {},
|
||||
"../aa": {},
|
||||
filepath.Join(wd, "../../bb"): {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tcases {
|
||||
t.Run(fmt.Sprintf("case%d", i), func(t *testing.T) {
|
||||
out, err := dedupPaths(tc.in)
|
||||
if err != nil {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// convert to relative paths as that is shown to user
|
||||
arr := make([]string, 0, len(out))
|
||||
for k := range out {
|
||||
arr = append(arr, k)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
arr = toRelativePaths(arr, wd)
|
||||
m := make(map[string]struct{})
|
||||
for _, v := range arr {
|
||||
m[filepath.ToSlash(v)] = struct{}{}
|
||||
}
|
||||
o := make(map[string]struct{}, len(tc.out))
|
||||
for k := range tc.out {
|
||||
o[filepath.ToSlash(k)] = struct{}{}
|
||||
}
|
||||
require.Equal(t, o, m)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEntitlements(t *testing.T) {
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
|
||||
// the paths returned by entitlements validation will have symlinks resolved
|
||||
expDir1, err := filepath.EvalSymlinks(dir1)
|
||||
require.NoError(t, err)
|
||||
expDir2, err := filepath.EvalSymlinks(dir2)
|
||||
require.NoError(t, err)
|
||||
|
||||
escapeLink := filepath.Join(dir1, "escape_link")
|
||||
require.NoError(t, os.Symlink("../../aa", escapeLink))
|
||||
|
||||
wd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
expWd, err := filepath.EvalSymlinks(wd)
|
||||
require.NoError(t, err)
|
||||
|
||||
tcases := []struct {
|
||||
name string
|
||||
conf EntitlementConf
|
||||
opt build.Options
|
||||
expected EntitlementConf
|
||||
}{
|
||||
{
|
||||
name: "No entitlements",
|
||||
opt: build.Options{
|
||||
Inputs: build.Inputs{
|
||||
ContextState: &llb.State{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NetworkHostMissing",
|
||||
opt: build.Options{
|
||||
Allow: []entitlements.Entitlement{
|
||||
entitlements.EntitlementNetworkHost,
|
||||
},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
NetworkHost: true,
|
||||
FSRead: []string{expWd},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NetworkHostSet",
|
||||
conf: EntitlementConf{
|
||||
NetworkHost: true,
|
||||
},
|
||||
opt: build.Options{
|
||||
Allow: []entitlements.Entitlement{
|
||||
entitlements.EntitlementNetworkHost,
|
||||
},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
FSRead: []string{expWd},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SecurityAndNetworkHostMissing",
|
||||
opt: build.Options{
|
||||
Allow: []entitlements.Entitlement{
|
||||
entitlements.EntitlementNetworkHost,
|
||||
entitlements.EntitlementSecurityInsecure,
|
||||
},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
NetworkHost: true,
|
||||
SecurityInsecure: true,
|
||||
FSRead: []string{expWd},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SecurityMissingAndNetworkHostSet",
|
||||
conf: EntitlementConf{
|
||||
NetworkHost: true,
|
||||
},
|
||||
opt: build.Options{
|
||||
Allow: []entitlements.Entitlement{
|
||||
entitlements.EntitlementNetworkHost,
|
||||
entitlements.EntitlementSecurityInsecure,
|
||||
},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
SecurityInsecure: true,
|
||||
FSRead: []string{expWd},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SSHMissing",
|
||||
opt: build.Options{
|
||||
SSHSpecs: []*pb.SSH{
|
||||
{
|
||||
ID: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
SSH: true,
|
||||
FSRead: []string{expWd},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ExportLocal",
|
||||
opt: build.Options{
|
||||
ExportsLocalPathsTemporary: []string{
|
||||
dir1,
|
||||
filepath.Join(dir1, "subdir"),
|
||||
dir2,
|
||||
},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
FSWrite: func() []string {
|
||||
exp := []string{expDir1, expDir2}
|
||||
slices.Sort(exp)
|
||||
return exp
|
||||
}(),
|
||||
FSRead: []string{expWd},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SecretFromSubFile",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
{
|
||||
FilePath: filepath.Join(dir1, "subfile"),
|
||||
},
|
||||
},
|
||||
},
|
||||
conf: EntitlementConf{
|
||||
FSRead: []string{wd, dir1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SecretFromEscapeLink",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
{
|
||||
FilePath: escapeLink,
|
||||
},
|
||||
},
|
||||
},
|
||||
conf: EntitlementConf{
|
||||
FSRead: []string{wd, dir1},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
FSRead: []string{filepath.Join(expDir1, "../..")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SecretFromEscapeLinkAllowRoot",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
{
|
||||
FilePath: escapeLink,
|
||||
},
|
||||
},
|
||||
},
|
||||
conf: EntitlementConf{
|
||||
FSRead: []string{"/"},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
FSRead: func() []string {
|
||||
// on windows root (/) is only allowed if it is the same volume as wd
|
||||
if filepath.VolumeName(wd) == filepath.VolumeName(escapeLink) {
|
||||
return nil
|
||||
}
|
||||
// if not, then escapeLink is not allowed
|
||||
exp, _, err := evaluateToExistingPath(escapeLink)
|
||||
require.NoError(t, err)
|
||||
exp, err = filepath.EvalSymlinks(exp)
|
||||
require.NoError(t, err)
|
||||
return []string{exp}
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "SecretFromEscapeLinkAllowAny",
|
||||
opt: build.Options{
|
||||
SecretSpecs: []*pb.Secret{
|
||||
{
|
||||
FilePath: escapeLink,
|
||||
},
|
||||
},
|
||||
},
|
||||
conf: EntitlementConf{
|
||||
FSRead: []string{"*"},
|
||||
},
|
||||
expected: EntitlementConf{},
|
||||
},
|
||||
{
|
||||
name: "NonExistingAllowedPathSubpath",
|
||||
opt: build.Options{
|
||||
ExportsLocalPathsTemporary: []string{
|
||||
dir1,
|
||||
},
|
||||
},
|
||||
conf: EntitlementConf{
|
||||
FSRead: []string{wd},
|
||||
FSWrite: []string{filepath.Join(dir1, "not/exists")},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
FSWrite: []string{expDir1}, // dir1 is still needed as only subpath was allowed
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NonExistingAllowedPathMatches",
|
||||
opt: build.Options{
|
||||
ExportsLocalPathsTemporary: []string{
|
||||
filepath.Join(dir1, "not/exists"),
|
||||
},
|
||||
},
|
||||
conf: EntitlementConf{
|
||||
FSRead: []string{wd},
|
||||
FSWrite: []string{filepath.Join(dir1, "not/exists")},
|
||||
},
|
||||
expected: EntitlementConf{
|
||||
FSWrite: []string{expDir1}, // dir1 is still needed as build also needs to write not/exists directory
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NonExistingBuildPath",
|
||||
opt: build.Options{
|
||||
ExportsLocalPathsTemporary: []string{
|
||||
filepath.Join(dir1, "not/exists"),
|
||||
},
|
||||
},
|
||||
conf: EntitlementConf{
|
||||
FSRead: []string{wd},
|
||||
FSWrite: []string{dir1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
expected, err := tc.conf.Validate(map[string]build.Options{"test": tc.opt})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupSamePaths(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in1 []string
|
||||
in2 []string
|
||||
expected1 []string
|
||||
expected2 []string
|
||||
expectedC []string
|
||||
}{
|
||||
{
|
||||
name: "All common paths",
|
||||
in1: []string{"/path/a", "/path/b", "/path/c"},
|
||||
in2: []string{"/path/a", "/path/b", "/path/c"},
|
||||
expected1: []string{},
|
||||
expected2: []string{},
|
||||
expectedC: []string{"/path/a", "/path/b", "/path/c"},
|
||||
},
|
||||
{
|
||||
name: "No common paths",
|
||||
in1: []string{"/path/a", "/path/b"},
|
||||
in2: []string{"/path/c", "/path/d"},
|
||||
expected1: []string{"/path/a", "/path/b"},
|
||||
expected2: []string{"/path/c", "/path/d"},
|
||||
expectedC: []string{},
|
||||
},
|
||||
{
|
||||
name: "Some common paths",
|
||||
in1: []string{"/path/a", "/path/b", "/path/c"},
|
||||
in2: []string{"/path/b", "/path/c", "/path/d"},
|
||||
expected1: []string{"/path/a"},
|
||||
expected2: []string{"/path/d"},
|
||||
expectedC: []string{"/path/b", "/path/c"},
|
||||
},
|
||||
{
|
||||
name: "Empty inputs",
|
||||
in1: []string{},
|
||||
in2: []string{},
|
||||
expected1: []string{},
|
||||
expected2: []string{},
|
||||
expectedC: []string{},
|
||||
},
|
||||
{
|
||||
name: "One empty input",
|
||||
in1: []string{"/path/a", "/path/b"},
|
||||
in2: []string{},
|
||||
expected1: []string{"/path/a", "/path/b"},
|
||||
expected2: []string{},
|
||||
expectedC: []string{},
|
||||
},
|
||||
{
|
||||
name: "Unsorted inputs with common paths",
|
||||
in1: []string{"/path/c", "/path/a", "/path/b"},
|
||||
in2: []string{"/path/b", "/path/c", "/path/a"},
|
||||
expected1: []string{},
|
||||
expected2: []string{},
|
||||
expectedC: []string{"/path/a", "/path/b", "/path/c"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
out1, out2, common := groupSamePaths(tt.in1, tt.in2)
|
||||
require.Equal(t, tt.expected1, out1, "in1 should match expected1")
|
||||
require.Equal(t, tt.expected2, out2, "in2 should match expected2")
|
||||
require.Equal(t, tt.expectedC, common, "common should match expectedC")
|
||||
})
|
||||
}
|
||||
}
|
217
bake/hcl_test.go
217
bake/hcl_test.go
@@ -17,6 +17,7 @@ func TestHCLBasic(t *testing.T) {
|
||||
target "db" {
|
||||
context = "./db"
|
||||
tags = ["docker.io/tonistiigi/db"]
|
||||
output = ["type=image"]
|
||||
}
|
||||
|
||||
target "webapp" {
|
||||
@@ -25,6 +26,9 @@ func TestHCLBasic(t *testing.T) {
|
||||
args = {
|
||||
buildno = "123"
|
||||
}
|
||||
output = [
|
||||
{ type = "image" }
|
||||
]
|
||||
}
|
||||
|
||||
target "cross" {
|
||||
@@ -49,18 +53,18 @@ func TestHCLBasic(t *testing.T) {
|
||||
require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 4, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "db")
|
||||
require.Equal(t, "db", c.Targets[0].Name)
|
||||
require.Equal(t, "./db", *c.Targets[0].Context)
|
||||
|
||||
require.Equal(t, c.Targets[1].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[1].Name)
|
||||
require.Equal(t, 1, len(c.Targets[1].Args))
|
||||
require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
|
||||
|
||||
require.Equal(t, c.Targets[2].Name, "cross")
|
||||
require.Equal(t, "cross", c.Targets[2].Name)
|
||||
require.Equal(t, 2, len(c.Targets[2].Platforms))
|
||||
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[2].Platforms)
|
||||
|
||||
require.Equal(t, c.Targets[3].Name, "webapp-plus")
|
||||
require.Equal(t, "webapp-plus", c.Targets[3].Name)
|
||||
require.Equal(t, 1, len(c.Targets[3].Args))
|
||||
require.Equal(t, map[string]*string{"IAMCROSS": ptrstr("true")}, c.Targets[3].Args)
|
||||
}
|
||||
@@ -109,18 +113,18 @@ func TestHCLBasicInJSON(t *testing.T) {
|
||||
require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 4, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "db")
|
||||
require.Equal(t, "db", c.Targets[0].Name)
|
||||
require.Equal(t, "./db", *c.Targets[0].Context)
|
||||
|
||||
require.Equal(t, c.Targets[1].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[1].Name)
|
||||
require.Equal(t, 1, len(c.Targets[1].Args))
|
||||
require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
|
||||
|
||||
require.Equal(t, c.Targets[2].Name, "cross")
|
||||
require.Equal(t, "cross", c.Targets[2].Name)
|
||||
require.Equal(t, 2, len(c.Targets[2].Platforms))
|
||||
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[2].Platforms)
|
||||
|
||||
require.Equal(t, c.Targets[3].Name, "webapp-plus")
|
||||
require.Equal(t, "webapp-plus", c.Targets[3].Name)
|
||||
require.Equal(t, 1, len(c.Targets[3].Args))
|
||||
require.Equal(t, map[string]*string{"IAMCROSS": ptrstr("true")}, c.Targets[3].Args)
|
||||
}
|
||||
@@ -146,7 +150,7 @@ func TestHCLWithFunctions(t *testing.T) {
|
||||
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("124"), c.Targets[0].Args["buildno"])
|
||||
}
|
||||
|
||||
@@ -176,7 +180,7 @@ func TestHCLWithUserDefinedFunctions(t *testing.T) {
|
||||
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("124"), c.Targets[0].Args["buildno"])
|
||||
}
|
||||
|
||||
@@ -205,7 +209,7 @@ func TestHCLWithVariables(t *testing.T) {
|
||||
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("123"), c.Targets[0].Args["buildno"])
|
||||
|
||||
t.Setenv("BUILD_NUMBER", "456")
|
||||
@@ -218,7 +222,7 @@ func TestHCLWithVariables(t *testing.T) {
|
||||
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("456"), c.Targets[0].Args["buildno"])
|
||||
}
|
||||
|
||||
@@ -241,7 +245,7 @@ func TestHCLWithVariablesInFunctions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[0].Name)
|
||||
require.Equal(t, []string{"user/repo:v1"}, c.Targets[0].Tags)
|
||||
|
||||
t.Setenv("REPO", "docker/buildx")
|
||||
@@ -250,7 +254,7 @@ func TestHCLWithVariablesInFunctions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "webapp")
|
||||
require.Equal(t, "webapp", c.Targets[0].Name)
|
||||
require.Equal(t, []string{"docker/buildx:v1"}, c.Targets[0].Tags)
|
||||
}
|
||||
|
||||
@@ -279,7 +283,7 @@ func TestHCLMultiFileSharedVariables(t *testing.T) {
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre-abc"), c.Targets[0].Args["v1"])
|
||||
require.Equal(t, ptrstr("abc-post"), c.Targets[0].Args["v2"])
|
||||
|
||||
@@ -292,7 +296,7 @@ func TestHCLMultiFileSharedVariables(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre-def"), c.Targets[0].Args["v1"])
|
||||
require.Equal(t, ptrstr("def-post"), c.Targets[0].Args["v2"])
|
||||
}
|
||||
@@ -328,7 +332,7 @@ func TestHCLVarsWithVars(t *testing.T) {
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre--ABCDEF-"), c.Targets[0].Args["v1"])
|
||||
require.Equal(t, ptrstr("ABCDEF-post"), c.Targets[0].Args["v2"])
|
||||
|
||||
@@ -341,7 +345,7 @@ func TestHCLVarsWithVars(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre--NEWDEF-"), c.Targets[0].Args["v1"])
|
||||
require.Equal(t, ptrstr("NEWDEF-post"), c.Targets[0].Args["v2"])
|
||||
}
|
||||
@@ -366,7 +370,7 @@ func TestHCLTypedVariables(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("lower"), c.Targets[0].Args["v1"])
|
||||
require.Equal(t, ptrstr("yes"), c.Targets[0].Args["v2"])
|
||||
|
||||
@@ -377,7 +381,7 @@ func TestHCLTypedVariables(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("higher"), c.Targets[0].Args["v1"])
|
||||
require.Equal(t, ptrstr("no"), c.Targets[0].Args["v2"])
|
||||
|
||||
@@ -475,7 +479,7 @@ func TestHCLAttrs(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("attr-abcdef"), c.Targets[0].Args["v1"])
|
||||
|
||||
// env does not apply if no variable
|
||||
@@ -484,7 +488,7 @@ func TestHCLAttrs(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("attr-abcdef"), c.Targets[0].Args["v1"])
|
||||
// attr-multifile
|
||||
}
|
||||
@@ -592,11 +596,126 @@ func TestHCLAttrsCustomType(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, []string{"linux/arm64", "linux/amd64"}, c.Targets[0].Platforms)
|
||||
require.Equal(t, ptrstr("linux/arm64"), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
func TestHCLAttrsCapsuleType(t *testing.T) {
|
||||
dt := []byte(`
|
||||
target "app" {
|
||||
attest = [
|
||||
{ type = "provenance", mode = "max" },
|
||||
"type=sbom,disabled=true",
|
||||
]
|
||||
|
||||
cache-from = [
|
||||
{ type = "registry", ref = "user/app:cache" },
|
||||
"type=local,src=path/to/cache",
|
||||
]
|
||||
|
||||
cache-to = [
|
||||
{ type = "local", dest = "path/to/cache" },
|
||||
]
|
||||
|
||||
output = [
|
||||
{ type = "oci", dest = "../out.tar" },
|
||||
"type=local,dest=../out",
|
||||
]
|
||||
|
||||
secret = [
|
||||
{ id = "mysecret", src = "/local/secret" },
|
||||
{ id = "mysecret2", env = "TOKEN" },
|
||||
]
|
||||
|
||||
ssh = [
|
||||
{ id = "default" },
|
||||
{ id = "key", paths = ["path/to/key"] },
|
||||
]
|
||||
}
|
||||
`)
|
||||
|
||||
c, err := ParseFile(dt, "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
|
||||
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=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,dest=path/to/cache"}, stringify(c.Targets[0].CacheTo))
|
||||
require.Equal(t, []string{"id=mysecret,src=/local/secret", "id=mysecret2,env=TOKEN"}, stringify(c.Targets[0].Secrets))
|
||||
require.Equal(t, []string{"default", "key=path/to/key"}, stringify(c.Targets[0].SSH))
|
||||
}
|
||||
|
||||
func TestHCLAttrsCapsuleTypeVars(t *testing.T) {
|
||||
dt := []byte(`
|
||||
variable "foo" {
|
||||
default = "bar"
|
||||
}
|
||||
|
||||
target "app" {
|
||||
cache-from = [
|
||||
{ type = "registry", ref = "user/app:cache" },
|
||||
"type=local,src=path/to/cache",
|
||||
]
|
||||
|
||||
cache-to = [ target.app.cache-from[0] ]
|
||||
|
||||
output = [
|
||||
{ type = "oci", dest = "../out.tar" },
|
||||
"type=local,dest=../out",
|
||||
]
|
||||
|
||||
secret = [
|
||||
{ id = "mysecret", src = "/local/secret" },
|
||||
]
|
||||
|
||||
ssh = [
|
||||
{ id = "default" },
|
||||
{ id = "key", paths = ["path/to/${target.app.output[0].type}"] },
|
||||
]
|
||||
}
|
||||
|
||||
target "web" {
|
||||
cache-from = target.app.cache-from
|
||||
|
||||
output = [ "type=oci,dest=../${foo}.tar" ]
|
||||
|
||||
secret = [
|
||||
{ id = target.app.output[0].type, src = "/${target.app.cache-from[1].type}/secret" },
|
||||
]
|
||||
}
|
||||
`)
|
||||
|
||||
c, err := ParseFile(dt, "docker-bake.hcl")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 2, len(c.Targets))
|
||||
|
||||
findTarget := func(t *testing.T, name string) *Target {
|
||||
t.Helper()
|
||||
for _, tgt := range c.Targets {
|
||||
if tgt.Name == name {
|
||||
return tgt
|
||||
}
|
||||
}
|
||||
t.Fatalf("could not find target %q", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
app := findTarget(t, "app")
|
||||
require.Equal(t, []string{"type=local,dest=../out", "type=oci,dest=../out.tar"}, stringify(app.Outputs))
|
||||
require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(app.CacheFrom))
|
||||
require.Equal(t, []string{"user/app:cache"}, stringify(app.CacheTo))
|
||||
require.Equal(t, []string{"id=mysecret,src=/local/secret"}, stringify(app.Secrets))
|
||||
require.Equal(t, []string{"default", "key=path/to/oci"}, stringify(app.SSH))
|
||||
|
||||
web := findTarget(t, "web")
|
||||
require.Equal(t, []string{"type=oci,dest=../bar.tar"}, stringify(web.Outputs))
|
||||
require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(web.CacheFrom))
|
||||
require.Equal(t, []string{"id=oci,src=/local/secret"}, stringify(web.Secrets))
|
||||
}
|
||||
|
||||
func TestHCLMultiFileAttrs(t *testing.T) {
|
||||
dt := []byte(`
|
||||
variable "FOO" {
|
||||
@@ -618,7 +737,7 @@ func TestHCLMultiFileAttrs(t *testing.T) {
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre-def"), c.Targets[0].Args["v1"])
|
||||
|
||||
t.Setenv("FOO", "ghi")
|
||||
@@ -630,7 +749,7 @@ func TestHCLMultiFileAttrs(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre-ghi"), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
@@ -653,7 +772,7 @@ func TestHCLMultiFileGlobalAttrs(t *testing.T) {
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, "pre-def", *c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
@@ -839,12 +958,12 @@ func TestHCLRenameMultiFile(t *testing.T) {
|
||||
|
||||
require.Equal(t, 2, len(c.Targets))
|
||||
|
||||
require.Equal(t, c.Targets[0].Name, "bar")
|
||||
require.Equal(t, *c.Targets[0].Dockerfile, "x")
|
||||
require.Equal(t, *c.Targets[0].Target, "z")
|
||||
require.Equal(t, "bar", c.Targets[0].Name)
|
||||
require.Equal(t, "x", *c.Targets[0].Dockerfile)
|
||||
require.Equal(t, "z", *c.Targets[0].Target)
|
||||
|
||||
require.Equal(t, c.Targets[1].Name, "foo")
|
||||
require.Equal(t, *c.Targets[1].Context, "y")
|
||||
require.Equal(t, "foo", c.Targets[1].Name)
|
||||
require.Equal(t, "y", *c.Targets[1].Context)
|
||||
}
|
||||
|
||||
func TestHCLMatrixBasic(t *testing.T) {
|
||||
@@ -862,10 +981,10 @@ func TestHCLMatrixBasic(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 2, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "x")
|
||||
require.Equal(t, c.Targets[1].Name, "y")
|
||||
require.Equal(t, *c.Targets[0].Dockerfile, "x.Dockerfile")
|
||||
require.Equal(t, *c.Targets[1].Dockerfile, "y.Dockerfile")
|
||||
require.Equal(t, "x", c.Targets[0].Name)
|
||||
require.Equal(t, "y", c.Targets[1].Name)
|
||||
require.Equal(t, "x.Dockerfile", *c.Targets[0].Dockerfile)
|
||||
require.Equal(t, "y.Dockerfile", *c.Targets[1].Dockerfile)
|
||||
|
||||
require.Equal(t, 1, len(c.Groups))
|
||||
require.Equal(t, "default", c.Groups[0].Name)
|
||||
@@ -948,9 +1067,9 @@ func TestHCLMatrixMaps(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 2, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "aa")
|
||||
require.Equal(t, "aa", c.Targets[0].Name)
|
||||
require.Equal(t, c.Targets[0].Args["target"], ptrstr("valbb"))
|
||||
require.Equal(t, c.Targets[1].Name, "cc")
|
||||
require.Equal(t, "cc", c.Targets[1].Name)
|
||||
require.Equal(t, c.Targets[1].Args["target"], ptrstr("valdd"))
|
||||
}
|
||||
|
||||
@@ -1141,7 +1260,7 @@ func TestJSONAttributes(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre-abc-def"), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
@@ -1166,7 +1285,7 @@ func TestJSONFunctions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("pre-<FOO-abc>"), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
@@ -1184,7 +1303,7 @@ func TestJSONInvalidFunctions(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr(`myfunc("foo")`), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
@@ -1212,7 +1331,7 @@ func TestHCLFunctionInAttr(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("FOO <> [baz]"), c.Targets[0].Args["v1"])
|
||||
}
|
||||
|
||||
@@ -1243,7 +1362,7 @@ services:
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, ptrstr("foo"), c.Targets[0].Args["v1"])
|
||||
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["v2"])
|
||||
require.Equal(t, "dir", *c.Targets[0].Context)
|
||||
@@ -1266,7 +1385,7 @@ func TestHCLBuiltinVars(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, 1, len(c.Targets))
|
||||
require.Equal(t, c.Targets[0].Name, "app")
|
||||
require.Equal(t, "app", c.Targets[0].Name)
|
||||
require.Equal(t, "foo", *c.Targets[0].Context)
|
||||
require.Equal(t, "test", *c.Targets[0].Dockerfile)
|
||||
}
|
||||
@@ -1332,17 +1451,17 @@ target "b" {
|
||||
|
||||
require.Equal(t, 4, len(c.Targets))
|
||||
|
||||
require.Equal(t, c.Targets[0].Name, "metadata-a")
|
||||
require.Equal(t, "metadata-a", c.Targets[0].Name)
|
||||
require.Equal(t, []string{"app/a:1.0.0", "app/a:latest"}, c.Targets[0].Tags)
|
||||
|
||||
require.Equal(t, c.Targets[1].Name, "metadata-b")
|
||||
require.Equal(t, "metadata-b", c.Targets[1].Name)
|
||||
require.Equal(t, []string{"app/b:1.0.0", "app/b:latest"}, c.Targets[1].Tags)
|
||||
|
||||
require.Equal(t, c.Targets[2].Name, "a")
|
||||
require.Equal(t, "a", c.Targets[2].Name)
|
||||
require.Equal(t, ".", *c.Targets[2].Context)
|
||||
require.Equal(t, "a", *c.Targets[2].Target)
|
||||
|
||||
require.Equal(t, c.Targets[3].Name, "b")
|
||||
require.Equal(t, "b", c.Targets[3].Name)
|
||||
require.Equal(t, ".", *c.Targets[3].Context)
|
||||
require.Equal(t, "b", *c.Targets[3].Target)
|
||||
}
|
||||
@@ -1389,10 +1508,10 @@ target "two" {
|
||||
|
||||
require.Equal(t, 2, len(c.Targets))
|
||||
|
||||
require.Equal(t, c.Targets[0].Name, "one")
|
||||
require.Equal(t, "one", c.Targets[0].Name)
|
||||
require.Equal(t, map[string]*string{"a": ptrstr("pre-ghi-jkl")}, c.Targets[0].Args)
|
||||
|
||||
require.Equal(t, c.Targets[1].Name, "two")
|
||||
require.Equal(t, "two", c.Targets[1].Name)
|
||||
require.Equal(t, map[string]*string{"b": ptrstr("pre-jkl")}, c.Targets[1].Args)
|
||||
}
|
||||
|
||||
|
355
bake/hclparser/LICENSE
Normal file
355
bake/hclparser/LICENSE
Normal file
@@ -0,0 +1,355 @@
|
||||
Copyright (c) 2014 HashiCorp, Inc.
|
||||
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. “Contributor”
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. “Contributor Version”
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor’s Contribution.
|
||||
|
||||
1.3. “Contribution”
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. “Covered Software”
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. “Incompatible With Secondary Licenses”
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of version
|
||||
1.1 or earlier of the License, but not also under the terms of a
|
||||
Secondary License.
|
||||
|
||||
1.6. “Executable Form”
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. “Larger Work”
|
||||
|
||||
means a work that combines Covered Software with other material, in a separate
|
||||
file or files, that is not Covered Software.
|
||||
|
||||
1.8. “License”
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. “Licensable”
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether at the
|
||||
time of the initial grant or subsequently, any and all of the rights conveyed by
|
||||
this License.
|
||||
|
||||
1.10. “Modifications”
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to, deletion
|
||||
from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. “Patent Claims” of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method, process,
|
||||
and apparatus claims, in any patent Licensable by such Contributor that
|
||||
would be infringed, but for the grant of the License, by the making,
|
||||
using, selling, offering for sale, having made, import, or transfer of
|
||||
either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. “Secondary License”
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. “Source Code Form”
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. “You” (or “Your”)
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, “You” includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, “control” means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or as
|
||||
part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its Contributions
|
||||
or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution become
|
||||
effective for each Contribution on the date the Contributor first distributes
|
||||
such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under this
|
||||
License. No additional rights or licenses will be implied from the distribution
|
||||
or licensing of Covered Software under this License. Notwithstanding Section
|
||||
2.1(b) above, no patent license is granted by a Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party’s
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of its
|
||||
Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks, or
|
||||
logos of any Contributor (except as may be necessary to comply with the
|
||||
notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this License
|
||||
(see Section 10.2) or under the terms of a Secondary License (if permitted
|
||||
under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its Contributions
|
||||
are its original creation(s) or it has sufficient rights to grant the
|
||||
rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under applicable
|
||||
copyright doctrines of fair use, fair dealing, or other equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under the
|
||||
terms of this License. You must inform recipients that the Source Code Form
|
||||
of the Covered Software is governed by the terms of this License, and how
|
||||
they can obtain a copy of this License. You may not attempt to alter or
|
||||
restrict the recipients’ rights in the Source Code Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this License,
|
||||
or sublicense it under different terms, provided that the license for
|
||||
the Executable Form does not attempt to limit or alter the recipients’
|
||||
rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for the
|
||||
Covered Software. If the Larger Work is a combination of Covered Software
|
||||
with a work governed by one or more Secondary Licenses, and the Covered
|
||||
Software is not Incompatible With Secondary Licenses, this License permits
|
||||
You to additionally distribute such Covered Software under the terms of
|
||||
such Secondary License(s), so that the recipient of the Larger Work may, at
|
||||
their option, further distribute the Covered Software under the terms of
|
||||
either this License or such Secondary License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices (including
|
||||
copyright notices, patent notices, disclaimers of warranty, or limitations
|
||||
of liability) contained within the Source Code Form of the Covered
|
||||
Software, except that You may alter any license notices to the extent
|
||||
required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on behalf
|
||||
of any Contributor. You must make it absolutely clear that any such
|
||||
warranty, support, indemnity, or liability obligation is offered by You
|
||||
alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute, judicial
|
||||
order, or regulation then You must: (a) comply with the terms of this License
|
||||
to the maximum extent possible; and (b) describe the limitations and the code
|
||||
they affect. Such description must be placed in a text file included with all
|
||||
distributions of the Covered Software under this License. Except to the
|
||||
extent prohibited by statute or regulation, such description must be
|
||||
sufficiently detailed for a recipient of ordinary skill to be able to
|
||||
understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing basis,
|
||||
if such Contributor fails to notify You of the non-compliance by some
|
||||
reasonable means prior to 60 days after You have come back into compliance.
|
||||
Moreover, Your grants from a particular Contributor are reinstated on an
|
||||
ongoing basis if such Contributor notifies You of the non-compliance by
|
||||
some reasonable means, this is the first time You have received notice of
|
||||
non-compliance with this License from such Contributor, and You become
|
||||
compliant prior to 30 days after Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions, counter-claims,
|
||||
and cross-claims) alleging that a Contributor Version directly or
|
||||
indirectly infringes any patent, then the rights granted to You by any and
|
||||
all Contributors for the Covered Software under Section 2.1 of this License
|
||||
shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an “as is” basis, without
|
||||
warranty of any kind, either expressed, implied, or statutory, including,
|
||||
without limitation, warranties that the Covered Software is free of defects,
|
||||
merchantable, fit for a particular purpose or non-infringing. The entire
|
||||
risk as to the quality and performance of the Covered Software is with You.
|
||||
Should any Covered Software prove defective in any respect, You (not any
|
||||
Contributor) assume the cost of any necessary servicing, repair, or
|
||||
correction. This disclaimer of warranty constitutes an essential part of this
|
||||
License. No use of any Covered Software is authorized under this License
|
||||
except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from such
|
||||
party’s negligence to the extent applicable law prohibits such limitation.
|
||||
Some jurisdictions do not allow the exclusion or limitation of incidental or
|
||||
consequential damages, so this exclusion and limitation may not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts of
|
||||
a jurisdiction where the defendant maintains its principal place of business
|
||||
and such litigation shall be governed by laws of that jurisdiction, without
|
||||
reference to its conflict-of-law provisions. Nothing in this Section shall
|
||||
prevent a party’s ability to bring cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject matter
|
||||
hereof. If any provision of this License is held to be unenforceable, such
|
||||
provision shall be reformed only to the extent necessary to make it
|
||||
enforceable. Any law or regulation which provides that the language of a
|
||||
contract shall be construed against the drafter shall not be used to construe
|
||||
this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version of
|
||||
the License under which You originally received the Covered Software, or
|
||||
under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a modified
|
||||
version of this License if you rename the license and remove any
|
||||
references to the name of the license steward (except to note that such
|
||||
modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file, then
|
||||
You may include the notice in a location (such as a LICENSE file in a relevant
|
||||
directory) where a recipient would be likely to look for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - “Incompatible With Secondary Licenses” Notice
|
||||
|
||||
This Source Code Form is “Incompatible
|
||||
With Secondary Licenses”, as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
348
bake/hclparser/gohcl/decode.go
Normal file
348
bake/hclparser/gohcl/decode.go
Normal file
@@ -0,0 +1,348 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gohcl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
// DecodeOptions allows customizing sections of the decoding process.
|
||||
type DecodeOptions struct {
|
||||
ImpliedType func(gv interface{}) (cty.Type, 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 {
|
||||
o = o.withDefaults()
|
||||
|
||||
rv := reflect.ValueOf(val)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
panic(fmt.Sprintf("target value must be a pointer, not %s", rv.Type().String()))
|
||||
}
|
||||
|
||||
return o.decodeBodyToValue(body, ctx, rv.Elem())
|
||||
}
|
||||
|
||||
// DecodeBody extracts the configuration within the given body into the given
|
||||
// value. This value must be a non-nil pointer to either a struct or
|
||||
// a map, where in the former case the configuration will be decoded using
|
||||
// struct tags and in the latter case only attributes are allowed and their
|
||||
// values are decoded into the map.
|
||||
//
|
||||
// The given EvalContext is used to resolve any variables or functions in
|
||||
// expressions encountered while decoding. This may be nil to require only
|
||||
// constant values, for simple applications that do not support variables or
|
||||
// functions.
|
||||
//
|
||||
// The returned diagnostics should be inspected with its HasErrors method to
|
||||
// determine if the populated value is valid and complete. If error diagnostics
|
||||
// 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
|
||||
// integration use-cases.
|
||||
func DecodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
return DecodeOptions{}.DecodeBody(body, ctx, val)
|
||||
}
|
||||
|
||||
func (o DecodeOptions) decodeBodyToValue(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value) hcl.Diagnostics {
|
||||
et := val.Type()
|
||||
switch et.Kind() {
|
||||
case reflect.Struct:
|
||||
return o.decodeBodyToStruct(body, ctx, val)
|
||||
case reflect.Map:
|
||||
return o.decodeBodyToMap(body, ctx, val)
|
||||
default:
|
||||
panic(fmt.Sprintf("target value must be pointer to struct or map, not %s", et.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func (o DecodeOptions) decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value) hcl.Diagnostics {
|
||||
schema, partial := ImpliedBodySchema(val.Interface())
|
||||
|
||||
var content *hcl.BodyContent
|
||||
var leftovers hcl.Body
|
||||
var diags hcl.Diagnostics
|
||||
if partial {
|
||||
content, leftovers, diags = body.PartialContent(schema)
|
||||
} else {
|
||||
content, diags = body.Content(schema)
|
||||
}
|
||||
if content == nil {
|
||||
return diags
|
||||
}
|
||||
|
||||
tags := getFieldTags(val.Type())
|
||||
|
||||
if tags.Body != nil {
|
||||
fieldIdx := *tags.Body
|
||||
field := val.Type().Field(fieldIdx)
|
||||
fieldV := val.Field(fieldIdx)
|
||||
switch {
|
||||
case bodyType.AssignableTo(field.Type):
|
||||
fieldV.Set(reflect.ValueOf(body))
|
||||
|
||||
default:
|
||||
diags = append(diags, o.decodeBodyToValue(body, ctx, fieldV)...)
|
||||
}
|
||||
}
|
||||
|
||||
if tags.Remain != nil {
|
||||
fieldIdx := *tags.Remain
|
||||
field := val.Type().Field(fieldIdx)
|
||||
fieldV := val.Field(fieldIdx)
|
||||
switch {
|
||||
case bodyType.AssignableTo(field.Type):
|
||||
fieldV.Set(reflect.ValueOf(leftovers))
|
||||
case attrsType.AssignableTo(field.Type):
|
||||
attrs, attrsDiags := leftovers.JustAttributes()
|
||||
if len(attrsDiags) > 0 {
|
||||
diags = append(diags, attrsDiags...)
|
||||
}
|
||||
fieldV.Set(reflect.ValueOf(attrs))
|
||||
default:
|
||||
diags = append(diags, o.decodeBodyToValue(leftovers, ctx, fieldV)...)
|
||||
}
|
||||
}
|
||||
|
||||
for name, fieldIdx := range tags.Attributes {
|
||||
attr := content.Attributes[name]
|
||||
field := val.Type().Field(fieldIdx)
|
||||
fieldV := val.Field(fieldIdx)
|
||||
|
||||
if attr == nil {
|
||||
if !exprType.AssignableTo(field.Type) {
|
||||
continue
|
||||
}
|
||||
|
||||
// As a special case, if the target is of type hcl.Expression then
|
||||
// we'll assign an actual expression that evalues to a cty null,
|
||||
// so the caller can deal with it within the cty realm rather
|
||||
// than within the Go realm.
|
||||
synthExpr := hcl.StaticExpr(cty.NullVal(cty.DynamicPseudoType), body.MissingItemRange())
|
||||
fieldV.Set(reflect.ValueOf(synthExpr))
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case attrType.AssignableTo(field.Type):
|
||||
fieldV.Set(reflect.ValueOf(attr))
|
||||
case exprType.AssignableTo(field.Type):
|
||||
fieldV.Set(reflect.ValueOf(attr.Expr))
|
||||
default:
|
||||
diags = append(diags, o.DecodeExpression(
|
||||
attr.Expr, ctx, fieldV.Addr().Interface(),
|
||||
)...)
|
||||
}
|
||||
}
|
||||
|
||||
blocksByType := content.Blocks.ByType()
|
||||
|
||||
for typeName, fieldIdx := range tags.Blocks {
|
||||
blocks := blocksByType[typeName]
|
||||
field := val.Type().Field(fieldIdx)
|
||||
|
||||
ty := field.Type
|
||||
isSlice := false
|
||||
isPtr := false
|
||||
if ty.Kind() == reflect.Slice {
|
||||
isSlice = true
|
||||
ty = ty.Elem()
|
||||
}
|
||||
if ty.Kind() == reflect.Ptr {
|
||||
isPtr = true
|
||||
ty = ty.Elem()
|
||||
}
|
||||
|
||||
if len(blocks) > 1 && !isSlice {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Duplicate %s block", typeName),
|
||||
Detail: fmt.Sprintf(
|
||||
"Only one %s block is allowed. Another was defined at %s.",
|
||||
typeName, blocks[0].DefRange.String(),
|
||||
),
|
||||
Subject: &blocks[1].DefRange,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if len(blocks) == 0 {
|
||||
if isSlice || isPtr {
|
||||
if val.Field(fieldIdx).IsNil() {
|
||||
val.Field(fieldIdx).Set(reflect.Zero(field.Type))
|
||||
}
|
||||
} else {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Missing %s block", typeName),
|
||||
Detail: fmt.Sprintf("A %s block is required.", typeName),
|
||||
Subject: body.MissingItemRange().Ptr(),
|
||||
})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case isSlice:
|
||||
elemType := ty
|
||||
if isPtr {
|
||||
elemType = reflect.PointerTo(ty)
|
||||
}
|
||||
sli := val.Field(fieldIdx)
|
||||
if sli.IsNil() {
|
||||
sli = reflect.MakeSlice(reflect.SliceOf(elemType), len(blocks), len(blocks))
|
||||
}
|
||||
|
||||
for i, block := range blocks {
|
||||
if isPtr {
|
||||
if i >= sli.Len() {
|
||||
sli = reflect.Append(sli, reflect.New(ty))
|
||||
}
|
||||
v := sli.Index(i)
|
||||
if v.IsNil() {
|
||||
v = reflect.New(ty)
|
||||
}
|
||||
diags = append(diags, o.decodeBlockToValue(block, ctx, v.Elem())...)
|
||||
sli.Index(i).Set(v)
|
||||
} else {
|
||||
if i >= sli.Len() {
|
||||
sli = reflect.Append(sli, reflect.Indirect(reflect.New(ty)))
|
||||
}
|
||||
diags = append(diags, o.decodeBlockToValue(block, ctx, sli.Index(i))...)
|
||||
}
|
||||
}
|
||||
|
||||
if sli.Len() > len(blocks) {
|
||||
sli.SetLen(len(blocks))
|
||||
}
|
||||
|
||||
val.Field(fieldIdx).Set(sli)
|
||||
|
||||
default:
|
||||
block := blocks[0]
|
||||
if isPtr {
|
||||
v := val.Field(fieldIdx)
|
||||
if v.IsNil() {
|
||||
v = reflect.New(ty)
|
||||
}
|
||||
diags = append(diags, o.decodeBlockToValue(block, ctx, v.Elem())...)
|
||||
val.Field(fieldIdx).Set(v)
|
||||
} else {
|
||||
diags = append(diags, o.decodeBlockToValue(block, ctx, val.Field(fieldIdx))...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func (o DecodeOptions) decodeBodyToMap(body hcl.Body, ctx *hcl.EvalContext, v reflect.Value) hcl.Diagnostics {
|
||||
attrs, diags := body.JustAttributes()
|
||||
if attrs == nil {
|
||||
return diags
|
||||
}
|
||||
|
||||
mv := reflect.MakeMap(v.Type())
|
||||
|
||||
for k, attr := range attrs {
|
||||
switch {
|
||||
case attrType.AssignableTo(v.Type().Elem()):
|
||||
mv.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(attr))
|
||||
case exprType.AssignableTo(v.Type().Elem()):
|
||||
mv.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(attr.Expr))
|
||||
default:
|
||||
ev := reflect.New(v.Type().Elem())
|
||||
diags = append(diags, o.DecodeExpression(attr.Expr, ctx, ev.Interface())...)
|
||||
mv.SetMapIndex(reflect.ValueOf(k), ev.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
v.Set(mv)
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func (o DecodeOptions) decodeBlockToValue(block *hcl.Block, ctx *hcl.EvalContext, v reflect.Value) hcl.Diagnostics {
|
||||
diags := o.decodeBodyToValue(block.Body, ctx, v)
|
||||
|
||||
if len(block.Labels) > 0 {
|
||||
blockTags := getFieldTags(v.Type())
|
||||
for li, lv := range block.Labels {
|
||||
lfieldIdx := blockTags.Labels[li].FieldIndex
|
||||
v.Field(lfieldIdx).Set(reflect.ValueOf(lv))
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func (o DecodeOptions) DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
o = o.withDefaults()
|
||||
|
||||
srcVal, diags := expr.Value(ctx)
|
||||
|
||||
convTy, err := o.ImpliedType(val)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unsuitable DecodeExpression target: %s", err))
|
||||
}
|
||||
|
||||
srcVal, err = o.Convert(srcVal, convTy)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Unsuitable value type",
|
||||
Detail: fmt.Sprintf("Unsuitable value: %s", err.Error()),
|
||||
Subject: expr.StartRange().Ptr(),
|
||||
Context: expr.Range().Ptr(),
|
||||
})
|
||||
return diags
|
||||
}
|
||||
|
||||
err = gocty.FromCtyValue(srcVal, val)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Unsuitable value type",
|
||||
Detail: fmt.Sprintf("Unsuitable value: %s", err.Error()),
|
||||
Subject: expr.StartRange().Ptr(),
|
||||
Context: expr.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
// DecodeExpression extracts the value of the given expression into the given
|
||||
// value. This value must be something that gocty is able to decode into,
|
||||
// since the final decoding is delegated to that package.
|
||||
//
|
||||
// The given EvalContext is used to resolve any variables or functions in
|
||||
// expressions encountered while decoding. This may be nil to require only
|
||||
// constant values, for simple applications that do not support variables or
|
||||
// functions.
|
||||
//
|
||||
// The returned diagnostics should be inspected with its HasErrors method to
|
||||
// determine if the populated value is valid and complete. If error diagnostics
|
||||
// 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
|
||||
// integration use-cases.
|
||||
func DecodeExpression(expr hcl.Expression, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
return DecodeOptions{}.DecodeExpression(expr, ctx, val)
|
||||
}
|
||||
|
||||
func (o DecodeOptions) withDefaults() DecodeOptions {
|
||||
if o.ImpliedType == nil {
|
||||
o.ImpliedType = gocty.ImpliedType
|
||||
}
|
||||
|
||||
if o.Convert == nil {
|
||||
o.Convert = convert.Convert
|
||||
}
|
||||
return o
|
||||
}
|
806
bake/hclparser/gohcl/decode_test.go
Normal file
806
bake/hclparser/gohcl/decode_test.go
Normal file
@@ -0,0 +1,806 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gohcl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
hclJSON "github.com/hashicorp/hcl/v2/json"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestDecodeBody(t *testing.T) {
|
||||
deepEquals := func(other interface{}) func(v interface{}) bool {
|
||||
return func(v interface{}) bool {
|
||||
return reflect.DeepEqual(v, other)
|
||||
}
|
||||
}
|
||||
|
||||
type withNameExpression struct {
|
||||
Name hcl.Expression `hcl:"name"`
|
||||
}
|
||||
|
||||
type withTwoAttributes struct {
|
||||
A string `hcl:"a,optional"`
|
||||
B string `hcl:"b,optional"`
|
||||
}
|
||||
|
||||
type withNestedBlock struct {
|
||||
Plain string `hcl:"plain,optional"`
|
||||
Nested *withTwoAttributes `hcl:"nested,block"`
|
||||
}
|
||||
|
||||
type withListofNestedBlocks struct {
|
||||
Nested []*withTwoAttributes `hcl:"nested,block"`
|
||||
}
|
||||
|
||||
type withListofNestedBlocksNoPointers struct {
|
||||
Nested []withTwoAttributes `hcl:"nested,block"`
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
Body map[string]interface{}
|
||||
Target func() interface{}
|
||||
Check func(v interface{}) bool
|
||||
DiagCount int
|
||||
}{
|
||||
{
|
||||
map[string]interface{}{},
|
||||
makeInstantiateType(struct{}{}),
|
||||
deepEquals(struct{}{}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name"`
|
||||
}{}),
|
||||
deepEquals(struct {
|
||||
Name string `hcl:"name"`
|
||||
}{}),
|
||||
1, // name is required
|
||||
},
|
||||
{
|
||||
map[string]interface{}{},
|
||||
makeInstantiateType(struct {
|
||||
Name *string `hcl:"name"`
|
||||
}{}),
|
||||
deepEquals(struct {
|
||||
Name *string `hcl:"name"`
|
||||
}{}),
|
||||
0,
|
||||
}, // name nil
|
||||
{
|
||||
map[string]interface{}{},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name,optional"`
|
||||
}{}),
|
||||
deepEquals(struct {
|
||||
Name string `hcl:"name,optional"`
|
||||
}{}),
|
||||
0,
|
||||
}, // name optional
|
||||
{
|
||||
map[string]interface{}{},
|
||||
makeInstantiateType(withNameExpression{}),
|
||||
func(v interface{}) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
wne, valid := v.(withNameExpression)
|
||||
if !valid {
|
||||
return false
|
||||
}
|
||||
|
||||
if wne.Name == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
nameVal, _ := wne.Name.Value(nil)
|
||||
return nameVal.IsNull()
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
},
|
||||
makeInstantiateType(withNameExpression{}),
|
||||
func(v interface{}) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
wne, valid := v.(withNameExpression)
|
||||
if !valid {
|
||||
return false
|
||||
}
|
||||
|
||||
if wne.Name == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
nameVal, _ := wne.Name.Value(nil)
|
||||
return nameVal.Equals(cty.StringVal("Ermintrude")).True()
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name"`
|
||||
}{}),
|
||||
deepEquals(struct {
|
||||
Name string `hcl:"name"`
|
||||
}{"Ermintrude"}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"age": 23,
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name"`
|
||||
}{}),
|
||||
deepEquals(struct {
|
||||
Name string `hcl:"name"`
|
||||
}{"Ermintrude"}),
|
||||
1, // Extraneous "age" property
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"age": 50,
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name"`
|
||||
Attrs hcl.Attributes `hcl:",remain"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
got := gotI.(struct {
|
||||
Name string `hcl:"name"`
|
||||
Attrs hcl.Attributes `hcl:",remain"`
|
||||
})
|
||||
return got.Name == "Ermintrude" && len(got.Attrs) == 1 && got.Attrs["age"] != nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"age": 50,
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name"`
|
||||
Remain hcl.Body `hcl:",remain"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
got := gotI.(struct {
|
||||
Name string `hcl:"name"`
|
||||
Remain hcl.Body `hcl:",remain"`
|
||||
})
|
||||
|
||||
attrs, _ := got.Remain.JustAttributes()
|
||||
|
||||
return got.Name == "Ermintrude" && len(attrs) == 1 && attrs["age"] != nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"living": true,
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name"`
|
||||
Remain map[string]cty.Value `hcl:",remain"`
|
||||
}{}),
|
||||
deepEquals(struct {
|
||||
Name string `hcl:"name"`
|
||||
Remain map[string]cty.Value `hcl:",remain"`
|
||||
}{
|
||||
Name: "Ermintrude",
|
||||
Remain: map[string]cty.Value{
|
||||
"living": cty.True,
|
||||
},
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"age": 50,
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Name string `hcl:"name"`
|
||||
Body hcl.Body `hcl:",body"`
|
||||
Remain hcl.Body `hcl:",remain"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
got := gotI.(struct {
|
||||
Name string `hcl:"name"`
|
||||
Body hcl.Body `hcl:",body"`
|
||||
Remain hcl.Body `hcl:",remain"`
|
||||
})
|
||||
|
||||
attrs, _ := got.Body.JustAttributes()
|
||||
|
||||
return got.Name == "Ermintrude" && len(attrs) == 2 &&
|
||||
attrs["name"] != nil && attrs["age"] != nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": map[string]interface{}{},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
// Generating no diagnostics is good enough for this one.
|
||||
return true
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{{}},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
// Generating no diagnostics is good enough for this one.
|
||||
return true
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{{}, {}},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
// Generating one diagnostic is good enough for this one.
|
||||
return true
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
// Generating one diagnostic is good enough for this one.
|
||||
return true
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
// Generating one diagnostic is good enough for this one.
|
||||
return true
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": map[string]interface{}{},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle *struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
return gotI.(struct {
|
||||
Noodle *struct{} `hcl:"noodle,block"`
|
||||
}).Noodle != nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{{}},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle *struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
return gotI.(struct {
|
||||
Noodle *struct{} `hcl:"noodle,block"`
|
||||
}).Noodle != nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle *struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
return gotI.(struct {
|
||||
Noodle *struct{} `hcl:"noodle,block"`
|
||||
}).Noodle == nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{{}, {}},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle *struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
// Generating one diagnostic is good enough for this one.
|
||||
return true
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle []struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
noodle := gotI.(struct {
|
||||
Noodle []struct{} `hcl:"noodle,block"`
|
||||
}).Noodle
|
||||
return len(noodle) == 0
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{{}},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle []struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
noodle := gotI.(struct {
|
||||
Noodle []struct{} `hcl:"noodle,block"`
|
||||
}).Noodle
|
||||
return len(noodle) == 1
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": []map[string]interface{}{{}, {}},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle []struct{} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
noodle := gotI.(struct {
|
||||
Noodle []struct{} `hcl:"noodle,block"`
|
||||
}).Noodle
|
||||
return len(noodle) == 2
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": map[string]interface{}{},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct {
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
//nolint:misspell
|
||||
// Generating two diagnostics is good enough for this one.
|
||||
// (one for the missing noodle block and the other for
|
||||
// the JSON serialization detecting the missing level of
|
||||
// heirarchy for the label.)
|
||||
return true
|
||||
},
|
||||
2,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": map[string]interface{}{
|
||||
"foo_foo": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct {
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
noodle := gotI.(struct {
|
||||
Noodle struct {
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"noodle,block"`
|
||||
}).Noodle
|
||||
return noodle.Name == "foo_foo"
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": map[string]interface{}{
|
||||
"foo_foo": map[string]interface{}{},
|
||||
"bar_baz": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct {
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
// One diagnostic is enough for this one.
|
||||
return true
|
||||
},
|
||||
1,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": map[string]interface{}{
|
||||
"foo_foo": map[string]interface{}{},
|
||||
"bar_baz": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodles []struct {
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
noodles := gotI.(struct {
|
||||
Noodles []struct {
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"noodle,block"`
|
||||
}).Noodles
|
||||
return len(noodles) == 2 && (noodles[0].Name == "foo_foo" || noodles[0].Name == "bar_baz") && (noodles[1].Name == "foo_foo" || noodles[1].Name == "bar_baz") && noodles[0].Name != noodles[1].Name
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"noodle": map[string]interface{}{
|
||||
"foo_foo": map[string]interface{}{
|
||||
"type": "rice",
|
||||
},
|
||||
},
|
||||
},
|
||||
makeInstantiateType(struct {
|
||||
Noodle struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Type string `hcl:"type"`
|
||||
} `hcl:"noodle,block"`
|
||||
}{}),
|
||||
func(gotI interface{}) bool {
|
||||
noodle := gotI.(struct {
|
||||
Noodle struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Type string `hcl:"type"`
|
||||
} `hcl:"noodle,block"`
|
||||
}).Noodle
|
||||
return noodle.Name == "foo_foo" && noodle.Type == "rice"
|
||||
},
|
||||
0,
|
||||
},
|
||||
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"age": 34,
|
||||
},
|
||||
makeInstantiateType(map[string]string(nil)),
|
||||
deepEquals(map[string]string{
|
||||
"name": "Ermintrude",
|
||||
"age": "34",
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"age": 89,
|
||||
},
|
||||
makeInstantiateType(map[string]*hcl.Attribute(nil)),
|
||||
func(gotI interface{}) bool {
|
||||
got := gotI.(map[string]*hcl.Attribute)
|
||||
return len(got) == 2 && got["name"] != nil && got["age"] != nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"age": 13,
|
||||
},
|
||||
makeInstantiateType(map[string]hcl.Expression(nil)),
|
||||
func(gotI interface{}) bool {
|
||||
got := gotI.(map[string]hcl.Expression)
|
||||
return len(got) == 2 && got["name"] != nil && got["age"] != nil
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"name": "Ermintrude",
|
||||
"living": true,
|
||||
},
|
||||
makeInstantiateType(map[string]cty.Value(nil)),
|
||||
deepEquals(map[string]cty.Value{
|
||||
"name": cty.StringVal("Ermintrude"),
|
||||
"living": cty.True,
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Retain "nested" block while decoding
|
||||
map[string]interface{}{
|
||||
"plain": "foo",
|
||||
},
|
||||
func() interface{} {
|
||||
return &withNestedBlock{
|
||||
Plain: "bar",
|
||||
Nested: &withTwoAttributes{
|
||||
A: "bar",
|
||||
},
|
||||
}
|
||||
},
|
||||
func(gotI interface{}) bool {
|
||||
foo := gotI.(withNestedBlock)
|
||||
return foo.Plain == "foo" && foo.Nested != nil && foo.Nested.A == "bar"
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Retain values in "nested" block while decoding
|
||||
map[string]interface{}{
|
||||
"nested": map[string]interface{}{
|
||||
"a": "foo",
|
||||
},
|
||||
},
|
||||
func() interface{} {
|
||||
return &withNestedBlock{
|
||||
Nested: &withTwoAttributes{
|
||||
B: "bar",
|
||||
},
|
||||
}
|
||||
},
|
||||
func(gotI interface{}) bool {
|
||||
foo := gotI.(withNestedBlock)
|
||||
return foo.Nested.A == "foo" && foo.Nested.B == "bar"
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Retain values in "nested" block list while decoding
|
||||
map[string]interface{}{
|
||||
"nested": []map[string]interface{}{
|
||||
{
|
||||
"a": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
func() interface{} {
|
||||
return &withListofNestedBlocks{
|
||||
Nested: []*withTwoAttributes{
|
||||
{
|
||||
B: "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
func(gotI interface{}) bool {
|
||||
n := gotI.(withListofNestedBlocks)
|
||||
return n.Nested[0].A == "foo" && n.Nested[0].B == "bar"
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Remove additional elements from the list while decoding nested blocks
|
||||
map[string]interface{}{
|
||||
"nested": []map[string]interface{}{
|
||||
{
|
||||
"a": "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
func() interface{} {
|
||||
return &withListofNestedBlocks{
|
||||
Nested: []*withTwoAttributes{
|
||||
{
|
||||
B: "bar",
|
||||
},
|
||||
{
|
||||
B: "bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
func(gotI interface{}) bool {
|
||||
n := gotI.(withListofNestedBlocks)
|
||||
return len(n.Nested) == 1
|
||||
},
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Make sure decoding value slices works the same as pointer slices.
|
||||
map[string]interface{}{
|
||||
"nested": []map[string]interface{}{
|
||||
{
|
||||
"b": "bar",
|
||||
},
|
||||
{
|
||||
"b": "baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
func() interface{} {
|
||||
return &withListofNestedBlocksNoPointers{
|
||||
Nested: []withTwoAttributes{
|
||||
{
|
||||
B: "foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
func(gotI interface{}) bool {
|
||||
n := gotI.(withListofNestedBlocksNoPointers)
|
||||
return n.Nested[0].B == "bar" && len(n.Nested) == 2
|
||||
},
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
// For convenience here we're going to use the JSON parser
|
||||
// to process the given body.
|
||||
buf, err := json.Marshal(test.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("error JSON-encoding body for test %d: %s", i, err)
|
||||
}
|
||||
|
||||
t.Run(string(buf), func(t *testing.T) {
|
||||
file, diags := hclJSON.Parse(buf, "test.json")
|
||||
if len(diags) != 0 {
|
||||
t.Fatalf("diagnostics while parsing: %s", diags.Error())
|
||||
}
|
||||
|
||||
targetVal := reflect.ValueOf(test.Target())
|
||||
|
||||
diags = DecodeBody(file.Body, nil, targetVal.Interface())
|
||||
if len(diags) != test.DiagCount {
|
||||
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
|
||||
for _, diag := range diags {
|
||||
t.Logf(" - %s", diag.Error())
|
||||
}
|
||||
}
|
||||
got := targetVal.Elem().Interface()
|
||||
if !test.Check(got) {
|
||||
t.Errorf("wrong result\ngot: %s", spew.Sdump(got))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeExpression(t *testing.T) {
|
||||
tests := []struct {
|
||||
Value cty.Value
|
||||
Target interface{}
|
||||
Want interface{}
|
||||
DiagCount int
|
||||
}{
|
||||
{
|
||||
cty.StringVal("hello"),
|
||||
"",
|
||||
"hello",
|
||||
0,
|
||||
},
|
||||
{
|
||||
cty.StringVal("hello"),
|
||||
cty.NilVal,
|
||||
cty.StringVal("hello"),
|
||||
0,
|
||||
},
|
||||
{
|
||||
cty.NumberIntVal(2),
|
||||
"",
|
||||
"2",
|
||||
0,
|
||||
},
|
||||
{
|
||||
cty.StringVal("true"),
|
||||
false,
|
||||
true,
|
||||
0,
|
||||
},
|
||||
{
|
||||
cty.NullVal(cty.String),
|
||||
"",
|
||||
"",
|
||||
1, // null value is not allowed
|
||||
},
|
||||
{
|
||||
cty.UnknownVal(cty.String),
|
||||
"",
|
||||
"",
|
||||
1, // value must be known
|
||||
},
|
||||
{
|
||||
cty.ListVal([]cty.Value{cty.True}),
|
||||
false,
|
||||
false,
|
||||
1, // bool required
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
|
||||
expr := &fixedExpression{test.Value}
|
||||
|
||||
targetVal := reflect.New(reflect.TypeOf(test.Target))
|
||||
|
||||
diags := DecodeExpression(expr, nil, targetVal.Interface())
|
||||
if len(diags) != test.DiagCount {
|
||||
t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount)
|
||||
for _, diag := range diags {
|
||||
t.Logf(" - %s", diag.Error())
|
||||
}
|
||||
}
|
||||
got := targetVal.Elem().Interface()
|
||||
if !reflect.DeepEqual(got, test.Want) {
|
||||
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type fixedExpression struct {
|
||||
val cty.Value
|
||||
}
|
||||
|
||||
func (e *fixedExpression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
return e.val, nil
|
||||
}
|
||||
|
||||
func (e *fixedExpression) Range() (r hcl.Range) {
|
||||
return
|
||||
}
|
||||
|
||||
func (e *fixedExpression) StartRange() (r hcl.Range) {
|
||||
return
|
||||
}
|
||||
|
||||
func (e *fixedExpression) Variables() []hcl.Traversal {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeInstantiateType(target interface{}) func() interface{} {
|
||||
return func() interface{} {
|
||||
return reflect.New(reflect.TypeOf(target)).Interface()
|
||||
}
|
||||
}
|
65
bake/hclparser/gohcl/doc.go
Normal file
65
bake/hclparser/gohcl/doc.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Package gohcl allows decoding HCL configurations into Go data structures.
|
||||
//
|
||||
// It provides a convenient and concise way of describing the schema for
|
||||
// configuration and then accessing the resulting data via native Go
|
||||
// types.
|
||||
//
|
||||
// A struct field tag scheme is used, similar to other decoding and
|
||||
// unmarshalling libraries. The tags are formatted as in the following example:
|
||||
//
|
||||
// ThingType string `hcl:"thing_type,attr"`
|
||||
//
|
||||
// Within each tag there are two comma-separated tokens. The first is the
|
||||
// name of the corresponding construct in configuration, while the second
|
||||
// is a keyword giving the kind of construct expected. The following
|
||||
// kind keywords are supported:
|
||||
//
|
||||
// attr (the default) indicates that the value is to be populated from an attribute
|
||||
// block indicates that the value is to populated from a block
|
||||
// label indicates that the value is to populated from a block label
|
||||
// optional is the same as attr, but the field is optional
|
||||
// remain indicates that the value is to be populated from the remaining body after populating other fields
|
||||
//
|
||||
// "attr" fields may either be of type *hcl.Expression, in which case the raw
|
||||
// expression is assigned, or of any type accepted by gocty, in which case
|
||||
// gocty will be used to assign the value to a native Go type.
|
||||
//
|
||||
// "block" fields may be a struct that recursively uses the same tags, or a
|
||||
// slice of such structs, in which case multiple blocks of the corresponding
|
||||
// type are decoded into the slice.
|
||||
//
|
||||
// "body" can be placed on a single field of type hcl.Body to capture
|
||||
// the full hcl.Body that was decoded for a block. This does not allow leftover
|
||||
// values like "remain", so a decoding error will still be returned if leftover
|
||||
// fields are given. If you want to capture the decoding body PLUS leftover
|
||||
// fields, you must specify a "remain" field as well to prevent errors. The
|
||||
// body field and the remain field will both contain the leftover fields.
|
||||
//
|
||||
// "label" fields are considered only in a struct used as the type of a field
|
||||
// marked as "block", and are used sequentially to capture the labels of
|
||||
// the blocks being decoded. In this case, the name token is used only as
|
||||
// an identifier for the label in diagnostic messages.
|
||||
//
|
||||
// "optional" fields behave like "attr" fields, but they are optional
|
||||
// and will not give parsing errors if they are missing.
|
||||
//
|
||||
// "remain" can be placed on a single field that may be either of type
|
||||
// hcl.Body or hcl.Attributes, in which case any remaining body content is
|
||||
// placed into this field for delayed processing. If no "remain" field is
|
||||
// present then any attributes or blocks not matched by another valid tag
|
||||
// will cause an error diagnostic.
|
||||
//
|
||||
// Only a subset of this tagging/typing vocabulary is supported for the
|
||||
// "Encode" family of functions. See the EncodeIntoBody docs for full details
|
||||
// on the constraints there.
|
||||
//
|
||||
// Broadly-speaking this package deals with two types of error. The first is
|
||||
// errors in the configuration itself, which are returned as diagnostics
|
||||
// written with the configuration author as the target audience. The second
|
||||
// is bugs in the calling program, such as invalid struct tags, which are
|
||||
// surfaced via panics since there can be no useful runtime handling of such
|
||||
// errors and they should certainly not be returned to the user as diagnostics.
|
||||
package gohcl
|
192
bake/hclparser/gohcl/encode.go
Normal file
192
bake/hclparser/gohcl/encode.go
Normal file
@@ -0,0 +1,192 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gohcl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
// EncodeIntoBody replaces the contents of the given hclwrite Body with
|
||||
// attributes and blocks derived from the given value, which must be a
|
||||
// struct value or a pointer to a struct value with the struct tags defined
|
||||
// in this package.
|
||||
//
|
||||
// This function can work only with fully-decoded data. It will ignore any
|
||||
// fields tagged as "remain", any fields that decode attributes into either
|
||||
// hcl.Attribute or hcl.Expression values, and any fields that decode blocks
|
||||
// into hcl.Attributes values. This function does not have enough information
|
||||
// to complete the decoding of these types.
|
||||
//
|
||||
// Any fields tagged as "label" are ignored by this function. Use EncodeAsBlock
|
||||
// to produce a whole hclwrite.Block including block labels.
|
||||
//
|
||||
// As long as a suitable value is given to encode and the destination body
|
||||
// is non-nil, this function will always complete. It will panic in case of
|
||||
// any errors in the calling program, such as passing an inappropriate type
|
||||
// or a nil body.
|
||||
//
|
||||
// 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.
|
||||
// Fields representing attributes should usually precede those representing
|
||||
// blocks so that the attributes can group togather in the result. For more
|
||||
// control, use the hclwrite API directly.
|
||||
func EncodeIntoBody(val interface{}, dst *hclwrite.Body) {
|
||||
rv := reflect.ValueOf(val)
|
||||
ty := rv.Type()
|
||||
if ty.Kind() == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
ty = rv.Type()
|
||||
}
|
||||
if ty.Kind() != reflect.Struct {
|
||||
panic(fmt.Sprintf("value is %s, not struct", ty.Kind()))
|
||||
}
|
||||
|
||||
tags := getFieldTags(ty)
|
||||
populateBody(rv, ty, tags, dst)
|
||||
}
|
||||
|
||||
// EncodeAsBlock creates a new hclwrite.Block populated with the data from
|
||||
// the given value, which must be a struct or pointer to struct with the
|
||||
// struct tags defined in this package.
|
||||
//
|
||||
// If the given struct type has fields tagged with "label" tags then they
|
||||
// will be used in order to annotate the created block with labels.
|
||||
//
|
||||
// This function has the same constraints as EncodeIntoBody and will panic
|
||||
// if they are violated.
|
||||
func EncodeAsBlock(val interface{}, blockType string) *hclwrite.Block {
|
||||
rv := reflect.ValueOf(val)
|
||||
ty := rv.Type()
|
||||
if ty.Kind() == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
ty = rv.Type()
|
||||
}
|
||||
if ty.Kind() != reflect.Struct {
|
||||
panic(fmt.Sprintf("value is %s, not struct", ty.Kind()))
|
||||
}
|
||||
|
||||
tags := getFieldTags(ty)
|
||||
labels := make([]string, len(tags.Labels))
|
||||
for i, lf := range tags.Labels {
|
||||
lv := rv.Field(lf.FieldIndex)
|
||||
// We just stringify whatever we find. It should always be a string
|
||||
// but if not then we'll still do something reasonable.
|
||||
labels[i] = fmt.Sprintf("%s", lv.Interface())
|
||||
}
|
||||
|
||||
block := hclwrite.NewBlock(blockType, labels)
|
||||
populateBody(rv, ty, tags, block.Body())
|
||||
return block
|
||||
}
|
||||
|
||||
func populateBody(rv reflect.Value, ty reflect.Type, tags *fieldTags, dst *hclwrite.Body) {
|
||||
nameIdxs := make(map[string]int, len(tags.Attributes)+len(tags.Blocks))
|
||||
namesOrder := make([]string, 0, len(tags.Attributes)+len(tags.Blocks))
|
||||
for n, i := range tags.Attributes {
|
||||
nameIdxs[n] = i
|
||||
namesOrder = append(namesOrder, n)
|
||||
}
|
||||
for n, i := range tags.Blocks {
|
||||
nameIdxs[n] = i
|
||||
namesOrder = append(namesOrder, n)
|
||||
}
|
||||
sort.SliceStable(namesOrder, func(i, j int) bool {
|
||||
ni, nj := namesOrder[i], namesOrder[j]
|
||||
return nameIdxs[ni] < nameIdxs[nj]
|
||||
})
|
||||
|
||||
dst.Clear()
|
||||
|
||||
prevWasBlock := false
|
||||
for _, name := range namesOrder {
|
||||
fieldIdx := nameIdxs[name]
|
||||
field := ty.Field(fieldIdx)
|
||||
fieldTy := field.Type
|
||||
fieldVal := rv.Field(fieldIdx)
|
||||
|
||||
if fieldTy.Kind() == reflect.Ptr {
|
||||
fieldTy = fieldTy.Elem()
|
||||
fieldVal = fieldVal.Elem()
|
||||
}
|
||||
|
||||
if _, isAttr := tags.Attributes[name]; isAttr {
|
||||
if exprType.AssignableTo(fieldTy) || attrType.AssignableTo(fieldTy) {
|
||||
continue // ignore undecoded fields
|
||||
}
|
||||
if !fieldVal.IsValid() {
|
||||
continue // ignore (field value is nil pointer)
|
||||
}
|
||||
if fieldTy.Kind() == reflect.Ptr && fieldVal.IsNil() {
|
||||
continue // ignore
|
||||
}
|
||||
if prevWasBlock {
|
||||
dst.AppendNewline()
|
||||
prevWasBlock = false
|
||||
}
|
||||
|
||||
valTy, err := gocty.ImpliedType(fieldVal.Interface())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot encode %T as HCL expression: %s", fieldVal.Interface(), err))
|
||||
}
|
||||
|
||||
val, err := gocty.ToCtyValue(fieldVal.Interface(), valTy)
|
||||
if err != nil {
|
||||
// This should never happen, since we should always be able
|
||||
// to decode into the implied type.
|
||||
panic(fmt.Sprintf("failed to encode %T as %#v: %s", fieldVal.Interface(), valTy, err))
|
||||
}
|
||||
|
||||
dst.SetAttributeValue(name, val)
|
||||
} else { // must be a block, then
|
||||
elemTy := fieldTy
|
||||
isSeq := false
|
||||
if elemTy.Kind() == reflect.Slice || elemTy.Kind() == reflect.Array {
|
||||
isSeq = true
|
||||
elemTy = elemTy.Elem()
|
||||
}
|
||||
|
||||
if bodyType.AssignableTo(elemTy) || attrsType.AssignableTo(elemTy) {
|
||||
continue // ignore undecoded fields
|
||||
}
|
||||
prevWasBlock = false
|
||||
|
||||
if isSeq {
|
||||
l := fieldVal.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
elemVal := fieldVal.Index(i)
|
||||
if !elemVal.IsValid() {
|
||||
continue // ignore (elem value is nil pointer)
|
||||
}
|
||||
if elemTy.Kind() == reflect.Ptr && elemVal.IsNil() {
|
||||
continue // ignore
|
||||
}
|
||||
block := EncodeAsBlock(elemVal.Interface(), name)
|
||||
if !prevWasBlock {
|
||||
dst.AppendNewline()
|
||||
prevWasBlock = true
|
||||
}
|
||||
dst.AppendBlock(block)
|
||||
}
|
||||
} else {
|
||||
if !fieldVal.IsValid() {
|
||||
continue // ignore (field value is nil pointer)
|
||||
}
|
||||
if elemTy.Kind() == reflect.Ptr && fieldVal.IsNil() {
|
||||
continue // ignore
|
||||
}
|
||||
block := EncodeAsBlock(fieldVal.Interface(), name)
|
||||
if !prevWasBlock {
|
||||
dst.AppendNewline()
|
||||
prevWasBlock = true
|
||||
}
|
||||
dst.AppendBlock(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
67
bake/hclparser/gohcl/encode_test.go
Normal file
67
bake/hclparser/gohcl/encode_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gohcl_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
)
|
||||
|
||||
func ExampleEncodeIntoBody() {
|
||||
type Service struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Exe []string `hcl:"executable"`
|
||||
}
|
||||
type Constraints struct {
|
||||
OS string `hcl:"os"`
|
||||
Arch string `hcl:"arch"`
|
||||
}
|
||||
type App struct {
|
||||
Name string `hcl:"name"`
|
||||
Desc string `hcl:"description"`
|
||||
Constraints *Constraints `hcl:"constraints,block"`
|
||||
Services []Service `hcl:"service,block"`
|
||||
}
|
||||
|
||||
app := App{
|
||||
Name: "awesome-app",
|
||||
Desc: "Such an awesome application",
|
||||
Constraints: &Constraints{
|
||||
OS: "linux",
|
||||
Arch: "amd64",
|
||||
},
|
||||
Services: []Service{
|
||||
{
|
||||
Name: "web",
|
||||
Exe: []string{"./web", "--listen=:8080"},
|
||||
},
|
||||
{
|
||||
Name: "worker",
|
||||
Exe: []string{"./worker"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
f := hclwrite.NewEmptyFile()
|
||||
gohcl.EncodeIntoBody(&app, f.Body())
|
||||
fmt.Printf("%s", f.Bytes())
|
||||
|
||||
// Output:
|
||||
// name = "awesome-app"
|
||||
// description = "Such an awesome application"
|
||||
//
|
||||
// constraints {
|
||||
// os = "linux"
|
||||
// arch = "amd64"
|
||||
// }
|
||||
//
|
||||
// service "web" {
|
||||
// executable = ["./web", "--listen=:8080"]
|
||||
// }
|
||||
// service "worker" {
|
||||
// executable = ["./worker"]
|
||||
// }
|
||||
}
|
185
bake/hclparser/gohcl/schema.go
Normal file
185
bake/hclparser/gohcl/schema.go
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gohcl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
)
|
||||
|
||||
// ImpliedBodySchema produces a hcl.BodySchema derived from the type of the
|
||||
// given value, which must be a struct value or a pointer to one. If an
|
||||
// inappropriate value is passed, this function will panic.
|
||||
//
|
||||
// The second return argument indicates whether the given struct includes
|
||||
// a "remain" field, and thus the returned schema is non-exhaustive.
|
||||
//
|
||||
// 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
|
||||
// mapping is attempted, this function will panic.
|
||||
func ImpliedBodySchema(val interface{}) (schema *hcl.BodySchema, partial bool) {
|
||||
ty := reflect.TypeOf(val)
|
||||
|
||||
if ty.Kind() == reflect.Ptr {
|
||||
ty = ty.Elem()
|
||||
}
|
||||
|
||||
if ty.Kind() != reflect.Struct {
|
||||
panic(fmt.Sprintf("given value must be struct, not %T", val))
|
||||
}
|
||||
|
||||
var attrSchemas []hcl.AttributeSchema
|
||||
var blockSchemas []hcl.BlockHeaderSchema
|
||||
|
||||
tags := getFieldTags(ty)
|
||||
|
||||
attrNames := make([]string, 0, len(tags.Attributes))
|
||||
for n := range tags.Attributes {
|
||||
attrNames = append(attrNames, n)
|
||||
}
|
||||
sort.Strings(attrNames)
|
||||
for _, n := range attrNames {
|
||||
idx := tags.Attributes[n]
|
||||
optional := tags.Optional[n]
|
||||
field := ty.Field(idx)
|
||||
|
||||
var required bool
|
||||
|
||||
switch {
|
||||
case field.Type.AssignableTo(exprType):
|
||||
//nolint:misspell
|
||||
// If we're decoding to hcl.Expression then absense can be
|
||||
// indicated via a null value, so we don't specify that
|
||||
// the field is required during decoding.
|
||||
required = false
|
||||
case field.Type.Kind() != reflect.Ptr && !optional:
|
||||
required = true
|
||||
default:
|
||||
required = false
|
||||
}
|
||||
|
||||
attrSchemas = append(attrSchemas, hcl.AttributeSchema{
|
||||
Name: n,
|
||||
Required: required,
|
||||
})
|
||||
}
|
||||
|
||||
blockNames := make([]string, 0, len(tags.Blocks))
|
||||
for n := range tags.Blocks {
|
||||
blockNames = append(blockNames, n)
|
||||
}
|
||||
sort.Strings(blockNames)
|
||||
for _, n := range blockNames {
|
||||
idx := tags.Blocks[n]
|
||||
field := ty.Field(idx)
|
||||
fty := field.Type
|
||||
if fty.Kind() == reflect.Slice {
|
||||
fty = fty.Elem()
|
||||
}
|
||||
if fty.Kind() == reflect.Ptr {
|
||||
fty = fty.Elem()
|
||||
}
|
||||
if fty.Kind() != reflect.Struct {
|
||||
panic(fmt.Sprintf(
|
||||
"hcl 'block' tag kind cannot be applied to %s field %s: struct required", field.Type.String(), field.Name,
|
||||
))
|
||||
}
|
||||
ftags := getFieldTags(fty)
|
||||
var labelNames []string
|
||||
if len(ftags.Labels) > 0 {
|
||||
labelNames = make([]string, len(ftags.Labels))
|
||||
for i, l := range ftags.Labels {
|
||||
labelNames[i] = l.Name
|
||||
}
|
||||
}
|
||||
|
||||
blockSchemas = append(blockSchemas, hcl.BlockHeaderSchema{
|
||||
Type: n,
|
||||
LabelNames: labelNames,
|
||||
})
|
||||
}
|
||||
|
||||
partial = tags.Remain != nil
|
||||
schema = &hcl.BodySchema{
|
||||
Attributes: attrSchemas,
|
||||
Blocks: blockSchemas,
|
||||
}
|
||||
return schema, partial
|
||||
}
|
||||
|
||||
type fieldTags struct {
|
||||
Attributes map[string]int
|
||||
Blocks map[string]int
|
||||
Labels []labelField
|
||||
Remain *int
|
||||
Body *int
|
||||
Optional map[string]bool
|
||||
}
|
||||
|
||||
type labelField struct {
|
||||
FieldIndex int
|
||||
Name string
|
||||
}
|
||||
|
||||
func getFieldTags(ty reflect.Type) *fieldTags {
|
||||
ret := &fieldTags{
|
||||
Attributes: map[string]int{},
|
||||
Blocks: map[string]int{},
|
||||
Optional: map[string]bool{},
|
||||
}
|
||||
|
||||
ct := ty.NumField()
|
||||
for i := 0; i < ct; i++ {
|
||||
field := ty.Field(i)
|
||||
tag := field.Tag.Get("hcl")
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
comma := strings.Index(tag, ",")
|
||||
var name, kind string
|
||||
if comma != -1 {
|
||||
name = tag[:comma]
|
||||
kind = tag[comma+1:]
|
||||
} else {
|
||||
name = tag
|
||||
kind = "attr"
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case "attr":
|
||||
ret.Attributes[name] = i
|
||||
case "block":
|
||||
ret.Blocks[name] = i
|
||||
case "label":
|
||||
ret.Labels = append(ret.Labels, labelField{
|
||||
FieldIndex: i,
|
||||
Name: name,
|
||||
})
|
||||
case "remain":
|
||||
if ret.Remain != nil {
|
||||
panic("only one 'remain' tag is permitted")
|
||||
}
|
||||
idx := i // copy, because this loop will continue assigning to i
|
||||
ret.Remain = &idx
|
||||
case "body":
|
||||
if ret.Body != nil {
|
||||
panic("only one 'body' tag is permitted")
|
||||
}
|
||||
idx := i // copy, because this loop will continue assigning to i
|
||||
ret.Body = &idx
|
||||
case "optional":
|
||||
ret.Attributes[name] = i
|
||||
ret.Optional[name] = true
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid hcl field tag kind %q on %s %q", kind, field.Type.String(), field.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
233
bake/hclparser/gohcl/schema_test.go
Normal file
233
bake/hclparser/gohcl/schema_test.go
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gohcl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
)
|
||||
|
||||
func TestImpliedBodySchema(t *testing.T) {
|
||||
tests := []struct {
|
||||
val interface{}
|
||||
wantSchema *hcl.BodySchema
|
||||
wantPartial bool
|
||||
}{
|
||||
{
|
||||
struct{}{},
|
||||
&hcl.BodySchema{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Ignored bool
|
||||
}{},
|
||||
&hcl.BodySchema{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Attr1 bool `hcl:"attr1"`
|
||||
Attr2 bool `hcl:"attr2"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "attr1",
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "attr2",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Attr *bool `hcl:"attr,attr"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "attr",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Thing struct{} `hcl:"thing,block"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "thing",
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Thing struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"thing,block"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "thing",
|
||||
LabelNames: []string{"type", "name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Thing []struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"thing,block"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "thing",
|
||||
LabelNames: []string{"type", "name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Thing *struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"thing,block"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "thing",
|
||||
LabelNames: []string{"type", "name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Thing struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Something string `hcl:"something"`
|
||||
} `hcl:"thing,block"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "thing",
|
||||
LabelNames: []string{"name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Doodad string `hcl:"doodad"`
|
||||
Thing struct {
|
||||
Name string `hcl:"name,label"`
|
||||
} `hcl:"thing,block"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "doodad",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{
|
||||
Type: "thing",
|
||||
LabelNames: []string{"name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Doodad string `hcl:"doodad"`
|
||||
Config string `hcl:",remain"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "doodad",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Expr hcl.Expression `hcl:"expr"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "expr",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
struct {
|
||||
Meh string `hcl:"meh,optional"`
|
||||
}{},
|
||||
&hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{
|
||||
Name: "meh",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%#v", test.val), func(t *testing.T) {
|
||||
schema, partial := ImpliedBodySchema(test.val)
|
||||
if !reflect.DeepEqual(schema, test.wantSchema) {
|
||||
t.Errorf(
|
||||
"wrong schema\ngot: %s\nwant: %s",
|
||||
spew.Sdump(schema), spew.Sdump(test.wantSchema),
|
||||
)
|
||||
}
|
||||
|
||||
if partial != test.wantPartial {
|
||||
t.Errorf(
|
||||
"wrong partial flag\ngot: %#v\nwant: %#v",
|
||||
partial, test.wantPartial,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
19
bake/hclparser/gohcl/types.go
Normal file
19
bake/hclparser/gohcl/types.go
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package gohcl
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
)
|
||||
|
||||
var victimExpr hcl.Expression
|
||||
var victimBody hcl.Body
|
||||
|
||||
var exprType = reflect.TypeOf(&victimExpr).Elem()
|
||||
var bodyType = reflect.TypeOf(&victimBody).Elem()
|
||||
var blockType = reflect.TypeOf((*hcl.Block)(nil)) //nolint:unused
|
||||
var attrType = reflect.TypeOf((*hcl.Attribute)(nil))
|
||||
var attrsType = reflect.TypeOf(hcl.Attributes(nil))
|
@@ -10,12 +10,11 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/buildx/bake/hclparser/gohcl"
|
||||
"github.com/docker/buildx/util/userfunc"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
type Opt struct {
|
||||
@@ -25,11 +24,17 @@ type Opt struct {
|
||||
}
|
||||
|
||||
type variable struct {
|
||||
Name string `json:"-" hcl:"name,label"`
|
||||
Default *hcl.Attribute `json:"default,omitempty" hcl:"default,optional"`
|
||||
Description string `json:"description,omitempty" hcl:"description,optional"`
|
||||
Body hcl.Body `json:"-" hcl:",body"`
|
||||
Remain hcl.Body `json:"-" hcl:",remain"`
|
||||
Name string `json:"-" hcl:"name,label"`
|
||||
Default *hcl.Attribute `json:"default,omitempty" hcl:"default,optional"`
|
||||
Description string `json:"description,omitempty" hcl:"description,optional"`
|
||||
Validations []*variableValidation `json:"validation,omitempty" hcl:"validation,block"`
|
||||
Body hcl.Body `json:"-" hcl:",body"`
|
||||
Remain hcl.Body `json:"-" hcl:",remain"`
|
||||
}
|
||||
|
||||
type variableValidation struct {
|
||||
Condition hcl.Expression `json:"condition" hcl:"condition"`
|
||||
ErrorMessage hcl.Expression `json:"error_message" hcl:"error_message"`
|
||||
}
|
||||
|
||||
type functionDef struct {
|
||||
@@ -448,7 +453,7 @@ func (p *parser) resolveBlock(block *hcl.Block, target *hcl.BodySchema) (err err
|
||||
}
|
||||
|
||||
// decode!
|
||||
diag = gohcl.DecodeBody(body(), ectx, output.Interface())
|
||||
diag = decodeBody(body(), ectx, output.Interface())
|
||||
if diag.HasErrors() {
|
||||
return diag
|
||||
}
|
||||
@@ -470,11 +475,11 @@ func (p *parser) resolveBlock(block *hcl.Block, target *hcl.BodySchema) (err err
|
||||
}
|
||||
|
||||
// store the result into the evaluation context (so it can be referenced)
|
||||
outputType, err := gocty.ImpliedType(output.Interface())
|
||||
outputType, err := ImpliedType(output.Interface())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputValue, err := gocty.ToCtyValue(output.Interface(), outputType)
|
||||
outputValue, err := ToCtyValue(output.Interface(), outputType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -486,7 +491,12 @@ func (p *parser) resolveBlock(block *hcl.Block, target *hcl.BodySchema) (err err
|
||||
m = map[string]cty.Value{}
|
||||
}
|
||||
m[name] = outputValue
|
||||
p.ectx.Variables[block.Type] = cty.MapVal(m)
|
||||
|
||||
// The logical contents of this structure is similar to a map,
|
||||
// but it's possible for some attributes to be different in a way that's
|
||||
// illegal for a map so we use an object here instead which is structurally
|
||||
// equivalent but allows disparate types for different keys.
|
||||
p.ectx.Variables[block.Type] = cty.ObjectVal(m)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -541,10 +551,37 @@ func (p *parser) resolveBlockNames(block *hcl.Block) ([]string, error) {
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (p *parser) validateVariables(vars map[string]*variable, ectx *hcl.EvalContext) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
for _, v := range vars {
|
||||
for _, validation := range v.Validations {
|
||||
condition, condDiags := validation.Condition.Value(ectx)
|
||||
if condDiags.HasErrors() {
|
||||
diags = append(diags, condDiags...)
|
||||
continue
|
||||
}
|
||||
if !condition.True() {
|
||||
message, msgDiags := validation.ErrorMessage.Value(ectx)
|
||||
if msgDiags.HasErrors() {
|
||||
diags = append(diags, msgDiags...)
|
||||
continue
|
||||
}
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Validation failed",
|
||||
Detail: message.AsString(),
|
||||
Subject: validation.Condition.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
type Variable struct {
|
||||
Name string
|
||||
Description string
|
||||
Value *string
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Value *string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
type ParseMeta struct {
|
||||
@@ -686,6 +723,9 @@ func Parse(b hcl.Body, opt Opt, val interface{}) (*ParseMeta, hcl.Diagnostics) {
|
||||
}
|
||||
vars = append(vars, v)
|
||||
}
|
||||
if diags := p.validateVariables(p.vars, p.ectx); diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
for k := range p.funcs {
|
||||
if err := p.resolveFunction(p.ectx, k); err != nil {
|
||||
@@ -947,3 +987,8 @@ func key(ks ...any) uint64 {
|
||||
}
|
||||
return hash.Sum64()
|
||||
}
|
||||
|
||||
func decodeBody(body hcl.Body, ctx *hcl.EvalContext, val interface{}) hcl.Diagnostics {
|
||||
dec := gohcl.DecodeOptions{ImpliedType: ImpliedType}
|
||||
return dec.DecodeBody(body, ctx, val)
|
||||
}
|
||||
|
@@ -170,7 +170,6 @@ func indexOfFunc() function.Function {
|
||||
}
|
||||
}
|
||||
return cty.NilVal, errors.New("item not found")
|
||||
|
||||
},
|
||||
})
|
||||
}
|
||||
|
160
bake/hclparser/type_implied.go
Normal file
160
bake/hclparser/type_implied.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017-2018 Martin Atkins
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
package hclparser
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// ImpliedType takes an arbitrary Go value (as an interface{}) and attempts
|
||||
// to find a suitable cty.Type instance that could be used for a conversion
|
||||
// with ToCtyValue.
|
||||
//
|
||||
// This allows -- for simple situations at least -- types to be defined just
|
||||
// once in Go and the cty types derived from the Go types, but in the process
|
||||
// it makes some assumptions that may be undesirable so applications are
|
||||
// encouraged to build their cty types directly if exacting control is
|
||||
// required.
|
||||
//
|
||||
// Not all Go types can be represented as cty types, so an error may be
|
||||
// returned which is usually considered to be a bug in the calling program.
|
||||
// In particular, ImpliedType will never use capsule types in its returned
|
||||
// type, because it cannot know the capsule types supported by the calling
|
||||
// program.
|
||||
func ImpliedType(gv interface{}) (cty.Type, error) {
|
||||
rt := reflect.TypeOf(gv)
|
||||
var path cty.Path
|
||||
return impliedType(rt, path)
|
||||
}
|
||||
|
||||
func impliedType(rt reflect.Type, path cty.Path) (cty.Type, error) {
|
||||
if ety, err := impliedTypeExt(rt, path); err == nil {
|
||||
return ety, nil
|
||||
}
|
||||
|
||||
switch rt.Kind() {
|
||||
case reflect.Ptr:
|
||||
return impliedType(rt.Elem(), path)
|
||||
|
||||
// Primitive types
|
||||
case reflect.Bool:
|
||||
return cty.Bool, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return cty.Number, nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return cty.Number, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return cty.Number, nil
|
||||
case reflect.String:
|
||||
return cty.String, nil
|
||||
|
||||
// Collection types
|
||||
case reflect.Slice:
|
||||
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.Number)})
|
||||
ety, err := impliedType(rt.Elem(), path)
|
||||
if err != nil {
|
||||
return cty.NilType, err
|
||||
}
|
||||
return cty.List(ety), nil
|
||||
case reflect.Map:
|
||||
if !stringType.AssignableTo(rt.Key()) {
|
||||
return cty.NilType, path.NewErrorf("no cty.Type for %s (must have string keys)", rt)
|
||||
}
|
||||
path := append(path, cty.IndexStep{Key: cty.UnknownVal(cty.String)})
|
||||
ety, err := impliedType(rt.Elem(), path)
|
||||
if err != nil {
|
||||
return cty.NilType, err
|
||||
}
|
||||
return cty.Map(ety), nil
|
||||
|
||||
// Structural types
|
||||
case reflect.Struct:
|
||||
return impliedStructType(rt, path)
|
||||
|
||||
default:
|
||||
return cty.NilType, path.NewErrorf("no cty.Type for %s", rt)
|
||||
}
|
||||
}
|
||||
|
||||
func impliedStructType(rt reflect.Type, path cty.Path) (cty.Type, error) {
|
||||
if valueType.AssignableTo(rt) {
|
||||
// Special case: cty.Value represents cty.DynamicPseudoType, for
|
||||
// type conformance checking.
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
fieldIdxs := structTagIndices(rt)
|
||||
if len(fieldIdxs) == 0 {
|
||||
return cty.NilType, path.NewErrorf("no cty.Type for %s (no cty field tags)", rt)
|
||||
}
|
||||
|
||||
atys := make(map[string]cty.Type, len(fieldIdxs))
|
||||
|
||||
{
|
||||
// Temporary extension of path for attributes
|
||||
path := append(path, nil)
|
||||
|
||||
for k, fi := range fieldIdxs {
|
||||
path[len(path)-1] = cty.GetAttrStep{Name: k}
|
||||
|
||||
ft := rt.Field(fi).Type
|
||||
aty, err := impliedType(ft, path)
|
||||
if err != nil {
|
||||
return cty.NilType, err
|
||||
}
|
||||
|
||||
atys[k] = aty
|
||||
}
|
||||
}
|
||||
|
||||
return cty.Object(atys), nil
|
||||
}
|
||||
|
||||
var (
|
||||
valueType = reflect.TypeOf(cty.Value{})
|
||||
stringType = reflect.TypeOf("")
|
||||
)
|
||||
|
||||
// structTagIndices interrogates the fields of the given type (which must
|
||||
// be a struct type, or we'll panic) and returns a map from the cty
|
||||
// attribute names declared via struct tags to the indices of the
|
||||
// fields holding those tags.
|
||||
//
|
||||
// This function will panic if two fields within the struct are tagged with
|
||||
// the same cty attribute name.
|
||||
func structTagIndices(st reflect.Type) map[string]int {
|
||||
ct := st.NumField()
|
||||
ret := make(map[string]int, ct)
|
||||
|
||||
for i := 0; i < ct; i++ {
|
||||
field := st.Field(i)
|
||||
attrName := field.Tag.Get("cty")
|
||||
if attrName != "" {
|
||||
ret[attrName] = i
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
166
bake/hclparser/type_implied_ext.go
Normal file
166
bake/hclparser/type_implied_ext.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package hclparser
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
)
|
||||
|
||||
type ToCtyValueConverter interface {
|
||||
// ToCtyValue will convert this capsule value into a native
|
||||
// cty.Value. This should not return a capsule type.
|
||||
ToCtyValue() cty.Value
|
||||
}
|
||||
|
||||
type FromCtyValueConverter interface {
|
||||
// FromCtyValue will initialize this value using a cty.Value.
|
||||
FromCtyValue(in cty.Value, path cty.Path) error
|
||||
}
|
||||
|
||||
type extensionType int
|
||||
|
||||
const (
|
||||
unwrapCapsuleValueExtension extensionType = iota
|
||||
)
|
||||
|
||||
func impliedTypeExt(rt reflect.Type, _ cty.Path) (cty.Type, error) {
|
||||
if rt.Kind() != reflect.Pointer {
|
||||
rt = reflect.PointerTo(rt)
|
||||
}
|
||||
|
||||
if isCapsuleType(rt) {
|
||||
return capsuleValueCapsuleType(rt), nil
|
||||
}
|
||||
return cty.NilType, errdefs.ErrNotImplemented
|
||||
}
|
||||
|
||||
func isCapsuleType(rt reflect.Type) bool {
|
||||
fromCtyValueType := reflect.TypeFor[FromCtyValueConverter]()
|
||||
toCtyValueType := reflect.TypeFor[ToCtyValueConverter]()
|
||||
return rt.Implements(fromCtyValueType) && rt.Implements(toCtyValueType)
|
||||
}
|
||||
|
||||
var capsuleValueTypes sync.Map
|
||||
|
||||
func capsuleValueCapsuleType(rt reflect.Type) cty.Type {
|
||||
if rt.Kind() != reflect.Pointer {
|
||||
panic("capsule value must be a pointer")
|
||||
}
|
||||
|
||||
elem := rt.Elem()
|
||||
if val, loaded := capsuleValueTypes.Load(elem); loaded {
|
||||
return val.(cty.Type)
|
||||
}
|
||||
|
||||
toCtyValueType := reflect.TypeFor[ToCtyValueConverter]()
|
||||
|
||||
// First time used. Initialize new capsule ops.
|
||||
ops := &cty.CapsuleOps{
|
||||
ConversionTo: func(_ cty.Type) func(cty.Value, cty.Path) (any, error) {
|
||||
return func(in cty.Value, p cty.Path) (any, error) {
|
||||
rv := reflect.New(elem).Interface()
|
||||
if err := rv.(FromCtyValueConverter).FromCtyValue(in, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
},
|
||||
ConversionFrom: func(want cty.Type) func(any, cty.Path) (cty.Value, error) {
|
||||
return func(in any, _ cty.Path) (cty.Value, error) {
|
||||
rv := reflect.ValueOf(in).Convert(toCtyValueType)
|
||||
v := rv.Interface().(ToCtyValueConverter).ToCtyValue()
|
||||
return convert.Convert(v, want)
|
||||
}
|
||||
},
|
||||
ExtensionData: func(key any) any {
|
||||
switch key {
|
||||
case unwrapCapsuleValueExtension:
|
||||
zero := reflect.Zero(elem).Interface()
|
||||
if conv, ok := zero.(ToCtyValueConverter); ok {
|
||||
return conv.ToCtyValue().Type()
|
||||
}
|
||||
|
||||
zero = reflect.Zero(rt).Interface()
|
||||
if conv, ok := zero.(ToCtyValueConverter); ok {
|
||||
return conv.ToCtyValue().Type()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// Attempt to store the new type. Use whichever was loaded first in the case
|
||||
// of a race condition.
|
||||
ety := cty.CapsuleWithOps(elem.Name(), elem, ops)
|
||||
val, _ := capsuleValueTypes.LoadOrStore(elem, ety)
|
||||
return val.(cty.Type)
|
||||
}
|
||||
|
||||
// UnwrapCtyValue will unwrap capsule type values into their native cty value
|
||||
// equivalents if possible.
|
||||
func UnwrapCtyValue(in cty.Value) cty.Value {
|
||||
want := toCtyValueType(in.Type())
|
||||
if in.Type().Equals(want) {
|
||||
return in
|
||||
} else if out, err := convert.Convert(in, want); err == nil {
|
||||
return out
|
||||
}
|
||||
return cty.NullVal(want)
|
||||
}
|
||||
|
||||
func toCtyValueType(in cty.Type) cty.Type {
|
||||
if et := in.MapElementType(); et != nil {
|
||||
return cty.Map(toCtyValueType(*et))
|
||||
}
|
||||
|
||||
if et := in.SetElementType(); et != nil {
|
||||
return cty.Set(toCtyValueType(*et))
|
||||
}
|
||||
|
||||
if et := in.ListElementType(); et != nil {
|
||||
return cty.List(toCtyValueType(*et))
|
||||
}
|
||||
|
||||
if in.IsObjectType() {
|
||||
var optional []string
|
||||
inAttrTypes := in.AttributeTypes()
|
||||
outAttrTypes := make(map[string]cty.Type, len(inAttrTypes))
|
||||
for name, typ := range inAttrTypes {
|
||||
outAttrTypes[name] = toCtyValueType(typ)
|
||||
if in.AttributeOptional(name) {
|
||||
optional = append(optional, name)
|
||||
}
|
||||
}
|
||||
return cty.ObjectWithOptionalAttrs(outAttrTypes, optional)
|
||||
}
|
||||
|
||||
if in.IsTupleType() {
|
||||
inTypes := in.TupleElementTypes()
|
||||
outTypes := make([]cty.Type, len(inTypes))
|
||||
for i, typ := range inTypes {
|
||||
outTypes[i] = toCtyValueType(typ)
|
||||
}
|
||||
return cty.Tuple(outTypes)
|
||||
}
|
||||
|
||||
if in.IsCapsuleType() {
|
||||
if out := in.CapsuleExtensionData(unwrapCapsuleValueExtension); out != nil {
|
||||
return out.(cty.Type)
|
||||
}
|
||||
return cty.DynamicPseudoType
|
||||
}
|
||||
|
||||
return in
|
||||
}
|
||||
|
||||
func ToCtyValue(val any, ty cty.Type) (cty.Value, error) {
|
||||
out, err := gocty.ToCtyValue(val, ty)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
return UnwrapCtyValue(out), nil
|
||||
}
|
@@ -15,9 +15,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/v2/core/images"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/builder"
|
||||
controllerapi "github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/driver"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
@@ -61,25 +62,28 @@ const (
|
||||
type Options struct {
|
||||
Inputs Inputs
|
||||
|
||||
Ref string
|
||||
Allow []entitlements.Entitlement
|
||||
Attests map[string]*string
|
||||
BuildArgs map[string]string
|
||||
CacheFrom []client.CacheOptionsEntry
|
||||
CacheTo []client.CacheOptionsEntry
|
||||
CgroupParent string
|
||||
Exports []client.ExportEntry
|
||||
ExtraHosts []string
|
||||
Labels map[string]string
|
||||
NetworkMode string
|
||||
NoCache bool
|
||||
NoCacheFilter []string
|
||||
Platforms []specs.Platform
|
||||
Pull bool
|
||||
ShmSize opts.MemBytes
|
||||
Tags []string
|
||||
Target string
|
||||
Ulimits *opts.UlimitOpt
|
||||
Ref string
|
||||
Allow []entitlements.Entitlement
|
||||
Attests map[string]*string
|
||||
BuildArgs map[string]string
|
||||
CacheFrom []client.CacheOptionsEntry
|
||||
CacheTo []client.CacheOptionsEntry
|
||||
CgroupParent string
|
||||
Exports []client.ExportEntry
|
||||
ExportsLocalPathsTemporary []string // should be removed after client.ExportEntry update in buildkit v0.19.0
|
||||
ExtraHosts []string
|
||||
Labels map[string]string
|
||||
NetworkMode string
|
||||
NoCache bool
|
||||
NoCacheFilter []string
|
||||
Platforms []specs.Platform
|
||||
Pull bool
|
||||
SecretSpecs []*controllerapi.Secret
|
||||
SSHSpecs []*controllerapi.SSH
|
||||
ShmSize opts.MemBytes
|
||||
Tags []string
|
||||
Target string
|
||||
Ulimits *opts.UlimitOpt
|
||||
|
||||
Session []session.Attachable
|
||||
Linked bool // Linked marks this target as exclusively linked (not requested by the user).
|
||||
|
@@ -23,7 +23,7 @@ func setupTest(tb testing.TB) {
|
||||
gitutil.GitInit(c, tb)
|
||||
|
||||
df := []byte("FROM alpine:latest\n")
|
||||
assert.NoError(tb, os.WriteFile("Dockerfile", df, 0644))
|
||||
require.NoError(tb, os.WriteFile("Dockerfile", df, 0644))
|
||||
|
||||
gitutil.GitAdd(c, tb, "Dockerfile")
|
||||
gitutil.GitCommit(c, tb, "initial commit")
|
||||
@@ -32,7 +32,7 @@ func setupTest(tb testing.TB) {
|
||||
|
||||
func TestGetGitAttributesNotGitRepo(t *testing.T) {
|
||||
_, err := getGitAttributes(context.Background(), t.TempDir(), "Dockerfile")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetGitAttributesBadGitRepo(t *testing.T) {
|
||||
@@ -47,7 +47,7 @@ func TestGetGitAttributesNoContext(t *testing.T) {
|
||||
setupTest(t)
|
||||
|
||||
addGitAttrs, err := getGitAttributes(context.Background(), "", "Dockerfile")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
var so client.SolveOpt
|
||||
addGitAttrs(&so)
|
||||
assert.Empty(t, so.FrontendAttrs)
|
||||
@@ -195,8 +195,8 @@ func TestLocalDirsSub(t *testing.T) {
|
||||
gitutil.GitInit(c, t)
|
||||
|
||||
df := []byte("FROM alpine:latest\n")
|
||||
assert.NoError(t, os.MkdirAll("app", 0755))
|
||||
assert.NoError(t, os.WriteFile("app/Dockerfile", df, 0644))
|
||||
require.NoError(t, os.MkdirAll("app", 0755))
|
||||
require.NoError(t, os.WriteFile("app/Dockerfile", df, 0644))
|
||||
|
||||
gitutil.GitAdd(c, t, "app/Dockerfile")
|
||||
gitutil.GitCommit(c, t, "initial commit")
|
||||
|
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
type Container struct {
|
||||
cancelOnce sync.Once
|
||||
containerCancel func()
|
||||
containerCancel func(error)
|
||||
isUnavailable atomic.Bool
|
||||
initStarted atomic.Bool
|
||||
container gateway.Container
|
||||
@@ -31,18 +31,18 @@ func NewContainer(ctx context.Context, resultCtx *ResultHandle, cfg *controllera
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
err := resultCtx.build(func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
ctx, cancel := context.WithCancelCause(ctx)
|
||||
go func() {
|
||||
<-mainCtx.Done()
|
||||
cancel()
|
||||
cancel(errors.WithStack(context.Canceled))
|
||||
}()
|
||||
|
||||
containerCfg, err := resultCtx.getContainerConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerCtx, containerCancel := context.WithCancel(ctx)
|
||||
defer containerCancel()
|
||||
containerCtx, containerCancel := context.WithCancelCause(ctx)
|
||||
defer containerCancel(errors.WithStack(context.Canceled))
|
||||
bkContainer, err := c.NewContainer(containerCtx, containerCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -83,7 +83,7 @@ func (c *Container) Cancel() {
|
||||
c.markUnavailable()
|
||||
c.cancelOnce.Do(func() {
|
||||
if c.containerCancel != nil {
|
||||
c.containerCancel()
|
||||
c.containerCancel(errors.WithStack(context.Canceled))
|
||||
}
|
||||
close(c.releaseCh)
|
||||
})
|
||||
|
@@ -11,8 +11,8 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/content/local"
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/plugins/content/local"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/buildx/builder"
|
||||
|
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/containerd/content"
|
||||
"github.com/containerd/containerd/content/proxy"
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
|
@@ -10,7 +10,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func generateRandomData(size int) []byte {
|
||||
@@ -57,7 +56,7 @@ func TestSyncMultiReaderParallel(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err, "Reader %d error", readerId)
|
||||
assert.NoError(t, err, "Reader %d error", readerId)
|
||||
|
||||
if mathrand.Intn(1000) == 0 { //nolint:gosec
|
||||
t.Logf("Reader %d closing", readerId)
|
||||
|
@@ -82,7 +82,7 @@ func NewResultHandle(ctx context.Context, cc *client.Client, opt client.SolveOpt
|
||||
var respHandle *ResultHandle
|
||||
|
||||
go func() {
|
||||
defer cancel(context.Canceled) // ensure no dangling processes
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }() // ensure no dangling processes
|
||||
|
||||
var res *gateway.Result
|
||||
var err error
|
||||
@@ -181,7 +181,7 @@ func NewResultHandle(ctx context.Context, cc *client.Client, opt client.SolveOpt
|
||||
case <-respHandle.done:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return nil, ctx.Err()
|
||||
return nil, context.Cause(ctx)
|
||||
}, nil)
|
||||
if respHandle != nil {
|
||||
return
|
||||
|
@@ -138,7 +138,7 @@ func TestToBuildkitExtraHosts(t *testing.T) {
|
||||
actualOut, actualErr := toBuildkitExtraHosts(context.TODO(), tc.input, nil)
|
||||
if tc.expectedErr == "" {
|
||||
require.Equal(t, tc.expectedOut, actualOut)
|
||||
require.Nil(t, actualErr)
|
||||
require.NoError(t, actualErr)
|
||||
} else {
|
||||
require.Zero(t, actualOut)
|
||||
require.Error(t, actualErr, tc.expectedErr)
|
||||
|
@@ -288,7 +288,15 @@ func GetBuilders(dockerCli command.Cli, txn *store.Txn) ([]*Builder, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
builders := make([]*Builder, len(storeng))
|
||||
contexts, err := dockerCli.ContextStore().List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(contexts, func(i, j int) bool {
|
||||
return contexts[i].Name < contexts[j].Name
|
||||
})
|
||||
|
||||
builders := make([]*Builder, len(storeng), len(storeng)+len(contexts))
|
||||
seen := make(map[string]struct{})
|
||||
for i, ng := range storeng {
|
||||
b, err := New(dockerCli,
|
||||
@@ -303,14 +311,6 @@ func GetBuilders(dockerCli command.Cli, txn *store.Txn) ([]*Builder, error) {
|
||||
seen[b.NodeGroup.Name] = struct{}{}
|
||||
}
|
||||
|
||||
contexts, err := dockerCli.ContextStore().List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(contexts, func(i, j int) bool {
|
||||
return contexts[i].Name < contexts[j].Name
|
||||
})
|
||||
|
||||
for _, c := range contexts {
|
||||
// if a context has the same name as an instance from the store, do not
|
||||
// add it to the builders list. An instance from the store takes
|
||||
@@ -522,8 +522,9 @@ func Create(ctx context.Context, txn *store.Txn, dockerCli command.Cli, opts Cre
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
cancelCtx, cancel := context.WithCancelCause(ctx)
|
||||
timeoutCtx, _ := context.WithTimeoutCause(cancelCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
|
||||
nodes, err := b.LoadNodes(timeoutCtx, WithData())
|
||||
if err != nil {
|
||||
|
@@ -19,17 +19,20 @@ func TestCsvToMap(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Contains(t, r, "tolerations")
|
||||
require.Equal(t, r["tolerations"], "key=foo,value=bar;key=foo2,value=bar2")
|
||||
require.Equal(t, "key=foo,value=bar;key=foo2,value=bar2", r["tolerations"])
|
||||
|
||||
require.Contains(t, r, "replicas")
|
||||
require.Equal(t, r["replicas"], "1")
|
||||
require.Equal(t, "1", r["replicas"])
|
||||
|
||||
require.Contains(t, r, "namespace")
|
||||
require.Equal(t, r["namespace"], "default")
|
||||
require.Equal(t, "default", r["namespace"])
|
||||
}
|
||||
|
||||
func TestParseBuildkitdFlags(t *testing.T) {
|
||||
buildkitdConf := `
|
||||
dirConf := t.TempDir()
|
||||
|
||||
buildkitdConfPath := path.Join(dirConf, "buildkitd-conf.toml")
|
||||
require.NoError(t, os.WriteFile(buildkitdConfPath, []byte(`
|
||||
# debug enables additional debug logging
|
||||
debug = true
|
||||
# insecure-entitlements allows insecure entitlements, disabled by default.
|
||||
@@ -37,10 +40,18 @@ insecure-entitlements = [ "network.host", "security.insecure" ]
|
||||
[log]
|
||||
# log formatter: json or text
|
||||
format = "text"
|
||||
`
|
||||
dirConf := t.TempDir()
|
||||
buildkitdConfPath := path.Join(dirConf, "buildkitd-conf.toml")
|
||||
require.NoError(t, os.WriteFile(buildkitdConfPath, []byte(buildkitdConf), 0644))
|
||||
`), 0644))
|
||||
|
||||
buildkitdConfBrokenPath := path.Join(dirConf, "buildkitd-conf-broken.toml")
|
||||
require.NoError(t, os.WriteFile(buildkitdConfBrokenPath, []byte(`
|
||||
[worker.oci]
|
||||
gc = "maybe"
|
||||
`), 0644))
|
||||
|
||||
buildkitdConfUnknownFieldPath := path.Join(dirConf, "buildkitd-unknown-field.toml")
|
||||
require.NoError(t, os.WriteFile(buildkitdConfUnknownFieldPath, []byte(`
|
||||
foo = "bar"
|
||||
`), 0644))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
@@ -157,6 +168,26 @@ insecure-entitlements = [ "network.host", "security.insecure" ]
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"error parsing buildkit config",
|
||||
"",
|
||||
"docker-container",
|
||||
nil,
|
||||
buildkitdConfBrokenPath,
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"unknown field in buildkit config",
|
||||
"",
|
||||
"docker-container",
|
||||
nil,
|
||||
buildkitdConfUnknownFieldPath,
|
||||
[]string{
|
||||
"--allow-insecure-entitlement=network.host",
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
tt := tt
|
||||
|
@@ -20,9 +20,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"go.opentelemetry.io/otel"
|
||||
|
||||
//nolint:staticcheck // vendored dependencies may still use this
|
||||
"github.com/containerd/containerd/pkg/seed"
|
||||
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
||||
|
||||
_ "github.com/docker/buildx/driver/docker"
|
||||
@@ -35,9 +32,6 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
//nolint:staticcheck
|
||||
seed.WithTimeAndRand()
|
||||
|
||||
stack.SetVersionInfo(version.Version, version.Revision)
|
||||
}
|
||||
|
||||
|
176
commands/bake.go
176
commands/bake.go
@@ -25,7 +25,6 @@ import (
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/buildflags"
|
||||
"github.com/docker/buildx/util/cobrautil"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
@@ -38,24 +37,30 @@ import (
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
type bakeOptions struct {
|
||||
files []string
|
||||
overrides []string
|
||||
printOnly bool
|
||||
listTargets bool
|
||||
listVars bool
|
||||
sbom string
|
||||
provenance string
|
||||
allow []string
|
||||
files []string
|
||||
overrides []string
|
||||
|
||||
sbom string
|
||||
provenance string
|
||||
allow []string
|
||||
|
||||
builder string
|
||||
metadataFile string
|
||||
exportPush bool
|
||||
exportLoad bool
|
||||
callFunc string
|
||||
|
||||
print bool
|
||||
list string
|
||||
|
||||
// TODO: remove deprecated flags
|
||||
listTargets bool
|
||||
listVars bool
|
||||
}
|
||||
|
||||
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
||||
@@ -107,16 +112,27 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get current working directory")
|
||||
}
|
||||
// filesystem access under the current working directory is allowed by default
|
||||
ent.FSRead = append(ent.FSRead, wd)
|
||||
ent.FSWrite = append(ent.FSWrite, wd)
|
||||
|
||||
ctx2, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx2, cancel := context.WithCancelCause(context.TODO())
|
||||
defer cancel(errors.WithStack(context.Canceled))
|
||||
|
||||
var nodes []builder.Node
|
||||
var progressConsoleDesc, progressTextDesc string
|
||||
|
||||
if in.print && in.list != "" {
|
||||
return errors.New("--print and --list are mutually exclusive")
|
||||
}
|
||||
|
||||
// instance only needed for reading remote bake files or building
|
||||
var driverType string
|
||||
if url != "" || !(in.printOnly || in.listTargets || in.listVars) {
|
||||
if url != "" || !(in.print || in.list != "") {
|
||||
b, err := builder.New(dockerCli,
|
||||
builder.WithName(in.builder),
|
||||
builder.WithContextPathHash(contextPathHash),
|
||||
@@ -177,7 +193,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||
"BAKE_LOCAL_PLATFORM": platforms.Format(platforms.DefaultSpec()),
|
||||
}
|
||||
|
||||
if in.listTargets || in.listVars {
|
||||
if in.list != "" {
|
||||
cfg, pm, err := bake.ParseFiles(files, defaults)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -185,14 +201,19 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||
if err = printer.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
if in.listTargets {
|
||||
return printTargetList(dockerCli.Out(), cfg)
|
||||
} else if in.listVars {
|
||||
return printVars(dockerCli.Out(), pm.AllVariables)
|
||||
list, err := parseList(in.list)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch list.Type {
|
||||
case "targets":
|
||||
return printTargetList(dockerCli.Out(), list.Format, cfg)
|
||||
case "variables":
|
||||
return printVars(dockerCli.Out(), list.Format, pm.AllVariables)
|
||||
}
|
||||
}
|
||||
|
||||
tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, defaults)
|
||||
tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, defaults, &ent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -224,7 +245,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||
Target: tgts,
|
||||
}
|
||||
|
||||
if in.printOnly {
|
||||
if in.print {
|
||||
if err = printer.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -250,7 +271,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := exp.Prompt(ctx, &syncWriter{w: dockerCli.Err(), wait: printer.Wait}); err != nil {
|
||||
if err := exp.Prompt(ctx, url != "", &syncWriter{w: dockerCli.Err(), wait: printer.Wait}); err != nil {
|
||||
return err
|
||||
}
|
||||
if printer.IsDone() {
|
||||
@@ -420,6 +441,13 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||
if !cmd.Flags().Lookup("pull").Changed {
|
||||
cFlags.pull = nil
|
||||
}
|
||||
if options.list == "" {
|
||||
if options.listTargets {
|
||||
options.list = "targets"
|
||||
} else if options.listVars {
|
||||
options.list = "variables"
|
||||
}
|
||||
}
|
||||
options.builder = rootOpts.builder
|
||||
options.metadataFile = cFlags.metadataFile
|
||||
// Other common flags (noCache, pull and progress) are processed in runBake function.
|
||||
@@ -432,7 +460,6 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||
|
||||
flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file")
|
||||
flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`)
|
||||
flags.BoolVar(&options.printOnly, "print", false, "Print the options without building")
|
||||
flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`)
|
||||
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`)
|
||||
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`)
|
||||
@@ -443,13 +470,16 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
||||
flags.VarPF(callAlias(&options.callFunc, "check"), "check", "", `Shorthand for "--call=check"`)
|
||||
flags.Lookup("check").NoOptDefVal = "true"
|
||||
|
||||
flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets")
|
||||
cobrautil.MarkFlagsExperimental(flags, "list-targets")
|
||||
flags.MarkHidden("list-targets")
|
||||
flags.BoolVar(&options.print, "print", false, "Print the options without building")
|
||||
flags.StringVar(&options.list, "list", "", "List targets or variables")
|
||||
|
||||
// TODO: remove deprecated flags
|
||||
flags.BoolVar(&options.listTargets, "list-targets", false, "List available targets")
|
||||
flags.MarkHidden("list-targets")
|
||||
flags.MarkDeprecated("list-targets", "list-targets is deprecated, use list=targets instead")
|
||||
flags.BoolVar(&options.listVars, "list-variables", false, "List defined variables")
|
||||
cobrautil.MarkFlagsExperimental(flags, "list-variables")
|
||||
flags.MarkHidden("list-variables")
|
||||
flags.MarkDeprecated("list-variables", "list-variables is deprecated, use list=variables instead")
|
||||
|
||||
commonBuildFlags(&cFlags, flags)
|
||||
|
||||
@@ -550,10 +580,70 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names
|
||||
return
|
||||
}
|
||||
|
||||
func printVars(w io.Writer, vars []*hclparser.Variable) error {
|
||||
type listEntry struct {
|
||||
Type string
|
||||
Format string
|
||||
}
|
||||
|
||||
func parseList(input string) (listEntry, error) {
|
||||
res := listEntry{}
|
||||
|
||||
fields, err := csvvalue.Fields(input, nil)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if len(fields) == 1 && fields[0] == input && !strings.HasPrefix(input, "type=") {
|
||||
res.Type = input
|
||||
}
|
||||
|
||||
if res.Type == "" {
|
||||
for _, field := range fields {
|
||||
key, value, ok := strings.Cut(field, "=")
|
||||
if !ok {
|
||||
return res, errors.Errorf("invalid value %s", field)
|
||||
}
|
||||
key = strings.TrimSpace(strings.ToLower(key))
|
||||
switch key {
|
||||
case "type":
|
||||
res.Type = value
|
||||
case "format":
|
||||
res.Format = value
|
||||
default:
|
||||
return res, errors.Errorf("unexpected key '%s' in '%s'", key, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
if res.Format == "" {
|
||||
res.Format = "table"
|
||||
}
|
||||
|
||||
switch res.Type {
|
||||
case "targets", "variables":
|
||||
default:
|
||||
return res, errors.Errorf("invalid list type %q", res.Type)
|
||||
}
|
||||
|
||||
switch res.Format {
|
||||
case "table", "json":
|
||||
default:
|
||||
return res, errors.Errorf("invalid list format %q", res.Format)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func printVars(w io.Writer, format string, vars []*hclparser.Variable) error {
|
||||
slices.SortFunc(vars, func(a, b *hclparser.Variable) int {
|
||||
return cmp.Compare(a.Name, b.Name)
|
||||
})
|
||||
|
||||
if format == "json" {
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(vars)
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
defer tw.Flush()
|
||||
|
||||
@@ -571,12 +661,7 @@ func printVars(w io.Writer, vars []*hclparser.Variable) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func printTargetList(w io.Writer, cfg *bake.Config) error {
|
||||
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
defer tw.Flush()
|
||||
|
||||
tw.Write([]byte("TARGET\tDESCRIPTION\n"))
|
||||
|
||||
func printTargetList(w io.Writer, format string, cfg *bake.Config) error {
|
||||
type targetOrGroup struct {
|
||||
name string
|
||||
target *bake.Target
|
||||
@@ -595,6 +680,20 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
|
||||
return cmp.Compare(a.name, b.name)
|
||||
})
|
||||
|
||||
var tw *tabwriter.Writer
|
||||
if format == "table" {
|
||||
tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
defer tw.Flush()
|
||||
tw.Write([]byte("TARGET\tDESCRIPTION\n"))
|
||||
}
|
||||
|
||||
type targetList struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Group bool `json:"group,omitempty"`
|
||||
}
|
||||
var targetsList []targetList
|
||||
|
||||
for _, tgt := range list {
|
||||
if strings.HasPrefix(tgt.name, "_") {
|
||||
// convention for a private target
|
||||
@@ -603,9 +702,9 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
|
||||
var descr string
|
||||
if tgt.target != nil {
|
||||
descr = tgt.target.Description
|
||||
targetsList = append(targetsList, targetList{Name: tgt.name, Description: descr})
|
||||
} else if tgt.group != nil {
|
||||
descr = tgt.group.Description
|
||||
|
||||
if len(tgt.group.Targets) > 0 {
|
||||
slices.Sort(tgt.group.Targets)
|
||||
names := strings.Join(tgt.group.Targets, ", ")
|
||||
@@ -615,8 +714,17 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
|
||||
descr = names
|
||||
}
|
||||
}
|
||||
targetsList = append(targetsList, targetList{Name: tgt.name, Description: descr, Group: true})
|
||||
}
|
||||
fmt.Fprintf(tw, "%s\t%s\n", tgt.name, descr)
|
||||
if format == "table" {
|
||||
fmt.Fprintf(tw, "%s\t%s\n", tgt.name, descr)
|
||||
}
|
||||
}
|
||||
|
||||
if format == "json" {
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(targetsList)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -183,14 +183,17 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
|
||||
}
|
||||
}
|
||||
|
||||
opts.CacheFrom, err = buildflags.ParseCacheEntry(o.cacheFrom)
|
||||
cacheFrom, err := buildflags.ParseCacheEntry(o.cacheFrom)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.CacheTo, err = buildflags.ParseCacheEntry(o.cacheTo)
|
||||
opts.CacheFrom = cacheFrom.ToPB()
|
||||
|
||||
cacheTo, err := buildflags.ParseCacheEntry(o.cacheTo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.CacheTo = cacheTo.ToPB()
|
||||
|
||||
opts.Secrets, err = buildflags.ParseSecretSpecs(o.secrets)
|
||||
if err != nil {
|
||||
@@ -325,8 +328,8 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
||||
}
|
||||
attributes := buildMetricAttributes(dockerCli, driverType, &options)
|
||||
|
||||
ctx2, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx2, cancel := context.WithCancelCause(context.TODO())
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
progressMode, err := options.toDisplayMode()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -720,7 +723,7 @@ type commonFlags struct {
|
||||
|
||||
func commonBuildFlags(options *commonFlags, flags *pflag.FlagSet) {
|
||||
options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image")
|
||||
flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "tty", "rawjson"). Use plain to show container output`)
|
||||
flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "quiet", "plain", "tty", "rawjson"). Use plain to show container output`)
|
||||
options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images")
|
||||
flags.StringVar(&options.metadataFile, "metadata-file", "", "Write build result metadata to a file")
|
||||
}
|
||||
@@ -763,8 +766,11 @@ func decodeExporterResponse(exporterResponse map[string]string) map[string]inter
|
||||
}
|
||||
var raw map[string]interface{}
|
||||
if err = json.Unmarshal(dt, &raw); err != nil || len(raw) == 0 {
|
||||
out[k] = v
|
||||
continue
|
||||
var rawList []map[string]interface{}
|
||||
if err = json.Unmarshal(dt, &rawList); err != nil || len(rawList) == 0 {
|
||||
out[k] = v
|
||||
continue
|
||||
}
|
||||
}
|
||||
out[k] = json.RawMessage(dt)
|
||||
}
|
||||
@@ -882,7 +888,6 @@ func printWarnings(w io.Writer, warnings []client.VertexWarning, mode progressui
|
||||
src.Print(w)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
604
commands/history/inspect.go
Normal file
604
commands/history/inspect.go
Normal file
@@ -0,0 +1,604 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content"
|
||||
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||
"github.com/containerd/containerd/v2/core/images"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
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"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/solver/errdefs"
|
||||
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
|
||||
"github.com/moby/buildkit/util/grpcerrors"
|
||||
"github.com/moby/buildkit/util/stack"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tonistiigi/go-csvvalue"
|
||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
proto "google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type inspectOptions struct {
|
||||
builder string
|
||||
ref string
|
||||
}
|
||||
|
||||
func runInspect(ctx context.Context, dockerCli command.Cli, opts inspectOptions) 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
|
||||
}
|
||||
}
|
||||
|
||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(recs) == 0 {
|
||||
if opts.ref == "" {
|
||||
return errors.New("no records found")
|
||||
}
|
||||
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]
|
||||
|
||||
ls, err := localstate.New(confutil.NewConfig(dockerCli))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
|
||||
|
||||
tw := tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||
|
||||
attrs := rec.FrontendAttrs
|
||||
delete(attrs, "frontend.caps")
|
||||
|
||||
writeAttr := func(k, name string, f func(v string) (string, bool)) {
|
||||
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 dockerfile string
|
||||
if st != nil {
|
||||
context = st.LocalPath
|
||||
dockerfile = st.DockerfilePath
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
if dockerfile != "" && dockerfile != "-" {
|
||||
if rel, err := filepath.Rel(context, dockerfile); err == nil {
|
||||
if !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
dockerfile = rel
|
||||
}
|
||||
}
|
||||
}
|
||||
if context != "" {
|
||||
if rel, err := filepath.Rel(wd, context); err == nil {
|
||||
if !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
context = rel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := attrs["context"]; ok && context == "" {
|
||||
delete(attrs, "context")
|
||||
context = v
|
||||
}
|
||||
if dockerfile == "" {
|
||||
if v, ok := attrs["filename"]; ok {
|
||||
dockerfile = v
|
||||
if dfdir, ok := attrs["vcs:localdir:dockerfile"]; ok {
|
||||
dockerfile = filepath.Join(dfdir, dockerfile)
|
||||
}
|
||||
}
|
||||
}
|
||||
delete(attrs, "filename")
|
||||
|
||||
if context != "" {
|
||||
fmt.Fprintf(tw, "Context:\t%s\n", context)
|
||||
}
|
||||
if dockerfile != "" {
|
||||
fmt.Fprintf(tw, "Dockerfile:\t%s\n", dockerfile)
|
||||
}
|
||||
if _, ok := attrs["context"]; !ok {
|
||||
if src, ok := attrs["vcs:source"]; ok {
|
||||
fmt.Fprintf(tw, "VCS Repository:\t%s\n", src)
|
||||
}
|
||||
if rev, ok := attrs["vcs:revision"]; ok {
|
||||
fmt.Fprintf(tw, "VCS Revision:\t%s\n", rev)
|
||||
}
|
||||
}
|
||||
|
||||
writeAttr("target", "Target", nil)
|
||||
writeAttr("platform", "Platform", func(v string) (string, bool) {
|
||||
return tryParseValue(v, func(v string) (string, error) {
|
||||
var pp []string
|
||||
for _, v := range strings.Split(v, ",") {
|
||||
p, err := platforms.Parse(v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pp = append(pp, platforms.FormatAll(platforms.Normalize(p)))
|
||||
}
|
||||
return strings.Join(pp, ", "), nil
|
||||
}), true
|
||||
})
|
||||
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
|
||||
}), true
|
||||
})
|
||||
|
||||
tw.Flush()
|
||||
|
||||
fmt.Fprintln(dockerCli.Out())
|
||||
|
||||
printTable(dockerCli.Out(), attrs, "context:", "Named Context")
|
||||
|
||||
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||
|
||||
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)
|
||||
if rec.Error != nil {
|
||||
if codes.Code(rec.Error.Code) == codes.Canceled {
|
||||
fmt.Fprintf(tw, "Status:\tCanceled\n")
|
||||
} else {
|
||||
fmt.Fprintf(tw, "Error:\t%s %s\n", codes.Code(rec.Error.Code).String(), rec.Error.Message)
|
||||
}
|
||||
}
|
||||
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())
|
||||
|
||||
tw = tabwriter.NewWriter(dockerCli.Out(), 1, 8, 1, '\t', 0)
|
||||
|
||||
writeAttr("force-network-mode", "Network", nil)
|
||||
writeAttr("hostname", "Hostname", nil)
|
||||
writeAttr("add-hosts", "Extra Hosts", func(v string) (string, bool) {
|
||||
return tryParseValue(v, func(v string) (string, error) {
|
||||
fields, err := csvvalue.Fields(v, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join(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
|
||||
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:") {
|
||||
continue
|
||||
}
|
||||
unusedAttrs = append(unusedAttrs, k)
|
||||
}
|
||||
slices.Sort(unusedAttrs)
|
||||
|
||||
for _, k := range unusedAttrs {
|
||||
fmt.Fprintf(tw, "%s:\t%s\n", k, attrs[k])
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
provIndex := slices.IndexFunc(attachments, func(a attachment) bool {
|
||||
return descrType(a.descr) == slsa02.PredicateSLSAProvenance
|
||||
})
|
||||
if provIndex != -1 {
|
||||
prov := attachments[provIndex]
|
||||
|
||||
dt, err := content.ReadBlob(ctx, store, prov.descr)
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to read provenance %s: %v", prov.descr.Digest, err)
|
||||
}
|
||||
|
||||
var pred provenancetypes.ProvenancePredicate
|
||||
if err := json.Unmarshal(dt, &pred); err != nil {
|
||||
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 {
|
||||
fmt.Fprintf(tw, "%s\t%s\n", m.URI, strings.Join(digestSetToDigests(m.Digest), ", "))
|
||||
}
|
||||
tw.Flush()
|
||||
fmt.Fprintln(dockerCli.Out())
|
||||
}
|
||||
|
||||
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 {
|
||||
p := ""
|
||||
if a.platform != nil {
|
||||
p = platforms.FormatAll(*a.platform)
|
||||
}
|
||||
fmt.Fprintf(tw, "%s\t%s\t%s\n", a.descr.Digest, p, descrType(a.descr))
|
||||
}
|
||||
tw.Flush()
|
||||
fmt.Fprintln(dockerCli.Out())
|
||||
}
|
||||
|
||||
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))
|
||||
for _, s := range errdefs.Sources(retErr) {
|
||||
s.Print(dockerCli.Out())
|
||||
}
|
||||
fmt.Fprintln(dockerCli.Out())
|
||||
|
||||
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)
|
||||
}
|
||||
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 debug.IsEnabled() {
|
||||
fmt.Fprintf(dockerCli.Out(), "\n%+v\n", stack.Formatter(retErr))
|
||||
} else if len(stack.Traces(retErr)) > 0 {
|
||||
fmt.Fprintf(dockerCli.Out(), "Enable --debug to see stack traces for error\n")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "Print build logs: docker buildx history logs %s\n", rec.Ref)
|
||||
|
||||
fmt.Fprintf(dockerCli.Out(), "View build in Docker Desktop: %s\n", desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options inspectOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "inspect [OPTIONS] [REF]",
|
||||
Short: "Inspect a build",
|
||||
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 runInspect(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
attachmentCmd(dockerCli, rootOpts),
|
||||
)
|
||||
|
||||
// flags := cmd.Flags()
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func loadVertexLogs(ctx context.Context, c *client.Client, ref string, dgst digest.Digest, limit int) (string, []string, error) {
|
||||
st, err := c.ControlClient().Status(ctx, &controlapi.StatusRequest{
|
||||
Ref: ref,
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
var name string
|
||||
var logs []string
|
||||
lastState := map[int]int{}
|
||||
|
||||
loop0:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
st.CloseSend()
|
||||
return "", nil, context.Cause(ctx)
|
||||
default:
|
||||
ev, err := st.Recv()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break loop0
|
||||
}
|
||||
return "", nil, err
|
||||
}
|
||||
ss := client.NewSolveStatus(ev)
|
||||
for _, v := range ss.Vertexes {
|
||||
if v.Digest == dgst {
|
||||
name = v.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, l := range ss.Logs {
|
||||
if l.Vertex == dgst {
|
||||
parts := bytes.Split(l.Data, []byte("\n"))
|
||||
for i, p := range parts {
|
||||
var wrote bool
|
||||
if i == 0 {
|
||||
idx, ok := lastState[l.Stream]
|
||||
if ok && idx != -1 {
|
||||
logs[idx] = logs[idx] + string(p)
|
||||
wrote = true
|
||||
}
|
||||
}
|
||||
if !wrote {
|
||||
if len(p) > 0 {
|
||||
logs = append(logs, string(p))
|
||||
}
|
||||
lastState[l.Stream] = len(logs) - 1
|
||||
}
|
||||
if i == len(parts)-1 && len(p) == 0 {
|
||||
lastState[l.Stream] = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if limit > 0 && len(logs) > limit {
|
||||
logs = logs[len(logs)-limit:]
|
||||
}
|
||||
|
||||
return name, logs, nil
|
||||
}
|
||||
|
||||
type attachment struct {
|
||||
platform *ocispecs.Platform
|
||||
descr ocispecs.Descriptor
|
||||
}
|
||||
|
||||
func allAttachments(ctx context.Context, store content.Store, rec historyRecord) ([]attachment, error) {
|
||||
var attachments []attachment
|
||||
|
||||
if rec.Result != nil {
|
||||
for _, a := range rec.Result.Attestations {
|
||||
attachments = append(attachments, attachment{
|
||||
descr: ociDesc(a),
|
||||
})
|
||||
}
|
||||
for _, r := range rec.Result.Results {
|
||||
attachments = append(attachments, walkAttachments(ctx, store, ociDesc(r), nil)...)
|
||||
}
|
||||
}
|
||||
|
||||
for key, ri := range rec.Results {
|
||||
p, err := platforms.Parse(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, a := range ri.Attestations {
|
||||
attachments = append(attachments, attachment{
|
||||
platform: &p,
|
||||
descr: ociDesc(a),
|
||||
})
|
||||
}
|
||||
for _, r := range ri.Results {
|
||||
attachments = append(attachments, walkAttachments(ctx, store, ociDesc(r), &p)...)
|
||||
}
|
||||
}
|
||||
|
||||
slices.SortFunc(attachments, func(a, b attachment) int {
|
||||
pCmp := 0
|
||||
if a.platform == nil && b.platform != nil {
|
||||
return -1
|
||||
} else if a.platform != nil && b.platform == nil {
|
||||
return 1
|
||||
} else if a.platform != nil && b.platform != nil {
|
||||
pCmp = cmp.Compare(platforms.FormatAll(*a.platform), platforms.FormatAll(*b.platform))
|
||||
}
|
||||
return cmp.Or(
|
||||
pCmp,
|
||||
cmp.Compare(descrType(a.descr), descrType(b.descr)),
|
||||
)
|
||||
})
|
||||
|
||||
return attachments, nil
|
||||
}
|
||||
|
||||
func walkAttachments(ctx context.Context, store content.Store, desc ocispecs.Descriptor, platform *ocispecs.Platform) []attachment {
|
||||
_, err := store.Info(ctx, desc.Digest)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var out []attachment
|
||||
|
||||
if desc.Annotations["vnd.docker.reference.type"] != "attestation-manifest" {
|
||||
out = append(out, attachment{platform: platform, descr: desc})
|
||||
}
|
||||
|
||||
if desc.MediaType != ocispecs.MediaTypeImageIndex && desc.MediaType != images.MediaTypeDockerSchema2ManifestList {
|
||||
return out
|
||||
}
|
||||
|
||||
dt, err := content.ReadBlob(ctx, store, desc)
|
||||
if err != nil {
|
||||
return out
|
||||
}
|
||||
|
||||
var idx ocispecs.Index
|
||||
if err := json.Unmarshal(dt, &idx); err != nil {
|
||||
return out
|
||||
}
|
||||
|
||||
for _, d := range idx.Manifests {
|
||||
p := platform
|
||||
if d.Platform != nil {
|
||||
p = d.Platform
|
||||
}
|
||||
out = append(out, walkAttachments(ctx, store, d, p)...)
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func ociDesc(in *controlapi.Descriptor) ocispecs.Descriptor {
|
||||
return ocispecs.Descriptor{
|
||||
MediaType: in.MediaType,
|
||||
Digest: digest.Digest(in.Digest),
|
||||
Size: in.Size,
|
||||
Annotations: in.Annotations,
|
||||
}
|
||||
}
|
||||
func descrType(desc ocispecs.Descriptor) string {
|
||||
if typ, ok := desc.Annotations["in-toto.io/predicate-type"]; ok {
|
||||
return typ
|
||||
}
|
||||
return desc.MediaType
|
||||
}
|
||||
|
||||
func tryParseValue(s string, f func(string) (string, error)) string {
|
||||
v, err := f(s)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("%s (%v)", s, err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func printTable(w io.Writer, attrs map[string]string, prefix, title string) {
|
||||
var keys []string
|
||||
for k := range attrs {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
keys = append(keys, strings.TrimPrefix(k, prefix))
|
||||
}
|
||||
}
|
||||
slices.Sort(keys)
|
||||
|
||||
if len(keys) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
|
||||
fmt.Fprintf(tw, "%s\tVALUE\n", strings.ToUpper(title))
|
||||
for _, k := range keys {
|
||||
fmt.Fprintf(tw, "%s\t%s\n", k, attrs[prefix+k])
|
||||
}
|
||||
tw.Flush()
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
|
||||
func digestSetToDigests(ds slsa.DigestSet) []string {
|
||||
var out []string
|
||||
for k, v := range ds {
|
||||
out = append(out, fmt.Sprintf("%s:%s", k, v))
|
||||
}
|
||||
return out
|
||||
}
|
152
commands/history/inspect_attachment.go
Normal file
152
commands/history/inspect_attachment.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"github.com/containerd/containerd/v2/core/content/proxy"
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/cli/cli/command"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type attachmentOptions struct {
|
||||
builder string
|
||||
typ string
|
||||
platform string
|
||||
ref string
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func runAttachment(ctx context.Context, dockerCli command.Cli, opts attachmentOptions) 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
|
||||
}
|
||||
}
|
||||
|
||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(recs) == 0 {
|
||||
if opts.ref == "" {
|
||||
return errors.New("no records found")
|
||||
}
|
||||
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]
|
||||
|
||||
c, err := rec.node.Driver.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store := proxy.NewContentStore(c.ContentClient())
|
||||
|
||||
if opts.digest != "" {
|
||||
ra, err := store.ReaderAt(ctx, ocispecs.Descriptor{Digest: opts.digest})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(dockerCli.Out(), io.NewSectionReader(ra, 0, ra.Size()))
|
||||
return err
|
||||
}
|
||||
|
||||
attachments, err := allAttachments(ctx, store, *rec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
typ := opts.typ
|
||||
switch typ {
|
||||
case "index":
|
||||
typ = ocispecs.MediaTypeImageIndex
|
||||
case "manifest":
|
||||
typ = ocispecs.MediaTypeImageManifest
|
||||
case "image":
|
||||
typ = ocispecs.MediaTypeImageConfig
|
||||
case "provenance":
|
||||
typ = slsa02.PredicateSLSAProvenance
|
||||
case "sbom":
|
||||
typ = intoto.PredicateSPDX
|
||||
}
|
||||
|
||||
for _, a := range attachments {
|
||||
if opts.platform != "" && (a.platform == nil || platforms.FormatAll(*a.platform) != opts.platform) {
|
||||
continue
|
||||
}
|
||||
if typ != "" && descrType(a.descr) != typ {
|
||||
continue
|
||||
}
|
||||
ra, err := store.ReaderAt(ctx, a.descr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(dockerCli.Out(), io.NewSectionReader(ra, 0, ra.Size()))
|
||||
return err
|
||||
}
|
||||
|
||||
return errors.Errorf("no matching attachment found for ref %q", opts.ref)
|
||||
}
|
||||
|
||||
func attachmentCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options attachmentOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "attachment [OPTIONS] REF [DIGEST]",
|
||||
Short: "Inspect a build attachment",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
options.ref = args[0]
|
||||
}
|
||||
if len(args) > 1 {
|
||||
dgst, err := digest.Parse(args[1])
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "invalid digest %q", args[1])
|
||||
}
|
||||
options.digest = dgst
|
||||
}
|
||||
|
||||
if options.digest == "" && options.platform == "" && options.typ == "" {
|
||||
return errors.New("at least one of --type, --platform or DIGEST must be specified")
|
||||
}
|
||||
|
||||
options.builder = *rootOpts.Builder
|
||||
return runAttachment(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.typ, "type", "", "Type of attachment")
|
||||
flags.StringVar(&options.platform, "platform", "", "Platform of attachment")
|
||||
|
||||
return cmd
|
||||
}
|
124
commands/history/logs.go
Normal file
124
commands/history/logs.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"slices"
|
||||
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
"github.com/docker/cli/cli/command"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type logsOptions struct {
|
||||
builder string
|
||||
ref string
|
||||
progress string
|
||||
}
|
||||
|
||||
func runLogs(ctx context.Context, dockerCli command.Cli, opts logsOptions) 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
|
||||
}
|
||||
}
|
||||
|
||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(recs) == 0 {
|
||||
if opts.ref == "" {
|
||||
return errors.New("no records found")
|
||||
}
|
||||
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]
|
||||
c, err := rec.node.Driver.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cl, err := c.ControlClient().Status(ctx, &controlapi.StatusRequest{
|
||||
Ref: rec.Ref,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var mode progressui.DisplayMode = progressui.DisplayMode(opts.progress)
|
||||
if mode == progressui.AutoMode {
|
||||
mode = progressui.PlainMode
|
||||
}
|
||||
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
loop0:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cl.CloseSend()
|
||||
return context.Cause(ctx)
|
||||
default:
|
||||
ev, err := cl.Recv()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break loop0
|
||||
}
|
||||
return err
|
||||
}
|
||||
printer.Write(client.NewSolveStatus(ev))
|
||||
}
|
||||
}
|
||||
|
||||
return printer.Wait()
|
||||
}
|
||||
|
||||
func logsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options logsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "logs [OPTIONS] [REF]",
|
||||
Short: "Print the logs of a build",
|
||||
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 runLogs(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.progress, "progress", "plain", "Set type of progress output (plain, rawjson, tty)")
|
||||
|
||||
return cmd
|
||||
}
|
234
commands/history/ls.go
Normal file
234
commands/history/ls.go
Normal file
@@ -0,0 +1,234 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/localstate"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/docker/buildx/util/desktop"
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
const (
|
||||
lsHeaderBuildID = "BUILD ID"
|
||||
lsHeaderName = "NAME"
|
||||
lsHeaderStatus = "STATUS"
|
||||
lsHeaderCreated = "CREATED AT"
|
||||
lsHeaderDuration = "DURATION"
|
||||
lsHeaderLink = ""
|
||||
|
||||
lsDefaultTableFormat = "table {{.Ref}}\t{{.Name}}\t{{.Status}}\t{{.CreatedAt}}\t{{.Duration}}\t{{.Link}}"
|
||||
|
||||
headerKeyTimestamp = "buildkit-current-timestamp"
|
||||
)
|
||||
|
||||
type lsOptions struct {
|
||||
builder string
|
||||
format string
|
||||
noTrunc bool
|
||||
}
|
||||
|
||||
func runLs(ctx context.Context, dockerCli command.Cli, opts lsOptions) 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
|
||||
}
|
||||
}
|
||||
|
||||
out, err := queryRecords(ctx, "", nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ls, err := localstate.New(confutil.NewConfig(dockerCli))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, rec := range out {
|
||||
st, _ := ls.ReadRef(rec.node.Builder, rec.node.Name, rec.Ref)
|
||||
rec.name = buildName(rec.FrontendAttrs, st)
|
||||
out[i] = rec
|
||||
}
|
||||
|
||||
return lsPrint(dockerCli, out, opts)
|
||||
}
|
||||
|
||||
func lsCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options lsOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "ls",
|
||||
Short: "List build records",
|
||||
Args: cli.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
options.builder = *rootOpts.Builder
|
||||
return runLs(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
|
||||
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func lsPrint(dockerCli command.Cli, records []historyRecord, in lsOptions) error {
|
||||
if in.format == formatter.TableFormatKey {
|
||||
in.format = lsDefaultTableFormat
|
||||
}
|
||||
|
||||
ctx := formatter.Context{
|
||||
Output: dockerCli.Out(),
|
||||
Format: formatter.Format(in.format),
|
||||
Trunc: !in.noTrunc,
|
||||
}
|
||||
|
||||
slices.SortFunc(records, func(a, b historyRecord) int {
|
||||
if a.CompletedAt == nil && b.CompletedAt != nil {
|
||||
return -1
|
||||
}
|
||||
if a.CompletedAt != nil && b.CompletedAt == nil {
|
||||
return 1
|
||||
}
|
||||
return b.CreatedAt.AsTime().Compare(a.CreatedAt.AsTime())
|
||||
})
|
||||
|
||||
var term bool
|
||||
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
||||
term = true
|
||||
}
|
||||
render := func(format func(subContext formatter.SubContext) error) error {
|
||||
for _, r := range records {
|
||||
if err := format(&lsContext{
|
||||
format: formatter.Format(in.format),
|
||||
isTerm: term,
|
||||
trunc: !in.noTrunc,
|
||||
record: &r,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
lsCtx := lsContext{
|
||||
isTerm: term,
|
||||
trunc: !in.noTrunc,
|
||||
}
|
||||
lsCtx.Header = formatter.SubHeaderContext{
|
||||
"Ref": lsHeaderBuildID,
|
||||
"Name": lsHeaderName,
|
||||
"Status": lsHeaderStatus,
|
||||
"CreatedAt": lsHeaderCreated,
|
||||
"Duration": lsHeaderDuration,
|
||||
"Link": lsHeaderLink,
|
||||
}
|
||||
|
||||
return ctx.Write(&lsCtx, render)
|
||||
}
|
||||
|
||||
type lsContext struct {
|
||||
formatter.HeaderContext
|
||||
|
||||
isTerm bool
|
||||
trunc bool
|
||||
format formatter.Format
|
||||
record *historyRecord
|
||||
}
|
||||
|
||||
func (c *lsContext) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"ref": c.FullRef(),
|
||||
"name": c.Name(),
|
||||
"status": c.Status(),
|
||||
"created_at": c.record.CreatedAt.AsTime().Format(time.RFC3339Nano),
|
||||
"total_steps": c.record.NumTotalSteps,
|
||||
"completed_steps": c.record.NumCompletedSteps,
|
||||
"cached_steps": c.record.NumCachedSteps,
|
||||
}
|
||||
if c.record.CompletedAt != nil {
|
||||
m["completed_at"] = c.record.CompletedAt.AsTime().Format(time.RFC3339Nano)
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (c *lsContext) Ref() string {
|
||||
return c.record.Ref
|
||||
}
|
||||
|
||||
func (c *lsContext) FullRef() string {
|
||||
return fmt.Sprintf("%s/%s/%s", c.record.node.Builder, c.record.node.Name, c.record.Ref)
|
||||
}
|
||||
|
||||
func (c *lsContext) Name() string {
|
||||
name := c.record.name
|
||||
if c.trunc && c.format.IsTable() {
|
||||
return trimBeginning(name, 36)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (c *lsContext) Status() string {
|
||||
if c.record.CompletedAt != nil {
|
||||
if c.record.Error != nil {
|
||||
return "Error"
|
||||
}
|
||||
return "Completed"
|
||||
}
|
||||
return "Running"
|
||||
}
|
||||
|
||||
func (c *lsContext) CreatedAt() string {
|
||||
return units.HumanDuration(time.Since(c.record.CreatedAt.AsTime())) + " ago"
|
||||
}
|
||||
|
||||
func (c *lsContext) Duration() string {
|
||||
lastTime := c.record.currentTimestamp
|
||||
if c.record.CompletedAt != nil {
|
||||
tm := c.record.CompletedAt.AsTime()
|
||||
lastTime = &tm
|
||||
}
|
||||
if lastTime == nil {
|
||||
return ""
|
||||
}
|
||||
v := formatDuration(lastTime.Sub(c.record.CreatedAt.AsTime()))
|
||||
if c.record.CompletedAt == nil {
|
||||
v += "+"
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (c *lsContext) Link() string {
|
||||
url := desktop.BuildURL(c.FullRef())
|
||||
if c.format.IsTable() {
|
||||
if c.isTerm {
|
||||
return desktop.ANSIHyperlink(url, "Open")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return url
|
||||
}
|
80
commands/history/open.go
Normal file
80
commands/history/open.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/docker/buildx/builder"
|
||||
"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 openOptions struct {
|
||||
builder string
|
||||
ref string
|
||||
}
|
||||
|
||||
func runOpen(ctx context.Context, dockerCli command.Cli, opts openOptions) 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
|
||||
}
|
||||
}
|
||||
|
||||
recs, err := queryRecords(ctx, opts.ref, nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(recs) == 0 {
|
||||
if opts.ref == "" {
|
||||
return errors.New("no records found")
|
||||
}
|
||||
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]
|
||||
|
||||
url := desktop.BuildURL(fmt.Sprintf("%s/%s/%s", rec.node.Builder, rec.node.Name, rec.Ref))
|
||||
return browser.OpenURL(url)
|
||||
}
|
||||
|
||||
func openCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options openOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "open [OPTIONS] [REF]",
|
||||
Short: "Open a build in Docker Desktop",
|
||||
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 runOpen(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
151
commands/history/rm.go
Normal file
151
commands/history/rm.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type rmOptions struct {
|
||||
builder string
|
||||
refs []string
|
||||
all bool
|
||||
}
|
||||
|
||||
func runRm(ctx context.Context, dockerCli command.Cli, opts rmOptions) 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
|
||||
}
|
||||
}
|
||||
|
||||
errs := make([][]error, len(opts.refs))
|
||||
for i := range errs {
|
||||
errs[i] = make([]error, len(nodes))
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for i, node := range nodes {
|
||||
node := node
|
||||
eg.Go(func() error {
|
||||
if node.Driver == nil {
|
||||
return nil
|
||||
}
|
||||
c, err := node.Driver.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
refs := opts.refs
|
||||
|
||||
if opts.all {
|
||||
serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
|
||||
EarlyExit: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer serv.CloseSend()
|
||||
|
||||
for {
|
||||
resp, err := serv.Recv()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
if resp.Type == controlapi.BuildHistoryEventType_COMPLETE {
|
||||
refs = append(refs, resp.Record.Ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for j, ref := range refs {
|
||||
_, err = c.ControlClient().UpdateBuildHistory(ctx, &controlapi.UpdateBuildHistoryRequest{
|
||||
Ref: ref,
|
||||
Delete: true,
|
||||
})
|
||||
if opts.all {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
errs[j][i] = err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var out []error
|
||||
loop0:
|
||||
for _, nodeErrs := range errs {
|
||||
var nodeErr error
|
||||
for _, err1 := range nodeErrs {
|
||||
if err1 == nil {
|
||||
continue loop0
|
||||
}
|
||||
if nodeErr == nil {
|
||||
nodeErr = err1
|
||||
} else {
|
||||
nodeErr = multierror.Append(nodeErr, err1)
|
||||
}
|
||||
}
|
||||
out = append(out, nodeErr)
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(out) == 1 {
|
||||
return out[0]
|
||||
}
|
||||
return multierror.Append(out[0], out[1:]...)
|
||||
}
|
||||
|
||||
func rmCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
||||
var options rmOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "rm [OPTIONS] [REF...]",
|
||||
Short: "Remove build records",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 0 && !options.all {
|
||||
return errors.New("rm requires at least one argument")
|
||||
}
|
||||
if len(args) > 0 && options.all {
|
||||
return errors.New("rm requires either --all or at least one argument")
|
||||
}
|
||||
options.refs = args
|
||||
options.builder = *rootOpts.Builder
|
||||
return runRm(cmd.Context(), dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.Disable,
|
||||
}
|
||||
|
||||
flags := cmd.Flags()
|
||||
flags.BoolVar(&options.all, "all", false, "Remove all build records")
|
||||
|
||||
return cmd
|
||||
}
|
30
commands/history/root.go
Normal file
30
commands/history/root.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type RootOptions struct {
|
||||
Builder *string
|
||||
}
|
||||
|
||||
func RootCmd(rootcmd *cobra.Command, dockerCli command.Cli, opts RootOptions) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "history",
|
||||
Short: "Commands to work on build records",
|
||||
ValidArgsFunction: completion.Disable,
|
||||
RunE: rootcmd.RunE,
|
||||
}
|
||||
|
||||
cmd.AddCommand(
|
||||
lsCmd(dockerCli, opts),
|
||||
rmCmd(dockerCli, opts),
|
||||
logsCmd(dockerCli, opts),
|
||||
inspectCmd(dockerCli, opts),
|
||||
openCmd(dockerCli, opts),
|
||||
)
|
||||
|
||||
return cmd
|
||||
}
|
180
commands/history/utils.go
Normal file
180
commands/history/utils.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package history
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/builder"
|
||||
"github.com/docker/buildx/localstate"
|
||||
controlapi "github.com/moby/buildkit/api/services/control"
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func buildName(fattrs map[string]string, ls *localstate.State) string {
|
||||
var res string
|
||||
|
||||
var target, contextPath, dockerfilePath, vcsSource string
|
||||
if v, ok := fattrs["target"]; ok {
|
||||
target = v
|
||||
}
|
||||
if v, ok := fattrs["context"]; ok {
|
||||
contextPath = filepath.ToSlash(v)
|
||||
} else if v, ok := fattrs["vcs:localdir:context"]; ok && v != "." {
|
||||
contextPath = filepath.ToSlash(v)
|
||||
}
|
||||
if v, ok := fattrs["vcs:source"]; ok {
|
||||
vcsSource = v
|
||||
}
|
||||
if v, ok := fattrs["filename"]; ok && v != "Dockerfile" {
|
||||
dockerfilePath = filepath.ToSlash(v)
|
||||
}
|
||||
if v, ok := fattrs["vcs:localdir:dockerfile"]; ok && v != "." {
|
||||
dockerfilePath = filepath.ToSlash(filepath.Join(v, dockerfilePath))
|
||||
}
|
||||
|
||||
var localPath string
|
||||
if ls != nil && !build.IsRemoteURL(ls.LocalPath) {
|
||||
if ls.LocalPath != "" && ls.LocalPath != "-" {
|
||||
localPath = filepath.ToSlash(ls.LocalPath)
|
||||
}
|
||||
if ls.DockerfilePath != "" && ls.DockerfilePath != "-" && ls.DockerfilePath != "Dockerfile" {
|
||||
dockerfilePath = filepath.ToSlash(ls.DockerfilePath)
|
||||
}
|
||||
}
|
||||
|
||||
// remove default dockerfile name
|
||||
const defaultFilename = "/Dockerfile"
|
||||
hasDefaultFileName := strings.HasSuffix(dockerfilePath, defaultFilename) || dockerfilePath == ""
|
||||
dockerfilePath = strings.TrimSuffix(dockerfilePath, defaultFilename)
|
||||
|
||||
// dockerfile is a subpath of context
|
||||
if strings.HasPrefix(dockerfilePath, localPath) && len(dockerfilePath) > len(localPath) {
|
||||
res = dockerfilePath[strings.LastIndex(localPath, "/")+1:]
|
||||
} else {
|
||||
// Otherwise, use basename
|
||||
bpath := localPath
|
||||
if len(dockerfilePath) > 0 {
|
||||
bpath = dockerfilePath
|
||||
}
|
||||
if len(bpath) > 0 {
|
||||
lidx := strings.LastIndex(bpath, "/")
|
||||
res = bpath[lidx+1:]
|
||||
if !hasDefaultFileName {
|
||||
if lidx != -1 {
|
||||
res = filepath.ToSlash(filepath.Join(filepath.Base(bpath[:lidx]), res))
|
||||
} else {
|
||||
res = filepath.ToSlash(filepath.Join(filepath.Base(bpath), res))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(contextPath) > 0 {
|
||||
res = contextPath
|
||||
}
|
||||
if len(target) > 0 {
|
||||
if len(res) > 0 {
|
||||
res = res + " (" + target + ")"
|
||||
} else {
|
||||
res = target
|
||||
}
|
||||
}
|
||||
if res == "" && vcsSource != "" {
|
||||
return vcsSource
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func trimBeginning(s string, n int) string {
|
||||
if len(s) <= n {
|
||||
return s
|
||||
}
|
||||
return ".." + s[len(s)-n+2:]
|
||||
}
|
||||
|
||||
type historyRecord struct {
|
||||
*controlapi.BuildHistoryRecord
|
||||
currentTimestamp *time.Time
|
||||
node *builder.Node
|
||||
name string
|
||||
}
|
||||
|
||||
func queryRecords(ctx context.Context, ref string, nodes []builder.Node) ([]historyRecord, error) {
|
||||
var mu sync.Mutex
|
||||
var out []historyRecord
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
for _, node := range nodes {
|
||||
node := node
|
||||
eg.Go(func() error {
|
||||
if node.Driver == nil {
|
||||
return nil
|
||||
}
|
||||
var records []historyRecord
|
||||
c, err := node.Driver.Client(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serv, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
|
||||
EarlyExit: true,
|
||||
Ref: ref,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
md, err := serv.Header()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var ts *time.Time
|
||||
if v, ok := md[headerKeyTimestamp]; ok {
|
||||
t, err := time.Parse(time.RFC3339Nano, v[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts = &t
|
||||
}
|
||||
defer serv.CloseSend()
|
||||
for {
|
||||
he, err := serv.Recv()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
if he.Type == controlapi.BuildHistoryEventType_DELETED || he.Record == nil {
|
||||
continue
|
||||
}
|
||||
records = append(records, historyRecord{
|
||||
BuildHistoryRecord: he.Record,
|
||||
currentTimestamp: ts,
|
||||
node: &node,
|
||||
})
|
||||
}
|
||||
mu.Lock()
|
||||
out = append(out, records...)
|
||||
mu.Unlock()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func formatDuration(d time.Duration) string {
|
||||
if d < time.Minute {
|
||||
return fmt.Sprintf("%.1fs", d.Seconds())
|
||||
}
|
||||
return fmt.Sprintf("%dm %2ds", int(d.Minutes()), int(d.Seconds())%60)
|
||||
}
|
@@ -42,7 +42,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
||||
return errors.Errorf("can't push with no tags specified, please set --tag or --dry-run")
|
||||
}
|
||||
|
||||
fileArgs := make([]string, len(in.files))
|
||||
fileArgs := make([]string, len(in.files), len(in.files)+len(args))
|
||||
for i, f := range in.files {
|
||||
dt, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
@@ -173,8 +173,8 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
||||
// new resolver cause need new auth
|
||||
r = imagetools.New(imageopt)
|
||||
|
||||
ctx2, cancel := context.WithCancel(context.TODO())
|
||||
defer cancel()
|
||||
ctx2, cancel := context.WithCancelCause(context.TODO())
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
printer, err := progress.NewPrinter(ctx2, os.Stderr, progressui.DisplayMode(in.progress))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
"github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -34,8 +35,9 @@ func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) e
|
||||
return err
|
||||
}
|
||||
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
timeoutCtx, cancel := context.WithCancelCause(ctx)
|
||||
timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
|
||||
nodes, err := b.LoadNodes(timeoutCtx, builder.WithData())
|
||||
if in.bootstrap {
|
||||
|
@@ -18,6 +18,7 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/command/formatter"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@@ -57,8 +58,9 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
timeoutCtx, cancel := context.WithCancelCause(ctx)
|
||||
timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
|
||||
eg, _ := errgroup.WithContext(timeoutCtx)
|
||||
for _, b := range builders {
|
||||
@@ -319,7 +321,7 @@ func (tp truncatedPlatforms) String() string {
|
||||
if tpf, ok := tp.res[mpf]; ok {
|
||||
seen[mpf] = struct{}{}
|
||||
if len(tpf) == 1 {
|
||||
out = append(out, fmt.Sprintf("%s", tpf[0]))
|
||||
out = append(out, tpf[0])
|
||||
count++
|
||||
} else {
|
||||
hasPreferredPlatform := false
|
||||
@@ -347,7 +349,7 @@ func (tp truncatedPlatforms) String() string {
|
||||
continue
|
||||
}
|
||||
if len(tp.res[mpf]) == 1 {
|
||||
out = append(out, fmt.Sprintf("%s", tp.res[mpf][0]))
|
||||
out = append(out, tp.res[mpf][0])
|
||||
count++
|
||||
} else {
|
||||
hasPreferredPlatform := false
|
||||
|
@@ -150,8 +150,9 @@ func rmAllInactive(ctx context.Context, txn *store.Txn, dockerCli command.Cli, i
|
||||
return err
|
||||
}
|
||||
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
timeoutCtx, cancel := context.WithCancelCause(ctx)
|
||||
timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
|
||||
eg, _ := errgroup.WithContext(timeoutCtx)
|
||||
for _, b := range builders {
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
|
||||
debugcmd "github.com/docker/buildx/commands/debug"
|
||||
historycmd "github.com/docker/buildx/commands/history"
|
||||
imagetoolscmd "github.com/docker/buildx/commands/imagetools"
|
||||
"github.com/docker/buildx/controller/remote"
|
||||
"github.com/docker/buildx/util/cobrautil/completion"
|
||||
@@ -106,6 +107,7 @@ func addCommands(cmd *cobra.Command, opts *rootOptions, dockerCli command.Cli) {
|
||||
pruneCmd(dockerCli, opts),
|
||||
duCmd(dockerCli, opts),
|
||||
imagetoolscmd.RootCmd(cmd, dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
|
||||
historycmd.RootCmd(cmd, dockerCli, historycmd.RootOptions{Builder: &opts.builder}),
|
||||
)
|
||||
if confutil.IsExperimental() {
|
||||
cmd.AddCommand(debugcmd.RootCmd(dockerCli,
|
||||
|
@@ -46,7 +46,6 @@ func runUse(dockerCli command.Cli, in useOptions) error {
|
||||
return errors.Errorf("run `docker context use %s` to switch to context %s", in.builder, in.builder)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return errors.Wrapf(err, "failed to find instance %q", in.builder)
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in *controllerapi.Buil
|
||||
}
|
||||
opts.Session = append(opts.Session, ssh)
|
||||
|
||||
outputs, err := controllerapi.CreateExports(in.Exports)
|
||||
outputs, _, err := controllerapi.CreateExports(in.Exports)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
@@ -109,7 +109,7 @@ func (b *localController) Invoke(ctx context.Context, sessionID string, pid stri
|
||||
|
||||
// Attach containerIn to this process
|
||||
ioCancelledCh := make(chan struct{})
|
||||
proc.ForwardIO(&ioset.In{Stdin: ioIn, Stdout: ioOut, Stderr: ioErr}, func() { close(ioCancelledCh) })
|
||||
proc.ForwardIO(&ioset.In{Stdin: ioIn, Stdout: ioOut, Stderr: ioErr}, func(error) { close(ioCancelledCh) })
|
||||
|
||||
select {
|
||||
case <-ioCancelledCh:
|
||||
@@ -117,7 +117,7 @@ func (b *localController) Invoke(ctx context.Context, sessionID string, pid stri
|
||||
case err := <-proc.Done():
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
return context.Cause(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -10,15 +10,16 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
|
||||
func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, []string, error) {
|
||||
var outs []client.ExportEntry
|
||||
var localPaths []string
|
||||
if len(entries) == 0 {
|
||||
return nil, nil
|
||||
return nil, nil, nil
|
||||
}
|
||||
var stdoutUsed bool
|
||||
for _, entry := range entries {
|
||||
if entry.Type == "" {
|
||||
return nil, errors.Errorf("type is required for output")
|
||||
return nil, nil, errors.Errorf("type is required for output")
|
||||
}
|
||||
|
||||
out := client.ExportEntry{
|
||||
@@ -45,24 +46,26 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
|
||||
supportDir = !tar
|
||||
case "registry":
|
||||
out.Type = client.ExporterImage
|
||||
out.Attrs["push"] = "true"
|
||||
}
|
||||
|
||||
if supportDir {
|
||||
if entry.Destination == "" {
|
||||
return nil, errors.Errorf("dest is required for %s exporter", out.Type)
|
||||
return nil, nil, errors.Errorf("dest is required for %s exporter", out.Type)
|
||||
}
|
||||
if entry.Destination == "-" {
|
||||
return nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type)
|
||||
return nil, nil, errors.Errorf("dest cannot be stdout for %s exporter", out.Type)
|
||||
}
|
||||
|
||||
fi, err := os.Stat(entry.Destination)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination)
|
||||
return nil, nil, errors.Wrapf(err, "invalid destination directory: %s", entry.Destination)
|
||||
}
|
||||
if err == nil && !fi.IsDir() {
|
||||
return nil, errors.Errorf("destination directory %s is a file", entry.Destination)
|
||||
return nil, nil, errors.Errorf("destination directory %s is a file", entry.Destination)
|
||||
}
|
||||
out.OutputDir = entry.Destination
|
||||
localPaths = append(localPaths, entry.Destination)
|
||||
}
|
||||
if supportFile {
|
||||
if entry.Destination == "" && out.Type != client.ExporterDocker {
|
||||
@@ -70,32 +73,33 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
|
||||
}
|
||||
if entry.Destination == "-" {
|
||||
if stdoutUsed {
|
||||
return nil, errors.Errorf("multiple outputs configured to write to stdout")
|
||||
return nil, nil, errors.Errorf("multiple outputs configured to write to stdout")
|
||||
}
|
||||
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
||||
return nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
|
||||
return nil, nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
|
||||
}
|
||||
out.Output = wrapWriteCloser(os.Stdout)
|
||||
stdoutUsed = true
|
||||
} else if entry.Destination != "" {
|
||||
fi, err := os.Stat(entry.Destination)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination)
|
||||
return nil, nil, errors.Wrapf(err, "invalid destination file: %s", entry.Destination)
|
||||
}
|
||||
if err == nil && fi.IsDir() {
|
||||
return nil, errors.Errorf("destination file %s is a directory", entry.Destination)
|
||||
return nil, nil, errors.Errorf("destination file %s is a directory", entry.Destination)
|
||||
}
|
||||
f, err := os.Create(entry.Destination)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("failed to open %s", err)
|
||||
return nil, nil, errors.Errorf("failed to open %s", err)
|
||||
}
|
||||
out.Output = wrapWriteCloser(f)
|
||||
localPaths = append(localPaths, entry.Destination)
|
||||
}
|
||||
}
|
||||
|
||||
outs = append(outs, out)
|
||||
}
|
||||
return outs, nil
|
||||
return outs, localPaths, nil
|
||||
}
|
||||
|
||||
func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) {
|
||||
|
@@ -153,7 +153,6 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
|
||||
}
|
||||
}
|
||||
ps = append(ps, p)
|
||||
|
||||
}
|
||||
s.Paths = ps
|
||||
ssh = append(ssh, s)
|
||||
|
@@ -22,9 +22,7 @@ func (w *writer) Write(status *client.SolveStatus) {
|
||||
w.ch <- ToControlStatus(status)
|
||||
}
|
||||
|
||||
func (w *writer) WriteBuildRef(target string, ref string) {
|
||||
return
|
||||
}
|
||||
func (w *writer) WriteBuildRef(target string, ref string) {}
|
||||
|
||||
func (w *writer) ValidateLogSource(digest.Digest, interface{}) bool {
|
||||
return true
|
||||
|
@@ -18,16 +18,16 @@ type Process struct {
|
||||
invokeConfig *pb.InvokeConfig
|
||||
errCh chan error
|
||||
processCancel func()
|
||||
serveIOCancel func()
|
||||
serveIOCancel func(error)
|
||||
}
|
||||
|
||||
// ForwardIO forwards process's io to the specified reader/writer.
|
||||
// Optionally specify ioCancelCallback which will be called when
|
||||
// the process closes the specified IO. This will be useful for additional cleanup.
|
||||
func (p *Process) ForwardIO(in *ioset.In, ioCancelCallback func()) {
|
||||
func (p *Process) ForwardIO(in *ioset.In, ioCancelCallback func(error)) {
|
||||
p.inEnd.SetIn(in)
|
||||
if f := p.serveIOCancel; f != nil {
|
||||
f()
|
||||
f(errors.WithStack(context.Canceled))
|
||||
}
|
||||
p.serveIOCancel = ioCancelCallback
|
||||
}
|
||||
@@ -124,9 +124,16 @@ func (m *Manager) StartProcess(pid string, resultCtx *build.ResultHandle, cfg *p
|
||||
f.SetOut(&out)
|
||||
|
||||
// Register process
|
||||
ctx, cancel := context.WithCancel(context.TODO())
|
||||
ctx, cancel := context.WithCancelCause(context.TODO())
|
||||
var cancelOnce sync.Once
|
||||
processCancelFunc := func() { cancelOnce.Do(func() { cancel(); f.Close(); in.Close(); out.Close() }) }
|
||||
processCancelFunc := func() {
|
||||
cancelOnce.Do(func() {
|
||||
cancel(errors.WithStack(context.Canceled))
|
||||
f.Close()
|
||||
in.Close()
|
||||
out.Close()
|
||||
})
|
||||
}
|
||||
p := &Process{
|
||||
inEnd: f,
|
||||
invokeConfig: cfg,
|
||||
|
@@ -6,8 +6,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/defaults"
|
||||
"github.com/containerd/containerd/pkg/dialer"
|
||||
"github.com/containerd/containerd/v2/defaults"
|
||||
"github.com/containerd/containerd/v2/pkg/dialer"
|
||||
"github.com/docker/buildx/build"
|
||||
"github.com/docker/buildx/controller/pb"
|
||||
"github.com/docker/buildx/util/progress"
|
||||
|
@@ -62,9 +62,10 @@ func NewRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts
|
||||
serverRoot := filepath.Join(rootDir, "shared")
|
||||
|
||||
// connect to buildx server if it is already running
|
||||
ctx2, cancel := context.WithTimeout(ctx, 1*time.Second)
|
||||
ctx2, cancel := context.WithCancelCause(ctx)
|
||||
ctx2, _ = context.WithTimeoutCause(ctx2, 1*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
|
||||
c, err := newBuildxClientAndCheck(ctx2, filepath.Join(serverRoot, defaultSocketFilename))
|
||||
cancel()
|
||||
cancel(errors.WithStack(context.Canceled))
|
||||
if err != nil {
|
||||
if !errors.Is(err, context.DeadlineExceeded) {
|
||||
return nil, errors.Wrap(err, "cannot connect to the buildx server")
|
||||
@@ -90,9 +91,10 @@ func NewRemoteBuildxController(ctx context.Context, dockerCli command.Cli, opts
|
||||
go wait()
|
||||
|
||||
// wait for buildx server to be ready
|
||||
ctx2, cancel = context.WithTimeout(ctx, 10*time.Second)
|
||||
ctx2, cancel = context.WithCancelCause(ctx)
|
||||
ctx2, _ = context.WithTimeoutCause(ctx2, 10*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
|
||||
c, err = newBuildxClientAndCheck(ctx2, filepath.Join(serverRoot, defaultSocketFilename))
|
||||
cancel()
|
||||
cancel(errors.WithStack(context.Canceled))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "cannot connect to the buildx server")
|
||||
}
|
||||
|
@@ -302,7 +302,6 @@ func attachIO(ctx context.Context, stream msgStream, initMessage *pb.InitMessage
|
||||
out = cfg.stderr
|
||||
default:
|
||||
return errors.Errorf("unsupported fd %d", file.Fd)
|
||||
|
||||
}
|
||||
if out == nil {
|
||||
logrus.Warnf("attachIO: no writer for fd %d", file.Fd)
|
||||
@@ -345,7 +344,7 @@ func receive(ctx context.Context, stream msgStream) (*pb.Message, error) {
|
||||
case err := <-errCh:
|
||||
return nil, err
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
return nil, context.Cause(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -37,7 +37,7 @@ type Server struct {
|
||||
type session struct {
|
||||
buildOnGoing atomic.Bool
|
||||
statusChan chan *pb.StatusResponse
|
||||
cancelBuild func()
|
||||
cancelBuild func(error)
|
||||
buildOptions *pb.BuildOptions
|
||||
inputPipe *io.PipeWriter
|
||||
|
||||
@@ -109,7 +109,7 @@ func (m *Server) Disconnect(ctx context.Context, req *pb.DisconnectRequest) (res
|
||||
m.sessionMu.Lock()
|
||||
if s, ok := m.session[sessionID]; ok {
|
||||
if s.cancelBuild != nil {
|
||||
s.cancelBuild()
|
||||
s.cancelBuild(errors.WithStack(context.Canceled))
|
||||
}
|
||||
s.cancelRunningProcesses()
|
||||
if s.result != nil {
|
||||
@@ -127,7 +127,7 @@ func (m *Server) Close() error {
|
||||
for k := range m.session {
|
||||
if s, ok := m.session[k]; ok {
|
||||
if s.cancelBuild != nil {
|
||||
s.cancelBuild()
|
||||
s.cancelBuild(errors.WithStack(context.Canceled))
|
||||
}
|
||||
s.cancelRunningProcesses()
|
||||
}
|
||||
@@ -199,8 +199,8 @@ func (m *Server) Build(ctx context.Context, req *pb.BuildRequest) (*pb.BuildResp
|
||||
pw := pb.NewProgressWriter(statusChan)
|
||||
|
||||
// Build the specified request
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithCancelCause(ctx)
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
resp, res, _, buildErr := m.buildFunc(ctx, req.Options, inR, pw)
|
||||
m.sessionMu.Lock()
|
||||
if s, ok := m.session[sessionID]; ok {
|
||||
@@ -341,7 +341,7 @@ func (m *Server) Input(stream pb.Controller_InputServer) (err error) {
|
||||
select {
|
||||
case msg = <-msgCh:
|
||||
case <-ctx.Done():
|
||||
return errors.Wrap(ctx.Err(), "canceled")
|
||||
return context.Cause(ctx)
|
||||
}
|
||||
if msg == nil {
|
||||
return nil
|
||||
@@ -370,9 +370,9 @@ func (m *Server) Invoke(srv pb.Controller_InvokeServer) error {
|
||||
initDoneCh := make(chan *processes.Process)
|
||||
initErrCh := make(chan error)
|
||||
eg, egCtx := errgroup.WithContext(context.TODO())
|
||||
srvIOCtx, srvIOCancel := context.WithCancel(egCtx)
|
||||
srvIOCtx, srvIOCancel := context.WithCancelCause(egCtx)
|
||||
eg.Go(func() error {
|
||||
defer srvIOCancel()
|
||||
defer srvIOCancel(errors.WithStack(context.Canceled))
|
||||
return serveIO(srvIOCtx, srv, func(initMessage *pb.InitMessage) (retErr error) {
|
||||
defer func() {
|
||||
if retErr != nil {
|
||||
@@ -418,7 +418,7 @@ func (m *Server) Invoke(srv pb.Controller_InvokeServer) error {
|
||||
})
|
||||
})
|
||||
eg.Go(func() (rErr error) {
|
||||
defer srvIOCancel()
|
||||
defer srvIOCancel(errors.WithStack(context.Canceled))
|
||||
// Wait for init done
|
||||
var proc *processes.Process
|
||||
select {
|
||||
|
@@ -41,11 +41,15 @@ target "lint" {
|
||||
platforms = GOLANGCI_LINT_MULTIPLATFORM != "" ? [
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"freebsd/amd64",
|
||||
"freebsd/arm64",
|
||||
"linux/amd64",
|
||||
"linux/arm64",
|
||||
"linux/s390x",
|
||||
"linux/ppc64le",
|
||||
"linux/riscv64",
|
||||
"openbsd/amd64",
|
||||
"openbsd/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm64"
|
||||
] : []
|
||||
@@ -154,6 +158,8 @@ target "binaries-cross" {
|
||||
platforms = [
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
"freebsd/amd64",
|
||||
"freebsd/arm64",
|
||||
"linux/amd64",
|
||||
"linux/arm/v6",
|
||||
"linux/arm/v7",
|
||||
@@ -161,6 +167,8 @@ target "binaries-cross" {
|
||||
"linux/ppc64le",
|
||||
"linux/riscv64",
|
||||
"linux/s390x",
|
||||
"openbsd/amd64",
|
||||
"openbsd/arm64",
|
||||
"windows/amd64",
|
||||
"windows/arm64"
|
||||
]
|
||||
|
@@ -19,8 +19,8 @@ By default, Bake uses the following lookup order to find the configuration file:
|
||||
3. `docker-compose.yml`
|
||||
4. `docker-compose.yaml`
|
||||
5. `docker-bake.json`
|
||||
6. `docker-bake.override.json`
|
||||
7. `docker-bake.hcl`
|
||||
6. `docker-bake.hcl`
|
||||
7. `docker-bake.override.json`
|
||||
8. `docker-bake.override.hcl`
|
||||
|
||||
You can specify the file location explicitly using the `--file` flag:
|
||||
@@ -221,8 +221,10 @@ The following table shows the complete list of attributes that you can assign to
|
||||
| [`attest`](#targetattest) | List | Build attestations |
|
||||
| [`cache-from`](#targetcache-from) | List | External cache sources |
|
||||
| [`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 |
|
||||
| [`contexts`](#targetcontexts) | Map | Additional build contexts |
|
||||
| [`description`](#targetdescription) | String | Description of a target |
|
||||
| [`dockerfile-inline`](#targetdockerfile-inline) | String | Inline Dockerfile string |
|
||||
| [`dockerfile`](#targetdockerfile) | String | Dockerfile location |
|
||||
| [`inherits`](#targetinherits) | List | Inherit attributes from other targets |
|
||||
@@ -359,6 +361,28 @@ target "app" {
|
||||
}
|
||||
```
|
||||
|
||||
### `target.call`
|
||||
|
||||
Specifies the frontend method to use. Frontend methods let you, for example,
|
||||
execute build checks only, instead of running a build. This is the same as the
|
||||
`--call` flag.
|
||||
|
||||
```hcl
|
||||
target "app" {
|
||||
call = "check"
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
[`docker buildx build --call`](https://docs.docker.com/reference/cli/docker/buildx/build/#call).
|
||||
|
||||
### `target.context`
|
||||
|
||||
Specifies the location of the build context to use for this target.
|
||||
@@ -466,6 +490,25 @@ FROM baseapp
|
||||
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`
|
||||
|
||||
Uses the string value as an inline Dockerfile for the build target.
|
||||
|
@@ -17,6 +17,7 @@ Extended build capabilities with BuildKit
|
||||
| [`debug`](buildx_debug.md) | Start debugger (EXPERIMENTAL) |
|
||||
| [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance |
|
||||
| [`du`](buildx_du.md) | Disk usage |
|
||||
| [`history`](buildx_history.md) | Commands to work on build records |
|
||||
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
|
||||
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
|
||||
| [`ls`](buildx_ls.md) | List builder instances |
|
||||
|
@@ -13,24 +13,25 @@ Build from a file
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------------------------|:--------------|:--------|:----------------------------------------------------------------------------------------------------|
|
||||
| `--allow` | `stringArray` | | Allow build to access specified resources |
|
||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
||||
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
||||
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
|
||||
| `--load` | `bool` | | Shorthand for `--set=*.output=type=docker` |
|
||||
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file |
|
||||
| [`--no-cache`](#no-cache) | `bool` | | Do not use cache when building the image |
|
||||
| [`--print`](#print) | `bool` | | Print the options without building |
|
||||
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use plain to show container output |
|
||||
| [`--provenance`](#provenance) | `string` | | Shorthand for `--set=*.attest=type=provenance` |
|
||||
| [`--pull`](#pull) | `bool` | | Always attempt to pull all referenced images |
|
||||
| `--push` | `bool` | | Shorthand for `--set=*.output=type=registry` |
|
||||
| [`--sbom`](#sbom) | `string` | | Shorthand for `--set=*.attest=type=sbom` |
|
||||
| [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
|
||||
| Name | Type | Default | Description |
|
||||
|:------------------------------------|:--------------|:--------|:-------------------------------------------------------------------------------------------------------------|
|
||||
| [`--allow`](#allow) | `stringArray` | | Allow build to access specified resources |
|
||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
||||
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
||||
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
|
||||
| [`--list`](#list) | `string` | | List targets or variables |
|
||||
| `--load` | `bool` | | Shorthand for `--set=*.output=type=docker` |
|
||||
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file |
|
||||
| [`--no-cache`](#no-cache) | `bool` | | Do not use cache when building the image |
|
||||
| [`--print`](#print) | `bool` | | Print the options without building |
|
||||
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `quiet`, `plain`, `tty`, `rawjson`). Use plain to show container output |
|
||||
| [`--provenance`](#provenance) | `string` | | Shorthand for `--set=*.attest=type=provenance` |
|
||||
| [`--pull`](#pull) | `bool` | | Always attempt to pull all referenced images |
|
||||
| `--push` | `bool` | | Shorthand for `--set=*.output=type=registry` |
|
||||
| [`--sbom`](#sbom) | `string` | | Shorthand for `--set=*.attest=type=sbom` |
|
||||
| [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
@@ -50,6 +51,80 @@ guide for introduction to writing bake files.
|
||||
|
||||
## 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)
|
||||
|
||||
Same as [`buildx --builder`](buildx.md#builder).
|
||||
@@ -101,6 +176,42 @@ $ docker buildx bake -f docker-bake.dev.hcl db webapp-release
|
||||
See the [Bake file reference](https://docs.docker.com/build/bake/reference/)
|
||||
for more details.
|
||||
|
||||
### <a name="list"></a> List targets and variables (--list)
|
||||
|
||||
The `--list` flag displays all available targets or variables in the Bake
|
||||
configuration, along with a description (if set using the `description`
|
||||
property in the Bake file).
|
||||
|
||||
To list all targets:
|
||||
|
||||
```console {title="List targets"}
|
||||
$ docker buildx bake --list=targets
|
||||
TARGET DESCRIPTION
|
||||
binaries
|
||||
default binaries
|
||||
update-docs
|
||||
validate
|
||||
validate-golangci Validate .golangci.yml schema (does not run Go linter)
|
||||
```
|
||||
|
||||
To list variables:
|
||||
|
||||
```console
|
||||
$ docker buildx bake --list=variables
|
||||
VARIABLE VALUE DESCRIPTION
|
||||
REGISTRY docker.io/username Registry and namespace
|
||||
IMAGE_NAME my-app Image name
|
||||
GO_VERSION <null>
|
||||
```
|
||||
|
||||
By default, the output of `docker buildx bake --list` is presented in a table
|
||||
format. Alternatively, you can use a long-form CSV syntax and specify a
|
||||
`format` attribute to output the list in JSON.
|
||||
|
||||
```console
|
||||
$ docker buildx bake --list=type=targets,format=json
|
||||
```
|
||||
|
||||
### <a name="metadata-file"></a> Write build results metadata to a file (--metadata-file)
|
||||
|
||||
Similar to [`buildx build --metadata-file`](buildx_build.md#metadata-file) but
|
||||
|
@@ -13,46 +13,46 @@ Start a build
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------------------------|:--------------|:----------|:----------------------------------------------------------------------------------------------------|
|
||||
| [`--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`) |
|
||||
| [`--annotation`](#annotation) | `stringArray` | | Add annotation to the image |
|
||||
| [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
||||
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
|
||||
| [`--build-context`](#build-context) | `stringArray` | | Additional build contexts (e.g., name=path) |
|
||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
||||
| [`--cache-from`](#cache-from) | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
|
||||
| [`--cache-to`](#cache-to) | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
|
||||
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
||||
| [`--cgroup-parent`](#cgroup-parent) | `string` | | Set the parent cgroup for the `RUN` instructions during build |
|
||||
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--detach` | `bool` | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
|
||||
| [`-f`](#file), [`--file`](#file) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
||||
| `--iidfile` | `string` | | Write the image ID to a file |
|
||||
| `--label` | `stringArray` | | Set metadata for an image |
|
||||
| [`--load`](#load) | `bool` | | Shorthand for `--output=type=docker` |
|
||||
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file |
|
||||
| [`--network`](#network) | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
|
||||
| `--no-cache` | `bool` | | Do not use cache when building the image |
|
||||
| [`--no-cache-filter`](#no-cache-filter) | `stringArray` | | Do not cache specified stages |
|
||||
| [`-o`](#output), [`--output`](#output) | `stringArray` | | Output destination (format: `type=local,dest=path`) |
|
||||
| [`--platform`](#platform) | `stringArray` | | Set target platform for build |
|
||||
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use plain to show container output |
|
||||
| [`--provenance`](#provenance) | `string` | | Shorthand for `--attest=type=provenance` |
|
||||
| `--pull` | `bool` | | Always attempt to pull all referenced images |
|
||||
| [`--push`](#push) | `bool` | | Shorthand for `--output=type=registry` |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress the build output and print image ID on success |
|
||||
| `--root` | `string` | | Specify root directory of server to connect (EXPERIMENTAL) |
|
||||
| [`--sbom`](#sbom) | `string` | | Shorthand for `--attest=type=sbom` |
|
||||
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
|
||||
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
|
||||
| [`--shm-size`](#shm-size) | `bytes` | `0` | Shared memory size for build containers |
|
||||
| [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
||||
| [`-t`](#tag), [`--tag`](#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
||||
| [`--target`](#target) | `string` | | Set the target build stage to build |
|
||||
| [`--ulimit`](#ulimit) | `ulimit` | | Ulimit options |
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------------------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
|
||||
| [`--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`) |
|
||||
| [`--annotation`](#annotation) | `stringArray` | | Add annotation to the image |
|
||||
| [`--attest`](#attest) | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
||||
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
|
||||
| [`--build-context`](#build-context) | `stringArray` | | Additional build contexts (e.g., name=path) |
|
||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
||||
| [`--cache-from`](#cache-from) | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
|
||||
| [`--cache-to`](#cache-to) | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
|
||||
| [`--call`](#call) | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
||||
| [`--cgroup-parent`](#cgroup-parent) | `string` | | Set the parent cgroup for the `RUN` instructions during build |
|
||||
| [`--check`](#check) | `bool` | | Shorthand for `--call=check` |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--detach` | `bool` | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
|
||||
| [`-f`](#file), [`--file`](#file) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
||||
| `--iidfile` | `string` | | Write the image ID to a file |
|
||||
| `--label` | `stringArray` | | Set metadata for an image |
|
||||
| [`--load`](#load) | `bool` | | Shorthand for `--output=type=docker` |
|
||||
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file |
|
||||
| [`--network`](#network) | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
|
||||
| `--no-cache` | `bool` | | Do not use cache when building the image |
|
||||
| [`--no-cache-filter`](#no-cache-filter) | `stringArray` | | Do not cache specified stages |
|
||||
| [`-o`](#output), [`--output`](#output) | `stringArray` | | Output destination (format: `type=local,dest=path`) |
|
||||
| [`--platform`](#platform) | `stringArray` | | Set target platform for build |
|
||||
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `quiet`, `plain`, `tty`, `rawjson`). Use plain to show container output |
|
||||
| [`--provenance`](#provenance) | `string` | | Shorthand for `--attest=type=provenance` |
|
||||
| `--pull` | `bool` | | Always attempt to pull all referenced images |
|
||||
| [`--push`](#push) | `bool` | | Shorthand for `--output=type=registry` |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress the build output and print image ID on success |
|
||||
| `--root` | `string` | | Specify root directory of server to connect (EXPERIMENTAL) |
|
||||
| [`--sbom`](#sbom) | `string` | | Shorthand for `--attest=type=sbom` |
|
||||
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
|
||||
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
|
||||
| [`--shm-size`](#shm-size) | `bytes` | `0` | Shared memory size for build containers |
|
||||
| [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
||||
| [`-t`](#tag), [`--tag`](#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
||||
| [`--target`](#target) | `string` | | Set the target build stage to build |
|
||||
| [`--ulimit`](#ulimit) | `ulimit` | | Ulimit options |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
@@ -828,8 +828,12 @@ $ docker buildx build --platform=darwin .
|
||||
--progress=VALUE
|
||||
```
|
||||
|
||||
Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use `plain` to show container
|
||||
output (default `auto`).
|
||||
Set type of progress output. Supported values are:
|
||||
- `auto` (default): Uses the `tty` mode if the client is a TTY, or `plain` otherwise
|
||||
- `tty`: An interactive stream of the output with color and redrawing
|
||||
- `plain`: Prints the raw build progress in a plaintext format
|
||||
- `quiet`: Suppress the build output and print image ID on success (same as `--quiet`)
|
||||
- `rawjson`: Prints the raw build progress as JSON lines
|
||||
|
||||
> [!NOTE]
|
||||
> You can also use the `BUILDKIT_PROGRESS` environment variable to set its value.
|
||||
|
@@ -9,46 +9,46 @@ Start a build
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------|:--------------|:----------|:----------------------------------------------------------------------------------------------------|
|
||||
| `--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`) |
|
||||
| `--annotation` | `stringArray` | | Add annotation to the image |
|
||||
| `--attest` | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
||||
| `--build-arg` | `stringArray` | | Set build-time variables |
|
||||
| `--build-context` | `stringArray` | | Additional build contexts (e.g., name=path) |
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `--cache-from` | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
|
||||
| `--cache-to` | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
|
||||
| `--call` | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
||||
| `--cgroup-parent` | `string` | | Set the parent cgroup for the `RUN` instructions during build |
|
||||
| `--check` | `bool` | | Shorthand for `--call=check` |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--detach` | `bool` | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
|
||||
| `-f`, `--file` | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
||||
| `--iidfile` | `string` | | Write the image ID to a file |
|
||||
| `--label` | `stringArray` | | Set metadata for an image |
|
||||
| `--load` | `bool` | | Shorthand for `--output=type=docker` |
|
||||
| `--metadata-file` | `string` | | Write build result metadata to a file |
|
||||
| `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
|
||||
| `--no-cache` | `bool` | | Do not use cache when building the image |
|
||||
| `--no-cache-filter` | `stringArray` | | Do not cache specified stages |
|
||||
| `-o`, `--output` | `stringArray` | | Output destination (format: `type=local,dest=path`) |
|
||||
| `--platform` | `stringArray` | | Set target platform for build |
|
||||
| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use plain to show container output |
|
||||
| `--provenance` | `string` | | Shorthand for `--attest=type=provenance` |
|
||||
| `--pull` | `bool` | | Always attempt to pull all referenced images |
|
||||
| `--push` | `bool` | | Shorthand for `--output=type=registry` |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress the build output and print image ID on success |
|
||||
| `--root` | `string` | | Specify root directory of server to connect (EXPERIMENTAL) |
|
||||
| `--sbom` | `string` | | Shorthand for `--attest=type=sbom` |
|
||||
| `--secret` | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
|
||||
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
|
||||
| `--shm-size` | `bytes` | `0` | Shared memory size for build containers |
|
||||
| `--ssh` | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
||||
| `-t`, `--tag` | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
||||
| `--target` | `string` | | Set the target build stage to build |
|
||||
| `--ulimit` | `ulimit` | | Ulimit options |
|
||||
| Name | Type | Default | Description |
|
||||
|:--------------------|:--------------|:----------|:-------------------------------------------------------------------------------------------------------------|
|
||||
| `--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`) |
|
||||
| `--annotation` | `stringArray` | | Add annotation to the image |
|
||||
| `--attest` | `stringArray` | | Attestation parameters (format: `type=sbom,generator=image`) |
|
||||
| `--build-arg` | `stringArray` | | Set build-time variables |
|
||||
| `--build-context` | `stringArray` | | Additional build contexts (e.g., name=path) |
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `--cache-from` | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
|
||||
| `--cache-to` | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
|
||||
| `--call` | `string` | `build` | Set method for evaluating build (`check`, `outline`, `targets`) |
|
||||
| `--cgroup-parent` | `string` | | Set the parent cgroup for the `RUN` instructions during build |
|
||||
| `--check` | `bool` | | Shorthand for `--call=check` |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--detach` | `bool` | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
|
||||
| `-f`, `--file` | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
||||
| `--iidfile` | `string` | | Write the image ID to a file |
|
||||
| `--label` | `stringArray` | | Set metadata for an image |
|
||||
| `--load` | `bool` | | Shorthand for `--output=type=docker` |
|
||||
| `--metadata-file` | `string` | | Write build result metadata to a file |
|
||||
| `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
|
||||
| `--no-cache` | `bool` | | Do not use cache when building the image |
|
||||
| `--no-cache-filter` | `stringArray` | | Do not cache specified stages |
|
||||
| `-o`, `--output` | `stringArray` | | Output destination (format: `type=local,dest=path`) |
|
||||
| `--platform` | `stringArray` | | Set target platform for build |
|
||||
| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `quiet`, `plain`, `tty`, `rawjson`). Use plain to show container output |
|
||||
| `--provenance` | `string` | | Shorthand for `--attest=type=provenance` |
|
||||
| `--pull` | `bool` | | Always attempt to pull all referenced images |
|
||||
| `--push` | `bool` | | Shorthand for `--output=type=registry` |
|
||||
| `-q`, `--quiet` | `bool` | | Suppress the build output and print image ID on success |
|
||||
| `--root` | `string` | | Specify root directory of server to connect (EXPERIMENTAL) |
|
||||
| `--sbom` | `string` | | Shorthand for `--attest=type=sbom` |
|
||||
| `--secret` | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
|
||||
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
|
||||
| `--shm-size` | `bytes` | `0` | Shared memory size for build containers |
|
||||
| `--ssh` | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
||||
| `-t`, `--tag` | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
||||
| `--target` | `string` | | Set the target build stage to build |
|
||||
| `--ulimit` | `ulimit` | | Ulimit options |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
26
docs/reference/buildx_history.md
Normal file
26
docs/reference/buildx_history.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# docker buildx history
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Commands to work on build records
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:---------------------------------------|:-------------------------------|
|
||||
| [`inspect`](buildx_history_inspect.md) | Inspect a build |
|
||||
| [`logs`](buildx_history_logs.md) | Print the logs of a build |
|
||||
| [`ls`](buildx_history_ls.md) | List build records |
|
||||
| [`open`](buildx_history_open.md) | Open a build in Docker Desktop |
|
||||
| [`rm`](buildx_history_rm.md) | Remove build records |
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
22
docs/reference/buildx_history_inspect.md
Normal file
22
docs/reference/buildx_history_inspect.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# docker buildx history inspect
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Inspect a build
|
||||
|
||||
### Subcommands
|
||||
|
||||
| Name | Description |
|
||||
|:-----------------------------------------------------|:---------------------------|
|
||||
| [`attachment`](buildx_history_inspect_attachment.md) | Inspect a build attachment |
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
17
docs/reference/buildx_history_inspect_attachment.md
Normal file
17
docs/reference/buildx_history_inspect_attachment.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# docker buildx history inspect attachment
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Inspect a build attachment
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--platform` | `string` | | Platform of attachment |
|
||||
| `--type` | `string` | | Type of attachment |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
16
docs/reference/buildx_history_logs.md
Normal file
16
docs/reference/buildx_history_logs.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# docker buildx history logs
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Print the logs of a build
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:--------------------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--progress` | `string` | `plain` | Set type of progress output (plain, rawjson, tty) |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
17
docs/reference/buildx_history_ls.md
Normal file
17
docs/reference/buildx_history_ls.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# docker buildx history ls
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
List build records
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
| `--format` | `string` | `table` | Format the output |
|
||||
| `--no-trunc` | `bool` | | Don't truncate output |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
15
docs/reference/buildx_history_open.md
Normal file
15
docs/reference/buildx_history_open.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# docker buildx history open
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Open a build in Docker Desktop
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
16
docs/reference/buildx_history_rm.md
Normal file
16
docs/reference/buildx_history_rm.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# docker buildx history rm
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Remove build records
|
||||
|
||||
### Options
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|:----------------|:---------|:--------|:-----------------------------------------|
|
||||
| `--all` | `bool` | | Remove all build records |
|
||||
| `--builder` | `string` | | Override the configured builder instance |
|
||||
| `-D`, `--debug` | `bool` | | Enable debug logging |
|
||||
|
||||
|
||||
<!---MARKER_GEN_END-->
|
||||
|
@@ -177,7 +177,6 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
_, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, nil, d.Name)
|
||||
if err != nil && !errdefs.IsConflict(err) {
|
||||
@@ -213,7 +212,7 @@ func (d *Driver) wait(ctx context.Context, l progress.SubLogger) error {
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
return context.Cause(ctx)
|
||||
case <-time.After(time.Duration(try*120) * time.Millisecond):
|
||||
try++
|
||||
continue
|
||||
|
@@ -29,7 +29,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
||||
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
|
||||
_, err := d.DockerAPI.ServerVersion(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(driver.ErrNotConnecting{}, err.Error())
|
||||
return nil, errors.Wrap(driver.ErrNotConnecting{}, err.Error())
|
||||
}
|
||||
return &driver.Info{
|
||||
Status: driver.Running,
|
||||
@@ -39,7 +39,7 @@ func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
|
||||
func (d *Driver) Version(ctx context.Context) (string, error) {
|
||||
v, err := d.DockerAPI.ServerVersion(ctx)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(driver.ErrNotConnecting{}, err.Error())
|
||||
return "", errors.Wrap(driver.ErrNotConnecting{}, err.Error())
|
||||
}
|
||||
if bkversion, _ := resolveBuildKitVersion(v.Version); bkversion != "" {
|
||||
return bkversion, nil
|
||||
|
@@ -176,11 +176,6 @@ func resolveBuildKitVersion(ver string) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
//if _, errs := c.Validate(mobyVersion); len(errs) > 0 {
|
||||
// for _, err := range errs {
|
||||
// fmt.Printf("%s: %v\n", m.MobyVersionConstraint, err)
|
||||
// }
|
||||
//}
|
||||
if !c.Check(mobyVersion) {
|
||||
continue
|
||||
}
|
||||
|
@@ -191,7 +191,7 @@ func checkClientConfig(t *testing.T, ep Endpoint, server, namespace string, ca,
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, proxyURLString, proxyURL.String())
|
||||
} else {
|
||||
assert.True(t, cfg.Proxy == nil, "expected proxy to be nil, but is not nil instead")
|
||||
assert.Nil(t, cfg.Proxy, "expected proxy to be nil, but is not nil instead")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ func TestSaveLoadGKEConfig(t *testing.T) {
|
||||
persistedMetadata, err := store.GetMetadata("gke-context")
|
||||
require.NoError(t, err)
|
||||
persistedEPMeta := EndpointFromContext(persistedMetadata)
|
||||
assert.True(t, persistedEPMeta != nil)
|
||||
assert.NotNil(t, persistedEPMeta)
|
||||
persistedEP, err := persistedEPMeta.WithTLSData(store, "gke-context")
|
||||
require.NoError(t, err)
|
||||
persistedCfg := persistedEP.KubernetesConfig()
|
||||
@@ -249,7 +249,7 @@ func TestSaveLoadEKSConfig(t *testing.T) {
|
||||
persistedMetadata, err := store.GetMetadata("eks-context")
|
||||
require.NoError(t, err)
|
||||
persistedEPMeta := EndpointFromContext(persistedMetadata)
|
||||
assert.True(t, persistedEPMeta != nil)
|
||||
assert.NotNil(t, persistedEPMeta)
|
||||
persistedEP, err := persistedEPMeta.WithTLSData(store, "eks-context")
|
||||
require.NoError(t, err)
|
||||
persistedCfg := persistedEP.KubernetesConfig()
|
||||
@@ -274,14 +274,14 @@ func TestSaveLoadK3SConfig(t *testing.T) {
|
||||
persistedMetadata, err := store.GetMetadata("k3s-context")
|
||||
require.NoError(t, err)
|
||||
persistedEPMeta := EndpointFromContext(persistedMetadata)
|
||||
assert.True(t, persistedEPMeta != nil)
|
||||
assert.NotNil(t, persistedEPMeta)
|
||||
persistedEP, err := persistedEPMeta.WithTLSData(store, "k3s-context")
|
||||
require.NoError(t, err)
|
||||
persistedCfg := persistedEP.KubernetesConfig()
|
||||
actualCfg, err := persistedCfg.ClientConfig()
|
||||
require.NoError(t, err)
|
||||
assert.True(t, len(actualCfg.Username) > 0)
|
||||
assert.True(t, len(actualCfg.Password) > 0)
|
||||
assert.Greater(t, len(actualCfg.Username), 0)
|
||||
assert.Greater(t, len(actualCfg.Password), 0)
|
||||
assert.Equal(t, expectedCfg.Username, actualCfg.Username)
|
||||
assert.Equal(t, expectedCfg.Password, actualCfg.Password)
|
||||
}
|
||||
|
@@ -112,7 +112,7 @@ func (d *Driver) wait(ctx context.Context) error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
return context.Cause(ctx)
|
||||
case <-timeoutChan:
|
||||
return err
|
||||
case <-ticker.C:
|
||||
|
@@ -47,8 +47,9 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
|
||||
return err
|
||||
}
|
||||
return progress.Wrap("[internal] waiting for connection", l, func(_ progress.SubLogger) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||
defer cancel()
|
||||
cancelCtx, cancel := context.WithCancelCause(ctx)
|
||||
ctx, _ := context.WithTimeoutCause(cancelCtx, 20*time.Second, errors.WithStack(context.DeadlineExceeded)) //nolint:govet,lostcancel // no need to manually cancel this context as we already rely on parent
|
||||
defer func() { cancel(errors.WithStack(context.Canceled)) }()
|
||||
return c.Wait(ctx)
|
||||
})
|
||||
}
|
||||
|
167
go.mod
167
go.mod
@@ -5,89 +5,94 @@ go 1.22.0
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6
|
||||
github.com/compose-spec/compose-go/v2 v2.4.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
||||
github.com/compose-spec/compose-go/v2 v2.4.7
|
||||
github.com/containerd/console v1.0.4
|
||||
github.com/containerd/containerd v1.7.22
|
||||
github.com/containerd/continuity v0.4.4
|
||||
github.com/containerd/errdefs v0.1.0
|
||||
github.com/containerd/containerd/v2 v2.0.2
|
||||
github.com/containerd/continuity v0.4.5
|
||||
github.com/containerd/errdefs v1.0.0
|
||||
github.com/containerd/log v0.1.0
|
||||
github.com/containerd/platforms v0.2.1
|
||||
github.com/containerd/typeurl/v2 v2.2.0
|
||||
github.com/creack/pty v1.1.21
|
||||
github.com/containerd/platforms v1.0.0-rc.1
|
||||
github.com/containerd/typeurl/v2 v2.2.3
|
||||
github.com/creack/pty v1.1.24
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli v27.3.1+incompatible
|
||||
github.com/docker/cli-docs-tool v0.8.0
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/docker/cli v27.5.0+incompatible
|
||||
github.com/docker/cli-docs-tool v0.9.0
|
||||
github.com/docker/docker v27.5.0+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/gofrs/flock v0.12.1
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20230405223818-a090f58aa992
|
||||
github.com/hashicorp/hcl/v2 v2.20.1
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/hcl/v2 v2.23.0
|
||||
github.com/in-toto/in-toto-golang v0.5.0
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/moby/buildkit v0.17.0
|
||||
github.com/moby/buildkit v0.19.0
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/morikuni/aec v1.0.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tonistiigi/fsutil v0.0.0-20241028165955-397af5306b5c
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a
|
||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4
|
||||
github.com/zclconf/go-cty v1.14.4
|
||||
go.opentelemetry.io/otel v1.21.0
|
||||
go.opentelemetry.io/otel/metric v1.21.0
|
||||
go.opentelemetry.io/otel/sdk v1.21.0
|
||||
go.opentelemetry.io/otel/trace v1.21.0
|
||||
github.com/zclconf/go-cty v1.16.0
|
||||
go.opentelemetry.io/otel v1.31.0
|
||||
go.opentelemetry.io/otel/metric v1.31.0
|
||||
go.opentelemetry.io/otel/sdk 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.21.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/sys v0.26.0
|
||||
golang.org/x/term v0.24.0
|
||||
golang.org/x/text v0.18.0
|
||||
google.golang.org/grpc v1.66.2
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/term v0.27.0
|
||||
golang.org/x/text v0.21.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38
|
||||
google.golang.org/grpc v1.68.1
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||
google.golang.org/protobuf v1.35.1
|
||||
google.golang.org/protobuf v1.35.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.29.2
|
||||
k8s.io/apimachinery v0.29.2
|
||||
k8s.io/client-go v0.29.2
|
||||
k8s.io/api v0.31.2
|
||||
k8s.io/apimachinery v0.31.2
|
||||
k8s.io/client-go v0.31.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // 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/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-cidr v1.0.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/aws/smithy-go v1.19.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.20.3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/containerd/api v1.7.19 // indirect
|
||||
github.com/containerd/ttrpc v1.2.5 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/containerd/containerd/api v1.8.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
|
||||
@@ -96,11 +101,12 @@ require (
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
@@ -109,10 +115,9 @@ require (
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@@ -121,14 +126,13 @@ require (
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/spdystream v0.4.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
@@ -138,9 +142,9 @@ require (
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.17.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
@@ -150,34 +154,41 @@ require (
|
||||
github.com/tonistiigi/dchapes-mode v0.0.0-20241001053921-ca0759fec205 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
||||
golang.org/x/net v0.29.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.23.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.25.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
exclude (
|
||||
// FIXME(thaJeztah): remoove this once kubernetes updated their dependencies to no longer need this.
|
||||
//
|
||||
// For additional details, see this PR and links mentioned in that PR:
|
||||
// https://github.com/kubernetes-sigs/kustomize/pull/5830#issuecomment-2569960859
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
|
||||
)
|
||||
|
386
go.sum
386
go.sum
@@ -1,11 +1,7 @@
|
||||
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA=
|
||||
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
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/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-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
@@ -14,8 +10,8 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.12.5 h1:bpTInLlDy/nDRWFVcefDZZ1+U8tS+rz3MxjKgu9boo0=
|
||||
github.com/Microsoft/hcsshim v0.12.5/go.mod h1:tIUGego4G1EN5Hb6KC90aDYiUI2dqLSTTOCjVNpOgZ8=
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs=
|
||||
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
|
||||
@@ -32,32 +28,32 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6 h1:Z/7w9bUqlRI0FFQpetVuFYEsjzE3h7fpU6HuGmfPL/o=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.26.6/go.mod h1:uKU6cnDmYCvJ+pxO9S4cWDb2yWWIH5hra+32hVh1MI4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 h1:n3GDfwqF2tzEkXlv5cuy4iy7LpKDtqDMcNLfZDu9rls=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U=
|
||||
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
|
||||
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
|
||||
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
||||
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
@@ -73,67 +69,68 @@ github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembj
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b h1:ga8SEFjZ60pxLcmhnThWgvH2wg8376yUJmPhEH4H3kw=
|
||||
github.com/cncf/xds/go v0.0.0-20240423153145-555b57ec207b/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
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/compose-spec/compose-go/v2 v2.4.1 h1:tEg6Qn/9LZnKg42fZlFmxN4lxSqnCvsiG5TXnxzvI4c=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.1/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
|
||||
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.7 h1:WNpz5bIbKG+G+w9pfu72B1ZXr+Og9jez8TMEo8ecXPk=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.7/go.mod h1:lFN0DrMxIncJGYAXTfWuajfwj5haBJqrBkarHcnjJKc=
|
||||
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||
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/containerd v1.7.22 h1:nZuNnNRA6T6jB975rx2RRNqqH2k6ELYKDZfqTHqwyy0=
|
||||
github.com/containerd/containerd v1.7.22/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g=
|
||||
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
|
||||
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
|
||||
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
|
||||
github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
|
||||
github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
|
||||
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/v2 v2.0.2 h1:GmH/tRBlTvrXOLwSpWE2vNAm8+MqI6nmxKpKBNKY8Wc=
|
||||
github.com/containerd/containerd/v2 v2.0.2/go.mod h1:wIqEvQ/6cyPFUGJ5yMFanspPabMLor+bF865OHvNTTI=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
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/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
|
||||
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/nydus-snapshotter v0.14.0 h1:6/eAi6d7MjaeLLuMO8Udfe5GVsDudmrDNO4SGETMBco=
|
||||
github.com/containerd/nydus-snapshotter v0.14.0/go.mod h1:TT4jv2SnIDxEBu4H2YOvWQHPOap031ydTaHTuvc5VQk=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/containerd/stargz-snapshotter v0.15.1 h1:fpsP4kf/Z4n2EYnU0WT8ZCE3eiKDwikDhL6VwxIlgeA=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
|
||||
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
|
||||
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso=
|
||||
github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g=
|
||||
github.com/containerd/nydus-snapshotter v0.15.0 h1:RqZRs1GPeM6T3wmuxJV9u+2Rg4YETVMwTmiDeX+iWC8=
|
||||
github.com/containerd/nydus-snapshotter v0.15.0/go.mod h1:biq0ijpeZe0I5yZFSJyHzFSjjRZQ7P7y/OuHyd7hYOw=
|
||||
github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E=
|
||||
github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
|
||||
github.com/containerd/stargz-snapshotter v0.16.3 h1:zbQMm8dRuPHEOD4OqAYGajJJUwCeUzt4j7w9Iaw58u4=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ=
|
||||
github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli-docs-tool v0.8.0 h1:YcDWl7rQJC3lJ7WVZRwSs3bc9nka97QLWfyJQli8yJU=
|
||||
github.com/docker/cli-docs-tool v0.8.0/go.mod h1:8TQQ3E7mOXoYUs811LiPdUnAhXrcVsBIrW21a5pUbdk=
|
||||
github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM=
|
||||
github.com/docker/cli v27.5.0+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/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.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/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
|
||||
github.com/docker/docker v27.5.0+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/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
@@ -141,8 +138,6 @@ github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
@@ -153,34 +148,36 @@ github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNE
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
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.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/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
@@ -192,8 +189,6 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
|
||||
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -215,8 +210,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
|
||||
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -225,11 +220,10 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -237,12 +231,12 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20230405223818-a090f58aa992 h1:fYOrSfO5C9PmFGtmRWSYGqq52SOoE2dXMtAn2Xzh1LQ=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20230405223818-a090f58aa992/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd h1:nwSMaLX+rf/ZPHTJHWO9K73be04SritSKvKuvpBvC2A=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20241120183456-c51673e0b3dd/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc=
|
||||
github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
@@ -279,6 +273,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y=
|
||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
@@ -290,8 +286,6 @@ github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebG
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
@@ -303,16 +297,16 @@ 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 v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/buildkit v0.17.0 h1:ZA/4AxwBbve1f3ZaNNJQiCBtTV62R6YweWNwq4A+sTc=
|
||||
github.com/moby/buildkit v0.17.0/go.mod h1:ru8NFyDHD8HbuKaLXJIjK9nr3x6FZR+IWjtF07S+wdM=
|
||||
github.com/moby/buildkit v0.19.0 h1:w9G1p7sArvCGNkpWstAqJfRQTXBKukMyMK1bsah1HNo=
|
||||
github.com/moby/buildkit v0.19.0/go.mod h1:WiHBFTgWV8eB1AmPxIWsAlKjUACAwm3X/14xOV4VWew=
|
||||
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/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
|
||||
github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
@@ -343,12 +337,12 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4=
|
||||
github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg=
|
||||
github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
@@ -357,12 +351,16 @@ 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/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/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
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/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -375,18 +373,18 @@ github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@@ -395,8 +393,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE=
|
||||
@@ -437,23 +435,27 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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/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/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/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20241028165955-397af5306b5c h1:bQLsfX+uEPZUjyR2qmoJs5F8Z57bPVmF3IsUf22Xo9Y=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20241028165955-397af5306b5c/go.mod h1:Dl/9oEjK7IqnjAm21Okx/XIxUCFJzvh+XdVHUlBwXTw=
|
||||
github.com/tonistiigi/fsutil v0.0.0-20250113203817-b14e27f4135a h1:EfGw4G0x/8qXWgtcZ6KVaPS+wpWOQMaypczzP8ojkMY=
|
||||
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/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE=
|
||||
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/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw=
|
||||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc=
|
||||
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
|
||||
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -464,40 +466,40 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
||||
github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
|
||||
github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
||||
github.com/zclconf/go-cty v1.16.0 h1:xPKEhst+BW5D0wxebMZkxgapvOE/dw7bFTlgSc9nD6w=
|
||||
github.com/zclconf/go-cty v1.16.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 h1:gbhw/u49SS3gkPWiYweQNJGm/uJN5GkI/FrosxSHT7A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1/go.mod h1:GnOaBaFQ2we3b9AGWJpsBa7v1S5RlQzlC3O7dRMxZhM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I=
|
||||
go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4=
|
||||
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q=
|
||||
go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc=
|
||||
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0 h1:4BZHA+B1wXEQoGNHxW8mURaLhcdGwvRnmhGbm+odRbc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.56.0/go.mod h1:3qi2EEwMgB4xnKgPLqsDP3j9qxnHDZeHsnAxfjQqTko=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
|
||||
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/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
|
||||
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/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -508,8 +510,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
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/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -523,18 +525,18 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -547,16 +549,16 @@ 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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.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.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -570,19 +572,17 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
||||
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=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
|
||||
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/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
|
||||
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
|
||||
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
|
||||
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
|
||||
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/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII=
|
||||
@@ -610,21 +610,25 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
|
||||
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
|
||||
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
|
||||
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
|
||||
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
|
||||
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
||||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
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=
|
||||
|
18
hack/Vagrantfile.freebsd
Normal file
18
hack/Vagrantfile.freebsd
Normal file
@@ -0,0 +1,18 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "generic/freebsd14"
|
||||
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
|
||||
pkg bootstrap
|
||||
pkg install -y go123 git
|
||||
ln -s /usr/local/bin/go123 /usr/local/bin/go
|
||||
go install gotest.tools/gotestsum@#{ENV['GOTESTSUM_VERSION']}
|
||||
SHELL
|
||||
end
|
||||
end
|
21
hack/Vagrantfile.openbsd
Normal file
21
hack/Vagrantfile.openbsd
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "pygolo/openbsd7"
|
||||
config.vm.box_version = "7.5"
|
||||
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
|
||||
pkg_add -x git
|
||||
|
||||
ftp https://go.dev/dl/go1.23.3.openbsd-amd64.tar.gz
|
||||
tar -C /usr/local -xzf go1.23.3.openbsd-amd64.tar.gz
|
||||
ln -s /usr/local/go/bin/go /usr/local/bin/go
|
||||
go install gotest.tools/gotestsum@#{ENV['GOTESTSUM_VERSION']}
|
||||
SHELL
|
||||
end
|
||||
end
|
@@ -1,6 +1,6 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG ALPINE_VERSION=3.20
|
||||
ARG ALPINE_VERSION=3.21
|
||||
|
||||
FROM alpine:${ALPINE_VERSION} AS gen
|
||||
RUN apk add --no-cache git
|
||||
|
@@ -1,15 +1,17 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.22
|
||||
ARG GO_VERSION=1.23
|
||||
ARG ALPINE_VERSION=3.21
|
||||
|
||||
ARG FORMATS=md,yaml
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS docsgen
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS docsgen
|
||||
WORKDIR /src
|
||||
RUN --mount=target=. \
|
||||
--mount=target=/root/.cache,type=cache \
|
||||
go build -mod=vendor -o /out/docsgen ./docs/generate.go
|
||||
|
||||
FROM alpine AS gen
|
||||
FROM alpine:${ALPINE_VERSION} AS gen
|
||||
RUN apk add --no-cache rsync git
|
||||
WORKDIR /src
|
||||
COPY --from=docsgen /out/docsgen /usr/bin
|
||||
|
@@ -5,8 +5,8 @@
|
||||
# Copyright The Buildx Authors.
|
||||
# Licensed under the Apache License, Version 2.0
|
||||
|
||||
ARG GO_VERSION="1.22"
|
||||
ARG PROTOC_VERSION="3.11.4"
|
||||
ARG GO_VERSION=1.23
|
||||
ARG PROTOC_VERSION=3.11.4
|
||||
ARG PROTOC_GOOGLEAPIS_VERSION=2af421884dd468d565137215c946ebe4e245ae26
|
||||
|
||||
# protoc is dynamically linked to glibc so can't use alpine base
|
||||
|
@@ -1,10 +1,12 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION="1.22"
|
||||
ARG GOVULNCHECK_VERSION="v1.1.3"
|
||||
ARG GO_VERSION=1.23
|
||||
ARG ALPINE_VERSION=3.21
|
||||
|
||||
ARG GOVULNCHECK_VERSION=v1.1.3
|
||||
ARG FORMAT="text"
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS base
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
WORKDIR /go/src/github.com/docker/buildx
|
||||
RUN apk add --no-cache jq moreutils
|
||||
ARG GOVULNCHECK_VERSION
|
||||
|
@@ -1,16 +1,17 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.22
|
||||
ARG XX_VERSION=1.3.0
|
||||
ARG GOLANGCI_LINT_VERSION=1.57.2
|
||||
ARG GOPLS_VERSION=v0.20.0
|
||||
# disabled: deprecated unusedvariable simplifyrange
|
||||
ARG GOPLS_ANALYZERS="embeddirective fillreturns infertypeargs nonewvars noresultvalues simplifycompositelit simplifyslice stubmethods undeclaredname unusedparams useany"
|
||||
ARG GO_VERSION=1.23
|
||||
ARG ALPINE_VERSION=3.21
|
||||
ARG XX_VERSION=1.6.1
|
||||
|
||||
ARG GOLANGCI_LINT_VERSION=1.62.0
|
||||
ARG GOPLS_VERSION=v0.26.0
|
||||
# disabled: deprecated unusedvariable simplifyrange
|
||||
ARG GOPLS_ANALYZERS="embeddirective fillreturns infertypeargs nonewvars norangeoverfunc noresultvalues simplifycompositelit simplifyslice undeclaredname unusedparams useany"
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS golang-base
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS golang-base
|
||||
RUN apk add --no-cache git gcc musl-dev
|
||||
|
||||
FROM golang-base AS lint-base
|
||||
@@ -75,4 +76,4 @@ RUN --mount=target=. \
|
||||
done
|
||||
EOF
|
||||
|
||||
FROM lint
|
||||
FROM lint
|
||||
|
@@ -1,9 +1,11 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
ARG GO_VERSION=1.22
|
||||
ARG GO_VERSION=1.23
|
||||
ARG ALPINE_VERSION=3.21
|
||||
|
||||
ARG MODOUTDATED_VERSION=v0.9.0
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine AS base
|
||||
FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
|
||||
RUN apk add --no-cache git rsync
|
||||
WORKDIR /src
|
||||
|
||||
|
@@ -167,7 +167,8 @@ buildxCmd bake ${bakePlatformFlag} \
|
||||
--file="${bakedef}" \
|
||||
--builder="${builderName}" \
|
||||
--set "*.context=${context}" \
|
||||
--metadata-file="${context}/metadata-bake-def.json"
|
||||
--metadata-file="${context}/metadata-bake-def.json" \
|
||||
--allow fs="${context}"
|
||||
cat "${context}/metadata-bake-def.json"
|
||||
|
||||
# bake all target
|
||||
@@ -175,6 +176,7 @@ buildxCmd bake ${bakePlatformFlag} \
|
||||
--file="${bakedef}" \
|
||||
--builder="${builderName}" \
|
||||
--set "*.context=${context}" \
|
||||
--allow fs="${context}" \
|
||||
--metadata-file="${context}/metadata-bake-all.json" \
|
||||
all
|
||||
cat "${context}/metadata-bake-all.json"
|
||||
|
@@ -326,7 +326,7 @@ func (m *monitor) invoke(ctx context.Context, pid string, cfg *controllerapi.Inv
|
||||
if m.AttachedSessionID() == "" {
|
||||
return nil
|
||||
}
|
||||
invokeCtx, invokeCancel := context.WithCancel(ctx)
|
||||
invokeCtx, invokeCancel := context.WithCancelCause(ctx)
|
||||
|
||||
containerIn, containerOut := ioset.Pipe()
|
||||
m.invokeIO.SetOut(&containerOut)
|
||||
@@ -336,7 +336,7 @@ func (m *monitor) invoke(ctx context.Context, pid string, cfg *controllerapi.Inv
|
||||
cancelOnce.Do(func() {
|
||||
containerIn.Close()
|
||||
m.invokeIO.SetOut(nil)
|
||||
invokeCancel()
|
||||
invokeCancel(errors.WithStack(context.Canceled))
|
||||
})
|
||||
<-waitInvokeDoneCh
|
||||
}
|
||||
|
@@ -17,13 +17,13 @@ func TestNodeGroupUpdate(t *testing.T) {
|
||||
err = ng.Update("foo1", "foo1", []string{"linux/arm64", "linux/arm/v7"}, true, true, nil, "", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(ng.Nodes), 2)
|
||||
require.Equal(t, 2, len(ng.Nodes))
|
||||
|
||||
// update
|
||||
err = ng.Update("foo", "foo2", []string{"linux/amd64", "linux/arm"}, true, false, nil, "", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(ng.Nodes), 2)
|
||||
require.Equal(t, 2, len(ng.Nodes))
|
||||
require.Equal(t, []string{"linux/amd64", "linux/arm/v7"}, platformutil.Format(ng.Nodes[0].Platforms))
|
||||
require.Equal(t, []string{"linux/arm64"}, platformutil.Format(ng.Nodes[1].Platforms))
|
||||
|
||||
@@ -39,6 +39,6 @@ func TestNodeGroupUpdate(t *testing.T) {
|
||||
err = ng.Leave("foo")
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(ng.Nodes), 1)
|
||||
require.Equal(t, 1, len(ng.Nodes))
|
||||
require.Equal(t, []string{"linux/arm64"}, platformutil.Format(ng.Nodes[0].Platforms))
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/docker/buildx/util/confutil"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -44,7 +45,7 @@ func TestNodeLocking(t *testing.T) {
|
||||
|
||||
go func() {
|
||||
_, release, err := s.Txn()
|
||||
require.NoError(t, err)
|
||||
assert.NoError(t, err)
|
||||
release()
|
||||
close(ready)
|
||||
}()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user