mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-08-23 20:19:08 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
89154c7d33 | ||
![]() |
1a03187338 | ||
![]() |
b3e7827871 | ||
![]() |
d43cf8c2c6 | ||
![]() |
c954a45352 | ||
![]() |
ce521a3a85 | ||
![]() |
29f879990e | ||
![]() |
5e3bf8713a | ||
![]() |
57f7f1becc | ||
![]() |
091214d59d | ||
![]() |
5f26f514a1 | ||
![]() |
454306a8ef | ||
![]() |
30feaa1a91 | ||
![]() |
8fb1163577 | ||
![]() |
b68ee824c6 | ||
![]() |
2175f9ec7c | ||
![]() |
ba1ee7af6e | ||
![]() |
565b0b8991 | ||
![]() |
a494e9ccc4 | ||
![]() |
542e5d810e | ||
![]() |
89fb005922 | ||
![]() |
d353f6c426 | ||
![]() |
2271096e46 | ||
![]() |
95062ce8df | ||
![]() |
255aff71fb |
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -5,11 +5,6 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
ignore:
|
|
||||||
# ignore this dependency
|
|
||||||
# it seems a bug with dependabot as pining to commit sha should not
|
|
||||||
# trigger a new version: https://github.com/docker/buildx/pull/2222#issuecomment-1919092153
|
|
||||||
- dependency-name: "docker/docs"
|
|
||||||
labels:
|
labels:
|
||||||
- "dependencies"
|
- "dependencies"
|
||||||
- "bot"
|
- "bot"
|
||||||
|
220
.github/workflows/build.yml
vendored
220
.github/workflows/build.yml
vendored
@@ -24,70 +24,57 @@ env:
|
|||||||
REPO_SLUG: "docker/buildx-bin"
|
REPO_SLUG: "docker/buildx-bin"
|
||||||
DESTDIR: "./bin"
|
DESTDIR: "./bin"
|
||||||
TEST_CACHE_SCOPE: "test"
|
TEST_CACHE_SCOPE: "test"
|
||||||
TESTFLAGS: "-v --parallel=6 --timeout=30m"
|
|
||||||
GOTESTSUM_FORMAT: "standard-verbose"
|
|
||||||
GO_VERSION: "1.21"
|
|
||||||
GOTESTSUM_VERSION: "v1.9.0" # same as one in Dockerfile
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test-integration:
|
prepare-test:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.BUILDX_VERSION }}
|
||||||
|
driver-opts: image=${{ env.BUILDKIT_IMAGE }}
|
||||||
|
buildkitd-flags: --debug
|
||||||
|
-
|
||||||
|
name: Build
|
||||||
|
uses: docker/bake-action@v4
|
||||||
|
with:
|
||||||
|
targets: integration-test-base
|
||||||
|
set: |
|
||||||
|
*.cache-from=type=gha,scope=${{ env.TEST_CACHE_SCOPE }}
|
||||||
|
*.cache-to=type=gha,scope=${{ env.TEST_CACHE_SCOPE }}
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
needs:
|
||||||
|
- prepare-test
|
||||||
env:
|
env:
|
||||||
|
TESTFLAGS: "-v --parallel=6 --timeout=30m"
|
||||||
TESTFLAGS_DOCKER: "-v --parallel=1 --timeout=30m"
|
TESTFLAGS_DOCKER: "-v --parallel=1 --timeout=30m"
|
||||||
|
GOTESTSUM_FORMAT: "standard-verbose"
|
||||||
TEST_IMAGE_BUILD: "0"
|
TEST_IMAGE_BUILD: "0"
|
||||||
TEST_IMAGE_ID: "buildx-tests"
|
TEST_IMAGE_ID: "buildx-tests"
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
buildkit:
|
|
||||||
- master
|
|
||||||
- latest
|
|
||||||
- buildx-stable-1
|
|
||||||
- v0.13.1
|
|
||||||
- v0.12.5
|
|
||||||
- v0.11.6
|
|
||||||
worker:
|
worker:
|
||||||
|
- docker
|
||||||
|
- docker\+containerd # same as docker, but with containerd snapshotter
|
||||||
- docker-container
|
- docker-container
|
||||||
- remote
|
- remote
|
||||||
pkg:
|
pkg:
|
||||||
- ./tests
|
- ./tests
|
||||||
mode:
|
|
||||||
- ""
|
|
||||||
- experimental
|
|
||||||
include:
|
include:
|
||||||
- worker: docker
|
- pkg: ./...
|
||||||
pkg: ./tests
|
skip-integration-tests: 1
|
||||||
- worker: docker+containerd # same as docker, but with containerd snapshotter
|
|
||||||
pkg: ./tests
|
|
||||||
- worker: docker
|
|
||||||
pkg: ./tests
|
|
||||||
mode: experimental
|
|
||||||
- worker: docker+containerd # same as docker, but with containerd snapshotter
|
|
||||||
pkg: ./tests
|
|
||||||
mode: experimental
|
|
||||||
steps:
|
steps:
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
echo "TESTREPORTS_NAME=${{ github.job }}-$(echo "${{ matrix.pkg }}-${{ matrix.buildkit }}-${{ matrix.worker }}-${{ matrix.mode }}" | tr -dc '[:alnum:]-\n\r' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
|
|
||||||
if [ -n "${{ matrix.buildkit }}" ]; then
|
|
||||||
echo "TEST_BUILDKIT_TAG=${{ matrix.buildkit }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
testFlags="--run=//worker=$(echo "${{ matrix.worker }}" | sed 's/\+/\\+/g')$"
|
|
||||||
case "${{ matrix.worker }}" in
|
|
||||||
docker | docker+containerd)
|
|
||||||
echo "TESTFLAGS=${{ env.TESTFLAGS_DOCKER }} $testFlags" >> $GITHUB_ENV
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "TESTFLAGS=${{ env.TESTFLAGS }} $testFlags" >> $GITHUB_ENV
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
if [[ "${{ matrix.worker }}" == "docker"* ]]; then
|
|
||||||
echo "TEST_DOCKERD=1" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
if [ "${{ matrix.mode }}" = "experimental" ]; then
|
|
||||||
echo "TEST_BUILDX_EXPERIMENTAL=1" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -109,110 +96,40 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
targets: integration-test
|
targets: integration-test
|
||||||
set: |
|
set: |
|
||||||
|
*.cache-from=type=gha,scope=${{ env.TEST_CACHE_SCOPE }}
|
||||||
*.output=type=docker,name=${{ env.TEST_IMAGE_ID }}
|
*.output=type=docker,name=${{ env.TEST_IMAGE_ID }}
|
||||||
-
|
-
|
||||||
name: Test
|
name: Test
|
||||||
run: |
|
run: |
|
||||||
|
export TEST_REPORT_SUFFIX=-${{ github.job }}-$(echo "${{ matrix.pkg }}-${{ matrix.skip-integration-tests }}-${{ matrix.worker }}" | tr -dc '[:alnum:]-\n\r' | tr '[:upper:]' '[:lower:]')
|
||||||
./hack/test
|
./hack/test
|
||||||
env:
|
env:
|
||||||
TEST_REPORT_SUFFIX: "-${{ env.TESTREPORTS_NAME }}"
|
TEST_DOCKERD: "${{ startsWith(matrix.worker, 'docker') && '1' || '0' }}"
|
||||||
|
TESTFLAGS: "${{ (matrix.worker == 'docker' || matrix.worker == 'docker\\+containerd') && env.TESTFLAGS_DOCKER || env.TESTFLAGS }} --run=//worker=${{ matrix.worker }}$"
|
||||||
TESTPKGS: "${{ matrix.pkg }}"
|
TESTPKGS: "${{ matrix.pkg }}"
|
||||||
|
SKIP_INTEGRATION_TESTS: "${{ matrix.skip-integration-tests }}"
|
||||||
-
|
-
|
||||||
name: Send to Codecov
|
name: Send to Codecov
|
||||||
if: always()
|
if: always()
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
directory: ./bin/testreports
|
directory: ./bin/testreports
|
||||||
flags: integration
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
-
|
-
|
||||||
name: Generate annotations
|
name: Generate annotations
|
||||||
if: always()
|
if: always()
|
||||||
uses: crazy-max/.github/.github/actions/gotest-annotations@fa6141aedf23596fb8bdcceab9cce8dadaa31bd9
|
uses: crazy-max/.github/.github/actions/gotest-annotations@1a64ea6d01db9a48aa61954cb20e265782c167d9
|
||||||
with:
|
with:
|
||||||
directory: ./bin/testreports
|
directory: ./bin/testreports
|
||||||
-
|
-
|
||||||
name: Upload test reports
|
name: Upload test reports
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: test-reports-${{ env.TESTREPORTS_NAME }}
|
name: test-reports
|
||||||
path: ./bin/testreports
|
path: ./bin/testreports
|
||||||
|
|
||||||
test-unit:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os:
|
|
||||||
- ubuntu-24.04
|
|
||||||
- macos-12
|
|
||||||
- windows-2022
|
|
||||||
env:
|
|
||||||
SKIP_INTEGRATION_TESTS: 1
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: "${{ env.GO_VERSION }}"
|
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
testreportsName=${{ github.job }}--${{ matrix.os }}
|
|
||||||
testreportsBaseDir=./bin/testreports
|
|
||||||
testreportsDir=$testreportsBaseDir/$testreportsName
|
|
||||||
echo "TESTREPORTS_NAME=$testreportsName" >> $GITHUB_ENV
|
|
||||||
echo "TESTREPORTS_BASEDIR=$testreportsBaseDir" >> $GITHUB_ENV
|
|
||||||
echo "TESTREPORTS_DIR=$testreportsDir" >> $GITHUB_ENV
|
|
||||||
mkdir -p $testreportsDir
|
|
||||||
shell: bash
|
|
||||||
-
|
|
||||||
name: Install gotestsum
|
|
||||||
run: |
|
|
||||||
go install gotest.tools/gotestsum@${{ env.GOTESTSUM_VERSION }}
|
|
||||||
-
|
|
||||||
name: Test
|
|
||||||
env:
|
|
||||||
TMPDIR: ${{ runner.temp }}
|
|
||||||
run: |
|
|
||||||
gotestsum \
|
|
||||||
--jsonfile="${{ env.TESTREPORTS_DIR }}/go-test-report.json" \
|
|
||||||
--junitfile="${{ env.TESTREPORTS_DIR }}/junit-report.xml" \
|
|
||||||
--packages="./..." \
|
|
||||||
-- \
|
|
||||||
"-mod=vendor" \
|
|
||||||
"-coverprofile" "${{ env.TESTREPORTS_DIR }}/coverage.txt" \
|
|
||||||
"-covermode" "atomic" ${{ env.TESTFLAGS }}
|
|
||||||
shell: bash
|
|
||||||
-
|
|
||||||
name: Send to Codecov
|
|
||||||
if: always()
|
|
||||||
uses: codecov/codecov-action@v4
|
|
||||||
with:
|
|
||||||
directory: ${{ env.TESTREPORTS_DIR }}
|
|
||||||
env_vars: RUNNER_OS
|
|
||||||
flags: unit
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Generate annotations
|
|
||||||
if: always()
|
|
||||||
uses: crazy-max/.github/.github/actions/gotest-annotations@fa6141aedf23596fb8bdcceab9cce8dadaa31bd9
|
|
||||||
with:
|
|
||||||
directory: ${{ env.TESTREPORTS_DIR }}
|
|
||||||
-
|
|
||||||
name: Upload test reports
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: test-reports-${{ env.TESTREPORTS_NAME }}
|
|
||||||
path: ${{ env.TESTREPORTS_BASEDIR }}
|
|
||||||
|
|
||||||
prepare-binaries:
|
prepare-binaries:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
outputs:
|
outputs:
|
||||||
matrix: ${{ steps.platforms.outputs.matrix }}
|
matrix: ${{ steps.platforms.outputs.matrix }}
|
||||||
steps:
|
steps:
|
||||||
@@ -230,7 +147,7 @@ jobs:
|
|||||||
echo ${{ steps.platforms.outputs.matrix }}
|
echo ${{ steps.platforms.outputs.matrix }}
|
||||||
|
|
||||||
binaries:
|
binaries:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
needs:
|
needs:
|
||||||
- prepare-binaries
|
- prepare-binaries
|
||||||
strategy:
|
strategy:
|
||||||
@@ -266,17 +183,16 @@ jobs:
|
|||||||
CACHE_TO: type=gha,scope=binaries-${{ env.PLATFORM_PAIR }},mode=max
|
CACHE_TO: type=gha,scope=binaries-${{ env.PLATFORM_PAIR }},mode=max
|
||||||
-
|
-
|
||||||
name: Upload artifacts
|
name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: buildx-${{ env.PLATFORM_PAIR }}
|
name: buildx
|
||||||
path: ${{ env.DESTDIR }}/*
|
path: ${{ env.DESTDIR }}/*
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
bin-image:
|
bin-image:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
needs:
|
needs:
|
||||||
- test-integration
|
- test
|
||||||
- test-unit
|
|
||||||
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }}
|
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }}
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
@@ -326,10 +242,9 @@ jobs:
|
|||||||
*.cache-to=type=gha,scope=bin-image,mode=max
|
*.cache-to=type=gha,scope=bin-image,mode=max
|
||||||
|
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
needs:
|
needs:
|
||||||
- test-integration
|
- test
|
||||||
- test-unit
|
|
||||||
- binaries
|
- binaries
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
@@ -337,11 +252,10 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Download binaries
|
name: Download binaries
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
name: buildx
|
||||||
path: ${{ env.DESTDIR }}
|
path: ${{ env.DESTDIR }}
|
||||||
pattern: buildx-*
|
|
||||||
merge-multiple: true
|
|
||||||
-
|
-
|
||||||
name: Create checksums
|
name: Create checksums
|
||||||
run: ./hack/hash-files
|
run: ./hack/hash-files
|
||||||
@@ -356,9 +270,33 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: GitHub Release
|
name: GitHub Release
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
uses: softprops/action-gh-release@69320dbe05506a9a39fc8ae11030b214ec2d1f87 # v2.0.5
|
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
files: ${{ env.DESTDIR }}/*
|
files: ${{ env.DESTDIR }}/*
|
||||||
|
|
||||||
|
buildkit-edge:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
continue-on-error: true
|
||||||
|
steps:
|
||||||
|
-
|
||||||
|
name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
-
|
||||||
|
name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
-
|
||||||
|
name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
version: ${{ env.BUILDX_VERSION }}
|
||||||
|
driver-opts: image=moby/buildkit:master
|
||||||
|
buildkitd-flags: --debug
|
||||||
|
-
|
||||||
|
# Just run a bake target to check eveything runs fine
|
||||||
|
name: Build
|
||||||
|
uses: docker/bake-action@v4
|
||||||
|
with:
|
||||||
|
targets: binaries
|
||||||
|
12
.github/workflows/codeql.yml
vendored
12
.github/workflows/codeql.yml
vendored
@@ -13,30 +13,30 @@ permissions:
|
|||||||
security-events: write
|
security-events: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.21"
|
GO_VERSION: 1.21.6
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
codeql:
|
codeql:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
-
|
-
|
||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
-
|
-
|
||||||
name: Initialize CodeQL
|
name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: go
|
languages: go
|
||||||
-
|
-
|
||||||
name: Autobuild
|
name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v3
|
uses: github/codeql-action/autobuild@v2
|
||||||
-
|
-
|
||||||
name: Perform CodeQL Analysis
|
name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v2
|
||||||
with:
|
with:
|
||||||
category: "/language:go"
|
category: "/language:go"
|
||||||
|
41
.github/workflows/docs-release.yml
vendored
41
.github/workflows/docs-release.yml
vendored
@@ -1,19 +1,14 @@
|
|||||||
name: docs-release
|
name: docs-release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
tag:
|
|
||||||
description: 'Git tag'
|
|
||||||
required: true
|
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
- released
|
- released
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
open-pr:
|
open-pr:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
if: ${{ (github.event.release.prerelease != true || github.event.inputs.tag != '') && github.repository == 'docker/buildx' }}
|
if: ${{ github.event.release.prerelease != true && github.repository == 'docker/buildx' }}
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout docs repo
|
name: Checkout docs repo
|
||||||
@@ -25,47 +20,39 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Prepare
|
name: Prepare
|
||||||
run: |
|
run: |
|
||||||
rm -rf ./data/buildx/*
|
rm -rf ./_data/buildx/*
|
||||||
if [ -n "${{ github.event.inputs.tag }}" ]; then
|
|
||||||
echo "RELEASE_NAME=${{ github.event.inputs.tag }}" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "RELEASE_NAME=${{ github.event.release.name }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
-
|
-
|
||||||
name: Generate yaml
|
name: Build docs
|
||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v4
|
||||||
with:
|
with:
|
||||||
source: ${{ github.server_url }}/${{ github.repository }}.git#${{ env.RELEASE_NAME }}
|
source: ${{ github.server_url }}/${{ github.repository }}.git#${{ github.event.release.name }}
|
||||||
targets: update-docs
|
targets: update-docs
|
||||||
provenance: false
|
|
||||||
set: |
|
set: |
|
||||||
*.output=/tmp/buildx-docs
|
*.output=/tmp/buildx-docs
|
||||||
env:
|
env:
|
||||||
DOCS_FORMATS: yaml
|
DOCS_FORMATS: yaml
|
||||||
-
|
-
|
||||||
name: Copy yaml
|
name: Copy files
|
||||||
run: |
|
run: |
|
||||||
cp /tmp/buildx-docs/out/reference/*.yaml ./data/buildx/
|
cp /tmp/buildx-docs/out/reference/*.yaml ./_data/buildx/
|
||||||
-
|
-
|
||||||
name: Update vendor
|
name: Commit changes
|
||||||
run: |
|
run: |
|
||||||
make vendor
|
git add -A .
|
||||||
env:
|
|
||||||
VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }}
|
|
||||||
-
|
-
|
||||||
name: Create PR on docs repo
|
name: Create PR on docs repo
|
||||||
uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5
|
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
|
||||||
push-to-fork: docker-tools-robot/docker.github.io
|
push-to-fork: docker-tools-robot/docker.github.io
|
||||||
commit-message: "vendor: github.com/docker/buildx ${{ env.RELEASE_NAME }}"
|
commit-message: "build: update buildx reference to ${{ github.event.release.name }}"
|
||||||
signoff: true
|
signoff: true
|
||||||
branch: dispatch/buildx-ref-${{ env.RELEASE_NAME }}
|
branch: dispatch/buildx-ref-${{ github.event.release.name }}
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
title: Update buildx reference to ${{ env.RELEASE_NAME }}
|
title: Update buildx reference to ${{ github.event.release.name }}
|
||||||
body: |
|
body: |
|
||||||
Update the buildx reference documentation to keep in sync with the latest release `${{ env.RELEASE_NAME }}`
|
Update the buildx reference documentation to keep in sync with the latest release `${{ github.event.release.name }}`
|
||||||
draft: false
|
draft: false
|
||||||
|
9
.github/workflows/docs-upstream.yml
vendored
9
.github/workflows/docs-upstream.yml
vendored
@@ -22,7 +22,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docs-yaml:
|
docs-yaml:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
@@ -37,7 +37,6 @@ jobs:
|
|||||||
uses: docker/bake-action@v4
|
uses: docker/bake-action@v4
|
||||||
with:
|
with:
|
||||||
targets: update-docs
|
targets: update-docs
|
||||||
provenance: false
|
|
||||||
set: |
|
set: |
|
||||||
*.output=/tmp/buildx-docs
|
*.output=/tmp/buildx-docs
|
||||||
*.cache-from=type=gha,scope=docs-yaml
|
*.cache-from=type=gha,scope=docs-yaml
|
||||||
@@ -46,18 +45,18 @@ jobs:
|
|||||||
DOCS_FORMATS: yaml
|
DOCS_FORMATS: yaml
|
||||||
-
|
-
|
||||||
name: Upload reference YAML docs
|
name: Upload reference YAML docs
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: docs-yaml
|
name: docs-yaml
|
||||||
path: /tmp/buildx-docs/out/reference
|
path: /tmp/buildx-docs/out/reference
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
validate:
|
validate:
|
||||||
uses: docker/docs/.github/workflows/validate-upstream.yml@6b73b05acb21edf7995cc5b3c6672d8e314cee7a # pin for artifact v4 support: https://github.com/docker/docs/pull/19220
|
uses: docker/docs/.github/workflows/validate-upstream.yml@e864c797e391fa35aecf1ddcecdbdeb683546424 # pin for artifact v3 support
|
||||||
needs:
|
needs:
|
||||||
- docs-yaml
|
- docs-yaml
|
||||||
with:
|
with:
|
||||||
module-name: docker/buildx
|
module-name: docker/buildx
|
||||||
data-files-id: docs-yaml
|
data-files-id: docs-yaml
|
||||||
data-files-folder: buildx
|
data-files-folder: buildx
|
||||||
create-placeholder-stubs: true
|
data-files-placeholder-folder: engine/reference/commandline
|
||||||
|
73
.github/workflows/e2e.yml
vendored
73
.github/workflows/e2e.yml
vendored
@@ -22,7 +22,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
mv ${{ env.DESTDIR }}/build/buildx ${{ env.DESTDIR }}/build/docker-buildx
|
mv ${{ env.DESTDIR }}/build/buildx ${{ env.DESTDIR }}/build/docker-buildx
|
||||||
-
|
-
|
||||||
name: Upload artifacts
|
name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: binary
|
name: binary
|
||||||
path: ${{ env.DESTDIR }}/build
|
path: ${{ env.DESTDIR }}/build
|
||||||
@@ -82,8 +82,6 @@ jobs:
|
|||||||
driver-opt: qemu.install=true
|
driver-opt: qemu.install=true
|
||||||
- driver: remote
|
- driver: remote
|
||||||
endpoint: tcp://localhost:1234
|
endpoint: tcp://localhost:1234
|
||||||
- driver: docker-container
|
|
||||||
metadata-provenance: max
|
|
||||||
exclude:
|
exclude:
|
||||||
- driver: docker
|
- driver: docker
|
||||||
multi-node: mnode-true
|
multi-node: mnode-true
|
||||||
@@ -105,7 +103,7 @@ jobs:
|
|||||||
if: matrix.driver == 'docker' || matrix.driver == 'docker-container'
|
if: matrix.driver == 'docker' || matrix.driver == 'docker-container'
|
||||||
-
|
-
|
||||||
name: Install buildx
|
name: Install buildx
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: binary
|
name: binary
|
||||||
path: /home/runner/.docker/cli-plugins
|
path: /home/runner/.docker/cli-plugins
|
||||||
@@ -131,15 +129,70 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "MULTI_NODE=0" >> $GITHUB_ENV
|
echo "MULTI_NODE=0" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
if [ -n "${{ matrix.metadata-provenance }}" ]; then
|
|
||||||
echo "BUILDX_METADATA_PROVENANCE=${{ matrix.metadata-provenance }}" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
-
|
-
|
||||||
name: Install k3s
|
name: Install k3s
|
||||||
if: matrix.driver == 'kubernetes'
|
if: matrix.driver == 'kubernetes'
|
||||||
uses: crazy-max/.github/.github/actions/install-k3s@fa6141aedf23596fb8bdcceab9cce8dadaa31bd9
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
version: ${{ env.K3S_VERSION }}
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
let wait = function(milliseconds) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (typeof(milliseconds) !== 'number') {
|
||||||
|
throw new Error('milleseconds not a number');
|
||||||
|
}
|
||||||
|
setTimeout(() => resolve("done!"), milliseconds)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const kubeconfig="/tmp/buildkit-k3s/kubeconfig.yaml";
|
||||||
|
core.info(`storing kubeconfig in ${kubeconfig}`);
|
||||||
|
|
||||||
|
await exec.exec('docker', ["run", "-d",
|
||||||
|
"--privileged",
|
||||||
|
"--name=buildkit-k3s",
|
||||||
|
"-e", "K3S_KUBECONFIG_OUTPUT="+kubeconfig,
|
||||||
|
"-e", "K3S_KUBECONFIG_MODE=666",
|
||||||
|
"-v", "/tmp/buildkit-k3s:/tmp/buildkit-k3s",
|
||||||
|
"-p", "6443:6443",
|
||||||
|
"-p", "80:80",
|
||||||
|
"-p", "443:443",
|
||||||
|
"-p", "8080:8080",
|
||||||
|
"rancher/k3s:${{ env.K3S_VERSION }}", "server"
|
||||||
|
]);
|
||||||
|
await wait(10000);
|
||||||
|
|
||||||
|
core.exportVariable('KUBECONFIG', kubeconfig);
|
||||||
|
|
||||||
|
let nodeName;
|
||||||
|
for (let count = 1; count <= 5; count++) {
|
||||||
|
try {
|
||||||
|
const nodeNameOutput = await exec.getExecOutput("kubectl get nodes --no-headers -oname");
|
||||||
|
nodeName = nodeNameOutput.stdout
|
||||||
|
} catch (error) {
|
||||||
|
core.info(`Unable to resolve node name (${error.message}). Attempt ${count} of 5.`)
|
||||||
|
} finally {
|
||||||
|
if (nodeName) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await wait(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!nodeName) {
|
||||||
|
throw new Error(`Unable to resolve node name after 5 attempts.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await exec.exec(`kubectl wait --for=condition=Ready ${nodeName}`);
|
||||||
|
} catch (error) {
|
||||||
|
core.setFailed(error.message);
|
||||||
|
}
|
||||||
|
-
|
||||||
|
name: Print KUBECONFIG
|
||||||
|
if: matrix.driver == 'kubernetes'
|
||||||
|
run: |
|
||||||
|
yq ${{ env.KUBECONFIG }}
|
||||||
-
|
-
|
||||||
name: Launch remote buildkitd
|
name: Launch remote buildkitd
|
||||||
if: matrix.driver == 'remote'
|
if: matrix.driver == 'remote'
|
||||||
|
74
.github/workflows/validate.yml
vendored
74
.github/workflows/validate.yml
vendored
@@ -17,70 +17,17 @@ on:
|
|||||||
- '.github/releases.json'
|
- '.github/releases.json'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
outputs:
|
|
||||||
includes: ${{ steps.matrix.outputs.includes }}
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Matrix
|
|
||||||
id: matrix
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
let def = {};
|
|
||||||
await core.group(`Parsing definition`, async () => {
|
|
||||||
const printEnv = Object.assign({}, process.env, {
|
|
||||||
GOLANGCI_LINT_MULTIPLATFORM: process.env.GITHUB_REPOSITORY === 'docker/buildx' ? '1' : ''
|
|
||||||
});
|
|
||||||
const resPrint = await exec.getExecOutput('docker', ['buildx', 'bake', 'validate', '--print'], {
|
|
||||||
ignoreReturnCode: true,
|
|
||||||
env: printEnv
|
|
||||||
});
|
|
||||||
if (resPrint.stderr.length > 0 && resPrint.exitCode != 0) {
|
|
||||||
throw new Error(res.stderr);
|
|
||||||
}
|
|
||||||
def = JSON.parse(resPrint.stdout.trim());
|
|
||||||
});
|
|
||||||
await core.group(`Generating matrix`, async () => {
|
|
||||||
const includes = [];
|
|
||||||
for (const targetName of Object.keys(def.target)) {
|
|
||||||
const target = def.target[targetName];
|
|
||||||
if (target.platforms && target.platforms.length > 0) {
|
|
||||||
target.platforms.forEach(platform => {
|
|
||||||
includes.push({
|
|
||||||
target: targetName,
|
|
||||||
platform: platform
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
includes.push({
|
|
||||||
target: targetName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
core.info(JSON.stringify(includes, null, 2));
|
|
||||||
core.setOutput('includes', JSON.stringify(includes));
|
|
||||||
});
|
|
||||||
|
|
||||||
validate:
|
validate:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
needs:
|
|
||||||
- prepare
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include: ${{ fromJson(needs.prepare.outputs.includes) }}
|
target:
|
||||||
|
- lint
|
||||||
|
- validate-vendor
|
||||||
|
- validate-docs
|
||||||
|
- validate-generated-files
|
||||||
steps:
|
steps:
|
||||||
-
|
|
||||||
name: Prepare
|
|
||||||
run: |
|
|
||||||
if [ "$GITHUB_REPOSITORY" = "docker/buildx" ]; then
|
|
||||||
echo "GOLANGCI_LINT_MULTIPLATFORM=1" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -90,9 +37,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
-
|
-
|
||||||
name: Validate
|
name: Run
|
||||||
uses: docker/bake-action@v4
|
run: |
|
||||||
with:
|
make ${{ matrix.target }}
|
||||||
targets: ${{ matrix.target }}
|
|
||||||
set: |
|
|
||||||
*.platform=${{ matrix.platform }}
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
run:
|
run:
|
||||||
timeout: 30m
|
timeout: 10m
|
||||||
skip-files:
|
skip-files:
|
||||||
- ".*\\.pb\\.go$"
|
- ".*\\.pb\\.go$"
|
||||||
|
|
||||||
@@ -25,14 +25,6 @@ linters:
|
|||||||
disable-all: true
|
disable-all: true
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
govet:
|
|
||||||
enable:
|
|
||||||
- nilness
|
|
||||||
- unusedwrite
|
|
||||||
# enable-all: true
|
|
||||||
# disable:
|
|
||||||
# - fieldalignment
|
|
||||||
# - shadow
|
|
||||||
depguard:
|
depguard:
|
||||||
rules:
|
rules:
|
||||||
main:
|
main:
|
||||||
|
41
Dockerfile
41
Dockerfile
@@ -1,22 +1,17 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
ARG GO_VERSION=1.21
|
ARG GO_VERSION=1.21.6
|
||||||
ARG XX_VERSION=1.4.0
|
ARG XX_VERSION=1.2.1
|
||||||
|
|
||||||
# for testing
|
ARG DOCKER_VERSION=24.0.6
|
||||||
ARG DOCKER_VERSION=26.0.0
|
|
||||||
ARG GOTESTSUM_VERSION=v1.9.0
|
ARG GOTESTSUM_VERSION=v1.9.0
|
||||||
ARG REGISTRY_VERSION=2.8.0
|
ARG REGISTRY_VERSION=2.8.0
|
||||||
ARG BUILDKIT_VERSION=v0.13.1
|
ARG BUILDKIT_VERSION=v0.11.6
|
||||||
ARG UNDOCK_VERSION=0.7.0
|
|
||||||
|
|
||||||
|
# xx is a helper for cross-compilation
|
||||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||||
|
|
||||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS golatest
|
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS golatest
|
||||||
FROM moby/moby-bin:$DOCKER_VERSION AS docker-engine
|
|
||||||
FROM dockereng/cli-bin:$DOCKER_VERSION AS docker-cli
|
|
||||||
FROM registry:$REGISTRY_VERSION AS registry
|
|
||||||
FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit
|
|
||||||
FROM crazymax/undock:$UNDOCK_VERSION AS undock
|
|
||||||
|
|
||||||
FROM golatest AS gobase
|
FROM golatest AS gobase
|
||||||
COPY --from=xx / /
|
COPY --from=xx / /
|
||||||
@@ -25,6 +20,26 @@ ENV GOFLAGS=-mod=vendor
|
|||||||
ENV CGO_ENABLED=0
|
ENV CGO_ENABLED=0
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
|
FROM registry:$REGISTRY_VERSION AS registry
|
||||||
|
|
||||||
|
FROM moby/buildkit:$BUILDKIT_VERSION AS buildkit
|
||||||
|
|
||||||
|
FROM gobase AS docker
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
ARG DOCKER_VERSION
|
||||||
|
WORKDIR /opt/docker
|
||||||
|
RUN DOCKER_ARCH=$(case ${TARGETPLATFORM:-linux/amd64} in \
|
||||||
|
"linux/amd64") echo "x86_64" ;; \
|
||||||
|
"linux/arm/v6") echo "armel" ;; \
|
||||||
|
"linux/arm/v7") echo "armhf" ;; \
|
||||||
|
"linux/arm64") echo "aarch64" ;; \
|
||||||
|
"linux/ppc64le") echo "ppc64le" ;; \
|
||||||
|
"linux/s390x") echo "s390x" ;; \
|
||||||
|
*) echo "" ;; esac) \
|
||||||
|
&& echo "DOCKER_ARCH=$DOCKER_ARCH" \
|
||||||
|
&& wget -qO- "https://download.docker.com/linux/static/stable/${DOCKER_ARCH}/docker-${DOCKER_VERSION}.tgz" | tar xvz --strip 1
|
||||||
|
RUN ./dockerd --version && ./containerd --version && ./ctr --version && ./runc --version
|
||||||
|
|
||||||
FROM gobase AS gotestsum
|
FROM gobase AS gotestsum
|
||||||
ARG GOTESTSUM_VERSION
|
ARG GOTESTSUM_VERSION
|
||||||
ENV GOFLAGS=
|
ENV GOFLAGS=
|
||||||
@@ -90,11 +105,9 @@ RUN apk add --no-cache \
|
|||||||
xz
|
xz
|
||||||
COPY --link --from=gotestsum /out/gotestsum /usr/bin/
|
COPY --link --from=gotestsum /out/gotestsum /usr/bin/
|
||||||
COPY --link --from=registry /bin/registry /usr/bin/
|
COPY --link --from=registry /bin/registry /usr/bin/
|
||||||
COPY --link --from=docker-engine / /usr/bin/
|
COPY --link --from=docker /opt/docker/* /usr/bin/
|
||||||
COPY --link --from=docker-cli / /usr/bin/
|
|
||||||
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
|
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
|
||||||
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
|
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
|
||||||
COPY --link --from=undock /usr/local/bin/undock /usr/bin/
|
|
||||||
COPY --link --from=binaries /buildx /usr/bin/
|
COPY --link --from=binaries /buildx /usr/bin/
|
||||||
|
|
||||||
FROM integration-test-base AS integration-test
|
FROM integration-test-base AS integration-test
|
||||||
|
@@ -153,7 +153,6 @@ made through a pull request.
|
|||||||
"akihirosuda",
|
"akihirosuda",
|
||||||
"crazy-max",
|
"crazy-max",
|
||||||
"jedevc",
|
"jedevc",
|
||||||
"jsternberg",
|
|
||||||
"tiborvass",
|
"tiborvass",
|
||||||
"tonistiigi",
|
"tonistiigi",
|
||||||
]
|
]
|
||||||
@@ -195,11 +194,6 @@ made through a pull request.
|
|||||||
Email = "me@jedevc.com"
|
Email = "me@jedevc.com"
|
||||||
GitHub = "jedevc"
|
GitHub = "jedevc"
|
||||||
|
|
||||||
[people.jsternberg]
|
|
||||||
Name = "Jonathan Sternberg"
|
|
||||||
Email = "jonathan.sternberg@docker.com"
|
|
||||||
GitHub = "jsternberg"
|
|
||||||
|
|
||||||
[people.thajeztah]
|
[people.thajeztah]
|
||||||
Name = "Sebastiaan van Stijn"
|
Name = "Sebastiaan van Stijn"
|
||||||
Email = "github@gone.nl"
|
Email = "github@gone.nl"
|
||||||
|
32
Makefile
32
Makefile
@@ -8,8 +8,6 @@ endif
|
|||||||
|
|
||||||
export BUILDX_CMD ?= docker buildx
|
export BUILDX_CMD ?= docker buildx
|
||||||
|
|
||||||
BAKE_TARGETS := binaries binaries-cross lint lint-gopls validate-vendor validate-docs validate-authors validate-generated-files
|
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: binaries
|
all: binaries
|
||||||
|
|
||||||
@@ -21,9 +19,13 @@ build:
|
|||||||
shell:
|
shell:
|
||||||
./hack/shell
|
./hack/shell
|
||||||
|
|
||||||
.PHONY: $(BAKE_TARGETS)
|
.PHONY: binaries
|
||||||
$(BAKE_TARGETS):
|
binaries:
|
||||||
$(BUILDX_CMD) bake $@
|
$(BUILDX_CMD) bake binaries
|
||||||
|
|
||||||
|
.PHONY: binaries-cross
|
||||||
|
binaries-cross:
|
||||||
|
$(BUILDX_CMD) bake binaries-cross
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install: binaries
|
install: binaries
|
||||||
@@ -37,6 +39,10 @@ release:
|
|||||||
.PHONY: validate-all
|
.PHONY: validate-all
|
||||||
validate-all: lint test validate-vendor validate-docs validate-generated-files
|
validate-all: lint test validate-vendor validate-docs validate-generated-files
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
$(BUILDX_CMD) bake lint
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
./hack/test
|
./hack/test
|
||||||
@@ -49,6 +55,22 @@ test-unit:
|
|||||||
test-integration:
|
test-integration:
|
||||||
TESTPKGS=./tests ./hack/test
|
TESTPKGS=./tests ./hack/test
|
||||||
|
|
||||||
|
.PHONY: validate-vendor
|
||||||
|
validate-vendor:
|
||||||
|
$(BUILDX_CMD) bake validate-vendor
|
||||||
|
|
||||||
|
.PHONY: validate-docs
|
||||||
|
validate-docs:
|
||||||
|
$(BUILDX_CMD) bake validate-docs
|
||||||
|
|
||||||
|
.PHONY: validate-authors
|
||||||
|
validate-authors:
|
||||||
|
$(BUILDX_CMD) bake validate-authors
|
||||||
|
|
||||||
|
.PHONY: validate-generated-files
|
||||||
|
validate-generated-files:
|
||||||
|
$(BUILDX_CMD) bake validate-generated-files
|
||||||
|
|
||||||
.PHONY: test-driver
|
.PHONY: test-driver
|
||||||
test-driver:
|
test-driver:
|
||||||
./hack/test-driver
|
./hack/test-driver
|
||||||
|
10
README.md
10
README.md
@@ -187,12 +187,12 @@ through various "drivers". Each driver defines how and where a build should
|
|||||||
run, and have different feature sets.
|
run, and have different feature sets.
|
||||||
|
|
||||||
We currently support the following drivers:
|
We currently support the following drivers:
|
||||||
- The `docker` driver ([guide](https://docs.docker.com/build/drivers/docker/), [reference](https://docs.docker.com/engine/reference/commandline/buildx_create/#driver))
|
- The `docker` driver ([guide](docs/manuals/drivers/docker.md), [reference](https://docs.docker.com/engine/reference/commandline/buildx_create/#driver))
|
||||||
- The `docker-container` driver ([guide](https://docs.docker.com/build/drivers/docker-container/), [reference](https://docs.docker.com/engine/reference/commandline/buildx_create/#driver))
|
- The `docker-container` driver ([guide](docs/manuals/drivers/docker-container.md), [reference](https://docs.docker.com/engine/reference/commandline/buildx_create/#driver))
|
||||||
- The `kubernetes` driver ([guide](https://docs.docker.com/build/drivers/kubernetes/), [reference](https://docs.docker.com/engine/reference/commandline/buildx_create/#driver))
|
- The `kubernetes` driver ([guide](docs/manuals/drivers/kubernetes.md), [reference](https://docs.docker.com/engine/reference/commandline/buildx_create/#driver))
|
||||||
- The `remote` driver ([guide](https://docs.docker.com/build/drivers/remote/))
|
- The `remote` driver ([guide](docs/manuals/drivers/remote.md))
|
||||||
|
|
||||||
For more information on drivers, see the [drivers guide](https://docs.docker.com/build/drivers/).
|
For more information on drivers, see the [drivers guide](docs/manuals/drivers/index.md).
|
||||||
|
|
||||||
## Working with builder instances
|
## Working with builder instances
|
||||||
|
|
||||||
|
132
bake/bake.go
132
bake/bake.go
@@ -13,7 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
composecli "github.com/compose-spec/compose-go/v2/cli"
|
composecli "github.com/compose-spec/compose-go/cli"
|
||||||
"github.com/docker/buildx/bake/hclparser"
|
"github.com/docker/buildx/bake/hclparser"
|
||||||
"github.com/docker/buildx/build"
|
"github.com/docker/buildx/build"
|
||||||
controllerapi "github.com/docker/buildx/controller/pb"
|
controllerapi "github.com/docker/buildx/controller/pb"
|
||||||
@@ -21,7 +21,6 @@ import (
|
|||||||
"github.com/docker/buildx/util/platformutil"
|
"github.com/docker/buildx/util/platformutil"
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
"github.com/docker/cli/cli/config"
|
"github.com/docker/cli/cli/config"
|
||||||
dockeropts "github.com/docker/cli/opts"
|
|
||||||
hcl "github.com/hashicorp/hcl/v2"
|
hcl "github.com/hashicorp/hcl/v2"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
"github.com/moby/buildkit/client/llb"
|
"github.com/moby/buildkit/client/llb"
|
||||||
@@ -700,8 +699,6 @@ type Target struct {
|
|||||||
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
|
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
|
||||||
NetworkMode *string `json:"-" hcl:"-" cty:"-"`
|
NetworkMode *string `json:"-" hcl:"-" cty:"-"`
|
||||||
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"`
|
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"`
|
|
||||||
// IMPORTANT: if you add more fields here, do not forget to update newOverrides and docs/bake-reference.md.
|
// IMPORTANT: if you add more fields here, do not forget to update newOverrides and docs/bake-reference.md.
|
||||||
|
|
||||||
// linked is a private field to mark a target used as a linked one
|
// linked is a private field to mark a target used as a linked one
|
||||||
@@ -724,7 +721,6 @@ func (t *Target) normalize() {
|
|||||||
t.CacheTo = removeDupes(t.CacheTo)
|
t.CacheTo = removeDupes(t.CacheTo)
|
||||||
t.Outputs = removeDupes(t.Outputs)
|
t.Outputs = removeDupes(t.Outputs)
|
||||||
t.NoCacheFilter = removeDupes(t.NoCacheFilter)
|
t.NoCacheFilter = removeDupes(t.NoCacheFilter)
|
||||||
t.Ulimits = removeDupes(t.Ulimits)
|
|
||||||
|
|
||||||
for k, v := range t.Contexts {
|
for k, v := range t.Contexts {
|
||||||
if v == "" {
|
if v == "" {
|
||||||
@@ -813,12 +809,6 @@ func (t *Target) Merge(t2 *Target) {
|
|||||||
if t2.NoCacheFilter != nil { // merge
|
if t2.NoCacheFilter != nil { // merge
|
||||||
t.NoCacheFilter = append(t.NoCacheFilter, t2.NoCacheFilter...)
|
t.NoCacheFilter = append(t.NoCacheFilter, t2.NoCacheFilter...)
|
||||||
}
|
}
|
||||||
if t2.ShmSize != nil { // no merge
|
|
||||||
t.ShmSize = t2.ShmSize
|
|
||||||
}
|
|
||||||
if t2.Ulimits != nil { // merge
|
|
||||||
t.Ulimits = append(t.Ulimits, t2.Ulimits...)
|
|
||||||
}
|
|
||||||
t.Inherits = append(t.Inherits, t2.Inherits...)
|
t.Inherits = append(t.Inherits, t2.Inherits...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,10 +873,6 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
|||||||
t.NoCache = &noCache
|
t.NoCache = &noCache
|
||||||
case "no-cache-filter":
|
case "no-cache-filter":
|
||||||
t.NoCacheFilter = o.ArrValue
|
t.NoCacheFilter = o.ArrValue
|
||||||
case "shm-size":
|
|
||||||
t.ShmSize = &value
|
|
||||||
case "ulimits":
|
|
||||||
t.Ulimits = o.ArrValue
|
|
||||||
case "pull":
|
case "pull":
|
||||||
pull, err := strconv.ParseBool(value)
|
pull, err := strconv.ParseBool(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -894,17 +880,19 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
|
|||||||
}
|
}
|
||||||
t.Pull = &pull
|
t.Pull = &pull
|
||||||
case "push":
|
case "push":
|
||||||
push, err := strconv.ParseBool(value)
|
_, err := strconv.ParseBool(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("invalid value %s for boolean key push", value)
|
return errors.Errorf("invalid value %s for boolean key push", value)
|
||||||
}
|
}
|
||||||
t.Outputs = setPushOverride(t.Outputs, push)
|
if len(t.Outputs) == 0 {
|
||||||
case "load":
|
t.Outputs = append(t.Outputs, "type=image,push=true")
|
||||||
load, err := strconv.ParseBool(value)
|
} else {
|
||||||
if err != nil {
|
for i, output := range t.Outputs {
|
||||||
return errors.Errorf("invalid value %s for boolean key load", value)
|
if typ := parseOutputType(output); typ == "image" || typ == "registry" {
|
||||||
|
t.Outputs[i] = t.Outputs[i] + ",push=" + value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t.Outputs = setLoadOverride(t.Outputs, load)
|
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("unknown key: %s", keys[0])
|
return errors.Errorf("unknown key: %s", keys[0])
|
||||||
}
|
}
|
||||||
@@ -1023,17 +1011,12 @@ func (t *Target) GetName(ectx *hcl.EvalContext, block *hcl.Block, loadDeps func(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) {
|
func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) {
|
||||||
// make sure local credentials are loaded multiple times for different targets
|
|
||||||
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
|
|
||||||
authProvider := authprovider.NewDockerAuthProvider(dockerConfig, nil)
|
|
||||||
|
|
||||||
m2 := make(map[string]build.Options, len(m))
|
m2 := make(map[string]build.Options, len(m))
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
bo, err := toBuildOpt(v, inp)
|
bo, err := toBuildOpt(v, inp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
bo.Session = append(bo.Session, authProvider)
|
|
||||||
m2[k] = *bo
|
m2[k] = *bo
|
||||||
}
|
}
|
||||||
return m2, nil
|
return m2, nil
|
||||||
@@ -1245,12 +1228,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||||||
if t.NetworkMode != nil {
|
if t.NetworkMode != nil {
|
||||||
networkMode = *t.NetworkMode
|
networkMode = *t.NetworkMode
|
||||||
}
|
}
|
||||||
shmSize := new(dockeropts.MemBytes)
|
|
||||||
if t.ShmSize != nil {
|
|
||||||
if err := shmSize.Set(*t.ShmSize); err != nil {
|
|
||||||
return nil, errors.Errorf("invalid value %s for membytes key shm-size", *t.ShmSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bo := &build.Options{
|
bo := &build.Options{
|
||||||
Inputs: bi,
|
Inputs: bi,
|
||||||
@@ -1262,7 +1239,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||||||
Pull: pull,
|
Pull: pull,
|
||||||
NetworkMode: networkMode,
|
NetworkMode: networkMode,
|
||||||
Linked: t.linked,
|
Linked: t.linked,
|
||||||
ShmSize: *shmSize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platforms, err := platformutil.Parse(t.Platforms)
|
platforms, err := platformutil.Parse(t.Platforms)
|
||||||
@@ -1271,6 +1247,9 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||||||
}
|
}
|
||||||
bo.Platforms = platforms
|
bo.Platforms = platforms
|
||||||
|
|
||||||
|
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
|
||||||
|
bo.Session = append(bo.Session, authprovider.NewDockerAuthProvider(dockerConfig, nil))
|
||||||
|
|
||||||
secrets, err := buildflags.ParseSecretSpecs(t.Secrets)
|
secrets, err := buildflags.ParseSecretSpecs(t.Secrets)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1340,14 +1319,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ulimits := dockeropts.NewUlimitOpt(nil)
|
|
||||||
for _, field := range t.Ulimits {
|
|
||||||
if err := ulimits.Set(field); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bo.Ulimits = ulimits
|
|
||||||
|
|
||||||
return bo, nil
|
return bo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1392,90 +1363,23 @@ func removeAttestDupes(s []string) []string {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOutput(str string) map[string]string {
|
func parseOutputType(str string) string {
|
||||||
csvReader := csv.NewReader(strings.NewReader(str))
|
csvReader := csv.NewReader(strings.NewReader(str))
|
||||||
fields, err := csvReader.Read()
|
fields, err := csvReader.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return ""
|
||||||
}
|
}
|
||||||
res := map[string]string{}
|
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
parts := strings.SplitN(field, "=", 2)
|
parts := strings.SplitN(field, "=", 2)
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
res[parts[0]] = parts[1]
|
if parts[0] == "type" {
|
||||||
}
|
return parts[1]
|
||||||
}
|
}
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseOutputType(str string) string {
|
|
||||||
if out := parseOutput(str); out != nil {
|
|
||||||
if v, ok := out["type"]; ok {
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func setPushOverride(outputs []string, push bool) []string {
|
|
||||||
var out []string
|
|
||||||
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
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if push && setPush {
|
|
||||||
out = append(out, "type=image,push=true")
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
func setLoadOverride(outputs []string, load bool) []string {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if typ != "image" && typ != "registry" && typ != "oci" {
|
|
||||||
// if there is any output that is not an image, registry
|
|
||||||
// or oci, don't set "load" similar to push override
|
|
||||||
setLoad = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if setLoad {
|
|
||||||
outputs = append(outputs, "type=docker")
|
|
||||||
}
|
|
||||||
return outputs
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateTargetName(name string) error {
|
func validateTargetName(name string) error {
|
||||||
if !targetNamePattern.MatchString(name) {
|
if !targetNamePattern.MatchString(name) {
|
||||||
return errors.Errorf("only %q are allowed", validTargetNameChars)
|
return errors.Errorf("only %q are allowed", validTargetNameChars)
|
||||||
|
@@ -22,8 +22,6 @@ target "webDEP" {
|
|||||||
VAR_BOTH = "webDEP"
|
VAR_BOTH = "webDEP"
|
||||||
}
|
}
|
||||||
no-cache = true
|
no-cache = true
|
||||||
shm-size = "128m"
|
|
||||||
ulimits = ["nofile=1024:1024"]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target "webapp" {
|
target "webapp" {
|
||||||
@@ -47,8 +45,6 @@ target "webapp" {
|
|||||||
require.Equal(t, ".", *m["webapp"].Context)
|
require.Equal(t, ".", *m["webapp"].Context)
|
||||||
require.Equal(t, ptrstr("webDEP"), m["webapp"].Args["VAR_INHERITED"])
|
require.Equal(t, ptrstr("webDEP"), m["webapp"].Args["VAR_INHERITED"])
|
||||||
require.Equal(t, true, *m["webapp"].NoCache)
|
require.Equal(t, true, *m["webapp"].NoCache)
|
||||||
require.Equal(t, "128m", *m["webapp"].ShmSize)
|
|
||||||
require.Equal(t, []string{"nofile=1024:1024"}, m["webapp"].Ulimits)
|
|
||||||
require.Nil(t, m["webapp"].Pull)
|
require.Nil(t, m["webapp"].Pull)
|
||||||
|
|
||||||
require.Equal(t, 1, len(g))
|
require.Equal(t, 1, len(g))
|
||||||
@@ -133,12 +129,6 @@ target "webapp" {
|
|||||||
require.Equal(t, []string{"webapp"}, g["default"].Targets)
|
require.Equal(t, []string{"webapp"}, g["default"].Targets)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ShmSizeOverride", func(t *testing.T) {
|
|
||||||
m, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.shm-size=256m"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "256m", *m["webapp"].ShmSize)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("PullOverride", func(t *testing.T) {
|
t.Run("PullOverride", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil)
|
m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil)
|
||||||
@@ -217,252 +207,48 @@ target "webapp" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestPushOverride(t *testing.T) {
|
func TestPushOverride(t *testing.T) {
|
||||||
t.Run("empty output", func(t *testing.T) {
|
t.Parallel()
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, "type=image,push=true", m["app"].Outputs[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type image", func(t *testing.T) {
|
fp := File{
|
||||||
fp := File{
|
Name: "docker-bake.hcl",
|
||||||
Name: "docker-bake.hcl",
|
Data: []byte(
|
||||||
Data: []byte(
|
`target "app" {
|
||||||
`target "app" {
|
|
||||||
output = ["type=image,compression=zstd"]
|
output = ["type=image,compression=zstd"]
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
ctx := context.TODO()
|
||||||
require.NoError(t, err)
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
require.NoError(t, err)
|
||||||
require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type image push false", func(t *testing.T) {
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
fp := File{
|
require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0])
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
fp = File{
|
||||||
`target "app" {
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
output = ["type=image,compression=zstd"]
|
output = ["type=image,compression=zstd"]
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
|
ctx = context.TODO()
|
||||||
require.NoError(t, err)
|
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
require.NoError(t, err)
|
||||||
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type registry", func(t *testing.T) {
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
fp := File{
|
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
fp = File{
|
||||||
`target "app" {
|
Name: "docker-bake.hcl",
|
||||||
output = ["type=registry"]
|
Data: []byte(
|
||||||
|
`target "app" {
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
ctx = context.TODO()
|
||||||
require.NoError(t, err)
|
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
require.NoError(t, err)
|
||||||
require.Equal(t, "type=registry", m["app"].Outputs[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type registry push false", func(t *testing.T) {
|
require.Equal(t, 1, len(m["app"].Outputs))
|
||||||
fp := File{
|
require.Equal(t, "type=image,push=true", m["app"].Outputs[0])
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
output = ["type=registry"]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 0, len(m["app"].Outputs))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type local and empty target", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "foo" {
|
|
||||||
output = [ "type=local,dest=out" ]
|
|
||||||
}
|
|
||||||
target "bar" {
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.push=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 2, len(m))
|
|
||||||
require.Equal(t, 1, len(m["foo"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=local,dest=out"}, m["foo"].Outputs)
|
|
||||||
require.Equal(t, 1, len(m["bar"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=image,push=true"}, m["bar"].Outputs)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoadOverride(t *testing.T) {
|
|
||||||
t.Run("empty output", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, "type=docker", m["app"].Outputs[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type docker", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
output = ["type=docker"]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=docker"}, m["app"].Outputs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type image", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
output = ["type=image"]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 2, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=image", "type=docker"}, m["app"].Outputs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type image load false", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
output = ["type=image"]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=false"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=image"}, m["app"].Outputs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type registry", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
output = ["type=registry"]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 2, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=registry", "type=docker"}, m["app"].Outputs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type oci", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
output = ["type=oci,dest=out"]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 2, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=oci,dest=out", "type=docker"}, m["app"].Outputs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type docker with dest", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "app" {
|
|
||||||
output = ["type=docker,dest=out"]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.load=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 2, len(m["app"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=docker,dest=out", "type=docker"}, m["app"].Outputs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type local and empty target", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "foo" {
|
|
||||||
output = [ "type=local,dest=out" ]
|
|
||||||
}
|
|
||||||
target "bar" {
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 2, len(m))
|
|
||||||
require.Equal(t, 1, len(m["foo"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=local,dest=out"}, m["foo"].Outputs)
|
|
||||||
require.Equal(t, 1, len(m["bar"].Outputs))
|
|
||||||
require.Equal(t, []string{"type=docker"}, m["bar"].Outputs)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoadAndPushOverride(t *testing.T) {
|
|
||||||
t.Run("type local and empty target", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "foo" {
|
|
||||||
output = [ "type=local,dest=out" ]
|
|
||||||
}
|
|
||||||
target "bar" {
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo", "bar"}, []string{"*.load=true", "*.push=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 2, len(m))
|
|
||||||
|
|
||||||
require.Equal(t, 1, len(m["foo"].Outputs))
|
|
||||||
sort.Strings(m["foo"].Outputs)
|
|
||||||
require.Equal(t, []string{"type=local,dest=out"}, m["foo"].Outputs)
|
|
||||||
|
|
||||||
require.Equal(t, 2, len(m["bar"].Outputs))
|
|
||||||
sort.Strings(m["bar"].Outputs)
|
|
||||||
require.Equal(t, []string{"type=docker", "type=image,push=true"}, m["bar"].Outputs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("type registry", func(t *testing.T) {
|
|
||||||
fp := File{
|
|
||||||
Name: "docker-bake.hcl",
|
|
||||||
Data: []byte(
|
|
||||||
`target "foo" {
|
|
||||||
output = [ "type=registry" ]
|
|
||||||
}`),
|
|
||||||
}
|
|
||||||
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"foo"}, []string{"*.load=true", "*.push=true"}, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(m))
|
|
||||||
|
|
||||||
require.Equal(t, 2, len(m["foo"].Outputs))
|
|
||||||
sort.Strings(m["foo"].Outputs)
|
|
||||||
require.Equal(t, []string{"type=docker", "type=registry"}, m["foo"].Outputs)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadTargetsCompose(t *testing.T) {
|
func TestReadTargetsCompose(t *testing.T) {
|
||||||
@@ -511,6 +297,9 @@ services:
|
|||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
m, g, err := ReadTargets(ctx, []File{fp, fp2, fp3}, []string{"default"}, nil, nil)
|
m, g, err := ReadTargets(ctx, []File{fp, fp2, fp3}, []string{"default"}, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -519,7 +308,7 @@ services:
|
|||||||
|
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
|
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
|
||||||
require.Equal(t, ".", *m["webapp"].Context)
|
require.Equal(t, cwd, *m["webapp"].Context)
|
||||||
require.Equal(t, ptrstr("1"), m["webapp"].Args["buildno"])
|
require.Equal(t, ptrstr("1"), m["webapp"].Args["buildno"])
|
||||||
require.Equal(t, ptrstr("12"), m["webapp"].Args["buildno2"])
|
require.Equal(t, ptrstr("12"), m["webapp"].Args["buildno2"])
|
||||||
|
|
||||||
@@ -558,6 +347,9 @@ services:
|
|||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
m, _, err := ReadTargets(ctx, []File{fp}, []string{"web.app"}, nil, nil)
|
m, _, err := ReadTargets(ctx, []File{fp}, []string{"web.app"}, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(m))
|
require.Equal(t, 1, len(m))
|
||||||
@@ -580,7 +372,7 @@ services:
|
|||||||
_, ok = m["web_app"]
|
_, ok = m["web_app"]
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Equal(t, "Dockerfile.webapp", *m["web_app"].Dockerfile)
|
require.Equal(t, "Dockerfile.webapp", *m["web_app"].Dockerfile)
|
||||||
require.Equal(t, ".", *m["web_app"].Context)
|
require.Equal(t, cwd, *m["web_app"].Context)
|
||||||
require.Equal(t, ptrstr("1"), m["web_app"].Args["buildno"])
|
require.Equal(t, ptrstr("1"), m["web_app"].Args["buildno"])
|
||||||
require.Equal(t, ptrstr("12"), m["web_app"].Args["buildno2"])
|
require.Equal(t, ptrstr("12"), m["web_app"].Args["buildno2"])
|
||||||
|
|
||||||
@@ -789,6 +581,9 @@ services:
|
|||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
cwd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
m, _, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app1", "app2"}, nil, nil)
|
m, _, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app1", "app2"}, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@@ -801,7 +596,7 @@ services:
|
|||||||
require.Equal(t, "Dockerfile", *m["app1"].Dockerfile)
|
require.Equal(t, "Dockerfile", *m["app1"].Dockerfile)
|
||||||
require.Equal(t, ".", *m["app1"].Context)
|
require.Equal(t, ".", *m["app1"].Context)
|
||||||
require.Equal(t, "Dockerfile", *m["app2"].Dockerfile)
|
require.Equal(t, "Dockerfile", *m["app2"].Dockerfile)
|
||||||
require.Equal(t, ".", *m["app2"].Context)
|
require.Equal(t, cwd, *m["app2"].Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadContextFromTargetChain(t *testing.T) {
|
func TestReadContextFromTargetChain(t *testing.T) {
|
||||||
|
@@ -2,17 +2,13 @@ package bake
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
"github.com/compose-spec/compose-go/dotenv"
|
||||||
"github.com/compose-spec/compose-go/v2/loader"
|
"github.com/compose-spec/compose-go/loader"
|
||||||
composetypes "github.com/compose-spec/compose-go/v2/types"
|
compose "github.com/compose-spec/compose-go/types"
|
||||||
dockeropts "github.com/docker/cli/opts"
|
|
||||||
"github.com/docker/go-units"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
@@ -22,9 +18,9 @@ func ParseComposeFiles(fs []File) (*Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var cfgs []composetypes.ConfigFile
|
var cfgs []compose.ConfigFile
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
cfgs = append(cfgs, composetypes.ConfigFile{
|
cfgs = append(cfgs, compose.ConfigFile{
|
||||||
Filename: f.Name,
|
Filename: f.Name,
|
||||||
Content: f.Data,
|
Content: f.Data,
|
||||||
})
|
})
|
||||||
@@ -32,11 +28,11 @@ func ParseComposeFiles(fs []File) (*Config, error) {
|
|||||||
return ParseCompose(cfgs, envs)
|
return ParseCompose(cfgs, envs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Config, error) {
|
func ParseCompose(cfgs []compose.ConfigFile, envs map[string]string) (*Config, error) {
|
||||||
if envs == nil {
|
if envs == nil {
|
||||||
envs = make(map[string]string)
|
envs = make(map[string]string)
|
||||||
}
|
}
|
||||||
cfg, err := loader.LoadWithContext(context.Background(), composetypes.ConfigDetails{
|
cfg, err := loader.LoadWithContext(context.Background(), compose.ConfigDetails{
|
||||||
ConfigFiles: cfgs,
|
ConfigFiles: cfgs,
|
||||||
Environment: envs,
|
Environment: envs,
|
||||||
}, func(options *loader.Options) {
|
}, func(options *loader.Options) {
|
||||||
@@ -90,31 +86,6 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var shmSize *string
|
|
||||||
if s.Build.ShmSize > 0 {
|
|
||||||
shmSizeBytes := dockeropts.MemBytes(s.Build.ShmSize)
|
|
||||||
shmSizeStr := shmSizeBytes.String()
|
|
||||||
shmSize = &shmSizeStr
|
|
||||||
}
|
|
||||||
|
|
||||||
var ulimits []string
|
|
||||||
if s.Build.Ulimits != nil {
|
|
||||||
for n, u := range s.Build.Ulimits {
|
|
||||||
ulimit, err := units.ParseUlimit(fmt.Sprintf("%s=%d:%d", n, u.Soft, u.Hard))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ulimits = append(ulimits, ulimit.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ssh []string
|
|
||||||
for _, bkey := range s.Build.SSH {
|
|
||||||
sshkey := composeToBuildkitSSH(bkey)
|
|
||||||
ssh = append(ssh, sshkey)
|
|
||||||
}
|
|
||||||
sort.Strings(ssh)
|
|
||||||
|
|
||||||
var secrets []string
|
var secrets []string
|
||||||
for _, bs := range s.Build.Secrets {
|
for _, bs := range s.Build.Secrets {
|
||||||
secret, err := composeToBuildkitSecret(bs, cfg.Secrets[bs.Source])
|
secret, err := composeToBuildkitSecret(bs, cfg.Secrets[bs.Source])
|
||||||
@@ -150,10 +121,7 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
|
|||||||
CacheFrom: s.Build.CacheFrom,
|
CacheFrom: s.Build.CacheFrom,
|
||||||
CacheTo: s.Build.CacheTo,
|
CacheTo: s.Build.CacheTo,
|
||||||
NetworkMode: &s.Build.Network,
|
NetworkMode: &s.Build.Network,
|
||||||
SSH: ssh,
|
|
||||||
Secrets: secrets,
|
Secrets: secrets,
|
||||||
ShmSize: shmSize,
|
|
||||||
Ulimits: ulimits,
|
|
||||||
}
|
}
|
||||||
if err = t.composeExtTarget(s.Build.Extensions); err != nil {
|
if err = t.composeExtTarget(s.Build.Extensions); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -191,8 +159,8 @@ func validateComposeFile(dt []byte, fn string) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateCompose(dt []byte, envs map[string]string) error {
|
func validateCompose(dt []byte, envs map[string]string) error {
|
||||||
_, err := loader.Load(composetypes.ConfigDetails{
|
_, err := loader.Load(compose.ConfigDetails{
|
||||||
ConfigFiles: []composetypes.ConfigFile{
|
ConfigFiles: []compose.ConfigFile{
|
||||||
{
|
{
|
||||||
Content: dt,
|
Content: dt,
|
||||||
},
|
},
|
||||||
@@ -255,7 +223,7 @@ func loadDotEnv(curenv map[string]string, workingDir string) (map[string]string,
|
|||||||
return curenv, nil
|
return curenv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func flatten(in composetypes.MappingWithEquals) map[string]*string {
|
func flatten(in compose.MappingWithEquals) map[string]*string {
|
||||||
if len(in) == 0 {
|
if len(in) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -284,7 +252,7 @@ type xbake struct {
|
|||||||
NoCacheFilter stringArray `yaml:"no-cache-filter,omitempty"`
|
NoCacheFilter stringArray `yaml:"no-cache-filter,omitempty"`
|
||||||
Contexts stringMap `yaml:"contexts,omitempty"`
|
Contexts stringMap `yaml:"contexts,omitempty"`
|
||||||
// don't forget to update documentation if you add a new field:
|
// don't forget to update documentation if you add a new field:
|
||||||
// https://github.com/docker/docs/blob/main/content/build/bake/compose-file.md#extension-field-with-x-bake
|
// docs/manuals/bake/compose-file.md#extension-field-with-x-bake
|
||||||
}
|
}
|
||||||
|
|
||||||
type stringMap map[string]string
|
type stringMap map[string]string
|
||||||
@@ -334,7 +302,6 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
|
|||||||
}
|
}
|
||||||
if len(xb.SSH) > 0 {
|
if len(xb.SSH) > 0 {
|
||||||
t.SSH = dedupSlice(append(t.SSH, xb.SSH...))
|
t.SSH = dedupSlice(append(t.SSH, xb.SSH...))
|
||||||
sort.Strings(t.SSH)
|
|
||||||
}
|
}
|
||||||
if len(xb.Platforms) > 0 {
|
if len(xb.Platforms) > 0 {
|
||||||
t.Platforms = dedupSlice(append(t.Platforms, xb.Platforms...))
|
t.Platforms = dedupSlice(append(t.Platforms, xb.Platforms...))
|
||||||
@@ -360,8 +327,8 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
|
|||||||
|
|
||||||
// composeToBuildkitSecret converts secret from compose format to buildkit's
|
// composeToBuildkitSecret converts secret from compose format to buildkit's
|
||||||
// csv format.
|
// csv format.
|
||||||
func composeToBuildkitSecret(inp composetypes.ServiceSecretConfig, psecret composetypes.SecretConfig) (string, error) {
|
func composeToBuildkitSecret(inp compose.ServiceSecretConfig, psecret compose.SecretConfig) (string, error) {
|
||||||
if psecret.External {
|
if psecret.External.External {
|
||||||
return "", errors.Errorf("unsupported external secret %s", psecret.Name)
|
return "", errors.Errorf("unsupported external secret %s", psecret.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,17 +345,3 @@ func composeToBuildkitSecret(inp composetypes.ServiceSecretConfig, psecret compo
|
|||||||
|
|
||||||
return strings.Join(bkattrs, ","), nil
|
return strings.Join(bkattrs, ","), 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)
|
|
||||||
|
|
||||||
if sshKey.Path != "" {
|
|
||||||
bkattrs = append(bkattrs, sshKey.Path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(bkattrs, "=")
|
|
||||||
}
|
|
||||||
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
composetypes "github.com/compose-spec/compose-go/v2/types"
|
compose "github.com/compose-spec/compose-go/types"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@@ -32,9 +32,6 @@ services:
|
|||||||
- type=local,src=path/to/cache
|
- type=local,src=path/to/cache
|
||||||
cache_to:
|
cache_to:
|
||||||
- type=local,dest=path/to/cache
|
- type=local,dest=path/to/cache
|
||||||
ssh:
|
|
||||||
- key=path/to/key
|
|
||||||
- default
|
|
||||||
secrets:
|
secrets:
|
||||||
- token
|
- token
|
||||||
- aws
|
- aws
|
||||||
@@ -52,7 +49,10 @@ secrets:
|
|||||||
file: /root/.aws/credentials
|
file: /root/.aws/credentials
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
cwd, err := os.Getwd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 1, len(c.Groups))
|
require.Equal(t, 1, len(c.Groups))
|
||||||
@@ -65,26 +65,25 @@ secrets:
|
|||||||
return c.Targets[i].Name < c.Targets[j].Name
|
return c.Targets[i].Name < c.Targets[j].Name
|
||||||
})
|
})
|
||||||
require.Equal(t, "db", c.Targets[0].Name)
|
require.Equal(t, "db", c.Targets[0].Name)
|
||||||
require.Equal(t, "db", *c.Targets[0].Context)
|
require.Equal(t, filepath.Join(cwd, "db"), *c.Targets[0].Context)
|
||||||
require.Equal(t, []string{"docker.io/tonistiigi/db"}, c.Targets[0].Tags)
|
require.Equal(t, []string{"docker.io/tonistiigi/db"}, c.Targets[0].Tags)
|
||||||
|
|
||||||
require.Equal(t, "webapp", c.Targets[1].Name)
|
require.Equal(t, "webapp", c.Targets[1].Name)
|
||||||
require.Equal(t, "dir", *c.Targets[1].Context)
|
require.Equal(t, filepath.Join(cwd, "dir"), *c.Targets[1].Context)
|
||||||
require.Equal(t, map[string]string{"foo": "bar"}, c.Targets[1].Contexts)
|
require.Equal(t, map[string]string{"foo": filepath.Join(cwd, "bar")}, c.Targets[1].Contexts)
|
||||||
require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
|
require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
|
||||||
require.Equal(t, 1, len(c.Targets[1].Args))
|
require.Equal(t, 1, len(c.Targets[1].Args))
|
||||||
require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
|
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,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,dest=path/to/cache"}, c.Targets[1].CacheTo)
|
||||||
require.Equal(t, "none", *c.Targets[1].NetworkMode)
|
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{
|
require.Equal(t, []string{
|
||||||
"id=token,env=ENV_TOKEN",
|
"id=token,env=ENV_TOKEN",
|
||||||
"id=aws,src=/root/.aws/credentials",
|
"id=aws,src=/root/.aws/credentials",
|
||||||
}, c.Targets[1].Secrets)
|
}, c.Targets[1].Secrets)
|
||||||
|
|
||||||
require.Equal(t, "webapp2", c.Targets[2].Name)
|
require.Equal(t, "webapp2", c.Targets[2].Name)
|
||||||
require.Equal(t, "dir", *c.Targets[2].Context)
|
require.Equal(t, filepath.Join(cwd, "dir"), *c.Targets[2].Context)
|
||||||
require.Equal(t, "FROM alpine\n", *c.Targets[2].DockerfileInline)
|
require.Equal(t, "FROM alpine\n", *c.Targets[2].DockerfileInline)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +95,7 @@ services:
|
|||||||
webapp:
|
webapp:
|
||||||
build: ./db
|
build: ./db
|
||||||
`)
|
`)
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(c.Groups))
|
require.Equal(t, 1, len(c.Groups))
|
||||||
require.Equal(t, 1, len(c.Targets))
|
require.Equal(t, 1, len(c.Targets))
|
||||||
@@ -115,7 +114,7 @@ services:
|
|||||||
target: webapp
|
target: webapp
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 2, len(c.Targets))
|
require.Equal(t, 2, len(c.Targets))
|
||||||
@@ -140,7 +139,7 @@ services:
|
|||||||
target: webapp
|
target: webapp
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, len(c.Targets))
|
require.Equal(t, 2, len(c.Targets))
|
||||||
sort.Slice(c.Targets, func(i, j int) bool {
|
sort.Slice(c.Targets, func(i, j int) bool {
|
||||||
@@ -171,7 +170,7 @@ services:
|
|||||||
t.Setenv("BAR", "foo")
|
t.Setenv("BAR", "foo")
|
||||||
t.Setenv("ZZZ_BAR", "zzz_foo")
|
t.Setenv("ZZZ_BAR", "zzz_foo")
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, sliceToMap(os.Environ()))
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, sliceToMap(os.Environ()))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["FOO"])
|
require.Equal(t, ptrstr("bar"), c.Targets[0].Args["FOO"])
|
||||||
require.Equal(t, ptrstr("zzz_foo"), c.Targets[0].Args["BAR"])
|
require.Equal(t, ptrstr("zzz_foo"), c.Targets[0].Args["BAR"])
|
||||||
@@ -185,7 +184,7 @@ services:
|
|||||||
entrypoint: echo 1
|
entrypoint: echo 1
|
||||||
`)
|
`)
|
||||||
|
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +209,7 @@ networks:
|
|||||||
gateway: 10.5.0.254
|
gateway: 10.5.0.254
|
||||||
`)
|
`)
|
||||||
|
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +226,7 @@ services:
|
|||||||
- bar
|
- bar
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, []string{"foo", "bar"}, c.Targets[0].Tags)
|
require.Equal(t, []string{"foo", "bar"}, c.Targets[0].Tags)
|
||||||
}
|
}
|
||||||
@@ -264,7 +263,7 @@ networks:
|
|||||||
name: test-net
|
name: test-net
|
||||||
`)
|
`)
|
||||||
|
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,8 +281,6 @@ services:
|
|||||||
- user/app:cache
|
- user/app:cache
|
||||||
tags:
|
tags:
|
||||||
- ct-addon:baz
|
- ct-addon:baz
|
||||||
ssh:
|
|
||||||
key: path/to/key
|
|
||||||
args:
|
args:
|
||||||
CT_ECR: foo
|
CT_ECR: foo
|
||||||
CT_TAG: bar
|
CT_TAG: bar
|
||||||
@@ -293,9 +290,6 @@ services:
|
|||||||
tags:
|
tags:
|
||||||
- ct-addon:foo
|
- ct-addon:foo
|
||||||
- ct-addon:alp
|
- ct-addon:alp
|
||||||
ssh:
|
|
||||||
- default
|
|
||||||
- other=path/to/otherkey
|
|
||||||
platforms:
|
platforms:
|
||||||
- linux/amd64
|
- linux/amd64
|
||||||
- linux/arm64
|
- linux/arm64
|
||||||
@@ -312,11 +306,6 @@ services:
|
|||||||
args:
|
args:
|
||||||
CT_ECR: foo
|
CT_ECR: foo
|
||||||
CT_TAG: bar
|
CT_TAG: bar
|
||||||
shm_size: 128m
|
|
||||||
ulimits:
|
|
||||||
nofile:
|
|
||||||
soft: 1024
|
|
||||||
hard: 1024
|
|
||||||
x-bake:
|
x-bake:
|
||||||
secret:
|
secret:
|
||||||
- id=mysecret,src=/local/secret
|
- id=mysecret,src=/local/secret
|
||||||
@@ -327,7 +316,7 @@ services:
|
|||||||
no-cache: true
|
no-cache: true
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 2, len(c.Targets))
|
require.Equal(t, 2, len(c.Targets))
|
||||||
sort.Slice(c.Targets, func(i, j int) bool {
|
sort.Slice(c.Targets, func(i, j int) bool {
|
||||||
@@ -338,7 +327,6 @@ services:
|
|||||||
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[0].Platforms)
|
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,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{"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, newBool(true), c.Targets[0].Pull)
|
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, 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{"ct-fake-aws:bar"}, c.Targets[1].Tags)
|
||||||
@@ -347,8 +335,6 @@ services:
|
|||||||
require.Equal(t, []string{"linux/arm64"}, c.Targets[1].Platforms)
|
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"}, c.Targets[1].Outputs)
|
||||||
require.Equal(t, newBool(true), c.Targets[1].NoCache)
|
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) {
|
func TestComposeExtDedup(t *testing.T) {
|
||||||
@@ -363,8 +349,6 @@ services:
|
|||||||
- user/app:cache
|
- user/app:cache
|
||||||
tags:
|
tags:
|
||||||
- ct-addon:foo
|
- ct-addon:foo
|
||||||
ssh:
|
|
||||||
- default
|
|
||||||
x-bake:
|
x-bake:
|
||||||
tags:
|
tags:
|
||||||
- ct-addon:foo
|
- ct-addon:foo
|
||||||
@@ -374,18 +358,14 @@ services:
|
|||||||
- type=local,src=path/to/cache
|
- type=local,src=path/to/cache
|
||||||
cache-to:
|
cache-to:
|
||||||
- type=local,dest=path/to/cache
|
- type=local,dest=path/to/cache
|
||||||
ssh:
|
|
||||||
- default
|
|
||||||
- key=path/to/key
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 1, len(c.Targets))
|
require.Equal(t, 1, len(c.Targets))
|
||||||
require.Equal(t, []string{"ct-addon:foo", "ct-addon:baz"}, c.Targets[0].Tags)
|
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,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{"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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnv(t *testing.T) {
|
func TestEnv(t *testing.T) {
|
||||||
@@ -413,7 +393,7 @@ services:
|
|||||||
- ` + envf.Name() + `
|
- ` + envf.Name() + `
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, map[string]*string{"CT_ECR": ptrstr("foo"), "FOO": ptrstr("bsdf -csdf"), "NODE_ENV": ptrstr("test")}, c.Targets[0].Args)
|
require.Equal(t, map[string]*string{"CT_ECR": ptrstr("foo"), "FOO": ptrstr("bsdf -csdf"), "NODE_ENV": ptrstr("test")}, c.Targets[0].Args)
|
||||||
}
|
}
|
||||||
@@ -459,7 +439,7 @@ services:
|
|||||||
published: "3306"
|
published: "3306"
|
||||||
protocol: tcp
|
protocol: tcp
|
||||||
`)
|
`)
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,7 +485,7 @@ func TestServiceName(t *testing.T) {
|
|||||||
for _, tt := range cases {
|
for _, tt := range cases {
|
||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.svc, func(t *testing.T) {
|
t.Run(tt.svc, func(t *testing.T) {
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: []byte(`
|
_, err := ParseCompose([]compose.ConfigFile{{Content: []byte(`
|
||||||
services:
|
services:
|
||||||
` + tt.svc + `:
|
` + tt.svc + `:
|
||||||
build:
|
build:
|
||||||
@@ -576,7 +556,7 @@ services:
|
|||||||
for _, tt := range cases {
|
for _, tt := range cases {
|
||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: tt.dt}}, nil)
|
_, err := ParseCompose([]compose.ConfigFile{{Content: tt.dt}}, nil)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -674,7 +654,7 @@ services:
|
|||||||
bar: "baz"
|
bar: "baz"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, map[string]*string{"bar": ptrstr("baz")}, c.Targets[0].Args)
|
require.Equal(t, map[string]*string{"bar": ptrstr("baz")}, c.Targets[0].Args)
|
||||||
}
|
}
|
||||||
@@ -693,7 +673,7 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
`)
|
`)
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,7 +704,7 @@ services:
|
|||||||
|
|
||||||
chdir(t, tmpdir)
|
chdir(t, tmpdir)
|
||||||
c, err := ParseComposeFiles([]File{{
|
c, err := ParseComposeFiles([]File{{
|
||||||
Name: "composetypes.yml",
|
Name: "compose.yml",
|
||||||
Data: dt,
|
Data: dt,
|
||||||
}})
|
}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -754,7 +734,7 @@ services:
|
|||||||
- node_modules/
|
- node_modules/
|
||||||
`)
|
`)
|
||||||
|
|
||||||
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
|
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1445,39 +1445,6 @@ func TestVarUnsupportedType(t *testing.T) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHCLIndexOfFunc(t *testing.T) {
|
|
||||||
dt := []byte(`
|
|
||||||
variable "APP_VERSIONS" {
|
|
||||||
default = [
|
|
||||||
"1.42.4",
|
|
||||||
"1.42.3"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
target "default" {
|
|
||||||
args = {
|
|
||||||
APP_VERSION = app_version
|
|
||||||
}
|
|
||||||
matrix = {
|
|
||||||
app_version = APP_VERSIONS
|
|
||||||
}
|
|
||||||
name="app-${replace(app_version, ".", "-")}"
|
|
||||||
tags = [
|
|
||||||
"app:${app_version}",
|
|
||||||
indexof(APP_VERSIONS, app_version) == 0 ? "app:latest" : "",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
c, err := ParseFile(dt, "docker-bake.hcl")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, 2, len(c.Targets))
|
|
||||||
require.Equal(t, "app-1-42-4", c.Targets[0].Name)
|
|
||||||
require.Equal(t, "app:latest", c.Targets[0].Tags[1])
|
|
||||||
require.Equal(t, "app-1-42-3", c.Targets[1].Name)
|
|
||||||
require.Empty(t, c.Targets[1].Tags[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
func ptrstr(s interface{}) *string {
|
func ptrstr(s interface{}) *string {
|
||||||
var n *string
|
var n *string
|
||||||
if reflect.ValueOf(s).Kind() == reflect.String {
|
if reflect.ValueOf(s).Kind() == reflect.String {
|
||||||
|
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/hashicorp/go-cty-funcs/uuid"
|
"github.com/hashicorp/go-cty-funcs/uuid"
|
||||||
"github.com/hashicorp/hcl/v2/ext/tryfunc"
|
"github.com/hashicorp/hcl/v2/ext/tryfunc"
|
||||||
"github.com/hashicorp/hcl/v2/ext/typeexpr"
|
"github.com/hashicorp/hcl/v2/ext/typeexpr"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
"github.com/zclconf/go-cty/cty/function"
|
"github.com/zclconf/go-cty/cty/function"
|
||||||
"github.com/zclconf/go-cty/cty/function/stdlib"
|
"github.com/zclconf/go-cty/cty/function/stdlib"
|
||||||
@@ -53,7 +52,6 @@ var stdlibFunctions = map[string]function.Function{
|
|||||||
"hasindex": stdlib.HasIndexFunc,
|
"hasindex": stdlib.HasIndexFunc,
|
||||||
"indent": stdlib.IndentFunc,
|
"indent": stdlib.IndentFunc,
|
||||||
"index": stdlib.IndexFunc,
|
"index": stdlib.IndexFunc,
|
||||||
"indexof": indexOfFunc,
|
|
||||||
"int": stdlib.IntFunc,
|
"int": stdlib.IntFunc,
|
||||||
"join": stdlib.JoinFunc,
|
"join": stdlib.JoinFunc,
|
||||||
"jsondecode": stdlib.JSONDecodeFunc,
|
"jsondecode": stdlib.JSONDecodeFunc,
|
||||||
@@ -117,51 +115,6 @@ var stdlibFunctions = map[string]function.Function{
|
|||||||
"zipmap": stdlib.ZipmapFunc,
|
"zipmap": stdlib.ZipmapFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexOfFunc constructs a function that finds the element index for a given
|
|
||||||
// value in a list.
|
|
||||||
var indexOfFunc = function.New(&function.Spec{
|
|
||||||
Params: []function.Parameter{
|
|
||||||
{
|
|
||||||
Name: "list",
|
|
||||||
Type: cty.DynamicPseudoType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "value",
|
|
||||||
Type: cty.DynamicPseudoType,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Type: function.StaticReturnType(cty.Number),
|
|
||||||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
|
||||||
if !(args[0].Type().IsListType() || args[0].Type().IsTupleType()) {
|
|
||||||
return cty.NilVal, errors.New("argument must be a list or tuple")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !args[0].IsKnown() {
|
|
||||||
return cty.UnknownVal(cty.Number), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if args[0].LengthInt() == 0 { // Easy path
|
|
||||||
return cty.NilVal, errors.New("cannot search an empty list")
|
|
||||||
}
|
|
||||||
|
|
||||||
for it := args[0].ElementIterator(); it.Next(); {
|
|
||||||
i, v := it.Element()
|
|
||||||
eq, err := stdlib.Equal(v, args[1])
|
|
||||||
if err != nil {
|
|
||||||
return cty.NilVal, err
|
|
||||||
}
|
|
||||||
if !eq.IsKnown() {
|
|
||||||
return cty.UnknownVal(cty.Number), nil
|
|
||||||
}
|
|
||||||
if eq.True() {
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cty.NilVal, errors.New("item not found")
|
|
||||||
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// timestampFunc constructs a function that returns a string representation of the current date and time.
|
// timestampFunc constructs a function that returns a string representation of the current date and time.
|
||||||
//
|
//
|
||||||
// This function was imported from terraform's datetime utilities.
|
// This function was imported from terraform's datetime utilities.
|
||||||
|
@@ -1,49 +0,0 @@
|
|||||||
package hclparser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIndexOf(t *testing.T) {
|
|
||||||
type testCase struct {
|
|
||||||
input cty.Value
|
|
||||||
key cty.Value
|
|
||||||
want cty.Value
|
|
||||||
wantErr bool
|
|
||||||
}
|
|
||||||
tests := map[string]testCase{
|
|
||||||
"index 0": {
|
|
||||||
input: cty.TupleVal([]cty.Value{cty.StringVal("one"), cty.NumberIntVal(2.0), cty.NumberIntVal(3), cty.StringVal("four")}),
|
|
||||||
key: cty.StringVal("one"),
|
|
||||||
want: cty.NumberIntVal(0),
|
|
||||||
},
|
|
||||||
"index 3": {
|
|
||||||
input: cty.TupleVal([]cty.Value{cty.StringVal("one"), cty.NumberIntVal(2.0), cty.NumberIntVal(3), cty.StringVal("four")}),
|
|
||||||
key: cty.StringVal("four"),
|
|
||||||
want: cty.NumberIntVal(3),
|
|
||||||
},
|
|
||||||
"index -1": {
|
|
||||||
input: cty.TupleVal([]cty.Value{cty.StringVal("one"), cty.NumberIntVal(2.0), cty.NumberIntVal(3), cty.StringVal("four")}),
|
|
||||||
key: cty.StringVal("3"),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, test := range tests {
|
|
||||||
name, test := name, test
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
got, err := indexOfFunc.Call([]cty.Value{test.input, test.key})
|
|
||||||
if err != nil {
|
|
||||||
if test.wantErr {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t.Fatalf("unexpected error: %s", err)
|
|
||||||
}
|
|
||||||
if !got.RawEquals(test.want) {
|
|
||||||
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -4,8 +4,6 @@ import (
|
|||||||
"archive/tar"
|
"archive/tar"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
controllerapi "github.com/docker/buildx/controller/pb"
|
controllerapi "github.com/docker/buildx/controller/pb"
|
||||||
@@ -25,34 +23,13 @@ type Input struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, names []string, pw progress.Writer) ([]File, *Input, error) {
|
func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, names []string, pw progress.Writer) ([]File, *Input, error) {
|
||||||
var sessions []session.Attachable
|
var session []session.Attachable
|
||||||
var filename string
|
var filename string
|
||||||
|
|
||||||
st, ok := dockerui.DetectGitContext(url, false)
|
st, ok := dockerui.DetectGitContext(url, false)
|
||||||
if ok {
|
if ok {
|
||||||
if ssh, err := controllerapi.CreateSSH([]*controllerapi.SSH{{
|
ssh, err := controllerapi.CreateSSH([]*controllerapi.SSH{{ID: "default"}})
|
||||||
ID: "default",
|
if err == nil {
|
||||||
Paths: strings.Split(os.Getenv("BUILDX_BAKE_GIT_SSH"), ","),
|
session = append(session, ssh)
|
||||||
}}); err == nil {
|
|
||||||
sessions = append(sessions, ssh)
|
|
||||||
}
|
|
||||||
var gitAuthSecrets []*controllerapi.Secret
|
|
||||||
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_TOKEN"); ok {
|
|
||||||
gitAuthSecrets = append(gitAuthSecrets, &controllerapi.Secret{
|
|
||||||
ID: llb.GitAuthTokenKey,
|
|
||||||
Env: "BUILDX_BAKE_GIT_AUTH_TOKEN",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if _, ok := os.LookupEnv("BUILDX_BAKE_GIT_AUTH_HEADER"); ok {
|
|
||||||
gitAuthSecrets = append(gitAuthSecrets, &controllerapi.Secret{
|
|
||||||
ID: llb.GitAuthHeaderKey,
|
|
||||||
Env: "BUILDX_BAKE_GIT_AUTH_HEADER",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if len(gitAuthSecrets) > 0 {
|
|
||||||
if secrets, err := controllerapi.CreateSecrets(gitAuthSecrets); err == nil {
|
|
||||||
sessions = append(sessions, secrets)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
st, filename, ok = dockerui.DetectHTTPContext(url)
|
st, filename, ok = dockerui.DetectHTTPContext(url)
|
||||||
@@ -82,7 +59,7 @@ func ReadRemoteFiles(ctx context.Context, nodes []builder.Node, url string, name
|
|||||||
|
|
||||||
ch, done := progress.NewChannel(pw)
|
ch, done := progress.NewChannel(pw)
|
||||||
defer func() { <-done }()
|
defer func() { <-done }()
|
||||||
_, err = c.Build(ctx, client.SolveOpt{Session: sessions, Internal: true}, "buildx", func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
|
_, err = c.Build(ctx, client.SolveOpt{Session: session, Internal: true}, "buildx", func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
|
||||||
def, err := st.Marshal(ctx)
|
def, err := st.Marshal(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
1091
build/build.go
1091
build/build.go
File diff suppressed because it is too large
Load Diff
@@ -1,62 +0,0 @@
|
|||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
stderrors "errors"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
|
||||||
"github.com/docker/buildx/builder"
|
|
||||||
"github.com/docker/buildx/util/progress"
|
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Dial(ctx context.Context, nodes []builder.Node, pw progress.Writer, platform *v1.Platform) (net.Conn, error) {
|
|
||||||
nodes, err := filterAvailableNodes(nodes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nodes) == 0 {
|
|
||||||
return nil, errors.New("no nodes available")
|
|
||||||
}
|
|
||||||
|
|
||||||
var pls []v1.Platform
|
|
||||||
if platform != nil {
|
|
||||||
pls = []v1.Platform{*platform}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := map[string]Options{"default": {Platforms: pls}}
|
|
||||||
resolved, err := resolveDrivers(ctx, nodes, opts, pw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var dialError error
|
|
||||||
for _, ls := range resolved {
|
|
||||||
for _, rn := range ls {
|
|
||||||
if platform != nil {
|
|
||||||
p := *platform
|
|
||||||
var found bool
|
|
||||||
for _, pp := range rn.platforms {
|
|
||||||
if platforms.Only(p).Match(pp) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err := nodes[rn.driverIndex].Driver.Dial(ctx)
|
|
||||||
if err == nil {
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
dialError = stderrors.Join(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, errors.Wrap(dialError, "no nodes available")
|
|
||||||
}
|
|
352
build/driver.go
352
build/driver.go
@@ -1,352 +0,0 @@
|
|||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
|
||||||
"github.com/docker/buildx/builder"
|
|
||||||
"github.com/docker/buildx/driver"
|
|
||||||
"github.com/docker/buildx/util/progress"
|
|
||||||
"github.com/moby/buildkit/client"
|
|
||||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
|
||||||
"github.com/moby/buildkit/util/flightcontrol"
|
|
||||||
"github.com/moby/buildkit/util/tracing"
|
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
|
||||||
|
|
||||||
type resolvedNode struct {
|
|
||||||
resolver *nodeResolver
|
|
||||||
driverIndex int
|
|
||||||
platforms []specs.Platform
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dp resolvedNode) Node() builder.Node {
|
|
||||||
return dp.resolver.nodes[dp.driverIndex]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dp resolvedNode) Client(ctx context.Context) (*client.Client, error) {
|
|
||||||
clients, err := dp.resolver.boot(ctx, []int{dp.driverIndex}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return clients[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dp resolvedNode) BuildOpts(ctx context.Context) (gateway.BuildOpts, error) {
|
|
||||||
opts, err := dp.resolver.opts(ctx, []int{dp.driverIndex}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return gateway.BuildOpts{}, err
|
|
||||||
}
|
|
||||||
return opts[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type matchMaker func(specs.Platform) platforms.MatchComparer
|
|
||||||
|
|
||||||
type cachedGroup[T any] struct {
|
|
||||||
g flightcontrol.Group[T]
|
|
||||||
cache map[int]T
|
|
||||||
cacheMu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCachedGroup[T any]() cachedGroup[T] {
|
|
||||||
return cachedGroup[T]{
|
|
||||||
cache: map[int]T{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type nodeResolver struct {
|
|
||||||
nodes []builder.Node
|
|
||||||
clients cachedGroup[*client.Client]
|
|
||||||
buildOpts cachedGroup[gateway.BuildOpts]
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveDrivers(ctx context.Context, nodes []builder.Node, opt map[string]Options, pw progress.Writer) (map[string][]*resolvedNode, error) {
|
|
||||||
driverRes := newDriverResolver(nodes)
|
|
||||||
drivers, err := driverRes.Resolve(ctx, opt, pw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return drivers, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDriverResolver(nodes []builder.Node) *nodeResolver {
|
|
||||||
r := &nodeResolver{
|
|
||||||
nodes: nodes,
|
|
||||||
clients: newCachedGroup[*client.Client](),
|
|
||||||
buildOpts: newCachedGroup[gateway.BuildOpts](),
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *nodeResolver) Resolve(ctx context.Context, opt map[string]Options, pw progress.Writer) (map[string][]*resolvedNode, error) {
|
|
||||||
if len(r.nodes) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes := map[string][]*resolvedNode{}
|
|
||||||
for k, opt := range opt {
|
|
||||||
node, perfect, err := r.resolve(ctx, opt.Platforms, pw, platforms.OnlyStrict, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !perfect {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
nodes[k] = node
|
|
||||||
}
|
|
||||||
if len(nodes) != len(opt) {
|
|
||||||
// if we didn't get a perfect match, we need to boot all drivers
|
|
||||||
allIndexes := make([]int, len(r.nodes))
|
|
||||||
for i := range allIndexes {
|
|
||||||
allIndexes[i] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
clients, err := r.boot(ctx, allIndexes, pw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
eg, egCtx := errgroup.WithContext(ctx)
|
|
||||||
workers := make([][]specs.Platform, len(clients))
|
|
||||||
for i, c := range clients {
|
|
||||||
i, c := i, c
|
|
||||||
if c == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
eg.Go(func() error {
|
|
||||||
ww, err := c.ListWorkers(egCtx)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "listing workers")
|
|
||||||
}
|
|
||||||
|
|
||||||
ps := make(map[string]specs.Platform, len(ww))
|
|
||||||
for _, w := range ww {
|
|
||||||
for _, p := range w.Platforms {
|
|
||||||
pk := platforms.Format(platforms.Normalize(p))
|
|
||||||
ps[pk] = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, p := range ps {
|
|
||||||
workers[i] = append(workers[i], p)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := eg.Wait(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// then we can attempt to match against all the available platforms
|
|
||||||
// (this time we don't care about imperfect matches)
|
|
||||||
nodes = map[string][]*resolvedNode{}
|
|
||||||
for k, opt := range opt {
|
|
||||||
node, _, err := r.resolve(ctx, opt.Platforms, pw, platforms.Only, func(idx int, n builder.Node) []specs.Platform {
|
|
||||||
return workers[idx]
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nodes[k] = node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
idxs := make([]int, 0, len(r.nodes))
|
|
||||||
for _, nodes := range nodes {
|
|
||||||
for _, node := range nodes {
|
|
||||||
idxs = append(idxs, node.driverIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// preload capabilities
|
|
||||||
span, ctx := tracing.StartSpan(ctx, "load buildkit capabilities", trace.WithSpanKind(trace.SpanKindInternal))
|
|
||||||
_, err := r.opts(ctx, idxs, pw)
|
|
||||||
tracing.FinishWithError(span, err)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *nodeResolver) resolve(ctx context.Context, ps []specs.Platform, pw progress.Writer, matcher matchMaker, additional func(idx int, n builder.Node) []specs.Platform) ([]*resolvedNode, bool, error) {
|
|
||||||
if len(r.nodes) == 0 {
|
|
||||||
return nil, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
perfect := true
|
|
||||||
nodeIdxs := make([]int, 0)
|
|
||||||
for _, p := range ps {
|
|
||||||
idx := r.get(p, matcher, additional)
|
|
||||||
if idx == -1 {
|
|
||||||
idx = 0
|
|
||||||
perfect = false
|
|
||||||
}
|
|
||||||
nodeIdxs = append(nodeIdxs, idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
var nodes []*resolvedNode
|
|
||||||
if len(nodeIdxs) == 0 {
|
|
||||||
nodes = append(nodes, &resolvedNode{
|
|
||||||
resolver: r,
|
|
||||||
driverIndex: 0,
|
|
||||||
})
|
|
||||||
nodeIdxs = append(nodeIdxs, 0)
|
|
||||||
} else {
|
|
||||||
for i, idx := range nodeIdxs {
|
|
||||||
node := &resolvedNode{
|
|
||||||
resolver: r,
|
|
||||||
driverIndex: idx,
|
|
||||||
}
|
|
||||||
if len(ps) > 0 {
|
|
||||||
node.platforms = []specs.Platform{ps[i]}
|
|
||||||
}
|
|
||||||
nodes = append(nodes, node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes = recombineNodes(nodes)
|
|
||||||
if _, err := r.boot(ctx, nodeIdxs, pw); err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
return nodes, perfect, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *nodeResolver) get(p specs.Platform, matcher matchMaker, additionalPlatforms func(int, builder.Node) []specs.Platform) int {
|
|
||||||
best := -1
|
|
||||||
bestPlatform := specs.Platform{}
|
|
||||||
for i, node := range r.nodes {
|
|
||||||
platforms := node.Platforms
|
|
||||||
if additionalPlatforms != nil {
|
|
||||||
platforms = append([]specs.Platform{}, platforms...)
|
|
||||||
platforms = append(platforms, additionalPlatforms(i, node)...)
|
|
||||||
}
|
|
||||||
for _, p2 := range platforms {
|
|
||||||
m := matcher(p2)
|
|
||||||
if !m.Match(p) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if best == -1 {
|
|
||||||
best = i
|
|
||||||
bestPlatform = p2
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if matcher(p2).Less(p, bestPlatform) {
|
|
||||||
best = i
|
|
||||||
bestPlatform = p2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return best
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *nodeResolver) boot(ctx context.Context, idxs []int, pw progress.Writer) ([]*client.Client, error) {
|
|
||||||
clients := make([]*client.Client, len(idxs))
|
|
||||||
|
|
||||||
baseCtx := ctx
|
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
|
||||||
|
|
||||||
for i, idx := range idxs {
|
|
||||||
i, idx := i, idx
|
|
||||||
eg.Go(func() error {
|
|
||||||
c, err := r.clients.g.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (*client.Client, error) {
|
|
||||||
if r.nodes[idx].Driver == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
r.clients.cacheMu.Lock()
|
|
||||||
c, ok := r.clients.cache[idx]
|
|
||||||
r.clients.cacheMu.Unlock()
|
|
||||||
if ok {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
c, err := driver.Boot(ctx, baseCtx, r.nodes[idx].Driver, pw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r.clients.cacheMu.Lock()
|
|
||||||
r.clients.cache[idx] = c
|
|
||||||
r.clients.cacheMu.Unlock()
|
|
||||||
return c, nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
clients[i] = c
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := eg.Wait(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return clients, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *nodeResolver) opts(ctx context.Context, idxs []int, pw progress.Writer) ([]gateway.BuildOpts, error) {
|
|
||||||
clients, err := r.boot(ctx, idxs, pw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bopts := make([]gateway.BuildOpts, len(clients))
|
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
|
||||||
for i, idxs := range idxs {
|
|
||||||
i, idx := i, idxs
|
|
||||||
c := clients[i]
|
|
||||||
if c == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
eg.Go(func() error {
|
|
||||||
opt, err := r.buildOpts.g.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (gateway.BuildOpts, error) {
|
|
||||||
r.buildOpts.cacheMu.Lock()
|
|
||||||
opt, ok := r.buildOpts.cache[idx]
|
|
||||||
r.buildOpts.cacheMu.Unlock()
|
|
||||||
if ok {
|
|
||||||
return opt, nil
|
|
||||||
}
|
|
||||||
_, err := c.Build(ctx, client.SolveOpt{
|
|
||||||
Internal: true,
|
|
||||||
}, "buildx", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
|
|
||||||
opt = c.BuildOpts()
|
|
||||||
return nil, nil
|
|
||||||
}, nil)
|
|
||||||
if err != nil {
|
|
||||||
return gateway.BuildOpts{}, err
|
|
||||||
}
|
|
||||||
r.buildOpts.cacheMu.Lock()
|
|
||||||
r.buildOpts.cache[idx] = opt
|
|
||||||
r.buildOpts.cacheMu.Unlock()
|
|
||||||
return opt, err
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bopts[i] = opt
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := eg.Wait(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return bopts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// recombineDriverPairs recombines resolved nodes that are on the same driver
|
|
||||||
// back together into a single node.
|
|
||||||
func recombineNodes(nodes []*resolvedNode) []*resolvedNode {
|
|
||||||
result := make([]*resolvedNode, 0, len(nodes))
|
|
||||||
lookup := map[int]int{}
|
|
||||||
for _, node := range nodes {
|
|
||||||
if idx, ok := lookup[node.driverIndex]; ok {
|
|
||||||
result[idx].platforms = append(result[idx].platforms, node.platforms...)
|
|
||||||
} else {
|
|
||||||
lookup[node.driverIndex] = len(result)
|
|
||||||
result = append(result, node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
@@ -1,315 +0,0 @@
|
|||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
|
||||||
"github.com/docker/buildx/builder"
|
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFindDriverSanity(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.DefaultSpec()},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.DefaultSpec()}, nil, platforms.OnlyStrict, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 0, res[0].driverIndex)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
require.Equal(t, []specs.Platform{platforms.DefaultSpec()}, res[0].platforms)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindDriverEmpty(t *testing.T) {
|
|
||||||
r := makeTestResolver(nil)
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.DefaultSpec()}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Nil(t, res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindDriverWeirdName(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/foobar")},
|
|
||||||
})
|
|
||||||
|
|
||||||
// find first platform
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/foobar")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 1, res[0].driverIndex)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindDriverUnknown(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/riscv64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.False(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 0, res[0].driverIndex)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodeSinglePlatform(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/riscv64")},
|
|
||||||
})
|
|
||||||
|
|
||||||
// find first platform
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/amd64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 0, res[0].driverIndex)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
|
|
||||||
// find second platform
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/riscv64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 1, res[0].driverIndex)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
|
|
||||||
// find an unknown platform, should match the first driver
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/s390x")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.False(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 0, res[0].driverIndex)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodeMultiPlatform(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64"), platforms.MustParse("linux/arm64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/riscv64")},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/amd64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 0, res[0].driverIndex)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 0, res[0].driverIndex)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/riscv64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, 1, res[0].driverIndex)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodeNonStrict(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/arm64")},
|
|
||||||
})
|
|
||||||
|
|
||||||
// arm64 should match itself
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
|
|
||||||
// arm64 may support arm/v8
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v8")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
|
|
||||||
// arm64 may support arm/v7
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v7")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodeNonStrictARM(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/arm64")},
|
|
||||||
"ccc": {platforms.MustParse("linux/arm/v8")},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v8")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "ccc", res[0].Node().Builder)
|
|
||||||
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v7")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "ccc", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodeNonStrictLower(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/arm/v7")},
|
|
||||||
})
|
|
||||||
|
|
||||||
// v8 can't be built on v7 (so we should select the default)...
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v8")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.False(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
|
|
||||||
// ...but v6 can be built on v8
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v6")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodePreferStart(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/riscv64")},
|
|
||||||
"ccc": {platforms.MustParse("linux/riscv64")},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/riscv64")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodePreferExact(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/arm/v8")},
|
|
||||||
"bbb": {platforms.MustParse("linux/arm/v7")},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v7")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodeNoPlatform(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/foobar")},
|
|
||||||
"bbb": {platforms.DefaultSpec()},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
require.Empty(t, res[0].platforms)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectNodeAdditionalPlatforms(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/arm/v8")},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v7")}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "bbb", res[0].Node().Builder)
|
|
||||||
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{platforms.MustParse("linux/arm/v7")}, nil, platforms.Only, func(idx int, n builder.Node) []specs.Platform {
|
|
||||||
if n.Builder == "aaa" {
|
|
||||||
return []specs.Platform{platforms.MustParse("linux/arm/v7")}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitNodeMultiPlatform(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64"), platforms.MustParse("linux/arm64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/riscv64")},
|
|
||||||
})
|
|
||||||
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{
|
|
||||||
platforms.MustParse("linux/amd64"),
|
|
||||||
platforms.MustParse("linux/arm64"),
|
|
||||||
}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 1)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
|
|
||||||
res, perfect, err = r.resolve(context.TODO(), []specs.Platform{
|
|
||||||
platforms.MustParse("linux/amd64"),
|
|
||||||
platforms.MustParse("linux/riscv64"),
|
|
||||||
}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 2)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
require.Equal(t, "bbb", res[1].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitNodeMultiPlatformNoUnify(t *testing.T) {
|
|
||||||
r := makeTestResolver(map[string][]specs.Platform{
|
|
||||||
"aaa": {platforms.MustParse("linux/amd64")},
|
|
||||||
"bbb": {platforms.MustParse("linux/amd64"), platforms.MustParse("linux/riscv64")},
|
|
||||||
})
|
|
||||||
|
|
||||||
// the "best" choice would be the node with both platforms, but we're using
|
|
||||||
// a naive algorithm that doesn't try to unify the platforms
|
|
||||||
res, perfect, err := r.resolve(context.TODO(), []specs.Platform{
|
|
||||||
platforms.MustParse("linux/amd64"),
|
|
||||||
platforms.MustParse("linux/riscv64"),
|
|
||||||
}, nil, platforms.Only, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, perfect)
|
|
||||||
require.Len(t, res, 2)
|
|
||||||
require.Equal(t, "aaa", res[0].Node().Builder)
|
|
||||||
require.Equal(t, "bbb", res[1].Node().Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeTestResolver(nodes map[string][]specs.Platform) *nodeResolver {
|
|
||||||
var ns []builder.Node
|
|
||||||
for name, platforms := range nodes {
|
|
||||||
ns = append(ns, builder.Node{
|
|
||||||
Builder: name,
|
|
||||||
Platforms: platforms,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
sort.Slice(ns, func(i, j int) bool {
|
|
||||||
return ns[i].Builder < ns[j].Builder
|
|
||||||
})
|
|
||||||
return newDriverResolver(ns)
|
|
||||||
}
|
|
70
build/git.go
70
build/git.go
@@ -9,18 +9,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/buildx/util/gitutil"
|
"github.com/docker/buildx/util/gitutil"
|
||||||
"github.com/docker/buildx/util/osutil"
|
|
||||||
"github.com/moby/buildkit/client"
|
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DockerfileLabel = "com.docker.image.source.entrypoint"
|
const DockerfileLabel = "com.docker.image.source.entrypoint"
|
||||||
|
|
||||||
func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (map[string]string, func(key, dir string, so *client.SolveOpt), error) {
|
func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (res map[string]string, _ error) {
|
||||||
res := make(map[string]string)
|
res = make(map[string]string)
|
||||||
if contextPath == "" {
|
if contextPath == "" {
|
||||||
return nil, nil, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setGitLabels := false
|
setGitLabels := false
|
||||||
@@ -39,7 +37,7 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !setGitLabels && !setGitInfo {
|
if !setGitLabels && !setGitInfo {
|
||||||
return nil, nil, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// figure out in which directory the git command needs to run in
|
// figure out in which directory the git command needs to run in
|
||||||
@@ -47,32 +45,27 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st
|
|||||||
if filepath.IsAbs(contextPath) {
|
if filepath.IsAbs(contextPath) {
|
||||||
wd = contextPath
|
wd = contextPath
|
||||||
} else {
|
} else {
|
||||||
wd, _ = filepath.Abs(filepath.Join(osutil.GetWd(), contextPath))
|
cwd, _ := os.Getwd()
|
||||||
|
wd, _ = filepath.Abs(filepath.Join(cwd, contextPath))
|
||||||
}
|
}
|
||||||
wd = osutil.SanitizePath(wd)
|
|
||||||
|
|
||||||
gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd))
|
gitc, err := gitutil.New(gitutil.WithContext(ctx), gitutil.WithWorkingDir(wd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() {
|
if st, err1 := os.Stat(path.Join(wd, ".git")); err1 == nil && st.IsDir() {
|
||||||
return res, nil, errors.Wrap(err, "git was not found in the system")
|
return res, errors.Wrap(err, "git was not found in the system")
|
||||||
}
|
}
|
||||||
return nil, nil, nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !gitc.IsInsideWorkTree() {
|
if !gitc.IsInsideWorkTree() {
|
||||||
if st, err := os.Stat(path.Join(wd, ".git")); err == nil && st.IsDir() {
|
if st, err := os.Stat(path.Join(wd, ".git")); err == nil && st.IsDir() {
|
||||||
return res, nil, errors.New("failed to read current commit information with git rev-parse --is-inside-work-tree")
|
return res, errors.New("failed to read current commit information with git rev-parse --is-inside-work-tree")
|
||||||
}
|
}
|
||||||
return nil, nil, nil
|
return res, nil
|
||||||
}
|
|
||||||
|
|
||||||
root, err := gitc.RootDir()
|
|
||||||
if err != nil {
|
|
||||||
return res, nil, errors.Wrap(err, "failed to get git root dir")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if sha, err := gitc.FullCommit(); err != nil && !gitutil.IsUnknownRevision(err) {
|
if sha, err := gitc.FullCommit(); err != nil && !gitutil.IsUnknownRevision(err) {
|
||||||
return res, nil, errors.Wrap(err, "failed to get git commit")
|
return res, errors.Wrap(err, "failed to get git commit")
|
||||||
} else if sha != "" {
|
} else if sha != "" {
|
||||||
checkDirty := false
|
checkDirty := false
|
||||||
if v, ok := os.LookupEnv("BUILDX_GIT_CHECK_DIRTY"); ok {
|
if v, ok := os.LookupEnv("BUILDX_GIT_CHECK_DIRTY"); ok {
|
||||||
@@ -100,32 +93,23 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if setGitLabels && root != "" {
|
if setGitLabels {
|
||||||
if dockerfilePath == "" {
|
if root, err := gitc.RootDir(); err != nil {
|
||||||
dockerfilePath = filepath.Join(wd, "Dockerfile")
|
return res, errors.Wrap(err, "failed to get git root dir")
|
||||||
}
|
} else if root != "" {
|
||||||
if !filepath.IsAbs(dockerfilePath) {
|
if dockerfilePath == "" {
|
||||||
dockerfilePath = filepath.Join(osutil.GetWd(), dockerfilePath)
|
dockerfilePath = filepath.Join(wd, "Dockerfile")
|
||||||
}
|
}
|
||||||
if r, err := filepath.Rel(root, dockerfilePath); err == nil && !strings.HasPrefix(r, "..") {
|
if !filepath.IsAbs(dockerfilePath) {
|
||||||
res["label:"+DockerfileLabel] = r
|
cwd, _ := os.Getwd()
|
||||||
|
dockerfilePath = filepath.Join(cwd, dockerfilePath)
|
||||||
|
}
|
||||||
|
dockerfilePath, _ = filepath.Rel(root, dockerfilePath)
|
||||||
|
if !strings.HasPrefix(dockerfilePath, "..") {
|
||||||
|
res["label:"+DockerfileLabel] = dockerfilePath
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, func(key, dir string, so *client.SolveOpt) {
|
return
|
||||||
if !setGitInfo || root == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dir, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if lp, err := osutil.GetLongPathName(dir); err == nil {
|
|
||||||
dir = lp
|
|
||||||
}
|
|
||||||
dir = osutil.SanitizePath(dir)
|
|
||||||
if r, err := filepath.Rel(root, dir); err == nil && !strings.HasPrefix(r, "..") {
|
|
||||||
so.FrontendAttrs["vcs:localdir:"+key] = r
|
|
||||||
}
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/buildx/util/gitutil"
|
"github.com/docker/buildx/util/gitutil"
|
||||||
"github.com/moby/buildkit/client"
|
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -31,7 +30,7 @@ func setupTest(tb testing.TB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetGitAttributesNotGitRepo(t *testing.T) {
|
func TestGetGitAttributesNotGitRepo(t *testing.T) {
|
||||||
_, _, err := getGitAttributes(context.Background(), t.TempDir(), "Dockerfile")
|
_, err := getGitAttributes(context.Background(), t.TempDir(), "Dockerfile")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,14 +38,14 @@ func TestGetGitAttributesBadGitRepo(t *testing.T) {
|
|||||||
tmp := t.TempDir()
|
tmp := t.TempDir()
|
||||||
require.NoError(t, os.MkdirAll(path.Join(tmp, ".git"), 0755))
|
require.NoError(t, os.MkdirAll(path.Join(tmp, ".git"), 0755))
|
||||||
|
|
||||||
_, _, err := getGitAttributes(context.Background(), tmp, "Dockerfile")
|
_, err := getGitAttributes(context.Background(), tmp, "Dockerfile")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetGitAttributesNoContext(t *testing.T) {
|
func TestGetGitAttributesNoContext(t *testing.T) {
|
||||||
setupTest(t)
|
setupTest(t)
|
||||||
|
|
||||||
gitattrs, _, err := getGitAttributes(context.Background(), "", "Dockerfile")
|
gitattrs, err := getGitAttributes(context.Background(), "", "Dockerfile")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Empty(t, gitattrs)
|
assert.Empty(t, gitattrs)
|
||||||
}
|
}
|
||||||
@@ -115,7 +114,7 @@ func TestGetGitAttributes(t *testing.T) {
|
|||||||
if tt.envGitInfo != "" {
|
if tt.envGitInfo != "" {
|
||||||
t.Setenv("BUILDX_GIT_INFO", tt.envGitInfo)
|
t.Setenv("BUILDX_GIT_INFO", tt.envGitInfo)
|
||||||
}
|
}
|
||||||
gitattrs, _, err := getGitAttributes(context.Background(), ".", "Dockerfile")
|
gitattrs, err := getGitAttributes(context.Background(), ".", "Dockerfile")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for _, e := range tt.expected {
|
for _, e := range tt.expected {
|
||||||
assert.Contains(t, gitattrs, e)
|
assert.Contains(t, gitattrs, e)
|
||||||
@@ -140,7 +139,7 @@ func TestGetGitAttributesDirty(t *testing.T) {
|
|||||||
require.NoError(t, os.WriteFile(filepath.Join("dir", "Dockerfile"), df, 0644))
|
require.NoError(t, os.WriteFile(filepath.Join("dir", "Dockerfile"), df, 0644))
|
||||||
|
|
||||||
t.Setenv("BUILDX_GIT_LABELS", "true")
|
t.Setenv("BUILDX_GIT_LABELS", "true")
|
||||||
gitattrs, _, _ := getGitAttributes(context.Background(), ".", "Dockerfile")
|
gitattrs, _ := getGitAttributes(context.Background(), ".", "Dockerfile")
|
||||||
assert.Equal(t, 5, len(gitattrs))
|
assert.Equal(t, 5, len(gitattrs))
|
||||||
|
|
||||||
assert.Contains(t, gitattrs, "label:"+DockerfileLabel)
|
assert.Contains(t, gitattrs, "label:"+DockerfileLabel)
|
||||||
@@ -155,55 +154,3 @@ func TestGetGitAttributesDirty(t *testing.T) {
|
|||||||
assert.Contains(t, gitattrs, "vcs:revision")
|
assert.Contains(t, gitattrs, "vcs:revision")
|
||||||
assert.True(t, strings.HasSuffix(gitattrs["vcs:revision"], "-dirty"))
|
assert.True(t, strings.HasSuffix(gitattrs["vcs:revision"], "-dirty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLocalDirs(t *testing.T) {
|
|
||||||
setupTest(t)
|
|
||||||
|
|
||||||
so := &client.SolveOpt{
|
|
||||||
FrontendAttrs: map[string]string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, addVCSLocalDir, err := getGitAttributes(context.Background(), ".", "Dockerfile")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, addVCSLocalDir)
|
|
||||||
|
|
||||||
require.NoError(t, setLocalMount("context", ".", so, addVCSLocalDir))
|
|
||||||
require.Contains(t, so.FrontendAttrs, "vcs:localdir:context")
|
|
||||||
assert.Equal(t, ".", so.FrontendAttrs["vcs:localdir:context"])
|
|
||||||
|
|
||||||
require.NoError(t, setLocalMount("dockerfile", ".", so, addVCSLocalDir))
|
|
||||||
require.Contains(t, so.FrontendAttrs, "vcs:localdir:dockerfile")
|
|
||||||
assert.Equal(t, ".", so.FrontendAttrs["vcs:localdir:dockerfile"])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLocalDirsSub(t *testing.T) {
|
|
||||||
gitutil.Mktmp(t)
|
|
||||||
|
|
||||||
c, err := gitutil.New()
|
|
||||||
require.NoError(t, err)
|
|
||||||
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))
|
|
||||||
|
|
||||||
gitutil.GitAdd(c, t, "app/Dockerfile")
|
|
||||||
gitutil.GitCommit(c, t, "initial commit")
|
|
||||||
gitutil.GitSetRemote(c, t, "origin", "git@github.com:docker/buildx.git")
|
|
||||||
|
|
||||||
so := &client.SolveOpt{
|
|
||||||
FrontendAttrs: map[string]string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, addVCSLocalDir, err := getGitAttributes(context.Background(), ".", "app/Dockerfile")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, addVCSLocalDir)
|
|
||||||
|
|
||||||
require.NoError(t, setLocalMount("context", ".", so, addVCSLocalDir))
|
|
||||||
require.Contains(t, so.FrontendAttrs, "vcs:localdir:context")
|
|
||||||
assert.Equal(t, ".", so.FrontendAttrs["vcs:localdir:context"])
|
|
||||||
|
|
||||||
require.NoError(t, setLocalMount("dockerfile", "app", so, addVCSLocalDir))
|
|
||||||
require.Contains(t, so.FrontendAttrs, "vcs:localdir:dockerfile")
|
|
||||||
assert.Equal(t, "app", so.FrontendAttrs["vcs:localdir:dockerfile"])
|
|
||||||
}
|
|
||||||
|
@@ -37,7 +37,7 @@ func NewContainer(ctx context.Context, resultCtx *ResultHandle, cfg *controllera
|
|||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
containerCfg, err := resultCtx.getContainerConfig(cfg)
|
containerCfg, err := resultCtx.getContainerConfig(ctx, c, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
638
build/opt.go
638
build/opt.go
@@ -1,638 +0,0 @@
|
|||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/content"
|
|
||||||
"github.com/containerd/containerd/content/local"
|
|
||||||
"github.com/containerd/containerd/platforms"
|
|
||||||
"github.com/distribution/reference"
|
|
||||||
"github.com/docker/buildx/builder"
|
|
||||||
"github.com/docker/buildx/driver"
|
|
||||||
"github.com/docker/buildx/util/confutil"
|
|
||||||
"github.com/docker/buildx/util/dockerutil"
|
|
||||||
"github.com/docker/buildx/util/osutil"
|
|
||||||
"github.com/docker/buildx/util/progress"
|
|
||||||
"github.com/moby/buildkit/client"
|
|
||||||
"github.com/moby/buildkit/client/llb"
|
|
||||||
"github.com/moby/buildkit/client/ociindex"
|
|
||||||
gateway "github.com/moby/buildkit/frontend/gateway/client"
|
|
||||||
"github.com/moby/buildkit/identity"
|
|
||||||
"github.com/moby/buildkit/session/upload/uploadprovider"
|
|
||||||
"github.com/moby/buildkit/solver/pb"
|
|
||||||
"github.com/moby/buildkit/util/apicaps"
|
|
||||||
"github.com/moby/buildkit/util/entitlements"
|
|
||||||
"github.com/opencontainers/go-digest"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/tonistiigi/fsutil"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt Options, bopts gateway.BuildOpts, configDir string, addVCSLocalDir func(key, dir string, so *client.SolveOpt), pw progress.Writer, docker *dockerutil.Client) (_ *client.SolveOpt, release func(), err error) {
|
|
||||||
nodeDriver := node.Driver
|
|
||||||
defers := make([]func(), 0, 2)
|
|
||||||
releaseF := func() {
|
|
||||||
for _, f := range defers {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
releaseF()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// inline cache from build arg
|
|
||||||
if v, ok := opt.BuildArgs["BUILDKIT_INLINE_CACHE"]; ok {
|
|
||||||
if v, _ := strconv.ParseBool(v); v {
|
|
||||||
opt.CacheTo = append(opt.CacheTo, client.CacheOptionsEntry{
|
|
||||||
Type: "inline",
|
|
||||||
Attrs: map[string]string{},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, e := range opt.CacheTo {
|
|
||||||
if e.Type != "inline" && !nodeDriver.Features(ctx)[driver.CacheExport] {
|
|
||||||
return nil, nil, notSupported(driver.CacheExport, nodeDriver, "https://docs.docker.com/go/build-cache-backends/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheTo := make([]client.CacheOptionsEntry, 0, len(opt.CacheTo))
|
|
||||||
for _, e := range opt.CacheTo {
|
|
||||||
if e.Type == "gha" {
|
|
||||||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if e.Type == "s3" {
|
|
||||||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cacheTo = append(cacheTo, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheFrom := make([]client.CacheOptionsEntry, 0, len(opt.CacheFrom))
|
|
||||||
for _, e := range opt.CacheFrom {
|
|
||||||
if e.Type == "gha" {
|
|
||||||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else if e.Type == "s3" {
|
|
||||||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cacheFrom = append(cacheFrom, e)
|
|
||||||
}
|
|
||||||
|
|
||||||
so := client.SolveOpt{
|
|
||||||
Ref: opt.Ref,
|
|
||||||
Frontend: "dockerfile.v0",
|
|
||||||
FrontendAttrs: map[string]string{},
|
|
||||||
LocalMounts: map[string]fsutil.FS{},
|
|
||||||
CacheExports: cacheTo,
|
|
||||||
CacheImports: cacheFrom,
|
|
||||||
AllowedEntitlements: opt.Allow,
|
|
||||||
SourcePolicy: opt.SourcePolicy,
|
|
||||||
}
|
|
||||||
|
|
||||||
if so.Ref == "" {
|
|
||||||
so.Ref = identity.NewID()
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.CgroupParent != "" {
|
|
||||||
so.FrontendAttrs["cgroup-parent"] = opt.CgroupParent
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := opt.BuildArgs["BUILDKIT_MULTI_PLATFORM"]; ok {
|
|
||||||
if v, _ := strconv.ParseBool(v); v {
|
|
||||||
so.FrontendAttrs["multi-platform"] = "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if multiDriver {
|
|
||||||
// force creation of manifest list
|
|
||||||
so.FrontendAttrs["multi-platform"] = "true"
|
|
||||||
}
|
|
||||||
|
|
||||||
attests := make(map[string]string)
|
|
||||||
for k, v := range opt.Attests {
|
|
||||||
if v != nil {
|
|
||||||
attests[k] = *v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
supportAttestations := bopts.LLBCaps.Contains(apicaps.CapID("exporter.image.attestations")) && nodeDriver.Features(ctx)[driver.MultiPlatform]
|
|
||||||
if len(attests) > 0 {
|
|
||||||
if !supportAttestations {
|
|
||||||
if !nodeDriver.Features(ctx)[driver.MultiPlatform] {
|
|
||||||
return nil, nil, notSupported("Attestation", nodeDriver, "https://docs.docker.com/go/attestations/")
|
|
||||||
}
|
|
||||||
return nil, nil, errors.Errorf("Attestations are not supported by the current BuildKit daemon")
|
|
||||||
}
|
|
||||||
for k, v := range attests {
|
|
||||||
so.FrontendAttrs["attest:"+k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := opt.Attests["provenance"]; !ok && supportAttestations {
|
|
||||||
const noAttestEnv = "BUILDX_NO_DEFAULT_ATTESTATIONS"
|
|
||||||
var noProv bool
|
|
||||||
if v, ok := os.LookupEnv(noAttestEnv); ok {
|
|
||||||
noProv, err = strconv.ParseBool(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, errors.Wrap(err, "invalid "+noAttestEnv)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !noProv {
|
|
||||||
so.FrontendAttrs["attest:provenance"] = "mode=min,inline-only=true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch len(opt.Exports) {
|
|
||||||
case 1:
|
|
||||||
// valid
|
|
||||||
case 0:
|
|
||||||
if !noDefaultLoad() && opt.PrintFunc == nil {
|
|
||||||
if nodeDriver.IsMobyDriver() {
|
|
||||||
// backwards compat for docker driver only:
|
|
||||||
// this ensures the build results in a docker image.
|
|
||||||
opt.Exports = []client.ExportEntry{{Type: "image", Attrs: map[string]string{}}}
|
|
||||||
} else if nodeDriver.Features(ctx)[driver.DefaultLoad] {
|
|
||||||
opt.Exports = []client.ExportEntry{{Type: "docker", Attrs: map[string]string{}}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if err := bopts.LLBCaps.Supports(pb.CapMultipleExporters); err != nil {
|
|
||||||
return nil, nil, errors.Errorf("multiple outputs currently unsupported by the current BuildKit daemon, please upgrade to version v0.13+ or use a single output")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill in image exporter names from tags
|
|
||||||
if len(opt.Tags) > 0 {
|
|
||||||
tags := make([]string, len(opt.Tags))
|
|
||||||
for i, tag := range opt.Tags {
|
|
||||||
ref, err := reference.Parse(tag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, errors.Wrapf(err, "invalid tag %q", tag)
|
|
||||||
}
|
|
||||||
tags[i] = ref.String()
|
|
||||||
}
|
|
||||||
for i, e := range opt.Exports {
|
|
||||||
switch e.Type {
|
|
||||||
case "image", "oci", "docker":
|
|
||||||
opt.Exports[i].Attrs["name"] = strings.Join(tags, ",")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for _, e := range opt.Exports {
|
|
||||||
if e.Type == "image" && e.Attrs["name"] == "" && e.Attrs["push"] != "" {
|
|
||||||
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
|
|
||||||
return nil, nil, errors.Errorf("tag is needed when pushing to registry")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// cacheonly is a fake exporter to opt out of default behaviors
|
|
||||||
exports := make([]client.ExportEntry, 0, len(opt.Exports))
|
|
||||||
for _, e := range opt.Exports {
|
|
||||||
if e.Type != "cacheonly" {
|
|
||||||
exports = append(exports, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
opt.Exports = exports
|
|
||||||
|
|
||||||
// set up exporters
|
|
||||||
for i, e := range opt.Exports {
|
|
||||||
if e.Type == "oci" && !nodeDriver.Features(ctx)[driver.OCIExporter] {
|
|
||||||
return nil, nil, notSupported(driver.OCIExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/")
|
|
||||||
}
|
|
||||||
if e.Type == "docker" {
|
|
||||||
features := docker.Features(ctx, e.Attrs["context"])
|
|
||||||
if features[dockerutil.OCIImporter] && e.Output == nil {
|
|
||||||
// rely on oci importer if available (which supports
|
|
||||||
// multi-platform images), otherwise fall back to docker
|
|
||||||
opt.Exports[i].Type = "oci"
|
|
||||||
} else if len(opt.Platforms) > 1 || len(attests) > 0 {
|
|
||||||
if e.Output != nil {
|
|
||||||
return nil, nil, errors.Errorf("docker exporter does not support exporting manifest lists, use the oci exporter instead")
|
|
||||||
}
|
|
||||||
return nil, nil, errors.Errorf("docker exporter does not currently support exporting manifest lists")
|
|
||||||
}
|
|
||||||
if e.Output == nil {
|
|
||||||
if nodeDriver.IsMobyDriver() {
|
|
||||||
e.Type = "image"
|
|
||||||
} else {
|
|
||||||
w, cancel, err := docker.LoadImage(ctx, e.Attrs["context"], pw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defers = append(defers, cancel)
|
|
||||||
opt.Exports[i].Output = func(_ map[string]string) (io.WriteCloser, error) {
|
|
||||||
return w, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if !nodeDriver.Features(ctx)[driver.DockerExporter] {
|
|
||||||
return nil, nil, notSupported(driver.DockerExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e.Type == "image" && nodeDriver.IsMobyDriver() {
|
|
||||||
opt.Exports[i].Type = "moby"
|
|
||||||
if e.Attrs["push"] != "" {
|
|
||||||
if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok {
|
|
||||||
if ok, _ := strconv.ParseBool(e.Attrs["push-by-digest"]); ok {
|
|
||||||
return nil, nil, errors.Errorf("push-by-digest is currently not implemented for docker driver, please create a new builder instance")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e.Type == "docker" || e.Type == "image" || e.Type == "oci" {
|
|
||||||
// inline buildinfo attrs from build arg
|
|
||||||
if v, ok := opt.BuildArgs["BUILDKIT_INLINE_BUILDINFO_ATTRS"]; ok {
|
|
||||||
e.Attrs["buildinfo-attrs"] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
so.Exports = opt.Exports
|
|
||||||
so.Session = opt.Session
|
|
||||||
|
|
||||||
releaseLoad, err := loadInputs(ctx, nodeDriver, opt.Inputs, addVCSLocalDir, pw, &so)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defers = append(defers, releaseLoad)
|
|
||||||
|
|
||||||
if sharedKey := so.LocalDirs["context"]; sharedKey != "" {
|
|
||||||
if p, err := filepath.Abs(sharedKey); err == nil {
|
|
||||||
sharedKey = filepath.Base(p)
|
|
||||||
}
|
|
||||||
so.SharedKey = sharedKey + ":" + confutil.TryNodeIdentifier(configDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.Pull {
|
|
||||||
so.FrontendAttrs["image-resolve-mode"] = pb.AttrImageResolveModeForcePull
|
|
||||||
} else if nodeDriver.IsMobyDriver() {
|
|
||||||
// moby driver always resolves local images by default
|
|
||||||
so.FrontendAttrs["image-resolve-mode"] = pb.AttrImageResolveModePreferLocal
|
|
||||||
}
|
|
||||||
if opt.Target != "" {
|
|
||||||
so.FrontendAttrs["target"] = opt.Target
|
|
||||||
}
|
|
||||||
if len(opt.NoCacheFilter) > 0 {
|
|
||||||
so.FrontendAttrs["no-cache"] = strings.Join(opt.NoCacheFilter, ",")
|
|
||||||
}
|
|
||||||
if opt.NoCache {
|
|
||||||
so.FrontendAttrs["no-cache"] = ""
|
|
||||||
}
|
|
||||||
for k, v := range opt.BuildArgs {
|
|
||||||
so.FrontendAttrs["build-arg:"+k] = v
|
|
||||||
}
|
|
||||||
for k, v := range opt.Labels {
|
|
||||||
so.FrontendAttrs["label:"+k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range node.ProxyConfig {
|
|
||||||
if _, ok := opt.BuildArgs[k]; !ok {
|
|
||||||
so.FrontendAttrs["build-arg:"+k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set platforms
|
|
||||||
if len(opt.Platforms) != 0 {
|
|
||||||
pp := make([]string, len(opt.Platforms))
|
|
||||||
for i, p := range opt.Platforms {
|
|
||||||
pp[i] = platforms.Format(p)
|
|
||||||
}
|
|
||||||
if len(pp) > 1 && !nodeDriver.Features(ctx)[driver.MultiPlatform] {
|
|
||||||
return nil, nil, notSupported(driver.MultiPlatform, nodeDriver, "https://docs.docker.com/go/build-multi-platform/")
|
|
||||||
}
|
|
||||||
so.FrontendAttrs["platform"] = strings.Join(pp, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup networkmode
|
|
||||||
switch opt.NetworkMode {
|
|
||||||
case "host":
|
|
||||||
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
|
|
||||||
so.AllowedEntitlements = append(so.AllowedEntitlements, entitlements.EntitlementNetworkHost)
|
|
||||||
case "none":
|
|
||||||
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
|
|
||||||
case "", "default":
|
|
||||||
default:
|
|
||||||
return nil, nil, errors.Errorf("network mode %q not supported by buildkit - you can define a custom network for your builder using the network driver-opt in buildx create", opt.NetworkMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup extrahosts
|
|
||||||
extraHosts, err := toBuildkitExtraHosts(ctx, opt.ExtraHosts, nodeDriver)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if len(extraHosts) > 0 {
|
|
||||||
so.FrontendAttrs["add-hosts"] = extraHosts
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup shm size
|
|
||||||
if opt.ShmSize.Value() > 0 {
|
|
||||||
so.FrontendAttrs["shm-size"] = strconv.FormatInt(opt.ShmSize.Value(), 10)
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup ulimits
|
|
||||||
ulimits, err := toBuildkitUlimits(opt.Ulimits)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
} else if len(ulimits) > 0 {
|
|
||||||
so.FrontendAttrs["ulimit"] = ulimits
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark info request as internal
|
|
||||||
if opt.PrintFunc != nil {
|
|
||||||
so.Internal = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return &so, releaseF, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadInputs(ctx context.Context, d *driver.DriverHandle, inp Inputs, addVCSLocalDir func(key, dir string, so *client.SolveOpt), pw progress.Writer, target *client.SolveOpt) (func(), error) {
|
|
||||||
if inp.ContextPath == "" {
|
|
||||||
return nil, errors.New("please specify build context (e.g. \".\" for the current directory)")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: handle stdin, symlinks, remote contexts, check files exist
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
dockerfileReader io.Reader
|
|
||||||
dockerfileDir string
|
|
||||||
dockerfileName = inp.DockerfilePath
|
|
||||||
toRemove []string
|
|
||||||
)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case inp.ContextState != nil:
|
|
||||||
if target.FrontendInputs == nil {
|
|
||||||
target.FrontendInputs = make(map[string]llb.State)
|
|
||||||
}
|
|
||||||
target.FrontendInputs["context"] = *inp.ContextState
|
|
||||||
target.FrontendInputs["dockerfile"] = *inp.ContextState
|
|
||||||
case inp.ContextPath == "-":
|
|
||||||
if inp.DockerfilePath == "-" {
|
|
||||||
return nil, errStdinConflict
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bufio.NewReader(inp.InStream)
|
|
||||||
magic, err := buf.Peek(archiveHeaderSize * 2)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return nil, errors.Wrap(err, "failed to peek context header from STDIN")
|
|
||||||
}
|
|
||||||
if !(err == io.EOF && len(magic) == 0) {
|
|
||||||
if isArchive(magic) {
|
|
||||||
// stdin is context
|
|
||||||
up := uploadprovider.New()
|
|
||||||
target.FrontendAttrs["context"] = up.Add(buf)
|
|
||||||
target.Session = append(target.Session, up)
|
|
||||||
} else {
|
|
||||||
if inp.DockerfilePath != "" {
|
|
||||||
return nil, errDockerfileConflict
|
|
||||||
}
|
|
||||||
// stdin is dockerfile
|
|
||||||
dockerfileReader = buf
|
|
||||||
inp.ContextPath, _ = os.MkdirTemp("", "empty-dir")
|
|
||||||
toRemove = append(toRemove, inp.ContextPath)
|
|
||||||
if err := setLocalMount("context", inp.ContextPath, target, addVCSLocalDir); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case osutil.IsLocalDir(inp.ContextPath):
|
|
||||||
if err := setLocalMount("context", inp.ContextPath, target, addVCSLocalDir); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
switch inp.DockerfilePath {
|
|
||||||
case "-":
|
|
||||||
dockerfileReader = inp.InStream
|
|
||||||
case "":
|
|
||||||
dockerfileDir = inp.ContextPath
|
|
||||||
default:
|
|
||||||
dockerfileDir = filepath.Dir(inp.DockerfilePath)
|
|
||||||
dockerfileName = filepath.Base(inp.DockerfilePath)
|
|
||||||
}
|
|
||||||
case IsRemoteURL(inp.ContextPath):
|
|
||||||
if inp.DockerfilePath == "-" {
|
|
||||||
dockerfileReader = inp.InStream
|
|
||||||
} else if filepath.IsAbs(inp.DockerfilePath) {
|
|
||||||
dockerfileDir = filepath.Dir(inp.DockerfilePath)
|
|
||||||
dockerfileName = filepath.Base(inp.DockerfilePath)
|
|
||||||
target.FrontendAttrs["dockerfilekey"] = "dockerfile"
|
|
||||||
}
|
|
||||||
target.FrontendAttrs["context"] = inp.ContextPath
|
|
||||||
default:
|
|
||||||
return nil, errors.Errorf("unable to prepare context: path %q not found", inp.ContextPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if inp.DockerfileInline != "" {
|
|
||||||
dockerfileReader = strings.NewReader(inp.DockerfileInline)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dockerfileReader != nil {
|
|
||||||
dockerfileDir, err = createTempDockerfile(dockerfileReader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
toRemove = append(toRemove, dockerfileDir)
|
|
||||||
dockerfileName = "Dockerfile"
|
|
||||||
target.FrontendAttrs["dockerfilekey"] = "dockerfile"
|
|
||||||
}
|
|
||||||
if isHTTPURL(inp.DockerfilePath) {
|
|
||||||
dockerfileDir, err = createTempDockerfileFromURL(ctx, d, inp.DockerfilePath, pw)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
toRemove = append(toRemove, dockerfileDir)
|
|
||||||
dockerfileName = "Dockerfile"
|
|
||||||
target.FrontendAttrs["dockerfilekey"] = "dockerfile"
|
|
||||||
delete(target.FrontendInputs, "dockerfile")
|
|
||||||
}
|
|
||||||
|
|
||||||
if dockerfileName == "" {
|
|
||||||
dockerfileName = "Dockerfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
if dockerfileDir != "" {
|
|
||||||
if err := setLocalMount("dockerfile", dockerfileDir, target, addVCSLocalDir); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
dockerfileName = handleLowercaseDockerfile(dockerfileDir, dockerfileName)
|
|
||||||
}
|
|
||||||
|
|
||||||
target.FrontendAttrs["filename"] = dockerfileName
|
|
||||||
|
|
||||||
for k, v := range inp.NamedContexts {
|
|
||||||
target.FrontendAttrs["frontend.caps"] = "moby.buildkit.frontend.contexts+forward"
|
|
||||||
if v.State != nil {
|
|
||||||
target.FrontendAttrs["context:"+k] = "input:" + k
|
|
||||||
if target.FrontendInputs == nil {
|
|
||||||
target.FrontendInputs = make(map[string]llb.State)
|
|
||||||
}
|
|
||||||
target.FrontendInputs[k] = *v.State
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsRemoteURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") {
|
|
||||||
target.FrontendAttrs["context:"+k] = v.Path
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle OCI layout
|
|
||||||
if strings.HasPrefix(v.Path, "oci-layout://") {
|
|
||||||
localPath := strings.TrimPrefix(v.Path, "oci-layout://")
|
|
||||||
localPath, dig, hasDigest := strings.Cut(localPath, "@")
|
|
||||||
localPath, tag, hasTag := strings.Cut(localPath, ":")
|
|
||||||
if !hasTag {
|
|
||||||
tag = "latest"
|
|
||||||
}
|
|
||||||
if !hasDigest {
|
|
||||||
dig, err = resolveDigest(localPath, tag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "oci-layout reference %q could not be resolved", v.Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
store, err := local.NewStore(localPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "invalid store at %s", localPath)
|
|
||||||
}
|
|
||||||
storeName := identity.NewID()
|
|
||||||
if target.OCIStores == nil {
|
|
||||||
target.OCIStores = map[string]content.Store{}
|
|
||||||
}
|
|
||||||
target.OCIStores[storeName] = store
|
|
||||||
|
|
||||||
target.FrontendAttrs["context:"+k] = "oci-layout://" + storeName + ":" + tag + "@" + dig
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
st, err := os.Stat(v.Path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to get build context %v", k)
|
|
||||||
}
|
|
||||||
if !st.IsDir() {
|
|
||||||
return nil, errors.Wrapf(syscall.ENOTDIR, "failed to get build context path %v", v)
|
|
||||||
}
|
|
||||||
localName := k
|
|
||||||
if k == "context" || k == "dockerfile" {
|
|
||||||
localName = "_" + k // underscore to avoid collisions
|
|
||||||
}
|
|
||||||
if err := setLocalMount(localName, v.Path, target, addVCSLocalDir); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
target.FrontendAttrs["context:"+k] = "local:" + localName
|
|
||||||
}
|
|
||||||
|
|
||||||
release := func() {
|
|
||||||
for _, dir := range toRemove {
|
|
||||||
_ = os.RemoveAll(dir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return release, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveDigest(localPath, tag string) (dig string, _ error) {
|
|
||||||
idx := ociindex.NewStoreIndex(localPath)
|
|
||||||
|
|
||||||
// lookup by name
|
|
||||||
desc, err := idx.Get(tag)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
// lookup single
|
|
||||||
desc, err = idx.GetSingle()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if desc == nil {
|
|
||||||
return "", errors.New("failed to resolve digest")
|
|
||||||
}
|
|
||||||
|
|
||||||
dig = string(desc.Digest)
|
|
||||||
_, err = digest.Parse(dig)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.Wrapf(err, "invalid digest %s", dig)
|
|
||||||
}
|
|
||||||
|
|
||||||
return dig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setLocalMount(name, root string, so *client.SolveOpt, addVCSLocalDir func(key, dir string, so *client.SolveOpt)) error {
|
|
||||||
lm, err := fsutil.NewFS(root)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
root, err = filepath.EvalSymlinks(root) // keep same behavior as fsutil.NewFS
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if so.LocalMounts == nil {
|
|
||||||
so.LocalMounts = map[string]fsutil.FS{}
|
|
||||||
}
|
|
||||||
so.LocalMounts[name] = lm
|
|
||||||
if addVCSLocalDir != nil {
|
|
||||||
addVCSLocalDir(name, root, so)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTempDockerfile(r io.Reader) (string, error) {
|
|
||||||
dir, err := os.MkdirTemp("", "dockerfile")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
f, err := os.Create(filepath.Join(dir, "Dockerfile"))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
if _, err := io.Copy(f, r); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return dir, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle https://github.com/moby/moby/pull/10858
|
|
||||||
func handleLowercaseDockerfile(dir, p string) string {
|
|
||||||
if filepath.Base(p) != "Dockerfile" {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Open(filepath.Dir(filepath.Join(dir, p)))
|
|
||||||
if err != nil {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
names, err := f.Readdirnames(-1)
|
|
||||||
if err != nil {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
foundLowerCase := false
|
|
||||||
for _, n := range names {
|
|
||||||
if n == "Dockerfile" {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
if n == "dockerfile" {
|
|
||||||
foundLowerCase = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundLowerCase {
|
|
||||||
return filepath.Join(filepath.Dir(p), "dockerfile")
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
@@ -1,157 +0,0 @@
|
|||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/content"
|
|
||||||
"github.com/containerd/containerd/content/proxy"
|
|
||||||
"github.com/docker/buildx/util/confutil"
|
|
||||||
"github.com/docker/buildx/util/progress"
|
|
||||||
controlapi "github.com/moby/buildkit/api/services/control"
|
|
||||||
"github.com/moby/buildkit/client"
|
|
||||||
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
|
|
||||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
|
||||||
|
|
||||||
type provenancePredicate struct {
|
|
||||||
Builder *provenanceBuilder `json:"builder,omitempty"`
|
|
||||||
provenancetypes.ProvenancePredicate
|
|
||||||
}
|
|
||||||
|
|
||||||
type provenanceBuilder struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func setRecordProvenance(ctx context.Context, c *client.Client, sr *client.SolveResponse, ref string, pw progress.Writer) error {
|
|
||||||
mode := confutil.MetadataProvenance()
|
|
||||||
if mode == confutil.MetadataProvenanceModeDisabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pw = progress.ResetTime(pw)
|
|
||||||
return progress.Wrap("resolving provenance for metadata file", pw.Write, func(l progress.SubLogger) error {
|
|
||||||
res, err := fetchProvenance(ctx, c, ref, mode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for k, v := range res {
|
|
||||||
sr.ExporterResponse[k] = v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchProvenance(ctx context.Context, c *client.Client, ref string, mode confutil.MetadataProvenanceMode) (out map[string]string, err error) {
|
|
||||||
cl, err := c.ControlClient().ListenBuildHistory(ctx, &controlapi.BuildHistoryRequest{
|
|
||||||
Ref: ref,
|
|
||||||
EarlyExit: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var mu sync.Mutex
|
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
|
||||||
store := proxy.NewContentStore(c.ContentClient())
|
|
||||||
for {
|
|
||||||
ev, err := cl.Recv()
|
|
||||||
if errors.Is(err, io.EOF) {
|
|
||||||
break
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ev.Record == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ev.Record.Result != nil {
|
|
||||||
desc := lookupProvenance(ev.Record.Result)
|
|
||||||
if desc == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
eg.Go(func() error {
|
|
||||||
dt, err := content.ReadBlob(ctx, store, *desc)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to load provenance blob from build record")
|
|
||||||
}
|
|
||||||
prv, err := encodeProvenance(dt, mode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
if out == nil {
|
|
||||||
out = make(map[string]string)
|
|
||||||
}
|
|
||||||
out["buildx.build.provenance"] = prv
|
|
||||||
mu.Unlock()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
} else if ev.Record.Results != nil {
|
|
||||||
for platform, res := range ev.Record.Results {
|
|
||||||
platform := platform
|
|
||||||
desc := lookupProvenance(res)
|
|
||||||
if desc == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
eg.Go(func() error {
|
|
||||||
dt, err := content.ReadBlob(ctx, store, *desc)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to load provenance blob from build record")
|
|
||||||
}
|
|
||||||
prv, err := encodeProvenance(dt, mode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mu.Lock()
|
|
||||||
if out == nil {
|
|
||||||
out = make(map[string]string)
|
|
||||||
}
|
|
||||||
out["buildx.build.provenance/"+platform] = prv
|
|
||||||
mu.Unlock()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out, eg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupProvenance(res *controlapi.BuildResultInfo) *ocispecs.Descriptor {
|
|
||||||
for _, a := range res.Attestations {
|
|
||||||
if a.MediaType == "application/vnd.in-toto+json" && strings.HasPrefix(a.Annotations["in-toto.io/predicate-type"], "https://slsa.dev/provenance/") {
|
|
||||||
return &ocispecs.Descriptor{
|
|
||||||
Digest: a.Digest,
|
|
||||||
Size: a.Size_,
|
|
||||||
MediaType: a.MediaType,
|
|
||||||
Annotations: a.Annotations,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeProvenance(dt []byte, mode confutil.MetadataProvenanceMode) (string, error) {
|
|
||||||
var prv provenancePredicate
|
|
||||||
if err := json.Unmarshal(dt, &prv); err != nil {
|
|
||||||
return "", errors.Wrapf(err, "failed to unmarshal provenance")
|
|
||||||
}
|
|
||||||
if prv.Builder != nil && prv.Builder.ID == "" {
|
|
||||||
// reset builder if id is empty
|
|
||||||
prv.Builder = nil
|
|
||||||
}
|
|
||||||
if mode == confutil.MetadataProvenanceModeMin {
|
|
||||||
// reset fields for minimal provenance
|
|
||||||
prv.BuildConfig = nil
|
|
||||||
prv.Metadata = nil
|
|
||||||
}
|
|
||||||
dtprv, err := json.Marshal(prv)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.Wrapf(err, "failed to marshal provenance")
|
|
||||||
}
|
|
||||||
return base64.StdEncoding.EncodeToString(dtprv), nil
|
|
||||||
}
|
|
@@ -292,10 +292,10 @@ func (r *ResultHandle) build(buildFunc gateway.BuildFunc) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResultHandle) getContainerConfig(cfg *controllerapi.InvokeConfig) (containerCfg gateway.NewContainerRequest, _ error) {
|
func (r *ResultHandle) getContainerConfig(ctx context.Context, c gateway.Client, cfg *controllerapi.InvokeConfig) (containerCfg gateway.NewContainerRequest, _ error) {
|
||||||
if r.res != nil && r.solveErr == nil {
|
if r.res != nil && r.solveErr == nil {
|
||||||
logrus.Debugf("creating container from successful build")
|
logrus.Debugf("creating container from successful build")
|
||||||
ccfg, err := containerConfigFromResult(r.res, *cfg)
|
ccfg, err := containerConfigFromResult(ctx, r.res, c, *cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return containerCfg, err
|
return containerCfg, err
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ func (r *ResultHandle) getProcessConfig(cfg *controllerapi.InvokeConfig, stdin i
|
|||||||
return processCfg, nil
|
return processCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func containerConfigFromResult(res *gateway.Result, cfg controllerapi.InvokeConfig) (*gateway.NewContainerRequest, error) {
|
func containerConfigFromResult(ctx context.Context, res *gateway.Result, c gateway.Client, cfg controllerapi.InvokeConfig) (*gateway.NewContainerRequest, error) {
|
||||||
if cfg.Initial {
|
if cfg.Initial {
|
||||||
return nil, errors.Errorf("starting from the container from the initial state of the step is supported only on the failed steps")
|
return nil, errors.Errorf("starting from the container from the initial state of the step is supported only on the failed steps")
|
||||||
}
|
}
|
||||||
|
@@ -6,14 +6,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/buildx/driver"
|
"github.com/docker/buildx/driver"
|
||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
|
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||||
"github.com/moby/buildkit/util/gitutil"
|
"github.com/moby/buildkit/util/gitutil"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -25,15 +24,8 @@ const (
|
|||||||
mobyHostGatewayName = "host-gateway"
|
mobyHostGatewayName = "host-gateway"
|
||||||
)
|
)
|
||||||
|
|
||||||
// isHTTPURL returns true if the provided str is an HTTP(S) URL by checking if it
|
|
||||||
// has a http:// or https:// scheme. No validation is performed to verify if the
|
|
||||||
// URL is well-formed.
|
|
||||||
func isHTTPURL(str string) bool {
|
|
||||||
return strings.HasPrefix(str, "https://") || strings.HasPrefix(str, "http://")
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsRemoteURL(c string) bool {
|
func IsRemoteURL(c string) bool {
|
||||||
if isHTTPURL(c) {
|
if urlutil.IsURL(c) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, err := gitutil.ParseGitRef(c); err == nil {
|
if _, err := gitutil.ParseGitRef(c); err == nil {
|
||||||
@@ -42,6 +34,11 @@ func IsRemoteURL(c string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isLocalDir(c string) bool {
|
||||||
|
st, err := os.Stat(c)
|
||||||
|
return err == nil && st.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
func isArchive(header []byte) bool {
|
func isArchive(header []byte) bool {
|
||||||
for _, m := range [][]byte{
|
for _, m := range [][]byte{
|
||||||
{0x42, 0x5A, 0x68}, // bzip2
|
{0x42, 0x5A, 0x68}, // bzip2
|
||||||
@@ -68,10 +65,7 @@ func toBuildkitExtraHosts(ctx context.Context, inp []string, nodeDriver *driver.
|
|||||||
}
|
}
|
||||||
hosts := make([]string, 0, len(inp))
|
hosts := make([]string, 0, len(inp))
|
||||||
for _, h := range inp {
|
for _, h := range inp {
|
||||||
host, ip, ok := strings.Cut(h, "=")
|
host, ip, ok := strings.Cut(h, ":")
|
||||||
if !ok {
|
|
||||||
host, ip, ok = strings.Cut(h, ":")
|
|
||||||
}
|
|
||||||
if !ok || host == "" || ip == "" {
|
if !ok || host == "" || ip == "" {
|
||||||
return "", errors.Errorf("invalid host %s", h)
|
return "", errors.Errorf("invalid host %s", h)
|
||||||
}
|
}
|
||||||
@@ -83,16 +77,8 @@ func toBuildkitExtraHosts(ctx context.Context, inp []string, nodeDriver *driver.
|
|||||||
return "", errors.Wrap(err, "unable to derive the IP value for host-gateway")
|
return "", errors.Wrap(err, "unable to derive the IP value for host-gateway")
|
||||||
}
|
}
|
||||||
ip = hgip.String()
|
ip = hgip.String()
|
||||||
} else {
|
} else if net.ParseIP(ip) == nil {
|
||||||
// If the address is enclosed in square brackets, extract it (for IPv6, but
|
return "", errors.Errorf("invalid host %s", h)
|
||||||
// permit it for IPv4 as well; we don't know the address family here, but it's
|
|
||||||
// unambiguous).
|
|
||||||
if len(ip) > 2 && ip[0] == '[' && ip[len(ip)-1] == ']' {
|
|
||||||
ip = ip[1 : len(ip)-1]
|
|
||||||
}
|
|
||||||
if net.ParseIP(ip) == nil {
|
|
||||||
return "", errors.Errorf("invalid host %s", h)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
hosts = append(hosts, host+"="+ip)
|
hosts = append(hosts, host+"="+ip)
|
||||||
}
|
}
|
||||||
@@ -110,21 +96,3 @@ func toBuildkitUlimits(inp *opts.UlimitOpt) (string, error) {
|
|||||||
}
|
}
|
||||||
return strings.Join(ulimits, ","), nil
|
return strings.Join(ulimits, ","), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func notSupported(f driver.Feature, d *driver.DriverHandle, docs string) error {
|
|
||||||
return errors.Errorf(`%s is not supported for the %s driver.
|
|
||||||
Switch to a different driver, or turn on the containerd image store, and try again.
|
|
||||||
Learn more at %s`, f, d.Factory().Name(), docs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func noDefaultLoad() bool {
|
|
||||||
v, ok := os.LookupEnv("BUILDX_NO_DEFAULT_LOAD")
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
b, err := strconv.ParseBool(v)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Warnf("invalid non-bool value for BUILDX_NO_DEFAULT_LOAD: %s", v)
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
@@ -1,148 +0,0 @@
|
|||||||
package build
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestToBuildkitExtraHosts(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
doc string
|
|
||||||
input []string
|
|
||||||
expectedOut string // Expect output==input if not set.
|
|
||||||
expectedErr string // Expect success if not set.
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
doc: "IPv4, colon sep",
|
|
||||||
input: []string{`myhost:192.168.0.1`},
|
|
||||||
expectedOut: `myhost=192.168.0.1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv4, eq sep",
|
|
||||||
input: []string{`myhost=192.168.0.1`},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Weird but permitted, IPv4 with brackets",
|
|
||||||
input: []string{`myhost=[192.168.0.1]`},
|
|
||||||
expectedOut: `myhost=192.168.0.1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Host and domain",
|
|
||||||
input: []string{`host.and.domain.invalid:10.0.2.1`},
|
|
||||||
expectedOut: `host.and.domain.invalid=10.0.2.1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6, colon sep",
|
|
||||||
input: []string{`anipv6host:2003:ab34:e::1`},
|
|
||||||
expectedOut: `anipv6host=2003:ab34:e::1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6, colon sep, brackets",
|
|
||||||
input: []string{`anipv6host:[2003:ab34:e::1]`},
|
|
||||||
expectedOut: `anipv6host=2003:ab34:e::1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6, eq sep, brackets",
|
|
||||||
input: []string{`anipv6host=[2003:ab34:e::1]`},
|
|
||||||
expectedOut: `anipv6host=2003:ab34:e::1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6 localhost, colon sep",
|
|
||||||
input: []string{`ipv6local:::1`},
|
|
||||||
expectedOut: `ipv6local=::1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6 localhost, eq sep",
|
|
||||||
input: []string{`ipv6local=::1`},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6 localhost, eq sep, brackets",
|
|
||||||
input: []string{`ipv6local=[::1]`},
|
|
||||||
expectedOut: `ipv6local=::1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6 localhost, non-canonical, colon sep",
|
|
||||||
input: []string{`ipv6local:0:0:0:0:0:0:0:1`},
|
|
||||||
expectedOut: `ipv6local=0:0:0:0:0:0:0:1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6 localhost, non-canonical, eq sep",
|
|
||||||
input: []string{`ipv6local=0:0:0:0:0:0:0:1`},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "IPv6 localhost, non-canonical, eq sep, brackets",
|
|
||||||
input: []string{`ipv6local=[0:0:0:0:0:0:0:1]`},
|
|
||||||
expectedOut: `ipv6local=0:0:0:0:0:0:0:1`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Bad address, colon sep",
|
|
||||||
input: []string{`myhost:192.notanipaddress.1`},
|
|
||||||
expectedErr: `invalid IP address in add-host: "192.notanipaddress.1"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Bad address, eq sep",
|
|
||||||
input: []string{`myhost=192.notanipaddress.1`},
|
|
||||||
expectedErr: `invalid IP address in add-host: "192.notanipaddress.1"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "No sep",
|
|
||||||
input: []string{`thathost-nosemicolon10.0.0.1`},
|
|
||||||
expectedErr: `bad format for add-host: "thathost-nosemicolon10.0.0.1"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Bad IPv6",
|
|
||||||
input: []string{`anipv6host:::::1`},
|
|
||||||
expectedErr: `invalid IP address in add-host: "::::1"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Bad IPv6, trailing colons",
|
|
||||||
input: []string{`ipv6local:::0::`},
|
|
||||||
expectedErr: `invalid IP address in add-host: "::0::"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Bad IPv6, missing close bracket",
|
|
||||||
input: []string{`ipv6addr=[::1`},
|
|
||||||
expectedErr: `invalid IP address in add-host: "[::1"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Bad IPv6, missing open bracket",
|
|
||||||
input: []string{`ipv6addr=::1]`},
|
|
||||||
expectedErr: `invalid IP address in add-host: "::1]"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Missing address, colon sep",
|
|
||||||
input: []string{`myhost.invalid:`},
|
|
||||||
expectedErr: `invalid IP address in add-host: ""`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "Missing address, eq sep",
|
|
||||||
input: []string{`myhost.invalid=`},
|
|
||||||
expectedErr: `invalid IP address in add-host: ""`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
doc: "No input",
|
|
||||||
input: []string{``},
|
|
||||||
expectedErr: `bad format for add-host: ""`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
tc := tc
|
|
||||||
if tc.expectedOut == "" {
|
|
||||||
tc.expectedOut = strings.Join(tc.input, ",")
|
|
||||||
}
|
|
||||||
t.Run(tc.doc, func(t *testing.T) {
|
|
||||||
actualOut, actualErr := toBuildkitExtraHosts(context.TODO(), tc.input, nil)
|
|
||||||
if tc.expectedErr == "" {
|
|
||||||
require.Equal(t, tc.expectedOut, actualOut)
|
|
||||||
require.Nil(t, actualErr)
|
|
||||||
} else {
|
|
||||||
require.Zero(t, actualOut)
|
|
||||||
require.Error(t, actualErr, tc.expectedErr)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,31 +2,19 @@ package builder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/csv"
|
|
||||||
"encoding/json"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/buildx/driver"
|
"github.com/docker/buildx/driver"
|
||||||
k8sutil "github.com/docker/buildx/driver/kubernetes/util"
|
|
||||||
remoteutil "github.com/docker/buildx/driver/remote/util"
|
|
||||||
"github.com/docker/buildx/localstate"
|
|
||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
"github.com/docker/buildx/store/storeutil"
|
"github.com/docker/buildx/store/storeutil"
|
||||||
"github.com/docker/buildx/util/confutil"
|
|
||||||
"github.com/docker/buildx/util/dockerutil"
|
"github.com/docker/buildx/util/dockerutil"
|
||||||
"github.com/docker/buildx/util/imagetools"
|
"github.com/docker/buildx/util/imagetools"
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
dopts "github.com/docker/cli/opts"
|
|
||||||
"github.com/google/shlex"
|
|
||||||
"github.com/moby/buildkit/util/progress/progressui"
|
"github.com/moby/buildkit/util/progress/progressui"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -259,28 +247,6 @@ func (b *Builder) Factory(ctx context.Context, dialMeta map[string][]string) (_
|
|||||||
return b.driverFactory.Factory, err
|
return b.driverFactory.Factory, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) MarshalJSON() ([]byte, error) {
|
|
||||||
var berr string
|
|
||||||
if b.err != nil {
|
|
||||||
berr = strings.TrimSpace(b.err.Error())
|
|
||||||
}
|
|
||||||
return json.Marshal(struct {
|
|
||||||
Name string
|
|
||||||
Driver string
|
|
||||||
LastActivity time.Time `json:",omitempty"`
|
|
||||||
Dynamic bool
|
|
||||||
Nodes []Node
|
|
||||||
Err string `json:",omitempty"`
|
|
||||||
}{
|
|
||||||
Name: b.Name,
|
|
||||||
Driver: b.Driver,
|
|
||||||
LastActivity: b.LastActivity,
|
|
||||||
Dynamic: b.Dynamic,
|
|
||||||
Nodes: b.nodes,
|
|
||||||
Err: berr,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBuilders returns all builders
|
// GetBuilders returns all builders
|
||||||
func GetBuilders(dockerCli command.Cli, txn *store.Txn) ([]*Builder, error) {
|
func GetBuilders(dockerCli command.Cli, txn *store.Txn) ([]*Builder, error) {
|
||||||
storeng, err := txn.List()
|
storeng, err := txn.List()
|
||||||
@@ -331,347 +297,3 @@ func GetBuilders(dockerCli command.Cli, txn *store.Txn) ([]*Builder, error) {
|
|||||||
|
|
||||||
return builders, nil
|
return builders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateOpts struct {
|
|
||||||
Name string
|
|
||||||
Driver string
|
|
||||||
NodeName string
|
|
||||||
Platforms []string
|
|
||||||
BuildkitdFlags string
|
|
||||||
BuildkitdConfigFile string
|
|
||||||
DriverOpts []string
|
|
||||||
Use bool
|
|
||||||
Endpoint string
|
|
||||||
Append bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func Create(ctx context.Context, txn *store.Txn, dockerCli command.Cli, opts CreateOpts) (*Builder, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if opts.Name == "default" {
|
|
||||||
return nil, errors.Errorf("default is a reserved name and cannot be used to identify builder instance")
|
|
||||||
} else if opts.Append && opts.Name == "" {
|
|
||||||
return nil, errors.Errorf("append requires a builder name")
|
|
||||||
}
|
|
||||||
|
|
||||||
name := opts.Name
|
|
||||||
if name == "" {
|
|
||||||
name, err = store.GenerateName(txn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !opts.Append {
|
|
||||||
contexts, err := dockerCli.ContextStore().List()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, c := range contexts {
|
|
||||||
if c.Name == name {
|
|
||||||
return nil, errors.Errorf("instance name %q already exists as context builder", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ng, err := txn.NodeGroupByName(name)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(errors.Cause(err)) {
|
|
||||||
if opts.Append && opts.Name != "" {
|
|
||||||
return nil, errors.Errorf("failed to find instance %q for append", opts.Name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildkitHost := os.Getenv("BUILDKIT_HOST")
|
|
||||||
|
|
||||||
driverName := opts.Driver
|
|
||||||
if driverName == "" {
|
|
||||||
if ng != nil {
|
|
||||||
driverName = ng.Driver
|
|
||||||
} else if opts.Endpoint == "" && buildkitHost != "" {
|
|
||||||
driverName = "remote"
|
|
||||||
} else {
|
|
||||||
f, err := driver.GetDefaultFactory(ctx, opts.Endpoint, dockerCli.Client(), true, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if f == nil {
|
|
||||||
return nil, errors.Errorf("no valid drivers found")
|
|
||||||
}
|
|
||||||
driverName = f.Name()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ng != nil {
|
|
||||||
if opts.NodeName == "" && !opts.Append {
|
|
||||||
return nil, errors.Errorf("existing instance for %q but no append mode, specify the node name to make changes for existing instances", name)
|
|
||||||
}
|
|
||||||
if driverName != ng.Driver {
|
|
||||||
return nil, errors.Errorf("existing instance for %q but has mismatched driver %q", name, ng.Driver)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := driver.GetFactory(driverName, true); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOriginal := ng
|
|
||||||
if ngOriginal != nil {
|
|
||||||
ngOriginal = ngOriginal.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
if ng == nil {
|
|
||||||
ng = &store.NodeGroup{
|
|
||||||
Name: name,
|
|
||||||
Driver: driverName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
driverOpts, err := csvToMap(opts.DriverOpts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
buildkitdFlags, err := parseBuildkitdFlags(opts.BuildkitdFlags, driverName, driverOpts)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ep string
|
|
||||||
var setEp bool
|
|
||||||
switch {
|
|
||||||
case driverName == "kubernetes":
|
|
||||||
if opts.Endpoint != "" {
|
|
||||||
return nil, errors.Errorf("kubernetes driver does not support endpoint args %q", opts.Endpoint)
|
|
||||||
}
|
|
||||||
// generate node name if not provided to avoid duplicated endpoint
|
|
||||||
// error: https://github.com/docker/setup-buildx-action/issues/215
|
|
||||||
nodeName := opts.NodeName
|
|
||||||
if nodeName == "" {
|
|
||||||
nodeName, err = k8sutil.GenerateNodeName(name, txn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// naming endpoint to make append works
|
|
||||||
ep = (&url.URL{
|
|
||||||
Scheme: driverName,
|
|
||||||
Path: "/" + name,
|
|
||||||
RawQuery: (&url.Values{
|
|
||||||
"deployment": {nodeName},
|
|
||||||
"kubeconfig": {os.Getenv("KUBECONFIG")},
|
|
||||||
}).Encode(),
|
|
||||||
}).String()
|
|
||||||
setEp = false
|
|
||||||
case driverName == "remote":
|
|
||||||
if opts.Endpoint != "" {
|
|
||||||
ep = opts.Endpoint
|
|
||||||
} else if buildkitHost != "" {
|
|
||||||
ep = buildkitHost
|
|
||||||
} else {
|
|
||||||
return nil, errors.Errorf("no remote endpoint provided")
|
|
||||||
}
|
|
||||||
ep, err = validateBuildkitEndpoint(ep)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
setEp = true
|
|
||||||
case opts.Endpoint != "":
|
|
||||||
ep, err = validateEndpoint(dockerCli, opts.Endpoint)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
setEp = true
|
|
||||||
default:
|
|
||||||
if dockerCli.CurrentContext() == "default" && dockerCli.DockerEndpoint().TLSData != nil {
|
|
||||||
return nil, errors.Errorf("could not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with context set to <context-name>")
|
|
||||||
}
|
|
||||||
ep, err = dockerutil.GetCurrentEndpoint(dockerCli)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
setEp = false
|
|
||||||
}
|
|
||||||
|
|
||||||
buildkitdConfigFile := opts.BuildkitdConfigFile
|
|
||||||
if buildkitdConfigFile == "" {
|
|
||||||
// if buildkit daemon config is not provided, check if the default one
|
|
||||||
// is available and use it
|
|
||||||
if f, ok := confutil.DefaultConfigFile(dockerCli); ok {
|
|
||||||
buildkitdConfigFile = f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ng.Update(opts.NodeName, ep, opts.Platforms, setEp, opts.Append, buildkitdFlags, buildkitdConfigFile, driverOpts); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := txn.Save(ng); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := New(dockerCli,
|
|
||||||
WithName(ng.Name),
|
|
||||||
WithStore(txn),
|
|
||||||
WithSkippedValidation(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
nodes, err := b.LoadNodes(timeoutCtx, WithData())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, node := range nodes {
|
|
||||||
if err := node.Err; err != nil {
|
|
||||||
err := errors.Errorf("failed to initialize builder %s (%s): %s", ng.Name, node.Name, err)
|
|
||||||
var err2 error
|
|
||||||
if ngOriginal == nil {
|
|
||||||
err2 = txn.Remove(ng.Name)
|
|
||||||
} else {
|
|
||||||
err2 = txn.Save(ngOriginal)
|
|
||||||
}
|
|
||||||
if err2 != nil {
|
|
||||||
return nil, errors.Errorf("could not rollback to previous state: %s", err2)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Use && ep != "" {
|
|
||||||
current, err := dockerutil.GetCurrentEndpoint(dockerCli)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := txn.SetCurrent(current, ng.Name, false, false); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type LeaveOpts struct {
|
|
||||||
Name string
|
|
||||||
NodeName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func Leave(ctx context.Context, txn *store.Txn, dockerCli command.Cli, opts LeaveOpts) error {
|
|
||||||
if opts.Name == "" {
|
|
||||||
return errors.Errorf("leave requires instance name")
|
|
||||||
}
|
|
||||||
if opts.NodeName == "" {
|
|
||||||
return errors.Errorf("leave requires node name")
|
|
||||||
}
|
|
||||||
|
|
||||||
ng, err := txn.NodeGroupByName(opts.Name)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(errors.Cause(err)) {
|
|
||||||
return errors.Errorf("failed to find instance %q for leave", opts.Name)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ng.Leave(opts.NodeName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ls, err := localstate.New(confutil.ConfigDir(dockerCli))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ls.RemoveBuilderNode(ng.Name, opts.NodeName); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return txn.Save(ng)
|
|
||||||
}
|
|
||||||
|
|
||||||
func csvToMap(in []string) (map[string]string, error) {
|
|
||||||
if len(in) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
m := make(map[string]string, len(in))
|
|
||||||
for _, s := range in {
|
|
||||||
csvReader := csv.NewReader(strings.NewReader(s))
|
|
||||||
fields, err := csvReader.Read()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, v := range fields {
|
|
||||||
p := strings.SplitN(v, "=", 2)
|
|
||||||
if len(p) != 2 {
|
|
||||||
return nil, errors.Errorf("invalid value %q, expecting k=v", v)
|
|
||||||
}
|
|
||||||
m[p[0]] = p[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateEndpoint validates that endpoint is either a context or a docker host
|
|
||||||
func validateEndpoint(dockerCli command.Cli, ep string) (string, error) {
|
|
||||||
dem, err := dockerutil.GetDockerEndpoint(dockerCli, ep)
|
|
||||||
if err == nil && dem != nil {
|
|
||||||
if ep == "default" {
|
|
||||||
return dem.Host, nil
|
|
||||||
}
|
|
||||||
return ep, nil
|
|
||||||
}
|
|
||||||
h, err := dopts.ParseHost(true, ep)
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.Wrapf(err, "failed to parse endpoint %s", ep)
|
|
||||||
}
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateBuildkitEndpoint validates that endpoint is a valid buildkit host
|
|
||||||
func validateBuildkitEndpoint(ep string) (string, error) {
|
|
||||||
if err := remoteutil.IsValidEndpoint(ep); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return ep, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseBuildkitdFlags parses buildkit flags
|
|
||||||
func parseBuildkitdFlags(inp string, driver string, driverOpts map[string]string) (res []string, err error) {
|
|
||||||
if inp != "" {
|
|
||||||
res, err = shlex.Split(inp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to parse buildkit flags")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowInsecureEntitlements []string
|
|
||||||
flags := pflag.NewFlagSet("buildkitd", pflag.ContinueOnError)
|
|
||||||
flags.Usage = func() {}
|
|
||||||
flags.StringArrayVar(&allowInsecureEntitlements, "allow-insecure-entitlement", nil, "")
|
|
||||||
_ = flags.Parse(res)
|
|
||||||
|
|
||||||
var hasNetworkHostEntitlement bool
|
|
||||||
for _, e := range allowInsecureEntitlements {
|
|
||||||
if e == "network.host" {
|
|
||||||
hasNetworkHostEntitlement = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := driverOpts["network"]; ok && v == "host" && !hasNetworkHostEntitlement && driver == "docker-container" {
|
|
||||||
// always set network.host entitlement if user has set network=host
|
|
||||||
res = append(res, "--allow-insecure-entitlement=network.host")
|
|
||||||
} else if len(allowInsecureEntitlements) == 0 && (driver == "kubernetes" || driver == "docker-container") {
|
|
||||||
// set network.host entitlement if user does not provide any as
|
|
||||||
// network is isolated for container drivers.
|
|
||||||
res = append(res, "--allow-insecure-entitlement=network.host")
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
@@ -1,139 +0,0 @@
|
|||||||
package builder
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCsvToMap(t *testing.T) {
|
|
||||||
d := []string{
|
|
||||||
"\"tolerations=key=foo,value=bar;key=foo2,value=bar2\",replicas=1",
|
|
||||||
"namespace=default",
|
|
||||||
}
|
|
||||||
r, err := csvToMap(d)
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Contains(t, r, "tolerations")
|
|
||||||
require.Equal(t, r["tolerations"], "key=foo,value=bar;key=foo2,value=bar2")
|
|
||||||
|
|
||||||
require.Contains(t, r, "replicas")
|
|
||||||
require.Equal(t, r["replicas"], "1")
|
|
||||||
|
|
||||||
require.Contains(t, r, "namespace")
|
|
||||||
require.Equal(t, r["namespace"], "default")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseBuildkitdFlags(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
flags string
|
|
||||||
driver string
|
|
||||||
driverOpts map[string]string
|
|
||||||
expected []string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"docker-container no flags",
|
|
||||||
"",
|
|
||||||
"docker-container",
|
|
||||||
nil,
|
|
||||||
[]string{
|
|
||||||
"--allow-insecure-entitlement=network.host",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"kubernetes no flags",
|
|
||||||
"",
|
|
||||||
"kubernetes",
|
|
||||||
nil,
|
|
||||||
[]string{
|
|
||||||
"--allow-insecure-entitlement=network.host",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"remote no flags",
|
|
||||||
"",
|
|
||||||
"remote",
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"docker-container with insecure flag",
|
|
||||||
"--allow-insecure-entitlement=security.insecure",
|
|
||||||
"docker-container",
|
|
||||||
nil,
|
|
||||||
[]string{
|
|
||||||
"--allow-insecure-entitlement=security.insecure",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"docker-container with insecure and host flag",
|
|
||||||
"--allow-insecure-entitlement=network.host --allow-insecure-entitlement=security.insecure",
|
|
||||||
"docker-container",
|
|
||||||
nil,
|
|
||||||
[]string{
|
|
||||||
"--allow-insecure-entitlement=network.host",
|
|
||||||
"--allow-insecure-entitlement=security.insecure",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"docker-container with network host opt",
|
|
||||||
"",
|
|
||||||
"docker-container",
|
|
||||||
map[string]string{"network": "host"},
|
|
||||||
[]string{
|
|
||||||
"--allow-insecure-entitlement=network.host",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"docker-container with host flag and network host opt",
|
|
||||||
"--allow-insecure-entitlement=network.host",
|
|
||||||
"docker-container",
|
|
||||||
map[string]string{"network": "host"},
|
|
||||||
[]string{
|
|
||||||
"--allow-insecure-entitlement=network.host",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"docker-container with insecure, host flag and network host opt",
|
|
||||||
"--allow-insecure-entitlement=network.host --allow-insecure-entitlement=security.insecure",
|
|
||||||
"docker-container",
|
|
||||||
map[string]string{"network": "host"},
|
|
||||||
[]string{
|
|
||||||
"--allow-insecure-entitlement=network.host",
|
|
||||||
"--allow-insecure-entitlement=security.insecure",
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"error parsing flags",
|
|
||||||
"foo'",
|
|
||||||
"docker-container",
|
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range testCases {
|
|
||||||
tt := tt
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
flags, err := parseBuildkitdFlags(tt.flags, tt.driver, tt.driverOpts)
|
|
||||||
if tt.wantErr {
|
|
||||||
require.Error(t, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, tt.expected, flags)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,11 +2,8 @@ package builder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
|
||||||
"github.com/docker/buildx/driver"
|
"github.com/docker/buildx/driver"
|
||||||
ctxkube "github.com/docker/buildx/driver/kubernetes/context"
|
ctxkube "github.com/docker/buildx/driver/kubernetes/context"
|
||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
@@ -142,7 +139,7 @@ func (b *Builder) LoadNodes(ctx context.Context, opts ...LoadNodesOption) (_ []N
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := driver.GetDriver(ctx, driver.BuilderName(n.Name), factory, n.Endpoint, dockerapi, imageopt.Auth, kcc, n.BuildkitdFlags, n.Files, n.DriverOpts, n.Platforms, b.opts.contextPathHash, lno.dialMeta)
|
d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, factory, n.Endpoint, dockerapi, imageopt.Auth, kcc, n.Flags, n.Files, n.DriverOpts, n.Platforms, b.opts.contextPathHash, lno.dialMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
node.Err = err
|
node.Err = err
|
||||||
return nil
|
return nil
|
||||||
@@ -186,7 +183,7 @@ func (b *Builder) LoadNodes(ctx context.Context, opts ...LoadNodesOption) (_ []N
|
|||||||
if pl := di.DriverInfo.DynamicNodes[i].Platforms; len(pl) > 0 {
|
if pl := di.DriverInfo.DynamicNodes[i].Platforms; len(pl) > 0 {
|
||||||
diClone.Platforms = pl
|
diClone.Platforms = pl
|
||||||
}
|
}
|
||||||
nodes = append(nodes, diClone)
|
nodes = append(nodes, di)
|
||||||
}
|
}
|
||||||
dynamicNodes = append(dynamicNodes, di.DriverInfo.DynamicNodes...)
|
dynamicNodes = append(dynamicNodes, di.DriverInfo.DynamicNodes...)
|
||||||
}
|
}
|
||||||
@@ -202,51 +199,6 @@ func (b *Builder) LoadNodes(ctx context.Context, opts ...LoadNodesOption) (_ []N
|
|||||||
return b.nodes, nil
|
return b.nodes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) MarshalJSON() ([]byte, error) {
|
|
||||||
var status string
|
|
||||||
if n.DriverInfo != nil {
|
|
||||||
status = n.DriverInfo.Status.String()
|
|
||||||
}
|
|
||||||
var nerr string
|
|
||||||
if n.Err != nil {
|
|
||||||
status = "error"
|
|
||||||
nerr = strings.TrimSpace(n.Err.Error())
|
|
||||||
}
|
|
||||||
var pp []string
|
|
||||||
for _, p := range n.Platforms {
|
|
||||||
pp = append(pp, platforms.Format(p))
|
|
||||||
}
|
|
||||||
return json.Marshal(struct {
|
|
||||||
Name string
|
|
||||||
Endpoint string
|
|
||||||
BuildkitdFlags []string `json:"Flags,omitempty"`
|
|
||||||
DriverOpts map[string]string `json:",omitempty"`
|
|
||||||
Files map[string][]byte `json:",omitempty"`
|
|
||||||
Status string `json:",omitempty"`
|
|
||||||
ProxyConfig map[string]string `json:",omitempty"`
|
|
||||||
Version string `json:",omitempty"`
|
|
||||||
Err string `json:",omitempty"`
|
|
||||||
IDs []string `json:",omitempty"`
|
|
||||||
Platforms []string `json:",omitempty"`
|
|
||||||
GCPolicy []client.PruneInfo `json:",omitempty"`
|
|
||||||
Labels map[string]string `json:",omitempty"`
|
|
||||||
}{
|
|
||||||
Name: n.Name,
|
|
||||||
Endpoint: n.Endpoint,
|
|
||||||
BuildkitdFlags: n.BuildkitdFlags,
|
|
||||||
DriverOpts: n.DriverOpts,
|
|
||||||
Files: n.Files,
|
|
||||||
Status: status,
|
|
||||||
ProxyConfig: n.ProxyConfig,
|
|
||||||
Version: n.Version,
|
|
||||||
Err: nerr,
|
|
||||||
IDs: n.IDs,
|
|
||||||
Platforms: pp,
|
|
||||||
GCPolicy: n.GCPolicy,
|
|
||||||
Labels: n.Labels,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Node) loadData(ctx context.Context) error {
|
func (n *Node) loadData(ctx context.Context) error {
|
||||||
if n.Driver == nil {
|
if n.Driver == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -16,7 +15,6 @@ import (
|
|||||||
cliflags "github.com/docker/cli/cli/flags"
|
cliflags "github.com/docker/cli/cli/flags"
|
||||||
"github.com/moby/buildkit/solver/errdefs"
|
"github.com/moby/buildkit/solver/errdefs"
|
||||||
"github.com/moby/buildkit/util/stack"
|
"github.com/moby/buildkit/util/stack"
|
||||||
"go.opentelemetry.io/otel"
|
|
||||||
|
|
||||||
//nolint:staticcheck // vendored dependencies may still use this
|
//nolint:staticcheck // vendored dependencies may still use this
|
||||||
"github.com/containerd/containerd/pkg/seed"
|
"github.com/containerd/containerd/pkg/seed"
|
||||||
@@ -40,27 +38,10 @@ func runStandalone(cmd *command.DockerCli) error {
|
|||||||
if err := cmd.Initialize(cliflags.NewClientOptions()); err != nil {
|
if err := cmd.Initialize(cliflags.NewClientOptions()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer flushMetrics(cmd)
|
|
||||||
|
|
||||||
rootCmd := commands.NewRootCmd(os.Args[0], false, cmd)
|
rootCmd := commands.NewRootCmd(os.Args[0], false, cmd)
|
||||||
return rootCmd.Execute()
|
return rootCmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
// flushMetrics will manually flush metrics from the configured
|
|
||||||
// meter provider. This is needed when running in standalone mode
|
|
||||||
// because the meter provider is initialized by the cli library,
|
|
||||||
// but the mechanism for forcing it to report is not presently
|
|
||||||
// exposed and not invoked when run in standalone mode.
|
|
||||||
// There are plans to fix that in the next release, but this is
|
|
||||||
// needed temporarily until the API for this is more thorough.
|
|
||||||
func flushMetrics(cmd *command.DockerCli) {
|
|
||||||
if mp, ok := cmd.MeterProvider().(command.MeterProvider); ok {
|
|
||||||
if err := mp.ForceFlush(context.Background()); err != nil {
|
|
||||||
otel.Handle(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runPlugin(cmd *command.DockerCli) error {
|
func runPlugin(cmd *command.DockerCli) error {
|
||||||
rootCmd := commands.NewRootCmd("buildx", true, cmd)
|
rootCmd := commands.NewRootCmd("buildx", true, cmd)
|
||||||
return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{
|
return plugin.RunPlugin(cmd, rootCmd, manager.Metadata{
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/moby/buildkit/util/tracing/detect"
|
"github.com/moby/buildkit/util/tracing/detect"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
|
||||||
|
_ "github.com/moby/buildkit/util/tracing/detect/delegated"
|
||||||
_ "github.com/moby/buildkit/util/tracing/env"
|
_ "github.com/moby/buildkit/util/tracing/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/docker/buildx/util/tracing"
|
"github.com/docker/buildx/util/tracing"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/moby/buildkit/identity"
|
"github.com/moby/buildkit/identity"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/moby/buildkit/util/progress/progressui"
|
"github.com/moby/buildkit/util/progress/progressui"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -41,7 +42,9 @@ type bakeOptions struct {
|
|||||||
exportLoad bool
|
exportLoad bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
func runBake(dockerCli command.Cli, targets []string, in bakeOptions, cFlags commonFlags) (err error) {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -72,10 +75,12 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||||||
|
|
||||||
overrides := in.overrides
|
overrides := in.overrides
|
||||||
if in.exportPush {
|
if in.exportPush {
|
||||||
|
if in.exportLoad {
|
||||||
|
return errors.Errorf("push and load may not be set together at the moment")
|
||||||
|
}
|
||||||
overrides = append(overrides, "*.push=true")
|
overrides = append(overrides, "*.push=true")
|
||||||
}
|
} else if in.exportLoad {
|
||||||
if in.exportLoad {
|
overrides = append(overrides, "*.output=type=docker")
|
||||||
overrides = append(overrides, "*.load=true")
|
|
||||||
}
|
}
|
||||||
if cFlags.noCache != nil {
|
if cFlags.noCache != nil {
|
||||||
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache))
|
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *cFlags.noCache))
|
||||||
@@ -136,7 +141,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
err = err1
|
err = err1
|
||||||
}
|
}
|
||||||
if err == nil && progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode {
|
if err == nil && progressMode != progressui.QuietMode {
|
||||||
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
|
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,12 +207,12 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// local state group
|
||||||
groupRef := identity.NewID()
|
groupRef := identity.NewID()
|
||||||
var refs []string
|
var refs []string
|
||||||
for k, b := range bo {
|
for k, b := range bo {
|
||||||
b.Ref = identity.NewID()
|
b.Ref = identity.NewID()
|
||||||
b.GroupRef = groupRef
|
b.GroupRef = groupRef
|
||||||
b.WithProvenanceResponse = len(in.metadataFile) > 0
|
|
||||||
refs = append(refs, b.Ref)
|
refs = append(refs, b.Ref)
|
||||||
bo[k] = b
|
bo[k] = b
|
||||||
}
|
}
|
||||||
@@ -261,7 +266,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
options.builder = rootOpts.builder
|
options.builder = rootOpts.builder
|
||||||
options.metadataFile = cFlags.metadataFile
|
options.metadataFile = cFlags.metadataFile
|
||||||
// Other common flags (noCache, pull and progress) are processed in runBake function.
|
// Other common flags (noCache, pull and progress) are processed in runBake function.
|
||||||
return runBake(cmd.Context(), dockerCli, args, options, cFlags)
|
return runBake(dockerCli, args, options, cFlags)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.BakeTargets(options.files),
|
ValidArgsFunction: completion.BakeTargets(options.files),
|
||||||
}
|
}
|
||||||
@@ -290,17 +295,13 @@ func saveLocalStateGroup(dockerCli command.Cli, ref string, lsg localstate.State
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer) (files []bake.File, inp *bake.Input, err error) {
|
func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names []string, stdin io.Reader, pw progress.Writer) (files []bake.File, inp *bake.Input, err error) {
|
||||||
var lnames []string // local
|
var lnames []string
|
||||||
var rnames []string // remote
|
var rnames []string
|
||||||
var anames []string // both
|
|
||||||
for _, v := range names {
|
for _, v := range names {
|
||||||
if strings.HasPrefix(v, "cwd://") {
|
if strings.HasPrefix(v, "cwd://") {
|
||||||
tname := strings.TrimPrefix(v, "cwd://")
|
lnames = append(lnames, strings.TrimPrefix(v, "cwd://"))
|
||||||
lnames = append(lnames, tname)
|
|
||||||
anames = append(anames, tname)
|
|
||||||
} else {
|
} else {
|
||||||
rnames = append(rnames, v)
|
rnames = append(rnames, v)
|
||||||
anames = append(anames, v)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +320,7 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names
|
|||||||
if url != "" {
|
if url != "" {
|
||||||
lfiles, err = bake.ReadLocalFiles(lnames, stdin, sub)
|
lfiles, err = bake.ReadLocalFiles(lnames, stdin, sub)
|
||||||
} else {
|
} else {
|
||||||
lfiles, err = bake.ReadLocalFiles(anames, stdin, sub)
|
lfiles, err = bake.ReadLocalFiles(append(lnames, rnames...), stdin, sub)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
@@ -3,10 +3,8 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -15,8 +13,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
"github.com/docker/buildx/build"
|
"github.com/docker/buildx/build"
|
||||||
@@ -31,12 +27,8 @@ import (
|
|||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
"github.com/docker/buildx/store/storeutil"
|
"github.com/docker/buildx/store/storeutil"
|
||||||
"github.com/docker/buildx/util/buildflags"
|
"github.com/docker/buildx/util/buildflags"
|
||||||
"github.com/docker/buildx/util/cobrautil"
|
|
||||||
"github.com/docker/buildx/util/confutil"
|
|
||||||
"github.com/docker/buildx/util/desktop"
|
"github.com/docker/buildx/util/desktop"
|
||||||
"github.com/docker/buildx/util/ioset"
|
"github.com/docker/buildx/util/ioset"
|
||||||
"github.com/docker/buildx/util/metricutil"
|
|
||||||
"github.com/docker/buildx/util/osutil"
|
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
"github.com/docker/buildx/util/tracing"
|
"github.com/docker/buildx/util/tracing"
|
||||||
"github.com/docker/cli-docs-tool/annotation"
|
"github.com/docker/cli-docs-tool/annotation"
|
||||||
@@ -48,10 +40,10 @@ import (
|
|||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||||
"github.com/moby/buildkit/frontend/subrequests"
|
"github.com/moby/buildkit/frontend/subrequests"
|
||||||
"github.com/moby/buildkit/frontend/subrequests/lint"
|
|
||||||
"github.com/moby/buildkit/frontend/subrequests/outline"
|
"github.com/moby/buildkit/frontend/subrequests/outline"
|
||||||
"github.com/moby/buildkit/frontend/subrequests/targets"
|
"github.com/moby/buildkit/frontend/subrequests/targets"
|
||||||
"github.com/moby/buildkit/solver/errdefs"
|
"github.com/moby/buildkit/solver/errdefs"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/moby/buildkit/util/grpcerrors"
|
"github.com/moby/buildkit/util/grpcerrors"
|
||||||
"github.com/moby/buildkit/util/progress/progressui"
|
"github.com/moby/buildkit/util/progress/progressui"
|
||||||
"github.com/morikuni/aec"
|
"github.com/morikuni/aec"
|
||||||
@@ -59,8 +51,6 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
|
||||||
"go.opentelemetry.io/otel/metric"
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -206,8 +196,6 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.WithProvenanceResponse = opts.PrintFunc == nil && len(o.metadataFile) > 0
|
|
||||||
|
|
||||||
return &opts, nil
|
return &opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,55 +210,8 @@ func (o *buildOptions) toDisplayMode() (progressui.DisplayMode, error) {
|
|||||||
return progress, nil
|
return progress, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildMetricAttributes(dockerCli command.Cli, b *builder.Builder, options *buildOptions) attribute.Set {
|
func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
|
||||||
return attribute.NewSet(
|
ctx := appcontext.Context()
|
||||||
attribute.String("command.name", "build"),
|
|
||||||
attribute.Stringer("command.options.hash", &buildOptionsHash{
|
|
||||||
buildOptions: options,
|
|
||||||
configDir: confutil.ConfigDir(dockerCli),
|
|
||||||
}),
|
|
||||||
attribute.String("driver.name", options.builder),
|
|
||||||
attribute.String("driver.type", b.Driver),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildOptionsHash computes a hash for the buildOptions when the String method is invoked.
|
|
||||||
// This is done so we can delay the computation of the hash until needed by OTEL using
|
|
||||||
// the fmt.Stringer interface.
|
|
||||||
type buildOptionsHash struct {
|
|
||||||
*buildOptions
|
|
||||||
configDir string
|
|
||||||
result string
|
|
||||||
resultOnce sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *buildOptionsHash) String() string {
|
|
||||||
o.resultOnce.Do(func() {
|
|
||||||
target := o.target
|
|
||||||
contextPath := o.contextPath
|
|
||||||
dockerfile := o.dockerfileName
|
|
||||||
if dockerfile == "" {
|
|
||||||
dockerfile = "Dockerfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
if contextPath != "-" && osutil.IsLocalDir(contextPath) {
|
|
||||||
contextPath = osutil.ToAbs(contextPath)
|
|
||||||
}
|
|
||||||
salt := confutil.TryNodeIdentifier(o.configDir)
|
|
||||||
|
|
||||||
h := sha256.New()
|
|
||||||
for _, s := range []string{target, contextPath, dockerfile, salt} {
|
|
||||||
_, _ = io.WriteString(h, s)
|
|
||||||
h.Write([]byte{0})
|
|
||||||
}
|
|
||||||
o.result = hex.EncodeToString(h.Sum(nil))
|
|
||||||
})
|
|
||||||
return o.result
|
|
||||||
}
|
|
||||||
|
|
||||||
func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions) (err error) {
|
|
||||||
mp := dockerCli.MeterProvider()
|
|
||||||
|
|
||||||
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
|
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -311,7 +252,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||||||
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
|
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
|
||||||
term = true
|
term = true
|
||||||
}
|
}
|
||||||
attributes := buildMetricAttributes(dockerCli, b, &options)
|
|
||||||
|
|
||||||
ctx2, cancel := context.WithCancel(context.TODO())
|
ctx2, cancel := context.WithCancel(context.TODO())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -325,7 +265,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||||||
fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver),
|
fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver),
|
||||||
fmt.Sprintf("%s:%s", b.Driver, b.Name),
|
fmt.Sprintf("%s:%s", b.Driver, b.Name),
|
||||||
),
|
),
|
||||||
progress.WithMetrics(mp, attributes),
|
|
||||||
progress.WithOnClose(func() {
|
progress.WithOnClose(func() {
|
||||||
printWarnings(os.Stderr, printer.Warnings(), progressMode)
|
printWarnings(os.Stderr, printer.Warnings(), progressMode)
|
||||||
}),
|
}),
|
||||||
@@ -334,43 +273,38 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
done := timeBuildCommand(mp, attributes)
|
|
||||||
var resp *client.SolveResponse
|
var resp *client.SolveResponse
|
||||||
var retErr error
|
var retErr error
|
||||||
if confutil.IsExperimental() {
|
if isExperimental() {
|
||||||
resp, retErr = runControllerBuild(ctx, dockerCli, opts, options, printer)
|
resp, retErr = runControllerBuild(ctx, dockerCli, opts, options, printer)
|
||||||
} else {
|
} else {
|
||||||
resp, retErr = runBasicBuild(ctx, dockerCli, opts, printer)
|
resp, retErr = runBasicBuild(ctx, dockerCli, opts, options, printer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := printer.Wait(); retErr == nil {
|
if err := printer.Wait(); retErr == nil {
|
||||||
retErr = err
|
retErr = err
|
||||||
}
|
}
|
||||||
|
|
||||||
done(retErr)
|
|
||||||
if retErr != nil {
|
if retErr != nil {
|
||||||
return retErr
|
return retErr
|
||||||
}
|
}
|
||||||
|
|
||||||
switch progressMode {
|
if progressMode != progressui.QuietMode {
|
||||||
case progressui.RawJSONMode:
|
|
||||||
// no additional display
|
|
||||||
case progressui.QuietMode:
|
|
||||||
fmt.Println(getImageID(resp.ExporterResponse))
|
|
||||||
default:
|
|
||||||
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
|
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
|
||||||
|
} else {
|
||||||
|
fmt.Println(getImageID(resp.ExporterResponse))
|
||||||
}
|
}
|
||||||
if options.imageIDFile != "" {
|
if options.imageIDFile != "" {
|
||||||
if err := os.WriteFile(options.imageIDFile, []byte(getImageID(resp.ExporterResponse)), 0644); err != nil {
|
if err := os.WriteFile(options.imageIDFile, []byte(getImageID(resp.ExporterResponse)), 0644); err != nil {
|
||||||
return errors.Wrap(err, "writing image ID file")
|
return errors.Wrap(err, "writing image ID file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if opts.PrintFunc != nil {
|
if options.metadataFile != "" {
|
||||||
if err := printResult(opts.PrintFunc, resp.ExporterResponse); err != nil {
|
if err := writeMetadataFile(options.metadataFile, decodeExporterResponse(resp.ExporterResponse)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else if options.metadataFile != "" {
|
}
|
||||||
if err := writeMetadataFile(options.metadataFile, decodeExporterResponse(resp.ExporterResponse)); err != nil {
|
if opts.PrintFunc != nil {
|
||||||
|
if err := printResult(opts.PrintFunc, resp.ExporterResponse); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,7 +320,7 @@ func getImageID(resp map[string]string) string {
|
|||||||
return dgst
|
return dgst
|
||||||
}
|
}
|
||||||
|
|
||||||
func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, printer *progress.Printer) (*client.SolveResponse, error) {
|
func runBasicBuild(ctx context.Context, dockerCli command.Cli, opts *controllerapi.BuildOptions, options buildOptions, printer *progress.Printer) (*client.SolveResponse, error) {
|
||||||
resp, res, err := cbuild.RunBuild(ctx, dockerCli, *opts, dockerCli.In(), printer, false)
|
resp, res, err := cbuild.RunBuild(ctx, dockerCli, *opts, dockerCli.In(), printer, false)
|
||||||
if res != nil {
|
if res != nil {
|
||||||
res.Done()
|
res.Done()
|
||||||
@@ -419,22 +353,14 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
|
|||||||
var ref string
|
var ref string
|
||||||
var retErr error
|
var retErr error
|
||||||
var resp *client.SolveResponse
|
var resp *client.SolveResponse
|
||||||
|
f := ioset.NewSingleForwarder()
|
||||||
var f *ioset.SingleForwarder
|
f.SetReader(dockerCli.In())
|
||||||
var pr io.ReadCloser
|
pr, pw := io.Pipe()
|
||||||
var pw io.WriteCloser
|
f.SetWriter(pw, func() io.WriteCloser {
|
||||||
if options.invokeConfig == nil {
|
pw.Close() // propagate EOF
|
||||||
pr = dockerCli.In()
|
logrus.Debug("propagating stdin close")
|
||||||
} else {
|
return nil
|
||||||
f = ioset.NewSingleForwarder()
|
})
|
||||||
f.SetReader(dockerCli.In())
|
|
||||||
pr, pw = io.Pipe()
|
|
||||||
f.SetWriter(pw, func() io.WriteCloser {
|
|
||||||
pw.Close() // propagate EOF
|
|
||||||
logrus.Debug("propagating stdin close")
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
ref, resp, err = c.Build(ctx, *opts, pr, printer)
|
ref, resp, err = c.Build(ctx, *opts, pr, printer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -448,13 +374,11 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.invokeConfig != nil {
|
if err := pw.Close(); err != nil {
|
||||||
if err := pw.Close(); err != nil {
|
logrus.Debug("failed to close stdin pipe writer")
|
||||||
logrus.Debug("failed to close stdin pipe writer")
|
}
|
||||||
}
|
if err := pr.Close(); err != nil {
|
||||||
if err := pr.Close(); err != nil {
|
logrus.Debug("failed to close stdin pipe reader")
|
||||||
logrus.Debug("failed to close stdin pipe reader")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.invokeConfig != nil && options.invokeConfig.needsDebug(retErr) {
|
if options.invokeConfig != nil && options.invokeConfig.needsDebug(retErr) {
|
||||||
@@ -548,7 +472,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
|
|||||||
options.invokeConfig = iConfig
|
options.invokeConfig = iConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
return runBuild(cmd.Context(), dockerCli, *options)
|
return runBuild(dockerCli, *options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||||
return nil, cobra.ShellCompDirectiveFilterDirs
|
return nil, cobra.ShellCompDirectiveFilterDirs
|
||||||
@@ -583,7 +507,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
|
|||||||
flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`)
|
flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`)
|
||||||
flags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/reference/cli/docker/image/build/#file"})
|
flags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/reference/cli/docker/image/build/#file"})
|
||||||
|
|
||||||
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to a file")
|
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.labels, "label", []string{}, "Set metadata for an image")
|
flags.StringArrayVar(&options.labels, "label", []string{}, "Set metadata for an image")
|
||||||
|
|
||||||
@@ -597,13 +521,18 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
|
|||||||
|
|
||||||
flags.StringArrayVar(&options.platforms, "platform", platformsDefault, "Set target platform for build")
|
flags.StringArrayVar(&options.platforms, "platform", platformsDefault, "Set target platform for build")
|
||||||
|
|
||||||
|
if isExperimental() {
|
||||||
|
flags.StringVar(&options.printFunc, "print", "", "Print result of information request (e.g., outline, targets)")
|
||||||
|
flags.SetAnnotation("print", "experimentalCLI", nil)
|
||||||
|
}
|
||||||
|
|
||||||
flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--output=type=registry"`)
|
flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--output=type=registry"`)
|
||||||
|
|
||||||
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
|
||||||
|
|
||||||
flags.StringArrayVar(&options.secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`)
|
flags.StringArrayVar(&options.secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`)
|
||||||
|
|
||||||
flags.Var(&options.shmSize, "shm-size", `Shared memory size for build containers`)
|
flags.Var(&options.shmSize, "shm-size", `Size of "/dev/shm"`)
|
||||||
|
|
||||||
flags.StringArrayVar(&options.ssh, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|<id>[=<socket>|<key>[,<key>]]")`)
|
flags.StringArrayVar(&options.ssh, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|<id>[=<socket>|<key>[,<key>]]")`)
|
||||||
|
|
||||||
@@ -620,28 +549,22 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
|
|||||||
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--attest=type=sbom"`)
|
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--attest=type=sbom"`)
|
||||||
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--attest=type=provenance"`)
|
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--attest=type=provenance"`)
|
||||||
|
|
||||||
if confutil.IsExperimental() {
|
if isExperimental() {
|
||||||
// TODO: move this to debug command if needed
|
// TODO: move this to debug command if needed
|
||||||
flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect")
|
flags.StringVar(&options.Root, "root", "", "Specify root directory of server to connect")
|
||||||
|
flags.SetAnnotation("root", "experimentalCLI", nil)
|
||||||
flags.BoolVar(&options.Detach, "detach", false, "Detach buildx server (supported only on linux)")
|
flags.BoolVar(&options.Detach, "detach", false, "Detach buildx server (supported only on linux)")
|
||||||
|
flags.SetAnnotation("detach", "experimentalCLI", nil)
|
||||||
flags.StringVar(&options.ServerConfig, "server-config", "", "Specify buildx server config file (used only when launching new server)")
|
flags.StringVar(&options.ServerConfig, "server-config", "", "Specify buildx server config file (used only when launching new server)")
|
||||||
cobrautil.MarkFlagsExperimental(flags, "root", "detach", "server-config")
|
flags.SetAnnotation("server-config", "experimentalCLI", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
flags.StringVar(&options.printFunc, "call", "build", `Set method for evaluating build ("check", "outline", "targets")`)
|
|
||||||
flags.VarPF(callAlias(options, "check"), "check", "", `Shorthand for "--call=check"`)
|
|
||||||
flags.Lookup("check").NoOptDefVal = "true"
|
|
||||||
|
|
||||||
// hidden flags
|
// hidden flags
|
||||||
var ignore string
|
var ignore string
|
||||||
var ignoreSlice []string
|
var ignoreSlice []string
|
||||||
var ignoreBool bool
|
var ignoreBool bool
|
||||||
var ignoreInt int64
|
var ignoreInt int64
|
||||||
|
|
||||||
flags.StringVar(&options.printFunc, "print", "", "Print result of information request (e.g., outline, targets)")
|
|
||||||
cobrautil.MarkFlagsExperimental(flags, "print")
|
|
||||||
flags.MarkHidden("print")
|
|
||||||
|
|
||||||
flags.BoolVar(&ignoreBool, "compress", false, "Compress the build context using gzip")
|
flags.BoolVar(&ignoreBool, "compress", false, "Compress the build context using gzip")
|
||||||
flags.MarkHidden("compress")
|
flags.MarkHidden("compress")
|
||||||
|
|
||||||
@@ -656,7 +579,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
|
|||||||
flags.BoolVar(&ignoreBool, "squash", false, "Squash newly built layers into a single new layer")
|
flags.BoolVar(&ignoreBool, "squash", false, "Squash newly built layers into a single new layer")
|
||||||
flags.MarkHidden("squash")
|
flags.MarkHidden("squash")
|
||||||
flags.SetAnnotation("squash", "flag-warn", []string{"experimental flag squash is removed with BuildKit. You should squash inside build using a multi-stage Dockerfile for efficiency."})
|
flags.SetAnnotation("squash", "flag-warn", []string{"experimental flag squash is removed with BuildKit. You should squash inside build using a multi-stage Dockerfile for efficiency."})
|
||||||
cobrautil.MarkFlagsExperimental(flags, "squash")
|
flags.SetAnnotation("squash", "experimentalCLI", nil)
|
||||||
|
|
||||||
flags.StringVarP(&ignore, "memory", "m", "", "Memory limit")
|
flags.StringVarP(&ignore, "memory", "m", "", "Memory limit")
|
||||||
flags.MarkHidden("memory")
|
flags.MarkHidden("memory")
|
||||||
@@ -699,9 +622,9 @@ type commonFlags struct {
|
|||||||
|
|
||||||
func commonBuildFlags(options *commonFlags, flags *pflag.FlagSet) {
|
func commonBuildFlags(options *commonFlags, flags *pflag.FlagSet) {
|
||||||
options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image")
|
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", "plain", "tty"). Use plain to show container output`)
|
||||||
options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images")
|
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")
|
flags.StringVar(&options.metadataFile, "metadata-file", "", "Write build result metadata to the file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkWarnedFlags(f *pflag.Flag) {
|
func checkWarnedFlags(f *pflag.Flag) {
|
||||||
@@ -773,6 +696,14 @@ func (w *wrapped) Unwrap() error {
|
|||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isExperimental() bool {
|
||||||
|
if v, ok := os.LookupEnv("BUILDX_EXPERIMENTAL"); ok {
|
||||||
|
vv, _ := strconv.ParseBool(v)
|
||||||
|
return vv
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
|
func updateLastActivity(dockerCli command.Cli, ng *store.NodeGroup) error {
|
||||||
txn, release, err := storeutil.GetStore(dockerCli)
|
txn, release, err := storeutil.GetStore(dockerCli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -818,7 +749,7 @@ func dockerUlimitToControllerUlimit(u *dockeropts.UlimitOpt) *controllerapi.Ulim
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printWarnings(w io.Writer, warnings []client.VertexWarning, mode progressui.DisplayMode) {
|
func printWarnings(w io.Writer, warnings []client.VertexWarning, mode progressui.DisplayMode) {
|
||||||
if len(warnings) == 0 || mode == progressui.QuietMode || mode == progressui.RawJSONMode {
|
if len(warnings) == 0 || mode == progressui.QuietMode {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "\n ")
|
fmt.Fprintf(w, "\n ")
|
||||||
@@ -865,22 +796,13 @@ func printResult(f *controllerapi.PrintFunc, res map[string]string) error {
|
|||||||
return printValue(targets.PrintTargets, targets.SubrequestsTargetsDefinition.Version, f.Format, res)
|
return printValue(targets.PrintTargets, targets.SubrequestsTargetsDefinition.Version, f.Format, res)
|
||||||
case "subrequests.describe":
|
case "subrequests.describe":
|
||||||
return printValue(subrequests.PrintDescribe, subrequests.SubrequestsDescribeDefinition.Version, f.Format, res)
|
return printValue(subrequests.PrintDescribe, subrequests.SubrequestsDescribeDefinition.Version, f.Format, res)
|
||||||
case "lint":
|
|
||||||
return printValue(lint.PrintLintViolations, lint.SubrequestLintDefinition.Version, f.Format, res)
|
|
||||||
default:
|
default:
|
||||||
if dt, ok := res["result.json"]; ok && f.Format == "json" {
|
if dt, ok := res["result.txt"]; ok {
|
||||||
fmt.Println(dt)
|
|
||||||
} else if dt, ok := res["result.txt"]; ok {
|
|
||||||
fmt.Print(dt)
|
fmt.Print(dt)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("%s %+v", f, res)
|
log.Printf("%s %+v", f, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v, ok := res["result.statuscode"]; !f.IgnoreStatus && ok {
|
|
||||||
if n, err := strconv.Atoi(v); err == nil && n != 0 {
|
|
||||||
os.Exit(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,52 +926,3 @@ func maybeJSONArray(v string) []string {
|
|||||||
}
|
}
|
||||||
return []string{v}
|
return []string{v}
|
||||||
}
|
}
|
||||||
|
|
||||||
func callAlias(options *buildOptions, value string) cobrautil.BoolFuncValue {
|
|
||||||
return func(s string) error {
|
|
||||||
v, err := strconv.ParseBool(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if v {
|
|
||||||
options.printFunc = value
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// timeBuildCommand will start a timer for timing the build command. It records the time when the returned
|
|
||||||
// function is invoked into a metric.
|
|
||||||
func timeBuildCommand(mp metric.MeterProvider, attrs attribute.Set) func(err error) {
|
|
||||||
meter := metricutil.Meter(mp)
|
|
||||||
counter, _ := meter.Float64Counter("command.time",
|
|
||||||
metric.WithDescription("Measures the duration of the build command."),
|
|
||||||
metric.WithUnit("ms"),
|
|
||||||
)
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
return func(err error) {
|
|
||||||
dur := float64(time.Since(start)) / float64(time.Millisecond)
|
|
||||||
extraAttrs := attribute.NewSet()
|
|
||||||
if err != nil {
|
|
||||||
extraAttrs = attribute.NewSet(
|
|
||||||
attribute.String("error.type", otelErrorType(err)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
counter.Add(context.Background(), dur,
|
|
||||||
metric.WithAttributeSet(attrs),
|
|
||||||
metric.WithAttributeSet(extraAttrs),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otelErrorType returns an attribute for the error type based on the error category.
|
|
||||||
// If nil, this function returns an invalid attribute.
|
|
||||||
func otelErrorType(err error) string {
|
|
||||||
name := "generic"
|
|
||||||
if errors.Is(err, context.Canceled) {
|
|
||||||
name = "canceled"
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
@@ -3,34 +3,71 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/csv"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
"github.com/docker/buildx/driver"
|
"github.com/docker/buildx/driver"
|
||||||
|
k8sutil "github.com/docker/buildx/driver/kubernetes/util"
|
||||||
|
remoteutil "github.com/docker/buildx/driver/remote/util"
|
||||||
|
"github.com/docker/buildx/localstate"
|
||||||
|
"github.com/docker/buildx/store"
|
||||||
"github.com/docker/buildx/store/storeutil"
|
"github.com/docker/buildx/store/storeutil"
|
||||||
"github.com/docker/buildx/util/cobrautil"
|
"github.com/docker/buildx/util/cobrautil"
|
||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
|
"github.com/docker/buildx/util/confutil"
|
||||||
|
"github.com/docker/buildx/util/dockerutil"
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
dopts "github.com/docker/cli/opts"
|
||||||
|
"github.com/google/shlex"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
type createOptions struct {
|
type createOptions struct {
|
||||||
name string
|
name string
|
||||||
driver string
|
driver string
|
||||||
nodeName string
|
nodeName string
|
||||||
platform []string
|
platform []string
|
||||||
actionAppend bool
|
actionAppend bool
|
||||||
actionLeave bool
|
actionLeave bool
|
||||||
use bool
|
use bool
|
||||||
driverOpts []string
|
flags string
|
||||||
buildkitdFlags string
|
configFile string
|
||||||
buildkitdConfigFile string
|
driverOpts []string
|
||||||
bootstrap bool
|
bootstrap bool
|
||||||
// upgrade bool // perform upgrade of the driver
|
// upgrade bool // perform upgrade of the driver
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, args []string) error {
|
func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
|
if in.name == "default" {
|
||||||
|
return errors.Errorf("default is a reserved name and cannot be used to identify builder instance")
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.actionLeave {
|
||||||
|
if in.name == "" {
|
||||||
|
return errors.Errorf("leave requires instance name")
|
||||||
|
}
|
||||||
|
if in.nodeName == "" {
|
||||||
|
return errors.Errorf("leave requires node name but --node not set")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.actionAppend {
|
||||||
|
if in.name == "" {
|
||||||
|
logrus.Warnf("append used without name, creating a new instance instead")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
txn, release, err := storeutil.GetStore(dockerCli)
|
txn, release, err := storeutil.GetStore(dockerCli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -38,34 +75,232 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
|||||||
// Ensure the file lock gets released no matter what happens.
|
// Ensure the file lock gets released no matter what happens.
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
if in.actionLeave {
|
name := in.name
|
||||||
return builder.Leave(ctx, txn, dockerCli, builder.LeaveOpts{
|
if name == "" {
|
||||||
Name: in.name,
|
name, err = store.GenerateName(txn)
|
||||||
NodeName: in.nodeName,
|
if err != nil {
|
||||||
})
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !in.actionLeave && !in.actionAppend {
|
||||||
|
contexts, err := dockerCli.ContextStore().List()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, c := range contexts {
|
||||||
|
if c.Name == name {
|
||||||
|
logrus.Warnf("instance name %q already exists as context builder", name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ng, err := txn.NodeGroupByName(name)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(errors.Cause(err)) {
|
||||||
|
if in.actionAppend && in.name != "" {
|
||||||
|
logrus.Warnf("failed to find %q for append, creating a new instance instead", in.name)
|
||||||
|
}
|
||||||
|
if in.actionLeave {
|
||||||
|
return errors.Errorf("failed to find instance %q for leave", in.name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildkitHost := os.Getenv("BUILDKIT_HOST")
|
||||||
|
|
||||||
|
driverName := in.driver
|
||||||
|
if driverName == "" {
|
||||||
|
if ng != nil {
|
||||||
|
driverName = ng.Driver
|
||||||
|
} else if len(args) == 0 && buildkitHost != "" {
|
||||||
|
driverName = "remote"
|
||||||
|
} else {
|
||||||
|
var arg string
|
||||||
|
if len(args) > 0 {
|
||||||
|
arg = args[0]
|
||||||
|
}
|
||||||
|
f, err := driver.GetDefaultFactory(ctx, arg, dockerCli.Client(), true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f == nil {
|
||||||
|
return errors.Errorf("no valid drivers found")
|
||||||
|
}
|
||||||
|
driverName = f.Name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ng != nil {
|
||||||
|
if in.nodeName == "" && !in.actionAppend {
|
||||||
|
return errors.Errorf("existing instance for %q but no append mode, specify --node to make changes for existing instances", name)
|
||||||
|
}
|
||||||
|
if driverName != ng.Driver {
|
||||||
|
return errors.Errorf("existing instance for %q but has mismatched driver %q", name, ng.Driver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := driver.GetFactory(driverName, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOriginal := ng
|
||||||
|
if ngOriginal != nil {
|
||||||
|
ngOriginal = ngOriginal.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ng == nil {
|
||||||
|
ng = &store.NodeGroup{
|
||||||
|
Name: name,
|
||||||
|
Driver: driverName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var flags []string
|
||||||
|
if in.flags != "" {
|
||||||
|
flags, err = shlex.Split(in.flags)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "failed to parse buildkit flags")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ep string
|
var ep string
|
||||||
if len(args) > 0 {
|
var setEp bool
|
||||||
ep = args[0]
|
if in.actionLeave {
|
||||||
|
if err := ng.Leave(in.nodeName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ls, err := localstate.New(confutil.ConfigDir(dockerCli))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ls.RemoveBuilderNode(ng.Name, in.nodeName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch {
|
||||||
|
case driverName == "kubernetes":
|
||||||
|
if len(args) > 0 {
|
||||||
|
logrus.Warnf("kubernetes driver does not support endpoint args %q", args[0])
|
||||||
|
}
|
||||||
|
// generate node name if not provided to avoid duplicated endpoint
|
||||||
|
// error: https://github.com/docker/setup-buildx-action/issues/215
|
||||||
|
nodeName := in.nodeName
|
||||||
|
if nodeName == "" {
|
||||||
|
nodeName, err = k8sutil.GenerateNodeName(name, txn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// naming endpoint to make --append works
|
||||||
|
ep = (&url.URL{
|
||||||
|
Scheme: driverName,
|
||||||
|
Path: "/" + name,
|
||||||
|
RawQuery: (&url.Values{
|
||||||
|
"deployment": {nodeName},
|
||||||
|
"kubeconfig": {os.Getenv("KUBECONFIG")},
|
||||||
|
}).Encode(),
|
||||||
|
}).String()
|
||||||
|
setEp = false
|
||||||
|
case driverName == "remote":
|
||||||
|
if len(args) > 0 {
|
||||||
|
ep = args[0]
|
||||||
|
} else if buildkitHost != "" {
|
||||||
|
ep = buildkitHost
|
||||||
|
} else {
|
||||||
|
return errors.Errorf("no remote endpoint provided")
|
||||||
|
}
|
||||||
|
ep, err = validateBuildkitEndpoint(ep)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setEp = true
|
||||||
|
case len(args) > 0:
|
||||||
|
ep, err = validateEndpoint(dockerCli, args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setEp = true
|
||||||
|
default:
|
||||||
|
if dockerCli.CurrentContext() == "default" && dockerCli.DockerEndpoint().TLSData != nil {
|
||||||
|
return errors.Errorf("could not create a builder instance with TLS data loaded from environment. Please use `docker context create <context-name>` to create a context for current environment and then create a builder instance with `docker buildx create <context-name>`")
|
||||||
|
}
|
||||||
|
ep, err = dockerutil.GetCurrentEndpoint(dockerCli)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setEp = false
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := csvToMap(in.driverOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.configFile == "" {
|
||||||
|
// if buildkit config is not provided, check if the default one is
|
||||||
|
// available and use it
|
||||||
|
if f, ok := confutil.DefaultConfigFile(dockerCli); ok {
|
||||||
|
logrus.Warnf("Using default BuildKit config in %s", f)
|
||||||
|
in.configFile = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ng.Update(in.nodeName, ep, in.platform, setEp, in.actionAppend, flags, in.configFile, m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := builder.Create(ctx, txn, dockerCli, builder.CreateOpts{
|
if err := txn.Save(ng); err != nil {
|
||||||
Name: in.name,
|
return err
|
||||||
Driver: in.driver,
|
}
|
||||||
NodeName: in.nodeName,
|
|
||||||
Platforms: in.platform,
|
b, err := builder.New(dockerCli,
|
||||||
DriverOpts: in.driverOpts,
|
builder.WithName(ng.Name),
|
||||||
BuildkitdFlags: in.buildkitdFlags,
|
builder.WithStore(txn),
|
||||||
BuildkitdConfigFile: in.buildkitdConfigFile,
|
builder.WithSkippedValidation(),
|
||||||
Use: in.use,
|
)
|
||||||
Endpoint: ep,
|
|
||||||
Append: in.actionAppend,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
nodes, err := b.LoadNodes(timeoutCtx, builder.WithData())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, node := range nodes {
|
||||||
|
if err := node.Err; err != nil {
|
||||||
|
err := errors.Errorf("failed to initialize builder %s (%s): %s", ng.Name, node.Name, err)
|
||||||
|
var err2 error
|
||||||
|
if ngOriginal == nil {
|
||||||
|
err2 = txn.Remove(ng.Name)
|
||||||
|
} else {
|
||||||
|
err2 = txn.Save(ngOriginal)
|
||||||
|
}
|
||||||
|
if err2 != nil {
|
||||||
|
logrus.Warnf("Could not rollback to previous state: %s", err2)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if in.use && ep != "" {
|
||||||
|
current, err := dockerutil.GetCurrentEndpoint(dockerCli)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := txn.SetCurrent(current, ng.Name, false, false); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The store is no longer used from this point.
|
// The store is no longer used from this point.
|
||||||
// Release it so we aren't holding the file lock during the boot.
|
// Release it so we aren't holding the file lock during the boot.
|
||||||
release()
|
release()
|
||||||
@@ -76,7 +311,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s\n", b.Name)
|
fmt.Printf("%s\n", ng.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +331,7 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
|
|||||||
Short: "Create a new builder instance",
|
Short: "Create a new builder instance",
|
||||||
Args: cli.RequiresMaxArgs(1),
|
Args: cli.RequiresMaxArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runCreate(cmd.Context(), dockerCli, options, args)
|
return runCreate(dockerCli, options, args)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.Disable,
|
ValidArgsFunction: completion.Disable,
|
||||||
}
|
}
|
||||||
@@ -106,16 +341,12 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
|
|||||||
flags.StringVar(&options.name, "name", "", "Builder instance name")
|
flags.StringVar(&options.name, "name", "", "Builder instance name")
|
||||||
flags.StringVar(&options.driver, "driver", "", fmt.Sprintf("Driver to use (available: %s)", drivers.String()))
|
flags.StringVar(&options.driver, "driver", "", fmt.Sprintf("Driver to use (available: %s)", drivers.String()))
|
||||||
flags.StringVar(&options.nodeName, "node", "", "Create/modify node with given name")
|
flags.StringVar(&options.nodeName, "node", "", "Create/modify node with given name")
|
||||||
|
flags.StringVar(&options.flags, "buildkitd-flags", "", "Flags for buildkitd daemon")
|
||||||
|
flags.StringVar(&options.configFile, "config", "", "BuildKit config file")
|
||||||
flags.StringArrayVar(&options.platform, "platform", []string{}, "Fixed platforms for current node")
|
flags.StringArrayVar(&options.platform, "platform", []string{}, "Fixed platforms for current node")
|
||||||
flags.StringArrayVar(&options.driverOpts, "driver-opt", []string{}, "Options for the driver")
|
flags.StringArrayVar(&options.driverOpts, "driver-opt", []string{}, "Options for the driver")
|
||||||
flags.StringVar(&options.buildkitdFlags, "buildkitd-flags", "", "BuildKit daemon flags")
|
|
||||||
|
|
||||||
// we allow for both "--config" and "--buildkitd-config", although the latter is the recommended way to avoid ambiguity.
|
|
||||||
flags.StringVar(&options.buildkitdConfigFile, "buildkitd-config", "", "BuildKit daemon config file")
|
|
||||||
flags.StringVar(&options.buildkitdConfigFile, "config", "", "BuildKit daemon config file")
|
|
||||||
flags.MarkHidden("config")
|
|
||||||
|
|
||||||
flags.BoolVar(&options.bootstrap, "bootstrap", false, "Boot builder after creation")
|
flags.BoolVar(&options.bootstrap, "bootstrap", false, "Boot builder after creation")
|
||||||
|
|
||||||
flags.BoolVar(&options.actionAppend, "append", false, "Append a node to builder instead of changing it")
|
flags.BoolVar(&options.actionAppend, "append", false, "Append a node to builder instead of changing it")
|
||||||
flags.BoolVar(&options.actionLeave, "leave", false, "Remove a node from builder instead of changing it")
|
flags.BoolVar(&options.actionLeave, "leave", false, "Remove a node from builder instead of changing it")
|
||||||
flags.BoolVar(&options.use, "use", false, "Set the current builder instance")
|
flags.BoolVar(&options.use, "use", false, "Set the current builder instance")
|
||||||
@@ -125,3 +356,49 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
|
|||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func csvToMap(in []string) (map[string]string, error) {
|
||||||
|
if len(in) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
m := make(map[string]string, len(in))
|
||||||
|
for _, s := range in {
|
||||||
|
csvReader := csv.NewReader(strings.NewReader(s))
|
||||||
|
fields, err := csvReader.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, v := range fields {
|
||||||
|
p := strings.SplitN(v, "=", 2)
|
||||||
|
if len(p) != 2 {
|
||||||
|
return nil, errors.Errorf("invalid value %q, expecting k=v", v)
|
||||||
|
}
|
||||||
|
m[p[0]] = p[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateEndpoint validates that endpoint is either a context or a docker host
|
||||||
|
func validateEndpoint(dockerCli command.Cli, ep string) (string, error) {
|
||||||
|
dem, err := dockerutil.GetDockerEndpoint(dockerCli, ep)
|
||||||
|
if err == nil && dem != nil {
|
||||||
|
if ep == "default" {
|
||||||
|
return dem.Host, nil
|
||||||
|
}
|
||||||
|
return ep, nil
|
||||||
|
}
|
||||||
|
h, err := dopts.ParseHost(true, ep)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "failed to parse endpoint %s", ep)
|
||||||
|
}
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateBuildkitEndpoint validates that endpoint is a valid buildkit host
|
||||||
|
func validateBuildkitEndpoint(ep string) (string, error) {
|
||||||
|
if err := remoteutil.IsValidEndpoint(ep); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return ep, nil
|
||||||
|
}
|
||||||
|
26
commands/create_test.go
Normal file
26
commands/create_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCsvToMap(t *testing.T) {
|
||||||
|
d := []string{
|
||||||
|
"\"tolerations=key=foo,value=bar;key=foo2,value=bar2\",replicas=1",
|
||||||
|
"namespace=default",
|
||||||
|
}
|
||||||
|
r, err := csvToMap(d)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Contains(t, r, "tolerations")
|
||||||
|
require.Equal(t, r["tolerations"], "key=foo,value=bar;key=foo2,value=bar2")
|
||||||
|
|
||||||
|
require.Contains(t, r, "replicas")
|
||||||
|
require.Equal(t, r["replicas"], "1")
|
||||||
|
|
||||||
|
require.Contains(t, r, "namespace")
|
||||||
|
require.Equal(t, r["namespace"], "default")
|
||||||
|
}
|
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/docker/buildx/controller/control"
|
"github.com/docker/buildx/controller/control"
|
||||||
controllerapi "github.com/docker/buildx/controller/pb"
|
controllerapi "github.com/docker/buildx/controller/pb"
|
||||||
"github.com/docker/buildx/monitor"
|
"github.com/docker/buildx/monitor"
|
||||||
"github.com/docker/buildx/util/cobrautil"
|
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/moby/buildkit/util/progress/progressui"
|
"github.com/moby/buildkit/util/progress/progressui"
|
||||||
@@ -43,6 +42,9 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
|
|||||||
Use: "debug",
|
Use: "debug",
|
||||||
Short: "Start debugger",
|
Short: "Start debugger",
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
|
Annotations: map[string]string{
|
||||||
|
"experimentalCLI": "",
|
||||||
|
},
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, progressui.DisplayMode(progressMode))
|
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, progressui.DisplayMode(progressMode))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,18 +73,20 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cobrautil.MarkCommandExperimental(cmd)
|
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.StringVar(&options.InvokeFlag, "invoke", "", "Launch a monitor with executing specified command")
|
flags.StringVar(&options.InvokeFlag, "invoke", "", "Launch a monitor with executing specified command")
|
||||||
|
flags.SetAnnotation("invoke", "experimentalCLI", nil)
|
||||||
flags.StringVar(&options.OnFlag, "on", "error", "When to launch the monitor ([always, error])")
|
flags.StringVar(&options.OnFlag, "on", "error", "When to launch the monitor ([always, error])")
|
||||||
|
flags.SetAnnotation("on", "experimentalCLI", nil)
|
||||||
|
|
||||||
flags.StringVar(&controlOptions.Root, "root", "", "Specify root directory of server to connect for the monitor")
|
flags.StringVar(&controlOptions.Root, "root", "", "Specify root directory of server to connect for the monitor")
|
||||||
|
flags.SetAnnotation("root", "experimentalCLI", nil)
|
||||||
flags.BoolVar(&controlOptions.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server for the monitor (supported only on linux)")
|
flags.BoolVar(&controlOptions.Detach, "detach", runtime.GOOS == "linux", "Detach buildx server for the monitor (supported only on linux)")
|
||||||
|
flags.SetAnnotation("detach", "experimentalCLI", nil)
|
||||||
flags.StringVar(&controlOptions.ServerConfig, "server-config", "", "Specify buildx server config file for the monitor (used only when launching new server)")
|
flags.StringVar(&controlOptions.ServerConfig, "server-config", "", "Specify buildx server config file for the monitor (used only when launching new server)")
|
||||||
flags.StringVar(&progressMode, "progress", "auto", `Set type of progress output ("auto", "plain", "tty", "rawjson") for the monitor. Use plain to show container output`)
|
flags.SetAnnotation("server-config", "experimentalCLI", nil)
|
||||||
|
flags.StringVar(&progressMode, "progress", "auto", `Set type of progress output ("auto", "plain", "tty") for the monitor. Use plain to show container output`)
|
||||||
cobrautil.MarkFlagsExperimental(flags, "invoke", "on", "root", "detach", "server-config")
|
|
||||||
|
|
||||||
for _, c := range children {
|
for _, c := range children {
|
||||||
cmd.AddCommand(c.NewDebugger(&options))
|
cmd.AddCommand(c.NewDebugger(&options))
|
||||||
|
@@ -1,131 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/platforms"
|
|
||||||
"github.com/docker/buildx/build"
|
|
||||||
"github.com/docker/buildx/builder"
|
|
||||||
"github.com/docker/buildx/util/progress"
|
|
||||||
"github.com/docker/cli/cli/command"
|
|
||||||
"github.com/moby/buildkit/util/appcontext"
|
|
||||||
"github.com/moby/buildkit/util/progress/progressui"
|
|
||||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"golang.org/x/sync/errgroup"
|
|
||||||
)
|
|
||||||
|
|
||||||
type stdioOptions struct {
|
|
||||||
builder string
|
|
||||||
platform string
|
|
||||||
progress string
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDialStdio(dockerCli command.Cli, opts stdioOptions) error {
|
|
||||||
ctx := appcontext.Context()
|
|
||||||
|
|
||||||
contextPathHash, _ := os.Getwd()
|
|
||||||
b, err := builder.New(dockerCli,
|
|
||||||
builder.WithName(opts.builder),
|
|
||||||
builder.WithContextPathHash(contextPathHash),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = updateLastActivity(dockerCli, b.NodeGroup); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to update builder last activity time")
|
|
||||||
}
|
|
||||||
nodes, err := b.LoadNodes(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
printer, err := progress.NewPrinter(ctx, os.Stderr, progressui.DisplayMode(opts.progress), progress.WithPhase("dial-stdio"), progress.WithDesc("builder: "+b.Name, "builder:"+b.Name))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var p *v1.Platform
|
|
||||||
if opts.platform != "" {
|
|
||||||
pp, err := platforms.Parse(opts.platform)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "invalid platform %q", opts.platform)
|
|
||||||
}
|
|
||||||
p = &pp
|
|
||||||
}
|
|
||||||
|
|
||||||
defer printer.Wait()
|
|
||||||
|
|
||||||
return progress.Wrap("Proxying to builder", printer.Write, func(sub progress.SubLogger) error {
|
|
||||||
var conn net.Conn
|
|
||||||
|
|
||||||
err := sub.Wrap("Dialing builder", func() error {
|
|
||||||
conn, err = build.Dial(ctx, nodes, printer, p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
closeWrite(conn)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var eg errgroup.Group
|
|
||||||
|
|
||||||
eg.Go(func() error {
|
|
||||||
_, err := io.Copy(conn, os.Stdin)
|
|
||||||
closeWrite(conn)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
eg.Go(func() error {
|
|
||||||
_, err := io.Copy(os.Stdout, conn)
|
|
||||||
closeRead(conn)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
return eg.Wait()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeRead(conn net.Conn) error {
|
|
||||||
if c, ok := conn.(interface{ CloseRead() error }); ok {
|
|
||||||
return c.CloseRead()
|
|
||||||
}
|
|
||||||
return conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeWrite(conn net.Conn) error {
|
|
||||||
if c, ok := conn.(interface{ CloseWrite() error }); ok {
|
|
||||||
return c.CloseWrite()
|
|
||||||
}
|
|
||||||
return conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func dialStdioCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|
||||||
opts := stdioOptions{}
|
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "dial-stdio",
|
|
||||||
Short: "Proxy current stdio streams to builder instance",
|
|
||||||
Args: cobra.NoArgs,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
opts.builder = rootOpts.builder
|
|
||||||
return runDialStdio(dockerCli, opts)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
flags := cmd.Flags()
|
|
||||||
flags.StringVar(&opts.platform, "platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Target platform: this is used for node selection")
|
|
||||||
flags.StringVar(&opts.progress, "progress", "quiet", `Set type of progress output ("auto", "plain", "tty", "rawjson"). Use plain to show container output`)
|
|
||||||
return cmd
|
|
||||||
}
|
|
@@ -1,7 +1,6 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
@@ -16,6 +15,7 @@ import (
|
|||||||
"github.com/docker/cli/opts"
|
"github.com/docker/cli/opts"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
@@ -26,7 +26,9 @@ type duOptions struct {
|
|||||||
verbose bool
|
verbose bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDiskUsage(ctx context.Context, dockerCli command.Cli, opts duOptions) error {
|
func runDiskUsage(dockerCli command.Cli, opts duOptions) error {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
pi, err := toBuildkitPruneInfo(opts.filter.Value())
|
pi, err := toBuildkitPruneInfo(opts.filter.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -112,7 +114,7 @@ func duCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
options.builder = rootOpts.builder
|
options.builder = rootOpts.builder
|
||||||
return runDiskUsage(cmd.Context(), dockerCli, options)
|
return runDiskUsage(dockerCli, options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.Disable,
|
ValidArgsFunction: completion.Disable,
|
||||||
}
|
}
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/docker/buildx/util/imagetools"
|
"github.com/docker/buildx/util/imagetools"
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/moby/buildkit/util/progress/progressui"
|
"github.com/moby/buildkit/util/progress/progressui"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@@ -29,10 +30,9 @@ type createOptions struct {
|
|||||||
dryrun bool
|
dryrun bool
|
||||||
actionAppend bool
|
actionAppend bool
|
||||||
progress string
|
progress string
|
||||||
preferIndex bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, args []string) error {
|
func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
|
||||||
if len(args) == 0 && len(in.files) == 0 {
|
if len(args) == 0 && len(in.files) == 0 {
|
||||||
return errors.Errorf("no sources specified")
|
return errors.Errorf("no sources specified")
|
||||||
}
|
}
|
||||||
@@ -113,6 +113,8 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
b, err := builder.New(dockerCli, builder.WithName(in.builder))
|
b, err := builder.New(dockerCli, builder.WithName(in.builder))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -154,7 +156,7 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dt, desc, err := r.Combine(ctx, srcs, in.annotations, in.preferIndex)
|
dt, desc, err := r.Combine(ctx, srcs, in.annotations)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -272,7 +274,7 @@ func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
|
|||||||
Short: "Create a new image based on source images",
|
Short: "Create a new image based on source images",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
options.builder = *opts.Builder
|
options.builder = *opts.Builder
|
||||||
return runCreate(cmd.Context(), dockerCli, options, args)
|
return runCreate(dockerCli, options, args)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.Disable,
|
ValidArgsFunction: completion.Disable,
|
||||||
}
|
}
|
||||||
@@ -282,9 +284,8 @@ func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
|
|||||||
flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, "Set reference for new image")
|
flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, "Set reference for new image")
|
||||||
flags.BoolVar(&options.dryrun, "dry-run", false, "Show final image instead of pushing")
|
flags.BoolVar(&options.dryrun, "dry-run", false, "Show final image instead of pushing")
|
||||||
flags.BoolVar(&options.actionAppend, "append", false, "Append to existing manifest")
|
flags.BoolVar(&options.actionAppend, "append", false, "Append to existing manifest")
|
||||||
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", "plain", "tty"). Use plain to show container output`)
|
||||||
flags.StringArrayVarP(&options.annotations, "annotation", "", []string{}, "Add annotation to the image")
|
flags.StringArrayVarP(&options.annotations, "annotation", "", []string{}, "Add annotation to the image")
|
||||||
flags.BoolVar(&options.preferIndex, "prefer-index", true, "When only a single source is specified, prefer outputting an image index or manifest list instead of performing a carbon copy")
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
"github.com/docker/buildx/util/imagetools"
|
"github.com/docker/buildx/util/imagetools"
|
||||||
"github.com/docker/cli-docs-tool/annotation"
|
"github.com/docker/cli-docs-tool/annotation"
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@@ -19,7 +18,9 @@ type inspectOptions struct {
|
|||||||
raw bool
|
raw bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions, name string) error {
|
func runInspect(dockerCli command.Cli, in inspectOptions, name string) error {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
if in.format != "" && in.raw {
|
if in.format != "" && in.raw {
|
||||||
return errors.Errorf("format and raw cannot be used together")
|
return errors.Errorf("format and raw cannot be used together")
|
||||||
}
|
}
|
||||||
@@ -50,7 +51,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
|
|||||||
Args: cli.ExactArgs(1),
|
Args: cli.ExactArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
options.builder = *rootOpts.Builder
|
options.builder = *rootOpts.Builder
|
||||||
return runInspect(cmd.Context(), dockerCli, options, args[0])
|
return runInspect(dockerCli, options, args[0])
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.Disable,
|
ValidArgsFunction: completion.Disable,
|
||||||
}
|
}
|
||||||
|
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/debug"
|
"github.com/docker/cli/cli/debug"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,7 +26,9 @@ type inspectOptions struct {
|
|||||||
builder string
|
builder string
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) error {
|
func runInspect(dockerCli command.Cli, in inspectOptions) error {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
b, err := builder.New(dockerCli,
|
b, err := builder.New(dockerCli,
|
||||||
builder.WithName(in.builder),
|
builder.WithName(in.builder),
|
||||||
builder.WithSkippedValidation(),
|
builder.WithSkippedValidation(),
|
||||||
@@ -84,11 +87,11 @@ func runInspect(ctx context.Context, dockerCli command.Cli, in inspectOptions) e
|
|||||||
fmt.Fprintf(w, "Error:\t%s\n", err.Error())
|
fmt.Fprintf(w, "Error:\t%s\n", err.Error())
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(w, "Status:\t%s\n", nodes[i].DriverInfo.Status)
|
fmt.Fprintf(w, "Status:\t%s\n", nodes[i].DriverInfo.Status)
|
||||||
if len(n.BuildkitdFlags) > 0 {
|
if len(n.Flags) > 0 {
|
||||||
fmt.Fprintf(w, "BuildKit daemon flags:\t%s\n", strings.Join(n.BuildkitdFlags, " "))
|
fmt.Fprintf(w, "Flags:\t%s\n", strings.Join(n.Flags, " "))
|
||||||
}
|
}
|
||||||
if nodes[i].Version != "" {
|
if nodes[i].Version != "" {
|
||||||
fmt.Fprintf(w, "BuildKit version:\t%s\n", nodes[i].Version)
|
fmt.Fprintf(w, "Buildkit:\t%s\n", nodes[i].Version)
|
||||||
}
|
}
|
||||||
platforms := platformutil.FormatInGroups(n.Node.Platforms, n.Platforms)
|
platforms := platformutil.FormatInGroups(n.Node.Platforms, n.Platforms)
|
||||||
if len(platforms) > 0 {
|
if len(platforms) > 0 {
|
||||||
@@ -147,7 +150,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
options.builder = args[0]
|
options.builder = args[0]
|
||||||
}
|
}
|
||||||
return runInspect(cmd.Context(), dockerCli, options)
|
return runInspect(dockerCli, options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.BuilderNames(dockerCli),
|
ValidArgsFunction: completion.BuilderNames(dockerCli),
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ import (
|
|||||||
type installOptions struct {
|
type installOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInstall(_ command.Cli, _ installOptions) error {
|
func runInstall(dockerCli command.Cli, in installOptions) error {
|
||||||
dir := config.Dir()
|
dir := config.Dir()
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
return errors.Wrap(err, "could not create docker config")
|
return errors.Wrap(err, "could not create docker config")
|
||||||
|
233
commands/ls.go
233
commands/ls.go
@@ -2,43 +2,30 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/buildx/builder"
|
"github.com/docker/buildx/builder"
|
||||||
"github.com/docker/buildx/store"
|
|
||||||
"github.com/docker/buildx/store/storeutil"
|
"github.com/docker/buildx/store/storeutil"
|
||||||
"github.com/docker/buildx/util/cobrautil"
|
"github.com/docker/buildx/util/cobrautil"
|
||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
"github.com/docker/buildx/util/platformutil"
|
"github.com/docker/buildx/util/platformutil"
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/command/formatter"
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
lsNameNodeHeader = "NAME/NODE"
|
|
||||||
lsDriverEndpointHeader = "DRIVER/ENDPOINT"
|
|
||||||
lsStatusHeader = "STATUS"
|
|
||||||
lsLastActivityHeader = "LAST ACTIVITY"
|
|
||||||
lsBuildkitHeader = "BUILDKIT"
|
|
||||||
lsPlatformsHeader = "PLATFORMS"
|
|
||||||
|
|
||||||
lsIndent = ` \_ `
|
|
||||||
|
|
||||||
lsDefaultTableFormat = "table {{.Name}}\t{{.DriverEndpoint}}\t{{.Status}}\t{{.Buildkit}}\t{{.Platforms}}"
|
|
||||||
)
|
|
||||||
|
|
||||||
type lsOptions struct {
|
type lsOptions struct {
|
||||||
format string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error {
|
func runLs(dockerCli command.Cli, in lsOptions) error {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
txn, release, err := storeutil.GetStore(dockerCli)
|
txn, release, err := storeutil.GetStore(dockerCli)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -72,9 +59,22 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasErrors, err := lsPrint(dockerCli, current, builders, in.format); err != nil {
|
w := tabwriter.NewWriter(dockerCli.Out(), 0, 0, 1, ' ', 0)
|
||||||
return err
|
fmt.Fprintf(w, "NAME/NODE\tDRIVER/ENDPOINT\tSTATUS\tBUILDKIT\tPLATFORMS\n")
|
||||||
} else if hasErrors {
|
|
||||||
|
printErr := false
|
||||||
|
for _, b := range builders {
|
||||||
|
if current.Name == b.Name {
|
||||||
|
b.Name += " *"
|
||||||
|
}
|
||||||
|
if ok := printBuilder(w, b); !ok {
|
||||||
|
printErr = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Flush()
|
||||||
|
|
||||||
|
if printErr {
|
||||||
_, _ = fmt.Fprintf(dockerCli.Err(), "\n")
|
_, _ = fmt.Fprintf(dockerCli.Err(), "\n")
|
||||||
for _, b := range builders {
|
for _, b := range builders {
|
||||||
if b.Err() != nil {
|
if b.Err() != nil {
|
||||||
@@ -92,6 +92,31 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printBuilder(w io.Writer, b *builder.Builder) (ok bool) {
|
||||||
|
ok = true
|
||||||
|
var err string
|
||||||
|
if b.Err() != nil {
|
||||||
|
ok = false
|
||||||
|
err = "error"
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s\t%s\t%s\t\t\n", b.Name, b.Driver, err)
|
||||||
|
if b.Err() == nil {
|
||||||
|
for _, n := range b.Nodes() {
|
||||||
|
var status string
|
||||||
|
if n.DriverInfo != nil {
|
||||||
|
status = n.DriverInfo.Status.String()
|
||||||
|
}
|
||||||
|
if n.Err != nil {
|
||||||
|
ok = false
|
||||||
|
fmt.Fprintf(w, " %s\t%s\t%s\t\t\n", n.Name, n.Endpoint, "error")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, " %s\t%s\t%s\t%s\t%s\n", n.Name, n.Endpoint, status, n.Version, strings.Join(platformutil.FormatInGroups(n.Node.Platforms, n.Platforms), ", "))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func lsCmd(dockerCli command.Cli) *cobra.Command {
|
func lsCmd(dockerCli command.Cli) *cobra.Command {
|
||||||
var options lsOptions
|
var options lsOptions
|
||||||
|
|
||||||
@@ -100,175 +125,13 @@ func lsCmd(dockerCli command.Cli) *cobra.Command {
|
|||||||
Short: "List builder instances",
|
Short: "List builder instances",
|
||||||
Args: cli.ExactArgs(0),
|
Args: cli.ExactArgs(0),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return runLs(cmd.Context(), dockerCli, options)
|
return runLs(dockerCli, options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.Disable,
|
ValidArgsFunction: completion.Disable,
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
|
||||||
flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
|
|
||||||
|
|
||||||
// hide builder persistent flag for this command
|
// hide builder persistent flag for this command
|
||||||
cobrautil.HideInheritedFlags(cmd, "builder")
|
cobrautil.HideInheritedFlags(cmd, "builder")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func lsPrint(dockerCli command.Cli, current *store.NodeGroup, builders []*builder.Builder, format string) (hasErrors bool, _ error) {
|
|
||||||
if format == formatter.TableFormatKey {
|
|
||||||
format = lsDefaultTableFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := formatter.Context{
|
|
||||||
Output: dockerCli.Out(),
|
|
||||||
Format: formatter.Format(format),
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.SliceStable(builders, func(i, j int) bool {
|
|
||||||
ierr := builders[i].Err() != nil
|
|
||||||
jerr := builders[j].Err() != nil
|
|
||||||
if ierr && !jerr {
|
|
||||||
return false
|
|
||||||
} else if !ierr && jerr {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return i < j
|
|
||||||
})
|
|
||||||
|
|
||||||
render := func(format func(subContext formatter.SubContext) error) error {
|
|
||||||
for _, b := range builders {
|
|
||||||
if err := format(&lsContext{
|
|
||||||
Builder: &lsBuilder{
|
|
||||||
Builder: b,
|
|
||||||
Current: b.Name == current.Name,
|
|
||||||
},
|
|
||||||
format: ctx.Format,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if b.Err() != nil {
|
|
||||||
if ctx.Format.IsTable() {
|
|
||||||
hasErrors = true
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, n := range b.Nodes() {
|
|
||||||
if n.Err != nil {
|
|
||||||
if ctx.Format.IsTable() {
|
|
||||||
hasErrors = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := format(&lsContext{
|
|
||||||
format: ctx.Format,
|
|
||||||
Builder: &lsBuilder{
|
|
||||||
Builder: b,
|
|
||||||
Current: b.Name == current.Name,
|
|
||||||
},
|
|
||||||
node: n,
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
lsCtx := lsContext{}
|
|
||||||
lsCtx.Header = formatter.SubHeaderContext{
|
|
||||||
"Name": lsNameNodeHeader,
|
|
||||||
"DriverEndpoint": lsDriverEndpointHeader,
|
|
||||||
"LastActivity": lsLastActivityHeader,
|
|
||||||
"Status": lsStatusHeader,
|
|
||||||
"Buildkit": lsBuildkitHeader,
|
|
||||||
"Platforms": lsPlatformsHeader,
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasErrors, ctx.Write(&lsCtx, render)
|
|
||||||
}
|
|
||||||
|
|
||||||
type lsBuilder struct {
|
|
||||||
*builder.Builder
|
|
||||||
Current bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type lsContext struct {
|
|
||||||
formatter.HeaderContext
|
|
||||||
Builder *lsBuilder
|
|
||||||
|
|
||||||
format formatter.Format
|
|
||||||
node builder.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(c.Builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) Name() string {
|
|
||||||
if c.node.Name == "" {
|
|
||||||
name := c.Builder.Name
|
|
||||||
if c.Builder.Current && c.format.IsTable() {
|
|
||||||
name += "*"
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
if c.format.IsTable() {
|
|
||||||
return lsIndent + c.node.Name
|
|
||||||
}
|
|
||||||
return c.node.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) DriverEndpoint() string {
|
|
||||||
if c.node.Name == "" {
|
|
||||||
return c.Builder.Driver
|
|
||||||
}
|
|
||||||
if c.format.IsTable() {
|
|
||||||
return lsIndent + c.node.Endpoint
|
|
||||||
}
|
|
||||||
return c.node.Endpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) LastActivity() string {
|
|
||||||
if c.node.Name != "" || c.Builder.LastActivity.IsZero() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return c.Builder.LastActivity.UTC().Format(time.RFC3339)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) Status() string {
|
|
||||||
if c.node.Name == "" {
|
|
||||||
if c.Builder.Err() != nil {
|
|
||||||
return "error"
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if c.node.Err != nil {
|
|
||||||
return "error"
|
|
||||||
}
|
|
||||||
if c.node.DriverInfo != nil {
|
|
||||||
return c.node.DriverInfo.Status.String()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) Buildkit() string {
|
|
||||||
if c.node.Name == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return c.node.Version
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) Platforms() string {
|
|
||||||
if c.node.Name == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return strings.Join(platformutil.FormatInGroups(c.node.Node.Platforms, c.node.Platforms), ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *lsContext) Error() string {
|
|
||||||
if c.node.Name != "" && c.node.Err != nil {
|
|
||||||
return c.node.Err.Error()
|
|
||||||
} else if err := c.Builder.Err(); err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -16,6 +15,7 @@ import (
|
|||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
@@ -35,7 +35,9 @@ const (
|
|||||||
allCacheWarning = `WARNING! This will remove all build cache. Are you sure you want to continue?`
|
allCacheWarning = `WARNING! This will remove all build cache. Are you sure you want to continue?`
|
||||||
)
|
)
|
||||||
|
|
||||||
func runPrune(ctx context.Context, dockerCli command.Cli, opts pruneOptions) error {
|
func runPrune(dockerCli command.Cli, opts pruneOptions) error {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
pruneFilters := opts.filter.Value()
|
pruneFilters := opts.filter.Value()
|
||||||
pruneFilters = command.PruneFilters(dockerCli, pruneFilters)
|
pruneFilters = command.PruneFilters(dockerCli, pruneFilters)
|
||||||
|
|
||||||
@@ -49,12 +51,8 @@ func runPrune(ctx context.Context, dockerCli command.Cli, opts pruneOptions) err
|
|||||||
warning = allCacheWarning
|
warning = allCacheWarning
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opts.force {
|
if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
|
||||||
if ok, err := prompt(ctx, dockerCli.In(), dockerCli.Out(), warning); err != nil {
|
return nil
|
||||||
return err
|
|
||||||
} else if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
|
b, err := builder.New(dockerCli, builder.WithName(opts.builder))
|
||||||
@@ -140,7 +138,7 @@ func pruneCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
Args: cli.NoArgs,
|
Args: cli.NoArgs,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
options.builder = rootOpts.builder
|
options.builder = rootOpts.builder
|
||||||
return runPrune(cmd.Context(), dockerCli, options)
|
return runPrune(dockerCli, options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.Disable,
|
ValidArgsFunction: completion.Disable,
|
||||||
}
|
}
|
||||||
@@ -195,8 +193,6 @@ func toBuildkitPruneInfo(f filters.Args) (*client.PruneInfo, error) {
|
|||||||
case 1:
|
case 1:
|
||||||
if filterKey == "id" {
|
if filterKey == "id" {
|
||||||
filters = append(filters, filterKey+"~="+values[0])
|
filters = append(filters, filterKey+"~="+values[0])
|
||||||
} else if strings.HasSuffix(filterKey, "!") || strings.HasSuffix(filterKey, "~") {
|
|
||||||
filters = append(filters, filterKey+"="+values[0])
|
|
||||||
} else {
|
} else {
|
||||||
filters = append(filters, filterKey+"=="+values[0])
|
filters = append(filters, filterKey+"=="+values[0])
|
||||||
}
|
}
|
||||||
|
@@ -9,14 +9,16 @@ import (
|
|||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
"github.com/docker/buildx/store/storeutil"
|
"github.com/docker/buildx/store/storeutil"
|
||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
type rmOptions struct {
|
type rmOptions struct {
|
||||||
builders []string
|
builder string
|
||||||
keepState bool
|
keepState bool
|
||||||
keepDaemon bool
|
keepDaemon bool
|
||||||
allInactive bool
|
allInactive bool
|
||||||
@@ -27,13 +29,11 @@ const (
|
|||||||
rmInactiveWarning = `WARNING! This will remove all builders that are not in running state. Are you sure you want to continue?`
|
rmInactiveWarning = `WARNING! This will remove all builders that are not in running state. Are you sure you want to continue?`
|
||||||
)
|
)
|
||||||
|
|
||||||
func runRm(ctx context.Context, dockerCli command.Cli, in rmOptions) error {
|
func runRm(dockerCli command.Cli, in rmOptions) error {
|
||||||
if in.allInactive && !in.force {
|
ctx := appcontext.Context()
|
||||||
if ok, err := prompt(ctx, dockerCli.In(), dockerCli.Out(), rmInactiveWarning); err != nil {
|
|
||||||
return err
|
if in.allInactive && !in.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), rmInactiveWarning) {
|
||||||
} else if !ok {
|
return nil
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
txn, release, err := storeutil.GetStore(dockerCli)
|
txn, release, err := storeutil.GetStore(dockerCli)
|
||||||
@@ -46,52 +46,33 @@ func runRm(ctx context.Context, dockerCli command.Cli, in rmOptions) error {
|
|||||||
return rmAllInactive(ctx, txn, dockerCli, in)
|
return rmAllInactive(ctx, txn, dockerCli, in)
|
||||||
}
|
}
|
||||||
|
|
||||||
eg, _ := errgroup.WithContext(ctx)
|
b, err := builder.New(dockerCli,
|
||||||
for _, name := range in.builders {
|
builder.WithName(in.builder),
|
||||||
func(name string) {
|
builder.WithStore(txn),
|
||||||
eg.Go(func() (err error) {
|
builder.WithSkippedValidation(),
|
||||||
defer func() {
|
)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
_, _ = fmt.Fprintf(dockerCli.Err(), "%s removed\n", name)
|
return err
|
||||||
} else {
|
|
||||||
_, _ = fmt.Fprintf(dockerCli.Err(), "failed to remove %s: %v\n", name, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
b, err := builder.New(dockerCli,
|
|
||||||
builder.WithName(name),
|
|
||||||
builder.WithStore(txn),
|
|
||||||
builder.WithSkippedValidation(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes, err := b.LoadNodes(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cb := b.ContextName(); cb != "" {
|
|
||||||
return errors.Errorf("context builder cannot be removed, run `docker context rm %s` to remove this context", cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
err1 := rm(ctx, nodes, in)
|
|
||||||
if err := txn.Remove(b.Name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err1 != nil {
|
|
||||||
return err1
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}(name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := eg.Wait(); err != nil {
|
nodes, err := b.LoadNodes(ctx)
|
||||||
return errors.New("failed to remove one or more builders")
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cb := b.ContextName(); cb != "" {
|
||||||
|
return errors.Errorf("context builder cannot be removed, run `docker context rm %s` to remove this context", cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
err1 := rm(ctx, nodes, in)
|
||||||
|
if err := txn.Remove(b.Name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = fmt.Fprintf(dockerCli.Err(), "%s removed\n", b.Name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,24 +80,25 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
var options rmOptions
|
var options rmOptions
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "rm [OPTIONS] [NAME] [NAME...]",
|
Use: "rm [NAME]",
|
||||||
Short: "Remove one or more builder instances",
|
Short: "Remove a builder instance",
|
||||||
|
Args: cli.RequiresMaxArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
options.builders = []string{rootOpts.builder}
|
options.builder = rootOpts.builder
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
if options.allInactive {
|
if options.allInactive {
|
||||||
return errors.New("cannot specify builder name when --all-inactive is set")
|
return errors.New("cannot specify builder name when --all-inactive is set")
|
||||||
}
|
}
|
||||||
options.builders = args
|
options.builder = args[0]
|
||||||
}
|
}
|
||||||
return runRm(cmd.Context(), dockerCli, options)
|
return runRm(dockerCli, options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.BuilderNames(dockerCli),
|
ValidArgsFunction: completion.BuilderNames(dockerCli),
|
||||||
}
|
}
|
||||||
|
|
||||||
flags := cmd.Flags()
|
flags := cmd.Flags()
|
||||||
flags.BoolVar(&options.keepState, "keep-state", false, "Keep BuildKit state")
|
flags.BoolVar(&options.keepState, "keep-state", false, "Keep BuildKit state")
|
||||||
flags.BoolVar(&options.keepDaemon, "keep-daemon", false, "Keep the BuildKit daemon running")
|
flags.BoolVar(&options.keepDaemon, "keep-daemon", false, "Keep the buildkitd daemon running")
|
||||||
flags.BoolVar(&options.allInactive, "all-inactive", false, "Remove all inactive builders")
|
flags.BoolVar(&options.allInactive, "all-inactive", false, "Remove all inactive builders")
|
||||||
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
|
||||||
|
|
||||||
|
@@ -7,14 +7,12 @@ import (
|
|||||||
imagetoolscmd "github.com/docker/buildx/commands/imagetools"
|
imagetoolscmd "github.com/docker/buildx/commands/imagetools"
|
||||||
"github.com/docker/buildx/controller/remote"
|
"github.com/docker/buildx/controller/remote"
|
||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
"github.com/docker/buildx/util/confutil"
|
|
||||||
"github.com/docker/buildx/util/logutil"
|
"github.com/docker/buildx/util/logutil"
|
||||||
"github.com/docker/cli-docs-tool/annotation"
|
"github.com/docker/cli-docs-tool/annotation"
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli-plugins/plugin"
|
"github.com/docker/cli/cli-plugins/plugin"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
"github.com/docker/cli/cli/debug"
|
"github.com/docker/cli/cli/debug"
|
||||||
"github.com/moby/buildkit/util/appcontext"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
@@ -31,15 +29,12 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
|
|||||||
CompletionOptions: cobra.CompletionOptions{
|
CompletionOptions: cobra.CompletionOptions{
|
||||||
HiddenDefaultCmd: true,
|
HiddenDefaultCmd: true,
|
||||||
},
|
},
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
cmd.SetContext(appcontext.Context())
|
|
||||||
if !isPlugin {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return plugin.PersistentPreRunE(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
if !isPlugin {
|
if isPlugin {
|
||||||
|
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||||
|
return plugin.PersistentPreRunE(cmd, args)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// match plugin behavior for standalone mode
|
// match plugin behavior for standalone mode
|
||||||
// https://github.com/docker/cli/blob/6c9eb708fa6d17765d71965f90e1c59cea686ee9/cli-plugins/plugin/plugin.go#L117-L127
|
// https://github.com/docker/cli/blob/6c9eb708fa6d17765d71965f90e1c59cea686ee9/cli-plugins/plugin/plugin.go#L117-L127
|
||||||
cmd.SilenceUsage = true
|
cmd.SilenceUsage = true
|
||||||
@@ -64,10 +59,6 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
|
|||||||
"using default config store",
|
"using default config store",
|
||||||
))
|
))
|
||||||
|
|
||||||
if !confutil.IsExperimental() {
|
|
||||||
cmd.SetHelpTemplate(cmd.HelpTemplate() + "\nExperimental commands and flags are hidden. Set BUILDX_EXPERIMENTAL=1 to show them.\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommands(cmd, dockerCli)
|
addCommands(cmd, dockerCli)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
@@ -84,7 +75,6 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
|
|||||||
buildCmd(dockerCli, opts, nil),
|
buildCmd(dockerCli, opts, nil),
|
||||||
bakeCmd(dockerCli, opts),
|
bakeCmd(dockerCli, opts),
|
||||||
createCmd(dockerCli),
|
createCmd(dockerCli),
|
||||||
dialStdioCmd(dockerCli, opts),
|
|
||||||
rmCmd(dockerCli, opts),
|
rmCmd(dockerCli, opts),
|
||||||
lsCmd(dockerCli),
|
lsCmd(dockerCli),
|
||||||
useCmd(dockerCli, opts),
|
useCmd(dockerCli, opts),
|
||||||
@@ -97,7 +87,7 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
|
|||||||
duCmd(dockerCli, opts),
|
duCmd(dockerCli, opts),
|
||||||
imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
|
imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
|
||||||
)
|
)
|
||||||
if confutil.IsExperimental() {
|
if isExperimental() {
|
||||||
cmd.AddCommand(debugcmd.RootCmd(dockerCli,
|
cmd.AddCommand(debugcmd.RootCmd(dockerCli,
|
||||||
newDebuggableBuild(dockerCli, opts),
|
newDebuggableBuild(dockerCli, opts),
|
||||||
))
|
))
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/docker/buildx/util/cobrautil/completion"
|
"github.com/docker/buildx/util/cobrautil/completion"
|
||||||
"github.com/docker/cli/cli"
|
"github.com/docker/cli/cli"
|
||||||
"github.com/docker/cli/cli/command"
|
"github.com/docker/cli/cli/command"
|
||||||
|
"github.com/moby/buildkit/util/appcontext"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,7 +15,9 @@ type stopOptions struct {
|
|||||||
builder string
|
builder string
|
||||||
}
|
}
|
||||||
|
|
||||||
func runStop(ctx context.Context, dockerCli command.Cli, in stopOptions) error {
|
func runStop(dockerCli command.Cli, in stopOptions) error {
|
||||||
|
ctx := appcontext.Context()
|
||||||
|
|
||||||
b, err := builder.New(dockerCli,
|
b, err := builder.New(dockerCli,
|
||||||
builder.WithName(in.builder),
|
builder.WithName(in.builder),
|
||||||
builder.WithSkippedValidation(),
|
builder.WithSkippedValidation(),
|
||||||
@@ -42,7 +45,7 @@ func stopCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
|
|||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
options.builder = args[0]
|
options.builder = args[0]
|
||||||
}
|
}
|
||||||
return runStop(cmd.Context(), dockerCli, options)
|
return runStop(dockerCli, options)
|
||||||
},
|
},
|
||||||
ValidArgsFunction: completion.BuilderNames(dockerCli),
|
ValidArgsFunction: completion.BuilderNames(dockerCli),
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,7 @@ import (
|
|||||||
type uninstallOptions struct {
|
type uninstallOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func runUninstall(_ command.Cli, _ uninstallOptions) error {
|
func runUninstall(dockerCli command.Cli, in uninstallOptions) error {
|
||||||
dir := config.Dir()
|
dir := config.Dir()
|
||||||
cfg, err := config.Load(dir)
|
cfg, err := config.Load(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -1,57 +0,0 @@
|
|||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/cli/cli/streams"
|
|
||||||
)
|
|
||||||
|
|
||||||
func prompt(ctx context.Context, ins io.Reader, out io.Writer, msg string) (bool, error) {
|
|
||||||
done := make(chan struct{})
|
|
||||||
var ok bool
|
|
||||||
go func() {
|
|
||||||
ok = promptForConfirmation(ins, out, msg)
|
|
||||||
close(done)
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return false, context.Cause(ctx)
|
|
||||||
case <-done:
|
|
||||||
return ok, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// promptForConfirmation requests and checks confirmation from user.
|
|
||||||
// This will display the provided message followed by ' [y/N] '. If
|
|
||||||
// the user input 'y' or 'Y' it returns true other false. If no
|
|
||||||
// message is provided "Are you sure you want to proceed? [y/N] "
|
|
||||||
// will be used instead.
|
|
||||||
//
|
|
||||||
// Copied from github.com/docker/cli since the upstream version changed
|
|
||||||
// recently with an incompatible change.
|
|
||||||
//
|
|
||||||
// See https://github.com/docker/buildx/pull/2359#discussion_r1544736494
|
|
||||||
// for discussion on the issue.
|
|
||||||
func promptForConfirmation(ins io.Reader, outs io.Writer, message string) bool {
|
|
||||||
if message == "" {
|
|
||||||
message = "Are you sure you want to proceed?"
|
|
||||||
}
|
|
||||||
message += " [y/N] "
|
|
||||||
|
|
||||||
_, _ = fmt.Fprint(outs, message)
|
|
||||||
|
|
||||||
// On Windows, force the use of the regular OS stdin stream.
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
ins = streams.NewIn(os.Stdin)
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := bufio.NewReader(ins)
|
|
||||||
answer, _, _ := reader.ReadLine()
|
|
||||||
return strings.ToLower(string(answer)) == "y"
|
|
||||||
}
|
|
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func runVersion(_ command.Cli) error {
|
func runVersion(dockerCli command.Cli) error {
|
||||||
fmt.Println(version.Package, version.Version, version.Revision)
|
fmt.Println(version.Package, version.Version, version.Revision)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -53,21 +53,20 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
|
|||||||
InStream: inStream,
|
InStream: inStream,
|
||||||
NamedContexts: contexts,
|
NamedContexts: contexts,
|
||||||
},
|
},
|
||||||
Ref: in.Ref,
|
Ref: in.Ref,
|
||||||
BuildArgs: in.BuildArgs,
|
BuildArgs: in.BuildArgs,
|
||||||
CgroupParent: in.CgroupParent,
|
CgroupParent: in.CgroupParent,
|
||||||
ExtraHosts: in.ExtraHosts,
|
ExtraHosts: in.ExtraHosts,
|
||||||
Labels: in.Labels,
|
Labels: in.Labels,
|
||||||
NetworkMode: in.NetworkMode,
|
NetworkMode: in.NetworkMode,
|
||||||
NoCache: in.NoCache,
|
NoCache: in.NoCache,
|
||||||
NoCacheFilter: in.NoCacheFilter,
|
NoCacheFilter: in.NoCacheFilter,
|
||||||
Pull: in.Pull,
|
Pull: in.Pull,
|
||||||
ShmSize: dockeropts.MemBytes(in.ShmSize),
|
ShmSize: dockeropts.MemBytes(in.ShmSize),
|
||||||
Tags: in.Tags,
|
Tags: in.Tags,
|
||||||
Target: in.Target,
|
Target: in.Target,
|
||||||
Ulimits: controllerUlimitOpt2DockerUlimit(in.Ulimits),
|
Ulimits: controllerUlimitOpt2DockerUlimit(in.Ulimits),
|
||||||
GroupRef: in.GroupRef,
|
GroupRef: in.GroupRef,
|
||||||
WithProvenanceResponse: in.WithProvenanceResponse,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
platforms, err := platformutil.Parse(in.Platforms)
|
platforms, err := platformutil.Parse(in.Platforms)
|
||||||
@@ -100,37 +99,37 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if in.ExportPush {
|
if in.ExportPush {
|
||||||
var pushUsed bool
|
if in.ExportLoad {
|
||||||
for i := range outputs {
|
return nil, nil, errors.Errorf("push and load may not be set together at the moment")
|
||||||
if outputs[i].Type == client.ExporterImage {
|
|
||||||
outputs[i].Attrs["push"] = "true"
|
|
||||||
pushUsed = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !pushUsed {
|
if len(outputs) == 0 {
|
||||||
outputs = append(outputs, client.ExportEntry{
|
outputs = []client.ExportEntry{{
|
||||||
Type: client.ExporterImage,
|
Type: "image",
|
||||||
Attrs: map[string]string{
|
Attrs: map[string]string{
|
||||||
"push": "true",
|
"push": "true",
|
||||||
},
|
},
|
||||||
})
|
}}
|
||||||
|
} else {
|
||||||
|
switch outputs[0].Type {
|
||||||
|
case "image":
|
||||||
|
outputs[0].Attrs["push"] = "true"
|
||||||
|
default:
|
||||||
|
return nil, nil, errors.Errorf("push and %q output can't be used together", outputs[0].Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if in.ExportLoad {
|
if in.ExportLoad {
|
||||||
var loadUsed bool
|
if len(outputs) == 0 {
|
||||||
for i := range outputs {
|
outputs = []client.ExportEntry{{
|
||||||
if outputs[i].Type == client.ExporterDocker {
|
Type: "docker",
|
||||||
if _, ok := outputs[i].Attrs["dest"]; !ok {
|
|
||||||
loadUsed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !loadUsed {
|
|
||||||
outputs = append(outputs, client.ExportEntry{
|
|
||||||
Type: client.ExporterDocker,
|
|
||||||
Attrs: map[string]string{},
|
Attrs: map[string]string{},
|
||||||
})
|
}}
|
||||||
|
} else {
|
||||||
|
switch outputs[0].Type {
|
||||||
|
case "docker":
|
||||||
|
default:
|
||||||
|
return nil, nil, errors.Errorf("load and %q output can't be used together", outputs[0].Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,9 +160,8 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
|
|||||||
|
|
||||||
if in.PrintFunc != nil {
|
if in.PrintFunc != nil {
|
||||||
opts.PrintFunc = &build.PrintFunc{
|
opts.PrintFunc = &build.PrintFunc{
|
||||||
Name: in.PrintFunc.Name,
|
Name: in.PrintFunc.Name,
|
||||||
Format: in.PrintFunc.Format,
|
Format: in.PrintFunc.Format,
|
||||||
IgnoreStatus: in.PrintFunc.IgnoreStatus,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +187,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, res, err := buildTargets(ctx, dockerCli, nodes, map[string]build.Options{defaultTargetName: opts}, progress, generateResult)
|
resp, res, err := buildTargets(ctx, dockerCli, b.NodeGroup, nodes, map[string]build.Options{defaultTargetName: opts}, progress, generateResult)
|
||||||
err = wrapBuildError(err, false)
|
err = wrapBuildError(err, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// NOTE: buildTargets can return *build.ResultHandle even on error.
|
// NOTE: buildTargets can return *build.ResultHandle even on error.
|
||||||
@@ -203,7 +201,7 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
|
|||||||
// NOTE: When an error happens during the build and this function acquires the debuggable *build.ResultHandle,
|
// NOTE: When an error happens during the build and this function acquires the debuggable *build.ResultHandle,
|
||||||
// this function returns it in addition to the error (i.e. it does "return nil, res, err"). The caller can
|
// this function returns it in addition to the error (i.e. it does "return nil, res, err"). The caller can
|
||||||
// inspect the result and debug the cause of that error.
|
// inspect the result and debug the cause of that error.
|
||||||
func buildTargets(ctx context.Context, dockerCli command.Cli, nodes []builder.Node, opts map[string]build.Options, progress progress.Writer, generateResult bool) (*client.SolveResponse, *build.ResultHandle, error) {
|
func buildTargets(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, nodes []builder.Node, opts map[string]build.Options, progress progress.Writer, generateResult bool) (*client.SolveResponse, *build.ResultHandle, error) {
|
||||||
var res *build.ResultHandle
|
var res *build.ResultHandle
|
||||||
var resp map[string]*client.SolveResponse
|
var resp map[string]*client.SolveResponse
|
||||||
var err error
|
var err error
|
||||||
|
@@ -271,41 +271,40 @@ func (m *BuildRequest) GetOptions() *BuildOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BuildOptions struct {
|
type BuildOptions struct {
|
||||||
ContextPath string `protobuf:"bytes,1,opt,name=ContextPath,proto3" json:"ContextPath,omitempty"`
|
ContextPath string `protobuf:"bytes,1,opt,name=ContextPath,proto3" json:"ContextPath,omitempty"`
|
||||||
DockerfileName string `protobuf:"bytes,2,opt,name=DockerfileName,proto3" json:"DockerfileName,omitempty"`
|
DockerfileName string `protobuf:"bytes,2,opt,name=DockerfileName,proto3" json:"DockerfileName,omitempty"`
|
||||||
PrintFunc *PrintFunc `protobuf:"bytes,3,opt,name=PrintFunc,proto3" json:"PrintFunc,omitempty"`
|
PrintFunc *PrintFunc `protobuf:"bytes,3,opt,name=PrintFunc,proto3" json:"PrintFunc,omitempty"`
|
||||||
NamedContexts map[string]string `protobuf:"bytes,4,rep,name=NamedContexts,proto3" json:"NamedContexts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
NamedContexts map[string]string `protobuf:"bytes,4,rep,name=NamedContexts,proto3" json:"NamedContexts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
Allow []string `protobuf:"bytes,5,rep,name=Allow,proto3" json:"Allow,omitempty"`
|
Allow []string `protobuf:"bytes,5,rep,name=Allow,proto3" json:"Allow,omitempty"`
|
||||||
Attests []*Attest `protobuf:"bytes,6,rep,name=Attests,proto3" json:"Attests,omitempty"`
|
Attests []*Attest `protobuf:"bytes,6,rep,name=Attests,proto3" json:"Attests,omitempty"`
|
||||||
BuildArgs map[string]string `protobuf:"bytes,7,rep,name=BuildArgs,proto3" json:"BuildArgs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
BuildArgs map[string]string `protobuf:"bytes,7,rep,name=BuildArgs,proto3" json:"BuildArgs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
CacheFrom []*CacheOptionsEntry `protobuf:"bytes,8,rep,name=CacheFrom,proto3" json:"CacheFrom,omitempty"`
|
CacheFrom []*CacheOptionsEntry `protobuf:"bytes,8,rep,name=CacheFrom,proto3" json:"CacheFrom,omitempty"`
|
||||||
CacheTo []*CacheOptionsEntry `protobuf:"bytes,9,rep,name=CacheTo,proto3" json:"CacheTo,omitempty"`
|
CacheTo []*CacheOptionsEntry `protobuf:"bytes,9,rep,name=CacheTo,proto3" json:"CacheTo,omitempty"`
|
||||||
CgroupParent string `protobuf:"bytes,10,opt,name=CgroupParent,proto3" json:"CgroupParent,omitempty"`
|
CgroupParent string `protobuf:"bytes,10,opt,name=CgroupParent,proto3" json:"CgroupParent,omitempty"`
|
||||||
Exports []*ExportEntry `protobuf:"bytes,11,rep,name=Exports,proto3" json:"Exports,omitempty"`
|
Exports []*ExportEntry `protobuf:"bytes,11,rep,name=Exports,proto3" json:"Exports,omitempty"`
|
||||||
ExtraHosts []string `protobuf:"bytes,12,rep,name=ExtraHosts,proto3" json:"ExtraHosts,omitempty"`
|
ExtraHosts []string `protobuf:"bytes,12,rep,name=ExtraHosts,proto3" json:"ExtraHosts,omitempty"`
|
||||||
Labels map[string]string `protobuf:"bytes,13,rep,name=Labels,proto3" json:"Labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
Labels map[string]string `protobuf:"bytes,13,rep,name=Labels,proto3" json:"Labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
NetworkMode string `protobuf:"bytes,14,opt,name=NetworkMode,proto3" json:"NetworkMode,omitempty"`
|
NetworkMode string `protobuf:"bytes,14,opt,name=NetworkMode,proto3" json:"NetworkMode,omitempty"`
|
||||||
NoCacheFilter []string `protobuf:"bytes,15,rep,name=NoCacheFilter,proto3" json:"NoCacheFilter,omitempty"`
|
NoCacheFilter []string `protobuf:"bytes,15,rep,name=NoCacheFilter,proto3" json:"NoCacheFilter,omitempty"`
|
||||||
Platforms []string `protobuf:"bytes,16,rep,name=Platforms,proto3" json:"Platforms,omitempty"`
|
Platforms []string `protobuf:"bytes,16,rep,name=Platforms,proto3" json:"Platforms,omitempty"`
|
||||||
Secrets []*Secret `protobuf:"bytes,17,rep,name=Secrets,proto3" json:"Secrets,omitempty"`
|
Secrets []*Secret `protobuf:"bytes,17,rep,name=Secrets,proto3" json:"Secrets,omitempty"`
|
||||||
ShmSize int64 `protobuf:"varint,18,opt,name=ShmSize,proto3" json:"ShmSize,omitempty"`
|
ShmSize int64 `protobuf:"varint,18,opt,name=ShmSize,proto3" json:"ShmSize,omitempty"`
|
||||||
SSH []*SSH `protobuf:"bytes,19,rep,name=SSH,proto3" json:"SSH,omitempty"`
|
SSH []*SSH `protobuf:"bytes,19,rep,name=SSH,proto3" json:"SSH,omitempty"`
|
||||||
Tags []string `protobuf:"bytes,20,rep,name=Tags,proto3" json:"Tags,omitempty"`
|
Tags []string `protobuf:"bytes,20,rep,name=Tags,proto3" json:"Tags,omitempty"`
|
||||||
Target string `protobuf:"bytes,21,opt,name=Target,proto3" json:"Target,omitempty"`
|
Target string `protobuf:"bytes,21,opt,name=Target,proto3" json:"Target,omitempty"`
|
||||||
Ulimits *UlimitOpt `protobuf:"bytes,22,opt,name=Ulimits,proto3" json:"Ulimits,omitempty"`
|
Ulimits *UlimitOpt `protobuf:"bytes,22,opt,name=Ulimits,proto3" json:"Ulimits,omitempty"`
|
||||||
Builder string `protobuf:"bytes,23,opt,name=Builder,proto3" json:"Builder,omitempty"`
|
Builder string `protobuf:"bytes,23,opt,name=Builder,proto3" json:"Builder,omitempty"`
|
||||||
NoCache bool `protobuf:"varint,24,opt,name=NoCache,proto3" json:"NoCache,omitempty"`
|
NoCache bool `protobuf:"varint,24,opt,name=NoCache,proto3" json:"NoCache,omitempty"`
|
||||||
Pull bool `protobuf:"varint,25,opt,name=Pull,proto3" json:"Pull,omitempty"`
|
Pull bool `protobuf:"varint,25,opt,name=Pull,proto3" json:"Pull,omitempty"`
|
||||||
ExportPush bool `protobuf:"varint,26,opt,name=ExportPush,proto3" json:"ExportPush,omitempty"`
|
ExportPush bool `protobuf:"varint,26,opt,name=ExportPush,proto3" json:"ExportPush,omitempty"`
|
||||||
ExportLoad bool `protobuf:"varint,27,opt,name=ExportLoad,proto3" json:"ExportLoad,omitempty"`
|
ExportLoad bool `protobuf:"varint,27,opt,name=ExportLoad,proto3" json:"ExportLoad,omitempty"`
|
||||||
SourcePolicy *pb.Policy `protobuf:"bytes,28,opt,name=SourcePolicy,proto3" json:"SourcePolicy,omitempty"`
|
SourcePolicy *pb.Policy `protobuf:"bytes,28,opt,name=SourcePolicy,proto3" json:"SourcePolicy,omitempty"`
|
||||||
Ref string `protobuf:"bytes,29,opt,name=Ref,proto3" json:"Ref,omitempty"`
|
Ref string `protobuf:"bytes,29,opt,name=Ref,proto3" json:"Ref,omitempty"`
|
||||||
GroupRef string `protobuf:"bytes,30,opt,name=GroupRef,proto3" json:"GroupRef,omitempty"`
|
GroupRef string `protobuf:"bytes,30,opt,name=GroupRef,proto3" json:"GroupRef,omitempty"`
|
||||||
Annotations []string `protobuf:"bytes,31,rep,name=Annotations,proto3" json:"Annotations,omitempty"`
|
Annotations []string `protobuf:"bytes,31,rep,name=Annotations,proto3" json:"Annotations,omitempty"`
|
||||||
WithProvenanceResponse bool `protobuf:"varint,32,opt,name=WithProvenanceResponse,proto3" json:"WithProvenanceResponse,omitempty"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *BuildOptions) Reset() { *m = BuildOptions{} }
|
func (m *BuildOptions) Reset() { *m = BuildOptions{} }
|
||||||
@@ -549,13 +548,6 @@ func (m *BuildOptions) GetAnnotations() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *BuildOptions) GetWithProvenanceResponse() bool {
|
|
||||||
if m != nil {
|
|
||||||
return m.WithProvenanceResponse
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExportEntry struct {
|
type ExportEntry struct {
|
||||||
Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"`
|
Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"`
|
||||||
Attrs map[string]string `protobuf:"bytes,2,rep,name=Attrs,proto3" json:"Attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
Attrs map[string]string `protobuf:"bytes,2,rep,name=Attrs,proto3" json:"Attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
@@ -813,7 +805,6 @@ func (m *Secret) GetEnv() string {
|
|||||||
type PrintFunc struct {
|
type PrintFunc struct {
|
||||||
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
|
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||||
Format string `protobuf:"bytes,2,opt,name=Format,proto3" json:"Format,omitempty"`
|
Format string `protobuf:"bytes,2,opt,name=Format,proto3" json:"Format,omitempty"`
|
||||||
IgnoreStatus bool `protobuf:"varint,3,opt,name=IgnoreStatus,proto3" json:"IgnoreStatus,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@@ -857,13 +848,6 @@ func (m *PrintFunc) GetFormat() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PrintFunc) GetIgnoreStatus() bool {
|
|
||||||
if m != nil {
|
|
||||||
return m.IgnoreStatus
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type InspectRequest struct {
|
type InspectRequest struct {
|
||||||
Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
|
Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
@@ -2094,130 +2078,128 @@ func init() {
|
|||||||
func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) }
|
func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) }
|
||||||
|
|
||||||
var fileDescriptor_ed7f10298fa1d90f = []byte{
|
var fileDescriptor_ed7f10298fa1d90f = []byte{
|
||||||
// 1960 bytes of a gzipped FileDescriptorProto
|
// 1922 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0x5f, 0x73, 0x1b, 0x49,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0x5f, 0x73, 0x1b, 0x49,
|
||||||
0x11, 0x67, 0x25, 0x59, 0x7f, 0x5a, 0x96, 0xcf, 0x19, 0x9c, 0x30, 0xd9, 0xe4, 0x12, 0x67, 0x93,
|
0x11, 0x67, 0x25, 0x59, 0x7f, 0x5a, 0x96, 0xcf, 0x19, 0x9c, 0x30, 0xd9, 0xe4, 0x12, 0x67, 0x93,
|
||||||
0x1c, 0x2a, 0x42, 0xc9, 0x77, 0x3e, 0x72, 0xb9, 0x5c, 0xee, 0xaa, 0xb0, 0x65, 0x0b, 0xfb, 0x2a,
|
0x1c, 0x2a, 0x42, 0xc9, 0x77, 0x3e, 0x82, 0x2f, 0x97, 0xbb, 0x2a, 0x6c, 0xd9, 0xc2, 0xbe, 0x4a,
|
||||||
0xb1, 0x5d, 0x23, 0x27, 0x29, 0xb8, 0x2a, 0xae, 0x56, 0xd2, 0x58, 0xde, 0xd2, 0x6a, 0x47, 0xec,
|
0x6c, 0xd7, 0xca, 0xc9, 0x15, 0x50, 0xc5, 0xd5, 0x5a, 0x1a, 0xcb, 0x5b, 0x5a, 0xed, 0x88, 0x9d,
|
||||||
0x8c, 0x64, 0x8b, 0x27, 0x1e, 0xe0, 0x8d, 0xe2, 0x7b, 0x50, 0x7c, 0x04, 0x9e, 0x78, 0xe3, 0xe3,
|
0x91, 0x6d, 0xf1, 0xc4, 0x03, 0xbc, 0x51, 0x14, 0x5f, 0x83, 0xe2, 0x23, 0xf0, 0xc4, 0x37, 0xe2,
|
||||||
0xf0, 0x11, 0xa8, 0xf9, 0xb3, 0xab, 0x5d, 0x4b, 0x2b, 0xdb, 0xf0, 0xa4, 0xe9, 0x9e, 0x5f, 0x77,
|
0x23, 0x50, 0xd3, 0x33, 0xbb, 0x5a, 0x59, 0x5a, 0xd9, 0x86, 0x27, 0x4d, 0xf7, 0xfe, 0xba, 0x7b,
|
||||||
0x4f, 0xf7, 0xf6, 0x74, 0xf7, 0x08, 0xd6, 0xbb, 0x2c, 0x10, 0x21, 0xf3, 0x7d, 0x1a, 0x36, 0x46,
|
0xba, 0xa7, 0xa7, 0xbb, 0x47, 0xb0, 0xda, 0xe1, 0xa1, 0x8c, 0x78, 0x10, 0xb0, 0xa8, 0x31, 0x8c,
|
||||||
0x21, 0x13, 0x0c, 0x6d, 0x74, 0xc6, 0x9e, 0xdf, 0xbb, 0x6c, 0x24, 0x36, 0x26, 0x5f, 0xd8, 0x6f,
|
0xb8, 0xe4, 0x64, 0xed, 0x74, 0xe4, 0x07, 0xdd, 0xab, 0x46, 0xea, 0xc3, 0xc5, 0x17, 0xf6, 0xdb,
|
||||||
0xfa, 0x9e, 0x38, 0x1f, 0x77, 0x1a, 0x5d, 0x36, 0xdc, 0x1a, 0xb2, 0xce, 0x74, 0x4b, 0xa1, 0x06,
|
0x9e, 0x2f, 0xcf, 0x47, 0xa7, 0x8d, 0x0e, 0x1f, 0x6c, 0x0c, 0xf8, 0xe9, 0x78, 0x03, 0x51, 0x7d,
|
||||||
0x9e, 0xd8, 0x72, 0x47, 0xde, 0x16, 0xa7, 0xe1, 0xc4, 0xeb, 0x52, 0xbe, 0x65, 0x84, 0xa2, 0x5f,
|
0x5f, 0x6e, 0x78, 0x43, 0x7f, 0x43, 0xb0, 0xe8, 0xc2, 0xef, 0x30, 0xb1, 0x61, 0x84, 0xe2, 0x5f,
|
||||||
0xad, 0xd2, 0x7e, 0x99, 0x29, 0xcc, 0xd9, 0x38, 0xec, 0xd2, 0x11, 0xf3, 0xbd, 0xee, 0x74, 0x6b,
|
0xad, 0xd2, 0x7e, 0x9d, 0x29, 0x2c, 0xf8, 0x28, 0xea, 0xb0, 0x21, 0x0f, 0xfc, 0xce, 0x78, 0x63,
|
||||||
0xd4, 0xd9, 0xd2, 0x2b, 0x2d, 0xe6, 0xd4, 0x61, 0xe3, 0xad, 0xc7, 0xc5, 0x49, 0xc8, 0xba, 0x94,
|
0x78, 0xba, 0xa1, 0x57, 0x5a, 0xcc, 0xa9, 0xc3, 0xda, 0x3b, 0x5f, 0xc8, 0xe3, 0x88, 0x77, 0x98,
|
||||||
0x73, 0xca, 0x09, 0xfd, 0xc3, 0x98, 0x72, 0x81, 0xd6, 0x21, 0x4f, 0xe8, 0x19, 0xb6, 0x36, 0xad,
|
0x10, 0x4c, 0xb8, 0xec, 0x0f, 0x23, 0x26, 0x24, 0x59, 0x85, 0xbc, 0xcb, 0xce, 0xa8, 0xb5, 0x6e,
|
||||||
0x7a, 0x85, 0xc8, 0xa5, 0x73, 0x02, 0x77, 0xaf, 0x20, 0xf9, 0x88, 0x05, 0x9c, 0xa2, 0x57, 0xb0,
|
0xd5, 0x2b, 0xae, 0x5a, 0x3a, 0xc7, 0x70, 0xff, 0x1a, 0x52, 0x0c, 0x79, 0x28, 0x18, 0xd9, 0x82,
|
||||||
0x72, 0x18, 0x9c, 0x31, 0x8e, 0xad, 0xcd, 0x7c, 0xbd, 0xba, 0xfd, 0xa4, 0xb1, 0xc8, 0xb9, 0x86,
|
0xa5, 0x83, 0xf0, 0x8c, 0x0b, 0x6a, 0xad, 0xe7, 0xeb, 0xd5, 0xcd, 0x67, 0x8d, 0x79, 0xce, 0x35,
|
||||||
0x91, 0x93, 0x48, 0xa2, 0xf1, 0x0e, 0x87, 0x6a, 0x82, 0x8b, 0x1e, 0x42, 0x25, 0x22, 0xf7, 0x8c,
|
0x8c, 0x9c, 0x42, 0xba, 0x1a, 0xef, 0x08, 0xa8, 0xa6, 0xb8, 0xe4, 0x31, 0x54, 0x62, 0x72, 0xd7,
|
||||||
0xe1, 0x19, 0x03, 0xb5, 0x60, 0xf5, 0x30, 0x98, 0xb0, 0x01, 0x6d, 0xb2, 0xe0, 0xcc, 0xeb, 0xe3,
|
0x18, 0x9e, 0x30, 0x48, 0x0b, 0x96, 0x0f, 0xc2, 0x0b, 0xde, 0x67, 0x4d, 0x1e, 0x9e, 0xf9, 0x3d,
|
||||||
0xdc, 0xa6, 0x55, 0xaf, 0x6e, 0x3b, 0x8b, 0x8d, 0x25, 0x91, 0x24, 0x25, 0xe7, 0x7c, 0x0f, 0x78,
|
0x9a, 0x5b, 0xb7, 0xea, 0xd5, 0x4d, 0x67, 0xbe, 0xb1, 0x34, 0xd2, 0x9d, 0x92, 0x73, 0xbe, 0x03,
|
||||||
0xcf, 0xe3, 0x5d, 0x16, 0x04, 0xb4, 0x1b, 0x39, 0x93, 0xe9, 0x74, 0xfa, 0x4c, 0xb9, 0x2b, 0x67,
|
0xba, 0xeb, 0x8b, 0x0e, 0x0f, 0x43, 0xd6, 0x89, 0x9d, 0xc9, 0x74, 0x7a, 0x7a, 0x4f, 0xb9, 0x6b,
|
||||||
0x72, 0x1e, 0xc0, 0xfd, 0x05, 0xba, 0x74, 0x58, 0x9c, 0xdf, 0xc3, 0xea, 0xae, 0x3c, 0x5b, 0xb6,
|
0x7b, 0x72, 0x1e, 0xc1, 0xc3, 0x39, 0xba, 0x74, 0x58, 0x9c, 0xdf, 0xc3, 0xf2, 0x8e, 0xda, 0x5b,
|
||||||
0xf2, 0x6f, 0xa1, 0x74, 0x3c, 0x12, 0x1e, 0x0b, 0xf8, 0x72, 0x6f, 0x94, 0x1a, 0x83, 0x24, 0x91,
|
0xb6, 0xf2, 0x6f, 0xa0, 0x74, 0x34, 0x94, 0x3e, 0x0f, 0xc5, 0x62, 0x6f, 0x50, 0x8d, 0x41, 0xba,
|
||||||
0x88, 0xf3, 0xef, 0x55, 0x63, 0xc0, 0x30, 0xd0, 0x26, 0x54, 0x9b, 0x2c, 0x10, 0xf4, 0x52, 0x9c,
|
0xb1, 0x88, 0xf3, 0xf7, 0x65, 0x63, 0xc0, 0x30, 0xc8, 0x3a, 0x54, 0x9b, 0x3c, 0x94, 0xec, 0x4a,
|
||||||
0xb8, 0xe2, 0xdc, 0x18, 0x4a, 0xb2, 0xd0, 0x67, 0xb0, 0xb6, 0xc7, 0xba, 0x03, 0x1a, 0x9e, 0x79,
|
0x1e, 0x7b, 0xf2, 0xdc, 0x18, 0x4a, 0xb3, 0xc8, 0x67, 0xb0, 0xb2, 0xcb, 0x3b, 0x7d, 0x16, 0x9d,
|
||||||
0x3e, 0x3d, 0x72, 0x87, 0xd4, 0xb8, 0x74, 0x85, 0x8b, 0xbe, 0x93, 0x5e, 0x7b, 0x81, 0x68, 0x8d,
|
0xf9, 0x01, 0x3b, 0xf4, 0x06, 0xcc, 0xb8, 0x74, 0x8d, 0x4b, 0xbe, 0x55, 0x5e, 0xfb, 0xa1, 0x6c,
|
||||||
0x83, 0x2e, 0xce, 0xab, 0xa3, 0x3d, 0xce, 0xfa, 0xaa, 0x06, 0x46, 0x66, 0x12, 0xe8, 0x07, 0xa8,
|
0x8d, 0xc2, 0x0e, 0xcd, 0xe3, 0xd6, 0x9e, 0x66, 0x9d, 0xaa, 0x81, 0xb9, 0x13, 0x09, 0xf2, 0x3b,
|
||||||
0x49, 0x35, 0x3d, 0x63, 0x9a, 0xe3, 0x82, 0x4a, 0x8c, 0x97, 0xd7, 0x7b, 0xd7, 0x48, 0xc9, 0xed,
|
0xa8, 0x29, 0x35, 0x5d, 0x63, 0x5a, 0xd0, 0x02, 0x26, 0xc6, 0xeb, 0x9b, 0xbd, 0x6b, 0x4c, 0xc9,
|
||||||
0x07, 0x22, 0x9c, 0x92, 0xb4, 0x2e, 0xb4, 0x01, 0x2b, 0x3b, 0xbe, 0xcf, 0x2e, 0xf0, 0xca, 0x66,
|
0xed, 0x85, 0x32, 0x1a, 0xbb, 0xd3, 0xba, 0xc8, 0x1a, 0x2c, 0x6d, 0x07, 0x01, 0xbf, 0xa4, 0x4b,
|
||||||
0xbe, 0x5e, 0x21, 0x9a, 0x40, 0x5f, 0x41, 0x69, 0x47, 0x08, 0xca, 0x05, 0xc7, 0x45, 0x65, 0xec,
|
0xeb, 0xf9, 0x7a, 0xc5, 0xd5, 0x04, 0xf9, 0x25, 0x94, 0xb6, 0xa5, 0x64, 0x42, 0x0a, 0x5a, 0x44,
|
||||||
0xe1, 0x62, 0x63, 0x1a, 0x44, 0x22, 0x30, 0x3a, 0x86, 0x8a, 0xb2, 0xbf, 0x13, 0xf6, 0x39, 0x2e,
|
0x63, 0x8f, 0xe7, 0x1b, 0xd3, 0x20, 0x37, 0x06, 0x93, 0x23, 0xa8, 0xa0, 0xfd, 0xed, 0xa8, 0x27,
|
||||||
0x29, 0xc9, 0x2f, 0x6e, 0x70, 0xcc, 0x58, 0x46, 0x1f, 0x71, 0xa6, 0x03, 0xed, 0x43, 0xa5, 0xe9,
|
0x68, 0x09, 0x25, 0xbf, 0xb8, 0xc5, 0x36, 0x13, 0x19, 0xbd, 0xc5, 0x89, 0x0e, 0xb2, 0x07, 0x95,
|
||||||
0x76, 0xcf, 0x69, 0x2b, 0x64, 0x43, 0x5c, 0x56, 0x0a, 0x7f, 0xbe, 0x58, 0xa1, 0x82, 0x19, 0x85,
|
0xa6, 0xd7, 0x39, 0x67, 0xad, 0x88, 0x0f, 0x68, 0x19, 0x15, 0xfe, 0x74, 0xbe, 0x42, 0x84, 0x19,
|
||||||
0x46, 0x4d, 0x2c, 0x89, 0x76, 0xa0, 0xa4, 0x88, 0x53, 0x86, 0x2b, 0xb7, 0x53, 0x12, 0xc9, 0x21,
|
0x85, 0x46, 0x4d, 0x22, 0x49, 0xb6, 0xa1, 0x84, 0xc4, 0x09, 0xa7, 0x95, 0xbb, 0x29, 0x89, 0xe5,
|
||||||
0x07, 0x56, 0x9b, 0xfd, 0x90, 0x8d, 0x47, 0x27, 0x6e, 0x48, 0x03, 0x81, 0x41, 0x7d, 0xea, 0x14,
|
0x88, 0x03, 0xcb, 0xcd, 0x5e, 0xc4, 0x47, 0xc3, 0x63, 0x2f, 0x62, 0xa1, 0xa4, 0x80, 0x47, 0x3d,
|
||||||
0x0f, 0xbd, 0x81, 0xd2, 0xfe, 0xe5, 0x88, 0x85, 0x82, 0xe3, 0xea, 0xb2, 0xcb, 0xab, 0x41, 0xc6,
|
0xc5, 0x23, 0x6f, 0xa1, 0xb4, 0x77, 0x35, 0xe4, 0x91, 0x14, 0xb4, 0xba, 0xe8, 0xf2, 0x6a, 0x90,
|
||||||
0x80, 0x91, 0x40, 0x8f, 0x00, 0xf6, 0x2f, 0x45, 0xe8, 0x1e, 0x30, 0x19, 0xf6, 0x55, 0xf5, 0x39,
|
0x31, 0x60, 0x24, 0xc8, 0x13, 0x80, 0xbd, 0x2b, 0x19, 0x79, 0xfb, 0x5c, 0x85, 0x7d, 0x19, 0x8f,
|
||||||
0x12, 0x1c, 0xd4, 0x82, 0xe2, 0x5b, 0xb7, 0x43, 0x7d, 0x8e, 0x6b, 0x4a, 0x77, 0xe3, 0x06, 0x81,
|
0x23, 0xc5, 0x21, 0x2d, 0x28, 0xbe, 0xf3, 0x4e, 0x59, 0x20, 0x68, 0x0d, 0x75, 0x37, 0x6e, 0x11,
|
||||||
0xd5, 0x02, 0xda, 0x90, 0x91, 0x96, 0x79, 0x7d, 0x44, 0xc5, 0x05, 0x0b, 0x07, 0xef, 0x58, 0x8f,
|
0x58, 0x2d, 0xa0, 0x0d, 0x19, 0x69, 0x95, 0xd7, 0x87, 0x4c, 0x5e, 0xf2, 0xa8, 0xff, 0x9e, 0x77,
|
||||||
0xe2, 0x35, 0x9d, 0xd7, 0x09, 0x16, 0x7a, 0x06, 0xb5, 0x23, 0xa6, 0x83, 0xe7, 0xf9, 0x82, 0x86,
|
0x19, 0x5d, 0xd1, 0x79, 0x9d, 0x62, 0x91, 0x17, 0x50, 0x3b, 0xe4, 0x3a, 0x78, 0x7e, 0x20, 0x59,
|
||||||
0xf8, 0x13, 0x75, 0x98, 0x34, 0x53, 0xdd, 0x65, 0xdf, 0x15, 0x67, 0x2c, 0x1c, 0x72, 0xbc, 0xae,
|
0x44, 0x3f, 0xc1, 0xcd, 0x4c, 0x33, 0xf1, 0x2e, 0x07, 0x9e, 0x3c, 0xe3, 0xd1, 0x40, 0xd0, 0x55,
|
||||||
0x10, 0x33, 0x86, 0xcc, 0xa0, 0x36, 0xed, 0x86, 0x54, 0x70, 0x7c, 0x67, 0x59, 0x06, 0x69, 0x10,
|
0x44, 0x4c, 0x18, 0x2a, 0x83, 0xda, 0xac, 0x13, 0x31, 0x29, 0xe8, 0xbd, 0x45, 0x19, 0xa4, 0x41,
|
||||||
0x89, 0xc0, 0x08, 0x43, 0xa9, 0x7d, 0x3e, 0x6c, 0x7b, 0x7f, 0xa4, 0x18, 0x6d, 0x5a, 0xf5, 0x3c,
|
0x6e, 0x0c, 0x26, 0x14, 0x4a, 0xed, 0xf3, 0x41, 0xdb, 0xff, 0x23, 0xa3, 0x64, 0xdd, 0xaa, 0xe7,
|
||||||
0x89, 0x48, 0xf4, 0x02, 0xf2, 0xed, 0xf6, 0x01, 0xfe, 0xa9, 0xd2, 0x76, 0x3f, 0x43, 0x5b, 0xfb,
|
0xdd, 0x98, 0x24, 0xaf, 0x20, 0xdf, 0x6e, 0xef, 0xd3, 0x1f, 0xa3, 0xb6, 0x87, 0x19, 0xda, 0xda,
|
||||||
0x80, 0x48, 0x14, 0x42, 0x50, 0x38, 0x75, 0xfb, 0x1c, 0x6f, 0xa8, 0x73, 0xa9, 0x35, 0xba, 0x07,
|
0xfb, 0xae, 0x42, 0x11, 0x02, 0x85, 0x13, 0xaf, 0x27, 0xe8, 0x1a, 0xee, 0x0b, 0xd7, 0xe4, 0x01,
|
||||||
0xc5, 0x53, 0x37, 0xec, 0x53, 0x81, 0xef, 0x2a, 0x9f, 0x0d, 0x85, 0x5e, 0x43, 0xe9, 0xbd, 0xef,
|
0x14, 0x4f, 0xbc, 0xa8, 0xc7, 0x24, 0xbd, 0x8f, 0x3e, 0x1b, 0x8a, 0xbc, 0x81, 0xd2, 0x87, 0xc0,
|
||||||
0x0d, 0x3d, 0xc1, 0xf1, 0xbd, 0x65, 0x97, 0x53, 0x83, 0x8e, 0x47, 0x82, 0x44, 0x78, 0x79, 0x5a,
|
0x1f, 0xf8, 0x52, 0xd0, 0x07, 0x8b, 0x2e, 0xa7, 0x06, 0x1d, 0x0d, 0xa5, 0x1b, 0xe3, 0xd5, 0x6e,
|
||||||
0x15, 0x6f, 0x1a, 0xe2, 0x9f, 0x29, 0x9d, 0x11, 0x29, 0x77, 0x4c, 0xb8, 0x30, 0xde, 0xb4, 0xea,
|
0x31, 0xde, 0x2c, 0xa2, 0x3f, 0x41, 0x9d, 0x31, 0xa9, 0xbe, 0x98, 0x70, 0x51, 0xba, 0x6e, 0xd5,
|
||||||
0x65, 0x12, 0x91, 0xf2, 0x68, 0x27, 0x63, 0xdf, 0xc7, 0xf7, 0x15, 0x5b, 0xad, 0xf5, 0xb7, 0x97,
|
0xcb, 0x6e, 0x4c, 0xaa, 0xad, 0x1d, 0x8f, 0x82, 0x80, 0x3e, 0x44, 0x36, 0xae, 0xf5, 0xd9, 0xab,
|
||||||
0x69, 0x70, 0x32, 0xe6, 0xe7, 0xd8, 0x56, 0x3b, 0x09, 0xce, 0x6c, 0xff, 0x2d, 0x73, 0x7b, 0xf8,
|
0x34, 0x38, 0x1e, 0x89, 0x73, 0x6a, 0xe3, 0x97, 0x14, 0x67, 0xf2, 0xfd, 0x1d, 0xf7, 0xba, 0xf4,
|
||||||
0x41, 0x72, 0x5f, 0x72, 0xd0, 0x21, 0xac, 0xb6, 0x55, 0x5b, 0x3a, 0x51, 0xcd, 0x08, 0x3f, 0x54,
|
0x51, 0xfa, 0xbb, 0xe2, 0x90, 0x03, 0x58, 0x6e, 0x63, 0x5b, 0x3a, 0xc6, 0x66, 0x44, 0x1f, 0xa3,
|
||||||
0x7e, 0x3c, 0x6f, 0xc8, 0xce, 0xd5, 0x88, 0x3a, 0x97, 0xf4, 0x21, 0xd9, 0xbc, 0x1a, 0x1a, 0x4c,
|
0x1f, 0x2f, 0x1b, 0xaa, 0x73, 0x35, 0xe2, 0xce, 0xa5, 0x7c, 0x48, 0x37, 0xaf, 0x86, 0x06, 0xbb,
|
||||||
0x52, 0xa2, 0x51, 0x5d, 0xfd, 0x74, 0x56, 0x57, 0x6d, 0x28, 0xff, 0x46, 0x26, 0xb9, 0x64, 0x3f,
|
0x53, 0xa2, 0x71, 0x5d, 0xfd, 0x74, 0x52, 0x57, 0x6d, 0x28, 0xff, 0x5a, 0x25, 0xb9, 0x62, 0x3f,
|
||||||
0x52, 0xec, 0x98, 0x96, 0xc9, 0xb4, 0x13, 0x04, 0x4c, 0xb8, 0xba, 0xee, 0x3e, 0x56, 0xe1, 0x4e,
|
0x41, 0x76, 0x42, 0xab, 0x64, 0xda, 0x0e, 0x43, 0x2e, 0x3d, 0x5d, 0x77, 0x9f, 0x62, 0xb8, 0xd3,
|
||||||
0xb2, 0xd0, 0x57, 0x70, 0xef, 0xa3, 0x27, 0xce, 0x4f, 0x42, 0x36, 0xa1, 0x81, 0x1b, 0x74, 0x69,
|
0x2c, 0xfb, 0x57, 0x40, 0x66, 0xab, 0x90, 0xb2, 0xd2, 0x67, 0xe3, 0xb8, 0x7a, 0xf7, 0xd9, 0x58,
|
||||||
0x54, 0xd1, 0xf1, 0xa6, 0x72, 0x23, 0x63, 0xd7, 0xfe, 0x35, 0xa0, 0xf9, 0xea, 0x25, 0x4f, 0x37,
|
0x15, 0xa2, 0x0b, 0x2f, 0x18, 0xc5, 0x35, 0x54, 0x13, 0x5f, 0xe7, 0xbe, 0xb2, 0xec, 0x6f, 0x60,
|
||||||
0xa0, 0xd3, 0xa8, 0xea, 0x0f, 0xe8, 0x54, 0x16, 0xb0, 0x89, 0xeb, 0x8f, 0xa3, 0xda, 0xab, 0x89,
|
0x65, 0xba, 0x40, 0xdc, 0x49, 0xfa, 0x0d, 0x54, 0x53, 0xb7, 0xe0, 0x2e, 0xa2, 0xce, 0xbf, 0x2d,
|
||||||
0x6f, 0x72, 0x5f, 0x5b, 0xf6, 0xb7, 0xb0, 0x96, 0x2e, 0x2c, 0xb7, 0x92, 0x7e, 0x0d, 0xd5, 0xc4,
|
0xa8, 0xa6, 0xae, 0x2a, 0x26, 0xd5, 0x78, 0xc8, 0x8c, 0x30, 0xae, 0xc9, 0x0e, 0x2c, 0x6d, 0x4b,
|
||||||
0xed, 0xb9, 0x8d, 0xa8, 0xf3, 0x2f, 0x0b, 0xaa, 0x89, 0x2b, 0xae, 0x92, 0x71, 0x3a, 0xa2, 0x46,
|
0x19, 0xa9, 0x96, 0xa3, 0xf2, 0xf2, 0xe7, 0x37, 0x5e, 0xf8, 0x06, 0xc2, 0xf5, 0x95, 0xd4, 0xa2,
|
||||||
0x58, 0xad, 0xd1, 0x2e, 0xac, 0xec, 0x08, 0x11, 0xca, 0x56, 0x25, 0xf3, 0xf9, 0x97, 0xd7, 0x16,
|
0x2a, 0x88, 0xbb, 0x4c, 0x48, 0x3f, 0xc4, 0x90, 0x61, 0x87, 0xa8, 0xb8, 0x69, 0x96, 0xfd, 0x15,
|
||||||
0x8a, 0x86, 0x82, 0xeb, 0xab, 0xac, 0x45, 0x65, 0xf0, 0xf7, 0x28, 0x17, 0x5e, 0xa0, 0x42, 0xad,
|
0xc0, 0x44, 0xec, 0x4e, 0x3e, 0xfc, 0xd3, 0x82, 0x7b, 0x33, 0x55, 0x6d, 0xae, 0x27, 0xfb, 0xd3,
|
||||||
0x3a, 0x4b, 0x85, 0x24, 0x59, 0xf6, 0xd7, 0x00, 0x33, 0xb1, 0x5b, 0xf9, 0xf0, 0x0f, 0x0b, 0xee,
|
0x9e, 0x6c, 0xde, 0xb2, 0x42, 0xce, 0xfa, 0xf3, 0x7f, 0xec, 0xf6, 0x10, 0x8a, 0xba, 0x95, 0xcc,
|
||||||
0xcc, 0x55, 0xc3, 0x85, 0x9e, 0x1c, 0xa4, 0x3d, 0xd9, 0xbe, 0x61, 0x65, 0x9d, 0xf7, 0xe7, 0xff,
|
0xdd, 0xa1, 0x0d, 0xe5, 0x5d, 0x5f, 0x78, 0xa7, 0x01, 0xeb, 0xa2, 0x68, 0xd9, 0x4d, 0x68, 0xec,
|
||||||
0x38, 0xed, 0x11, 0x14, 0x75, 0x0b, 0x5a, 0x78, 0x42, 0x1b, 0xca, 0x7b, 0x1e, 0x77, 0x3b, 0x3e,
|
0x63, 0xb8, 0x7b, 0x1d, 0x3d, 0x4d, 0x38, 0xba, 0x66, 0x90, 0x15, 0xc8, 0x25, 0x33, 0x50, 0xee,
|
||||||
0xed, 0x29, 0xd1, 0x32, 0x89, 0x69, 0xd5, 0xff, 0xd4, 0xe9, 0x75, 0xf4, 0x34, 0xe1, 0xe8, 0x5a,
|
0x60, 0x57, 0x81, 0x55, 0x03, 0xd7, 0xae, 0x56, 0x5c, 0x4d, 0x38, 0x2d, 0x28, 0xea, 0x2a, 0x34,
|
||||||
0x83, 0xd6, 0x20, 0x17, 0xcf, 0x4e, 0xb9, 0xc3, 0x3d, 0x09, 0x96, 0x8d, 0x5f, 0xbb, 0x5a, 0x21,
|
0x83, 0xb7, 0xa1, 0xdc, 0xf2, 0x03, 0x86, 0x73, 0x80, 0xde, 0x73, 0x42, 0x2b, 0xf7, 0xf6, 0xc2,
|
||||||
0x9a, 0x70, 0x5a, 0x50, 0xd4, 0xd5, 0x6b, 0x0e, 0x6f, 0x43, 0xb9, 0xe5, 0xf9, 0x54, 0xcd, 0x0f,
|
0x0b, 0x63, 0x56, 0x2d, 0x9d, 0xad, 0x54, 0xbb, 0x57, 0x7e, 0xe0, 0x64, 0x60, 0xfc, 0xc0, 0x79,
|
||||||
0xfa, 0xcc, 0x31, 0x2d, 0xdd, 0xdb, 0x0f, 0x26, 0xc6, 0xac, 0x5c, 0x3a, 0x3f, 0x24, 0xc6, 0x04,
|
0xe0, 0x01, 0x14, 0x5b, 0x3c, 0x1a, 0x78, 0xd2, 0x28, 0x33, 0x94, 0xe3, 0xc0, 0xca, 0x41, 0x28,
|
||||||
0xe9, 0x87, 0x9a, 0x28, 0x8c, 0x1f, 0x6a, 0x8e, 0xb8, 0x07, 0xc5, 0x16, 0x0b, 0x87, 0xae, 0x30,
|
0x86, 0xac, 0x23, 0xb3, 0xc7, 0xc6, 0x23, 0xf8, 0x24, 0xc1, 0x98, 0x81, 0x31, 0x35, 0xf7, 0x58,
|
||||||
0xca, 0x0c, 0x25, 0x5b, 0xd3, 0x61, 0x3f, 0x60, 0x21, 0x6d, 0x0b, 0x57, 0x8c, 0xb5, 0x2b, 0x65,
|
0x77, 0x9f, 0x7b, 0xfe, 0x61, 0x41, 0x25, 0xa9, 0x6c, 0xa4, 0x09, 0x45, 0x3c, 0x8d, 0x78, 0xfa,
|
||||||
0x92, 0xe2, 0x39, 0x0e, 0xac, 0x1d, 0x06, 0x7c, 0x44, 0xbb, 0x22, 0x7b, 0x24, 0x3d, 0x86, 0x4f,
|
0x7c, 0x75, 0x43, 0x29, 0x6c, 0x7c, 0x44, 0xb4, 0xe9, 0x30, 0x5a, 0xd4, 0xfe, 0x1e, 0xaa, 0x29,
|
||||||
0x62, 0x8c, 0x19, 0x46, 0x13, 0x33, 0x95, 0x75, 0xfb, 0x99, 0xea, 0xef, 0x16, 0x54, 0xe2, 0xaa,
|
0xf6, 0x9c, 0x04, 0xd8, 0x4c, 0x27, 0x40, 0x66, 0x6b, 0xd0, 0x46, 0xd2, 0xe9, 0xb1, 0x0b, 0x45,
|
||||||
0x89, 0x9a, 0x50, 0x54, 0x5f, 0x2c, 0x9a, 0x6c, 0x5f, 0x5c, 0x53, 0x66, 0x1b, 0x1f, 0x14, 0xda,
|
0xcd, 0x9c, 0x1b, 0x56, 0x02, 0x85, 0x7d, 0x2f, 0xd2, 0xa9, 0x91, 0x77, 0x71, 0xad, 0x78, 0x6d,
|
||||||
0x74, 0x2f, 0x2d, 0x6a, 0x7f, 0x84, 0x6a, 0x82, 0xbd, 0x20, 0x49, 0xb6, 0x93, 0x49, 0x92, 0xd9,
|
0x7e, 0x26, 0xf1, 0x78, 0xf2, 0x2e, 0xae, 0x9d, 0x7f, 0x59, 0x50, 0x33, 0xa3, 0xa4, 0x89, 0x20,
|
||||||
0x76, 0xb4, 0x91, 0x64, 0x0a, 0xed, 0x41, 0x51, 0x33, 0x17, 0x86, 0x1e, 0x41, 0xe1, 0xc0, 0x0d,
|
0x83, 0x55, 0x7d, 0x43, 0x59, 0x14, 0xf3, 0x8c, 0xff, 0x6f, 0x16, 0x84, 0x32, 0x86, 0x36, 0xae,
|
||||||
0x75, 0xfa, 0xe4, 0x89, 0x5a, 0x4b, 0x5e, 0x9b, 0x9d, 0x09, 0x15, 0xee, 0x3c, 0x51, 0x6b, 0xe7,
|
0xcb, 0xea, 0x68, 0xcc, 0xa8, 0xb4, 0x9b, 0x70, 0x7f, 0x2e, 0xf4, 0x4e, 0x57, 0xe4, 0x25, 0xdc,
|
||||||
0x9f, 0x16, 0xd4, 0xcc, 0x98, 0x6a, 0x22, 0x48, 0x61, 0x5d, 0xdf, 0x62, 0x1a, 0xc6, 0x95, 0x4f,
|
0x9b, 0x0c, 0xc9, 0xd9, 0x79, 0xb2, 0x06, 0x24, 0x0d, 0x33, 0x43, 0xf4, 0x53, 0xa8, 0xaa, 0x47,
|
||||||
0xfb, 0xff, 0x7a, 0x49, 0x28, 0x23, 0x68, 0xe3, 0xaa, 0xac, 0x8e, 0xc6, 0x9c, 0x4a, 0xbb, 0x09,
|
0x47, 0xb6, 0x98, 0x03, 0xcb, 0x1a, 0x60, 0x22, 0x43, 0xa0, 0xd0, 0x67, 0x63, 0x9d, 0x0d, 0x15,
|
||||||
0x77, 0x17, 0x42, 0x6f, 0x75, 0x8d, 0x9e, 0xc3, 0x9d, 0xd9, 0x00, 0x9e, 0x9d, 0x27, 0x1b, 0x80,
|
0x17, 0xd7, 0xce, 0xdf, 0x2c, 0xf5, 0x76, 0x18, 0x8e, 0xe4, 0x7b, 0x26, 0x84, 0xd7, 0x53, 0x09,
|
||||||
0x92, 0x30, 0x33, 0xa0, 0x3f, 0x86, 0xaa, 0x7c, 0xd0, 0x64, 0x8b, 0x39, 0xb0, 0xaa, 0x01, 0x26,
|
0x58, 0x38, 0x08, 0x7d, 0x69, 0xb2, 0xef, 0xb3, 0xac, 0x37, 0xc4, 0x70, 0x24, 0x15, 0xcc, 0x48,
|
||||||
0x32, 0x08, 0x0a, 0x03, 0x3a, 0xd5, 0xd9, 0x50, 0x21, 0x6a, 0xed, 0xfc, 0xcd, 0x92, 0xef, 0x92,
|
0xed, 0xff, 0xc8, 0x45, 0x29, 0xb2, 0x05, 0x85, 0x5d, 0x4f, 0x7a, 0x26, 0x17, 0x32, 0x26, 0x26,
|
||||||
0xd1, 0x58, 0xbc, 0xa3, 0x9c, 0xbb, 0x7d, 0x99, 0x80, 0x85, 0xc3, 0xc0, 0x13, 0x26, 0xfb, 0x3e,
|
0x85, 0x48, 0x09, 0x2a, 0x72, 0xa7, 0xa4, 0x1e, 0x4a, 0xc3, 0x91, 0x74, 0x5e, 0xc0, 0xea, 0x75,
|
||||||
0xcb, 0x7a, 0x9f, 0x8c, 0xc6, 0x42, 0xc2, 0x8c, 0xd4, 0xc1, 0x4f, 0x88, 0x92, 0x42, 0xaf, 0xa0,
|
0xed, 0x73, 0x5c, 0xfb, 0x12, 0xaa, 0x29, 0x2d, 0x78, 0x6f, 0x8f, 0x5a, 0x08, 0x28, 0xbb, 0x6a,
|
||||||
0xb0, 0xe7, 0x0a, 0xd7, 0xe4, 0x42, 0xc6, 0x34, 0x26, 0x11, 0x09, 0x41, 0x49, 0xee, 0x96, 0xe4,
|
0xa9, 0x7c, 0x4d, 0x36, 0xb2, 0xac, 0x6d, 0x38, 0x9f, 0x40, 0x0d, 0x55, 0x27, 0x11, 0xfc, 0x53,
|
||||||
0x23, 0x6c, 0x34, 0x16, 0xce, 0x33, 0x58, 0xbf, 0xaa, 0x7d, 0x81, 0x6b, 0x5f, 0x42, 0x35, 0xa1,
|
0x0e, 0x4a, 0xb1, 0x8a, 0xad, 0x29, 0xbf, 0x9f, 0x65, 0xf9, 0x3d, 0xeb, 0xf2, 0x6b, 0x28, 0xa8,
|
||||||
0x45, 0xdd, 0xed, 0xe3, 0x96, 0x02, 0x94, 0x89, 0x5c, 0x4a, 0x5f, 0xe3, 0x83, 0xac, 0x6a, 0x1b,
|
0xfa, 0x61, 0x5c, 0xce, 0x18, 0x37, 0x5a, 0xdd, 0x94, 0x98, 0x82, 0x93, 0x6f, 0xa1, 0xe8, 0x32,
|
||||||
0xce, 0x27, 0x50, 0x53, 0xaa, 0xe3, 0x08, 0xfe, 0x29, 0x07, 0xa5, 0x48, 0xc5, 0xab, 0x94, 0xdf,
|
0xa1, 0x46, 0x23, 0xfd, 0x88, 0x78, 0x3e, 0x5f, 0x50, 0x63, 0x26, 0xc2, 0x46, 0x48, 0x89, 0xb7,
|
||||||
0x4f, 0xb2, 0xfc, 0x9e, 0x77, 0xf9, 0x25, 0x14, 0x64, 0x8d, 0x31, 0x2e, 0x67, 0x8c, 0x32, 0xad,
|
0xfd, 0x5e, 0xe8, 0x05, 0xb4, 0xb0, 0x48, 0x5c, 0x63, 0x52, 0xe2, 0x9a, 0x31, 0x09, 0xf7, 0x5f,
|
||||||
0x5e, 0x42, 0x4c, 0xc2, 0xd1, 0x77, 0x50, 0x24, 0x94, 0xcb, 0xb1, 0x4b, 0x3f, 0x50, 0x9e, 0x2e,
|
0x2c, 0xa8, 0x2e, 0x0c, 0xf5, 0xe2, 0x67, 0xde, 0xcc, 0xd3, 0x33, 0xff, 0x3f, 0x3e, 0x3d, 0xff,
|
||||||
0x16, 0xd4, 0x98, 0x99, 0xb0, 0x11, 0x92, 0xe2, 0x6d, 0xaf, 0x1f, 0xb8, 0x3e, 0x2e, 0x2c, 0x13,
|
0x9c, 0x9b, 0x56, 0x84, 0x53, 0x92, 0xba, 0x4f, 0x43, 0xee, 0x87, 0xd2, 0xa4, 0x6c, 0x8a, 0xa3,
|
||||||
0xd7, 0x98, 0x84, 0xb8, 0x66, 0xcc, 0xc2, 0xfd, 0x17, 0x0b, 0xaa, 0x4b, 0x43, 0xbd, 0xfc, 0x09,
|
0x36, 0xda, 0x1c, 0x74, 0x4d, 0xd1, 0x57, 0x4b, 0x75, 0xcd, 0x0e, 0xb9, 0xe2, 0x55, 0x31, 0x0d,
|
||||||
0x39, 0xf7, 0xac, 0xcd, 0xff, 0x8f, 0xcf, 0xda, 0x3f, 0xe7, 0xd2, 0x8a, 0xd4, 0x04, 0x26, 0xef,
|
0x34, 0x31, 0x29, 0xe9, 0x79, 0x53, 0xd2, 0x55, 0x6a, 0x7c, 0x10, 0x2c, 0xc2, 0xc0, 0x55, 0x5c,
|
||||||
0xd3, 0x88, 0x79, 0x81, 0x30, 0x29, 0x9b, 0xe0, 0xc8, 0x83, 0x36, 0x87, 0x3d, 0xd3, 0x18, 0xe4,
|
0x5c, 0xab, 0x2a, 0x7e, 0xc8, 0x91, 0xbb, 0x84, 0xc2, 0x86, 0x42, 0x2b, 0x97, 0x5d, 0x5a, 0xd4,
|
||||||
0x52, 0x5e, 0xb3, 0x23, 0x26, 0x79, 0x55, 0x95, 0x06, 0x9a, 0x98, 0x95, 0xfd, 0xbc, 0x29, 0xfb,
|
0xe1, 0x68, 0x5e, 0xc6, 0x56, 0x2e, 0xbb, 0xb4, 0x94, 0x58, 0xb9, 0x44, 0x2b, 0x27, 0x72, 0x4c,
|
||||||
0x32, 0x35, 0xde, 0x73, 0x1a, 0xaa, 0xc0, 0x55, 0x88, 0x5a, 0xcb, 0x4a, 0x7f, 0xc4, 0x14, 0x77,
|
0xcb, 0x3a, 0x01, 0x4f, 0xe4, 0x58, 0xb5, 0x19, 0x97, 0x07, 0xc1, 0xa9, 0xd7, 0xe9, 0xd3, 0x8a,
|
||||||
0x45, 0x09, 0x1b, 0x4a, 0x59, 0xb9, 0xe8, 0xe1, 0xa2, 0x0e, 0x47, 0xf3, 0x22, 0xb2, 0x72, 0xd1,
|
0xee, 0x6f, 0x31, 0xad, 0xe6, 0x49, 0x15, 0x73, 0xdf, 0x0b, 0xf0, 0xe5, 0x51, 0x76, 0x63, 0xd2,
|
||||||
0xc3, 0xa5, 0xd8, 0xca, 0x85, 0xb2, 0x72, 0x2a, 0xa6, 0xb8, 0xac, 0x13, 0xf0, 0x54, 0x4c, 0x65,
|
0xd9, 0x86, 0x4a, 0x92, 0x2a, 0xaa, 0x73, 0xb5, 0xba, 0x78, 0x14, 0x35, 0x37, 0xd7, 0xea, 0xc6,
|
||||||
0x2b, 0x22, 0xcc, 0xf7, 0x3b, 0x6e, 0x77, 0x80, 0x2b, 0xba, 0x07, 0x46, 0xb4, 0x9c, 0x55, 0x65,
|
0x59, 0x9e, 0x9b, 0xcd, 0xf2, 0x7c, 0x2a, 0xcb, 0xb7, 0xa0, 0x36, 0x95, 0x34, 0x0a, 0xe4, 0xf2,
|
||||||
0xcc, 0x3d, 0xd7, 0x57, 0xaf, 0x9a, 0x32, 0x89, 0x48, 0x67, 0x07, 0x2a, 0x71, 0xaa, 0xc8, 0xee,
|
0x4b, 0x61, 0x14, 0xe1, 0x5a, 0xf1, 0x9a, 0x3c, 0xd0, 0x6f, 0xeb, 0x9a, 0x8b, 0x6b, 0xe7, 0x39,
|
||||||
0xd6, 0xea, 0xa9, 0x4f, 0x51, 0x23, 0xb9, 0x56, 0x2f, 0xca, 0xf2, 0xdc, 0x7c, 0x96, 0xe7, 0x13,
|
0xd4, 0xa6, 0xd2, 0x65, 0x5e, 0x5d, 0x76, 0x9e, 0x41, 0xad, 0x2d, 0x3d, 0x39, 0x5a, 0xf0, 0x67,
|
||||||
0x59, 0xfe, 0x0a, 0x6a, 0xa9, 0xa4, 0x91, 0x20, 0xc2, 0x2e, 0xb8, 0x51, 0xa4, 0xd6, 0x92, 0xd7,
|
0xc8, 0x7f, 0x2c, 0x58, 0x89, 0x31, 0xa6, 0xf2, 0xfc, 0x02, 0xca, 0x17, 0x2c, 0x92, 0xec, 0x2a,
|
||||||
0x64, 0xbe, 0x7e, 0xb7, 0xd7, 0x88, 0x5a, 0x3b, 0x4f, 0xa1, 0x96, 0x4a, 0x97, 0x45, 0x75, 0xd9,
|
0xe9, 0x45, 0x74, 0x76, 0x9c, 0xfd, 0x88, 0x08, 0x37, 0x41, 0x92, 0xaf, 0xa1, 0x2c, 0x50, 0x0f,
|
||||||
0x79, 0x02, 0x35, 0xdd, 0xe0, 0xb2, 0xcb, 0xce, 0x7f, 0x2c, 0x58, 0x8b, 0x30, 0xa6, 0xf2, 0xfc,
|
0x8b, 0xe7, 0x98, 0x27, 0x59, 0x52, 0xc6, 0x5e, 0x82, 0x27, 0x1b, 0x50, 0x08, 0x78, 0x4f, 0xe0,
|
||||||
0x0a, 0xca, 0x13, 0x1a, 0x0a, 0x7a, 0x19, 0xf7, 0x22, 0x3c, 0x3f, 0x2a, 0x7f, 0x50, 0x08, 0x12,
|
0xb9, 0x57, 0x37, 0x1f, 0x65, 0xc9, 0xbd, 0xe3, 0x3d, 0x17, 0x81, 0xe4, 0x2d, 0x94, 0x2f, 0xbd,
|
||||||
0x23, 0xd1, 0x37, 0x50, 0xe6, 0x4a, 0x0f, 0x8d, 0x66, 0x9d, 0x47, 0x59, 0x52, 0xc6, 0x5e, 0x8c,
|
0x28, 0xf4, 0xc3, 0x5e, 0xfc, 0x26, 0x7f, 0x9a, 0x25, 0xf4, 0xbd, 0xc6, 0xb9, 0x89, 0x80, 0x53,
|
||||||
0x47, 0x5b, 0x50, 0xf0, 0x59, 0x9f, 0xab, 0xef, 0x5e, 0xdd, 0x7e, 0x90, 0x25, 0xf7, 0x96, 0xf5,
|
0x53, 0x97, 0xe8, 0x8c, 0x9b, 0x98, 0x38, 0xbf, 0x51, 0xb9, 0xac, 0x48, 0xe3, 0xfe, 0x01, 0xd4,
|
||||||
0x89, 0x02, 0xa2, 0x37, 0x50, 0xbe, 0x70, 0xc3, 0xc0, 0x0b, 0xfa, 0xd1, 0x7b, 0xff, 0x71, 0x96,
|
0xf4, 0x7d, 0xf8, 0xc8, 0x22, 0xa1, 0xa6, 0x42, 0x6b, 0xd1, 0x9d, 0xdd, 0x49, 0x43, 0xdd, 0x69,
|
||||||
0xd0, 0x47, 0x8d, 0x23, 0xb1, 0x80, 0x53, 0x93, 0x97, 0xe8, 0x8c, 0x99, 0x98, 0x38, 0xbf, 0x95,
|
0x49, 0xe7, 0x07, 0xd3, 0xee, 0x62, 0x86, 0xca, 0xa5, 0xa1, 0xd7, 0xe9, 0x7b, 0xbd, 0xf8, 0x9c,
|
||||||
0xb9, 0x2c, 0x49, 0xe3, 0xfe, 0x21, 0xd4, 0xf4, 0x7d, 0xf8, 0x40, 0x43, 0x2e, 0x27, 0x47, 0x6b,
|
0x62, 0x52, 0x7d, 0xb9, 0x30, 0xf6, 0xf4, 0xb5, 0x8d, 0x49, 0x95, 0x9b, 0x11, 0xbb, 0xf0, 0xc5,
|
||||||
0xd9, 0x9d, 0xdd, 0x4d, 0x42, 0x49, 0x5a, 0xd2, 0xf9, 0xd1, 0xb4, 0xbb, 0x88, 0x21, 0x73, 0x69,
|
0x64, 0x40, 0x4d, 0xe8, 0xcd, 0xbf, 0x96, 0x00, 0x9a, 0xc9, 0x7e, 0xc8, 0x31, 0x2c, 0xa1, 0x3d,
|
||||||
0xe4, 0x76, 0x07, 0x6e, 0x3f, 0xfa, 0x4e, 0x11, 0x29, 0x77, 0x26, 0xc6, 0x9e, 0xbe, 0xb6, 0x11,
|
0xe2, 0x2c, 0x6c, 0x9e, 0xe8, 0xb7, 0xfd, 0xfc, 0x16, 0x0d, 0x96, 0x7c, 0x54, 0xc9, 0x8f, 0x43,
|
||||||
0x29, 0x73, 0x33, 0xa4, 0x13, 0x8f, 0xcf, 0x86, 0xd8, 0x98, 0xde, 0xfe, 0x6b, 0x09, 0xa0, 0x19,
|
0x0f, 0x79, 0x91, 0x55, 0x26, 0xd2, 0x73, 0x93, 0xfd, 0xf2, 0x06, 0x94, 0xd1, 0xfb, 0x01, 0x8a,
|
||||||
0x9f, 0x07, 0x9d, 0xc0, 0x8a, 0xb2, 0x87, 0x9c, 0xa5, 0xcd, 0x53, 0xf9, 0x6d, 0x3f, 0xbd, 0x41,
|
0x3a, 0x0b, 0x48, 0x56, 0x2d, 0x4c, 0xe7, 0xad, 0xfd, 0x62, 0x31, 0x48, 0x2b, 0xfd, 0xdc, 0x22,
|
||||||
0x83, 0x45, 0x1f, 0x64, 0xf2, 0xab, 0xa1, 0x07, 0x3d, 0xcb, 0x2a, 0x13, 0xc9, 0xb9, 0xc9, 0x7e,
|
0xae, 0xa9, 0x94, 0xc4, 0x59, 0xd0, 0x0a, 0xcd, 0x8d, 0xc9, 0x0a, 0xc0, 0x54, 0xd7, 0xa9, 0x5b,
|
||||||
0x7e, 0x0d, 0xca, 0xe8, 0x7d, 0x0f, 0x45, 0x9d, 0x05, 0x28, 0xab, 0x16, 0x26, 0xf3, 0xd6, 0x7e,
|
0xe4, 0x3b, 0x28, 0xea, 0x5a, 0x47, 0x3e, 0x9d, 0x2f, 0x10, 0xeb, 0x5b, 0xfc, 0xb9, 0x6e, 0x7d,
|
||||||
0xb6, 0x1c, 0xa4, 0x95, 0x7e, 0x6e, 0x21, 0x62, 0x2a, 0x25, 0x72, 0x96, 0xb4, 0x42, 0x73, 0x63,
|
0x6e, 0x91, 0xf7, 0x50, 0x50, 0x4d, 0x9e, 0x64, 0x74, 0xac, 0xd4, 0x84, 0x60, 0x3b, 0x8b, 0x20,
|
||||||
0xb2, 0x02, 0x90, 0xea, 0x3a, 0x75, 0x0b, 0x7d, 0x0f, 0x45, 0x5d, 0xeb, 0xd0, 0xa7, 0x8b, 0x05,
|
0x26, 0x8a, 0x3f, 0x00, 0x4c, 0x46, 0x0d, 0x92, 0xf1, 0xcf, 0xca, 0xcc, 0xcc, 0x62, 0xd7, 0x6f,
|
||||||
0x22, 0x7d, 0xcb, 0xb7, 0xeb, 0xd6, 0xe7, 0x16, 0x7a, 0x07, 0x05, 0xd9, 0xe4, 0x51, 0x46, 0xc7,
|
0x06, 0x1a, 0x03, 0xef, 0x55, 0x9f, 0x3d, 0xe3, 0x24, 0xb3, 0xc3, 0x26, 0xd7, 0xc8, 0x76, 0x16,
|
||||||
0x4a, 0x4c, 0x08, 0xb6, 0xb3, 0x0c, 0x62, 0xa2, 0xf8, 0x23, 0xc0, 0x6c, 0xd4, 0x40, 0x19, 0xff,
|
0x41, 0x8c, 0xba, 0x73, 0xa8, 0x4d, 0xfd, 0xf3, 0x4a, 0x7e, 0x96, 0xed, 0xe4, 0xf5, 0x3f, 0x72,
|
||||||
0xda, 0xcc, 0xcd, 0x2c, 0x76, 0xfd, 0x7a, 0xa0, 0x31, 0xf0, 0x4e, 0xf6, 0xd9, 0x33, 0x86, 0x32,
|
0xed, 0x57, 0xb7, 0xc2, 0x1a, 0x4b, 0x32, 0x3d, 0xab, 0x99, 0xcf, 0xa4, 0x71, 0x93, 0xdf, 0xd3,
|
||||||
0x3b, 0x6c, 0x7c, 0x8d, 0x6c, 0x67, 0x19, 0xc4, 0xa8, 0x3b, 0x87, 0x5a, 0xea, 0x5f, 0x5d, 0xf4,
|
0xff, 0xa2, 0xda, 0x1b, 0xb7, 0xc6, 0x6b, 0xab, 0x3b, 0x85, 0xdf, 0xe6, 0x86, 0xa7, 0xa7, 0x45,
|
||||||
0x8b, 0x6c, 0x27, 0xaf, 0xfe, 0x49, 0x6c, 0xbf, 0xb8, 0x11, 0xd6, 0x58, 0x12, 0xc9, 0x59, 0xcd,
|
0xfc, 0x43, 0xfa, 0xcb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x77, 0x0e, 0x2f, 0x2e, 0x17,
|
||||||
0x6c, 0xa3, 0xc6, 0x75, 0x7e, 0xa7, 0xff, 0xa1, 0xb5, 0xb7, 0x6e, 0x8c, 0xd7, 0x56, 0x77, 0x0b,
|
0x00, 0x00,
|
||||||
0xbf, 0xcb, 0x8d, 0x3a, 0x9d, 0xa2, 0xfa, 0xb3, 0xfb, 0xcb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff,
|
|
||||||
0xc1, 0x07, 0x8b, 0x2b, 0x8a, 0x17, 0x00, 0x00,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
@@ -80,7 +80,6 @@ message BuildOptions {
|
|||||||
string Ref = 29;
|
string Ref = 29;
|
||||||
string GroupRef = 30;
|
string GroupRef = 30;
|
||||||
repeated string Annotations = 31;
|
repeated string Annotations = 31;
|
||||||
bool WithProvenanceResponse = 32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message ExportEntry {
|
message ExportEntry {
|
||||||
@@ -112,9 +111,8 @@ message Secret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message PrintFunc {
|
message PrintFunc {
|
||||||
string Name = 1;
|
string Name = 1;
|
||||||
string Format = 2;
|
string Format = 2;
|
||||||
bool IgnoreStatus = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message InspectRequest {
|
message InspectRequest {
|
||||||
|
@@ -15,7 +15,6 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
|
|||||||
if len(entries) == 0 {
|
if len(entries) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
var stdoutUsed bool
|
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry.Type == "" {
|
if entry.Type == "" {
|
||||||
return nil, errors.Errorf("type is required for output")
|
return nil, errors.Errorf("type is required for output")
|
||||||
@@ -69,14 +68,10 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
|
|||||||
entry.Destination = "-"
|
entry.Destination = "-"
|
||||||
}
|
}
|
||||||
if entry.Destination == "-" {
|
if entry.Destination == "-" {
|
||||||
if stdoutUsed {
|
|
||||||
return nil, errors.Errorf("multiple outputs configured to write to stdout")
|
|
||||||
}
|
|
||||||
if _, err := console.ConsoleFromFile(os.Stdout); err == nil {
|
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, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
|
||||||
}
|
}
|
||||||
out.Output = wrapWriteCloser(os.Stdout)
|
out.Output = wrapWriteCloser(os.Stdout)
|
||||||
stdoutUsed = true
|
|
||||||
} else if entry.Destination != "" {
|
} else if entry.Destination != "" {
|
||||||
fi, err := os.Stat(entry.Destination)
|
fi, err := os.Stat(entry.Destination)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/docker/builder/remotecontext/urlutil"
|
||||||
"github.com/moby/buildkit/util/gitutil"
|
"github.com/moby/buildkit/util/gitutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.DockerfileName != "" && options.DockerfileName != "-" {
|
if options.DockerfileName != "" && options.DockerfileName != "-" {
|
||||||
if localContext && !isHTTPURL(options.DockerfileName) {
|
if localContext && !urlutil.IsURL(options.DockerfileName) {
|
||||||
options.DockerfileName, err = filepath.Abs(options.DockerfileName)
|
options.DockerfileName, err = filepath.Abs(options.DockerfileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -163,15 +164,8 @@ func ResolveOptionPaths(options *BuildOptions) (_ *BuildOptions, err error) {
|
|||||||
return options, nil
|
return options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isHTTPURL returns true if the provided str is an HTTP(S) URL by checking if it
|
|
||||||
// has a http:// or https:// scheme. No validation is performed to verify if the
|
|
||||||
// URL is well-formed.
|
|
||||||
func isHTTPURL(str string) bool {
|
|
||||||
return strings.HasPrefix(str, "https://") || strings.HasPrefix(str, "http://")
|
|
||||||
}
|
|
||||||
|
|
||||||
func isRemoteURL(c string) bool {
|
func isRemoteURL(c string) bool {
|
||||||
if isHTTPURL(c) {
|
if urlutil.IsURL(c) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, err := gitutil.ParseGitRef(c); err == nil {
|
if _, err := gitutil.ParseGitRef(c); err == nil {
|
||||||
|
@@ -210,7 +210,7 @@ func (c *Client) build(ctx context.Context, ref string, options pb.BuildOptions,
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
} else if n > 0 {
|
} else if n > 0 {
|
||||||
if err := stream.Send(&pb.InputMessage{
|
if stream.Send(&pb.InputMessage{
|
||||||
Input: &pb.InputMessage_Data{
|
Input: &pb.InputMessage_Data{
|
||||||
Data: &pb.DataMessage{
|
Data: &pb.DataMessage{
|
||||||
Data: buf[:n],
|
Data: buf[:n],
|
||||||
|
@@ -358,7 +358,7 @@ func copyToStream(fd uint32, snd msgStream, r io.Reader) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
} else if n > 0 {
|
} else if n > 0 {
|
||||||
if err := snd.Send(&pb.Message{
|
if snd.Send(&pb.Message{
|
||||||
Input: &pb.Message_File{
|
Input: &pb.Message_File{
|
||||||
File: &pb.FdMessage{
|
File: &pb.FdMessage{
|
||||||
Fd: fd,
|
Fd: fd,
|
||||||
|
@@ -7,9 +7,6 @@ variable "DOCS_FORMATS" {
|
|||||||
variable "DESTDIR" {
|
variable "DESTDIR" {
|
||||||
default = "./bin"
|
default = "./bin"
|
||||||
}
|
}
|
||||||
variable "GOLANGCI_LINT_MULTIPLATFORM" {
|
|
||||||
default = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# Special target: https://github.com/docker/metadata-action#bake-definition
|
# Special target: https://github.com/docker/metadata-action#bake-definition
|
||||||
target "meta-helper" {
|
target "meta-helper" {
|
||||||
@@ -28,29 +25,13 @@ group "default" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group "validate" {
|
group "validate" {
|
||||||
targets = ["lint", "lint-gopls", "validate-vendor", "validate-docs"]
|
targets = ["lint", "validate-vendor", "validate-docs"]
|
||||||
}
|
}
|
||||||
|
|
||||||
target "lint" {
|
target "lint" {
|
||||||
inherits = ["_common"]
|
inherits = ["_common"]
|
||||||
dockerfile = "./hack/dockerfiles/lint.Dockerfile"
|
dockerfile = "./hack/dockerfiles/lint.Dockerfile"
|
||||||
output = ["type=cacheonly"]
|
output = ["type=cacheonly"]
|
||||||
platforms = GOLANGCI_LINT_MULTIPLATFORM != "" ? [
|
|
||||||
"darwin/amd64",
|
|
||||||
"darwin/arm64",
|
|
||||||
"linux/amd64",
|
|
||||||
"linux/arm64",
|
|
||||||
"linux/s390x",
|
|
||||||
"linux/ppc64le",
|
|
||||||
"linux/riscv64",
|
|
||||||
"windows/amd64",
|
|
||||||
"windows/arm64"
|
|
||||||
] : []
|
|
||||||
}
|
|
||||||
|
|
||||||
target "lint-gopls" {
|
|
||||||
inherits = ["lint"]
|
|
||||||
target = "gopls-analyze"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target "validate-vendor" {
|
target "validate-vendor" {
|
||||||
@@ -185,9 +166,6 @@ variable "HTTPS_PROXY" {
|
|||||||
variable "NO_PROXY" {
|
variable "NO_PROXY" {
|
||||||
default = ""
|
default = ""
|
||||||
}
|
}
|
||||||
variable "TEST_BUILDKIT_TAG" {
|
|
||||||
default = null
|
|
||||||
}
|
|
||||||
|
|
||||||
target "integration-test-base" {
|
target "integration-test-base" {
|
||||||
inherits = ["_common"]
|
inherits = ["_common"]
|
||||||
@@ -195,7 +173,6 @@ target "integration-test-base" {
|
|||||||
HTTP_PROXY = HTTP_PROXY
|
HTTP_PROXY = HTTP_PROXY
|
||||||
HTTPS_PROXY = HTTPS_PROXY
|
HTTPS_PROXY = HTTPS_PROXY
|
||||||
NO_PROXY = NO_PROXY
|
NO_PROXY = NO_PROXY
|
||||||
BUILDKIT_VERSION = TEST_BUILDKIT_TAG
|
|
||||||
}
|
}
|
||||||
target = "integration-test-base"
|
target = "integration-test-base"
|
||||||
output = ["type=cacheonly"]
|
output = ["type=cacheonly"]
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
---
|
# Bake file reference
|
||||||
title: Bake file reference
|
|
||||||
---
|
|
||||||
|
|
||||||
The Bake file is a file for defining workflows that you run using `docker buildx bake`.
|
The Bake file is a file for defining workflows that you run using `docker buildx bake`.
|
||||||
|
|
||||||
@@ -215,7 +213,7 @@ target "webapp" {
|
|||||||
The following table shows the complete list of attributes that you can assign to a target:
|
The following table shows the complete list of attributes that you can assign to a target:
|
||||||
|
|
||||||
| Name | Type | Description |
|
| Name | Type | Description |
|
||||||
|-------------------------------------------------|---------|----------------------------------------------------------------------|
|
| ----------------------------------------------- | ------- | -------------------------------------------------------------------- |
|
||||||
| [`args`](#targetargs) | Map | Build arguments |
|
| [`args`](#targetargs) | Map | Build arguments |
|
||||||
| [`annotations`](#targetannotations) | List | Exporter annotations |
|
| [`annotations`](#targetannotations) | List | Exporter annotations |
|
||||||
| [`attest`](#targetattest) | List | Build attestations |
|
| [`attest`](#targetattest) | List | Build attestations |
|
||||||
@@ -235,11 +233,9 @@ The following table shows the complete list of attributes that you can assign to
|
|||||||
| [`platforms`](#targetplatforms) | List | Target platforms |
|
| [`platforms`](#targetplatforms) | List | Target platforms |
|
||||||
| [`pull`](#targetpull) | Boolean | Always pull images |
|
| [`pull`](#targetpull) | Boolean | Always pull images |
|
||||||
| [`secret`](#targetsecret) | List | Secrets to expose to the build |
|
| [`secret`](#targetsecret) | List | Secrets to expose to the build |
|
||||||
| [`shm-size`](#targetshm-size) | List | Size of `/dev/shm` |
|
|
||||||
| [`ssh`](#targetssh) | List | SSH agent sockets or keys to expose to the build |
|
| [`ssh`](#targetssh) | List | SSH agent sockets or keys to expose to the build |
|
||||||
| [`tags`](#targettags) | List | Image names and tags |
|
| [`tags`](#targettags) | List | Image names and tags |
|
||||||
| [`target`](#targettarget) | String | Target build stage |
|
| [`target`](#targettarget) | String | Target build stage |
|
||||||
| [`ulimits`](#targetulimits) | List | Ulimit options |
|
|
||||||
|
|
||||||
### `target.args`
|
### `target.args`
|
||||||
|
|
||||||
@@ -836,29 +832,6 @@ RUN --mount=type=secret,id=KUBECONFIG \
|
|||||||
KUBECONFIG=$(cat /run/secrets/KUBECONFIG) helm upgrade --install
|
KUBECONFIG=$(cat /run/secrets/KUBECONFIG) helm upgrade --install
|
||||||
```
|
```
|
||||||
|
|
||||||
### `target.shm-size`
|
|
||||||
|
|
||||||
Sets the size of the shared memory allocated for build containers when using
|
|
||||||
`RUN` instructions.
|
|
||||||
|
|
||||||
The format is `<number><unit>`. `number` must be greater than `0`. Unit is
|
|
||||||
optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes), or `g`
|
|
||||||
(gigabytes). If you omit the unit, the system uses bytes.
|
|
||||||
|
|
||||||
This is the same as the `--shm-size` flag for `docker build`.
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
target "default" {
|
|
||||||
shm-size = "128m"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> In most cases, it is recommended to let the builder automatically determine
|
|
||||||
> the appropriate configurations. Manual adjustments should only be considered
|
|
||||||
> when specific performance tuning is required for complex build scenarios.
|
|
||||||
|
|
||||||
### `target.ssh`
|
### `target.ssh`
|
||||||
|
|
||||||
Defines SSH agent sockets or keys to expose to the build.
|
Defines SSH agent sockets or keys to expose to the build.
|
||||||
@@ -905,32 +878,6 @@ target "default" {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `target.ulimits`
|
|
||||||
|
|
||||||
Ulimits overrides the default ulimits of build's containers when using `RUN`
|
|
||||||
instructions and are specified with a soft and hard limit as such:
|
|
||||||
`<type>=<soft limit>[:<hard limit>]`, for example:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
target "app" {
|
|
||||||
ulimits = [
|
|
||||||
"nofile=1024:1024"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> If you do not provide a `hard limit`, the `soft limit` is used
|
|
||||||
> for both values. If no `ulimits` are set, they are inherited from
|
|
||||||
> the default `ulimits` set on the daemon.
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> In most cases, it is recommended to let the builder automatically determine
|
|
||||||
> the appropriate configurations. Manual adjustments should only be considered
|
|
||||||
> when specific performance tuning is required for complex build scenarios.
|
|
||||||
|
|
||||||
## Group
|
## Group
|
||||||
|
|
||||||
Groups allow you to invoke multiple builds (targets) at once.
|
Groups allow you to invoke multiple builds (targets) at once.
|
||||||
|
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/buildx/commands"
|
"github.com/docker/buildx/commands"
|
||||||
clidocstool "github.com/docker/cli-docs-tool"
|
clidocstool "github.com/docker/cli-docs-tool"
|
||||||
@@ -27,28 +26,6 @@ type options struct {
|
|||||||
formats []string
|
formats []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixUpExperimentalCLI trims the " (EXPERIMENTAL)" suffix from the CLI output,
|
|
||||||
// as docs.docker.com already displays "experimental (CLI)",
|
|
||||||
//
|
|
||||||
// https://github.com/docker/buildx/pull/2188#issuecomment-1889487022
|
|
||||||
func fixUpExperimentalCLI(cmd *cobra.Command) {
|
|
||||||
const (
|
|
||||||
annotationExperimentalCLI = "experimentalCLI"
|
|
||||||
suffixExperimental = " (EXPERIMENTAL)"
|
|
||||||
)
|
|
||||||
if _, ok := cmd.Annotations[annotationExperimentalCLI]; ok {
|
|
||||||
cmd.Short = strings.TrimSuffix(cmd.Short, suffixExperimental)
|
|
||||||
}
|
|
||||||
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
|
||||||
if _, ok := f.Annotations[annotationExperimentalCLI]; ok {
|
|
||||||
f.Usage = strings.TrimSuffix(f.Usage, suffixExperimental)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
for _, c := range cmd.Commands() {
|
|
||||||
fixUpExperimentalCLI(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func gen(opts *options) error {
|
func gen(opts *options) error {
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
|
|
||||||
@@ -80,8 +57,6 @@ func gen(opts *options) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "yaml":
|
case "yaml":
|
||||||
// fix up is needed only for yaml (used for generating docs.docker.com contents)
|
|
||||||
fixUpExperimentalCLI(cmd)
|
|
||||||
if err = c.GenYamlTree(cmd); err != nil {
|
if err = c.GenYamlTree(cmd); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
3
docs/guides/cicd.md
Normal file
3
docs/guides/cicd.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# CI/CD
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/ci/)
|
3
docs/guides/cni-networking.md
Normal file
3
docs/guides/cni-networking.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# CNI networking
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/buildkit/configure/#cni-networking)
|
3
docs/guides/color-output.md
Normal file
3
docs/guides/color-output.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Color output controls
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/env-vars/#buildkit_colors)
|
3
docs/guides/custom-network.md
Normal file
3
docs/guides/custom-network.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Using a custom network
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/drivers/docker-container/#custom-network)
|
3
docs/guides/custom-registry-config.md
Normal file
3
docs/guides/custom-registry-config.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Using a custom registry configuration
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/buildkit/configure/#setting-registry-certificates)
|
3
docs/guides/opentelemetry.md
Normal file
3
docs/guides/opentelemetry.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# OpenTelemetry support
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/opentelemetry/)
|
3
docs/guides/registry-mirror.md
Normal file
3
docs/guides/registry-mirror.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Registry mirror
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/buildkit/configure/#registry-mirror)
|
3
docs/guides/resource-limiting.md
Normal file
3
docs/guides/resource-limiting.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Resource limiting
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/buildkit/configure/#resource-limiting)
|
3
docs/manuals/bake/build-contexts.md
Normal file
3
docs/manuals/bake/build-contexts.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Defining additional build contexts and linking targets
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/bake/build-contexts)
|
3
docs/manuals/bake/compose-file.md
Normal file
3
docs/manuals/bake/compose-file.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Building from Compose file
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/bake/compose-file)
|
3
docs/manuals/bake/configuring-build.md
Normal file
3
docs/manuals/bake/configuring-build.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Configuring builds
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/bake/configuring-build)
|
3
docs/manuals/bake/file-definition.md
Normal file
3
docs/manuals/bake/file-definition.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Bake file definition
|
||||||
|
|
||||||
|
This page has moved to [docs/bake-reference.md](../../bake-reference.md)
|
3
docs/manuals/bake/hcl-funcs.md
Normal file
3
docs/manuals/bake/hcl-funcs.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# User defined HCL functions
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/bake/hcl-funcs)
|
3
docs/manuals/bake/index.md
Normal file
3
docs/manuals/bake/index.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# High-level build options with Bake
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/bake)
|
3
docs/manuals/cache/backends/azblob.md
vendored
Normal file
3
docs/manuals/cache/backends/azblob.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Azure Blob Storage cache storage
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/cache/backends/azblob)
|
3
docs/manuals/cache/backends/gha.md
vendored
Normal file
3
docs/manuals/cache/backends/gha.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# GitHub Actions cache storage
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/cache/backends/gha)
|
3
docs/manuals/cache/backends/index.md
vendored
Normal file
3
docs/manuals/cache/backends/index.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Cache storage backends
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/cache/backends)
|
3
docs/manuals/cache/backends/inline.md
vendored
Normal file
3
docs/manuals/cache/backends/inline.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Inline cache storage
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/cache/backends/inline)
|
3
docs/manuals/cache/backends/local.md
vendored
Normal file
3
docs/manuals/cache/backends/local.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Local cache storage
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/cache/backends/local)
|
3
docs/manuals/cache/backends/registry.md
vendored
Normal file
3
docs/manuals/cache/backends/registry.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Registry cache storage
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/cache/backends/registry)
|
3
docs/manuals/cache/backends/s3.md
vendored
Normal file
3
docs/manuals/cache/backends/s3.md
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Amazon S3 cache storage
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/cache/backends/s3)
|
3
docs/manuals/drivers/docker-container.md
Normal file
3
docs/manuals/drivers/docker-container.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Docker container driver
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/drivers/docker-container)
|
3
docs/manuals/drivers/docker.md
Normal file
3
docs/manuals/drivers/docker.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Docker driver
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/drivers/docker)
|
3
docs/manuals/drivers/index.md
Normal file
3
docs/manuals/drivers/index.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Buildx drivers overview
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/drivers)
|
3
docs/manuals/drivers/kubernetes.md
Normal file
3
docs/manuals/drivers/kubernetes.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Kubernetes driver
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/drivers/kubernetes)
|
3
docs/manuals/drivers/remote.md
Normal file
3
docs/manuals/drivers/remote.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Remote driver
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/drivers/remote)
|
3
docs/manuals/exporters/image-registry.md
Normal file
3
docs/manuals/exporters/image-registry.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Image and registry exporters
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/exporters/image-registry)
|
3
docs/manuals/exporters/index.md
Normal file
3
docs/manuals/exporters/index.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Exporters overview
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/exporters)
|
3
docs/manuals/exporters/local-tar.md
Normal file
3
docs/manuals/exporters/local-tar.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Local and tar exporters
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/exporters/local-tar)
|
3
docs/manuals/exporters/oci-docker.md
Normal file
3
docs/manuals/exporters/oci-docker.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# OCI and Docker exporters
|
||||||
|
|
||||||
|
This page has moved to [Docker Docs website](https://docs.docker.com/build/building/exporters/oci-docker)
|
@@ -9,22 +9,21 @@ Extended build capabilities with BuildKit
|
|||||||
|
|
||||||
### Subcommands
|
### Subcommands
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
|:-------------------------------------|:------------------------------------------------|
|
|:-------------------------------------|:---------------------------------------|
|
||||||
| [`bake`](buildx_bake.md) | Build from a file |
|
| [`bake`](buildx_bake.md) | Build from a file |
|
||||||
| [`build`](buildx_build.md) | Start a build |
|
| [`build`](buildx_build.md) | Start a build |
|
||||||
| [`create`](buildx_create.md) | Create a new builder instance |
|
| [`create`](buildx_create.md) | Create a new builder instance |
|
||||||
| [`debug`](buildx_debug.md) | Start debugger (EXPERIMENTAL) |
|
| [`debug`](buildx_debug.md) | Start debugger |
|
||||||
| [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance |
|
| [`du`](buildx_du.md) | Disk usage |
|
||||||
| [`du`](buildx_du.md) | Disk usage |
|
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
|
||||||
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
|
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
|
||||||
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
|
| [`ls`](buildx_ls.md) | List builder instances |
|
||||||
| [`ls`](buildx_ls.md) | List builder instances |
|
| [`prune`](buildx_prune.md) | Remove build cache |
|
||||||
| [`prune`](buildx_prune.md) | Remove build cache |
|
| [`rm`](buildx_rm.md) | Remove a builder instance |
|
||||||
| [`rm`](buildx_rm.md) | Remove one or more builder instances |
|
| [`stop`](buildx_stop.md) | Stop builder instance |
|
||||||
| [`stop`](buildx_stop.md) | Stop builder instance |
|
| [`use`](buildx_use.md) | Set the current builder instance |
|
||||||
| [`use`](buildx_use.md) | Set the current builder instance |
|
| [`version`](buildx_version.md) | Show buildx version information |
|
||||||
| [`version`](buildx_version.md) | Show buildx version information |
|
|
||||||
|
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
@@ -13,20 +13,20 @@ Build from a file
|
|||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|:------------------------------------|:--------------|:--------|:----------------------------------------------------------------------------------------------------|
|
|:---------------------------------|:--------------|:--------|:-----------------------------------------------------------------------------------------|
|
||||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
||||||
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
|
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
|
||||||
| `--load` | | | Shorthand for `--set=*.output=type=docker` |
|
| `--load` | | | Shorthand for `--set=*.output=type=docker` |
|
||||||
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file |
|
| `--metadata-file` | `string` | | Write build result metadata to the file |
|
||||||
| [`--no-cache`](#no-cache) | | | Do not use cache when building the image |
|
| [`--no-cache`](#no-cache) | | | Do not use cache when building the image |
|
||||||
| [`--print`](#print) | | | Print the options without building |
|
| [`--print`](#print) | | | Print the options without building |
|
||||||
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use plain to show container output |
|
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
|
||||||
| [`--provenance`](#provenance) | `string` | | Shorthand for `--set=*.attest=type=provenance` |
|
| [`--provenance`](#provenance) | `string` | | Shorthand for `--set=*.attest=type=provenance` |
|
||||||
| [`--pull`](#pull) | | | Always attempt to pull all referenced images |
|
| [`--pull`](#pull) | | | Always attempt to pull all referenced images |
|
||||||
| `--push` | | | Shorthand for `--set=*.output=type=registry` |
|
| `--push` | | | Shorthand for `--set=*.output=type=registry` |
|
||||||
| [`--sbom`](#sbom) | `string` | | Shorthand for `--set=*.attest=type=sbom` |
|
| [`--sbom`](#sbom) | `string` | | Shorthand for `--set=*.attest=type=sbom` |
|
||||||
| [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
|
| [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
|
||||||
|
|
||||||
|
|
||||||
<!---MARKER_GEN_END-->
|
<!---MARKER_GEN_END-->
|
||||||
@@ -90,77 +90,6 @@ $ docker buildx bake -f docker-bake.dev.hcl db webapp-release
|
|||||||
See the [Bake file reference](https://docs.docker.com/build/bake/reference/)
|
See the [Bake file reference](https://docs.docker.com/build/bake/reference/)
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
### <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
|
|
||||||
writes a map of results for each target such as:
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
# docker-bake.hcl
|
|
||||||
group "default" {
|
|
||||||
targets = ["db", "webapp-dev"]
|
|
||||||
}
|
|
||||||
|
|
||||||
target "db" {
|
|
||||||
dockerfile = "Dockerfile.db"
|
|
||||||
tags = ["docker.io/username/db"]
|
|
||||||
}
|
|
||||||
|
|
||||||
target "webapp-dev" {
|
|
||||||
dockerfile = "Dockerfile.webapp"
|
|
||||||
tags = ["docker.io/username/webapp"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker buildx bake --load --metadata-file metadata.json .
|
|
||||||
$ cat metadata.json
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"db": {
|
|
||||||
"buildx.build.provenance": {},
|
|
||||||
"buildx.build.ref": "mybuilder/mybuilder0/0fjb6ubs52xx3vygf6fgdl611",
|
|
||||||
"containerimage.config.digest": "sha256:2937f66a9722f7f4a2df583de2f8cb97fc9196059a410e7f00072fc918930e66",
|
|
||||||
"containerimage.descriptor": {
|
|
||||||
"annotations": {
|
|
||||||
"config.digest": "sha256:2937f66a9722f7f4a2df583de2f8cb97fc9196059a410e7f00072fc918930e66",
|
|
||||||
"org.opencontainers.image.created": "2022-02-08T21:28:03Z"
|
|
||||||
},
|
|
||||||
"digest": "sha256:19ffeab6f8bc9293ac2c3fdf94ebe28396254c993aea0b5a542cfb02e0883fa3",
|
|
||||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
|
||||||
"size": 506
|
|
||||||
},
|
|
||||||
"containerimage.digest": "sha256:19ffeab6f8bc9293ac2c3fdf94ebe28396254c993aea0b5a542cfb02e0883fa3"
|
|
||||||
},
|
|
||||||
"webapp-dev": {
|
|
||||||
"buildx.build.provenance": {},
|
|
||||||
"buildx.build.ref": "mybuilder/mybuilder0/kamngmcgyzebqxwu98b4lfv3n",
|
|
||||||
"containerimage.config.digest": "sha256:9651cc2b3c508f697c9c43b67b64c8359c2865c019e680aac1c11f4b875b67e0",
|
|
||||||
"containerimage.descriptor": {
|
|
||||||
"annotations": {
|
|
||||||
"config.digest": "sha256:9651cc2b3c508f697c9c43b67b64c8359c2865c019e680aac1c11f4b875b67e0",
|
|
||||||
"org.opencontainers.image.created": "2022-02-08T21:28:15Z"
|
|
||||||
},
|
|
||||||
"digest": "sha256:6d9ac9237a84afe1516540f40a0fafdc86859b2141954b4d643af7066d598b74",
|
|
||||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
|
||||||
"size": 506
|
|
||||||
},
|
|
||||||
"containerimage.digest": "sha256:6d9ac9237a84afe1516540f40a0fafdc86859b2141954b4d643af7066d598b74"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> Build record [provenance](https://docs.docker.com/build/attestations/slsa-provenance/#provenance-attestation-example)
|
|
||||||
> (`buildx.build.provenance`) includes minimal provenance by default. Set the
|
|
||||||
> `BUILDX_METADATA_PROVENANCE` environment variable to customize this behavior:
|
|
||||||
> * `min` sets minimal provenance (default).
|
|
||||||
> * `max` sets full provenance.
|
|
||||||
> * `disabled`, `false` or `0` does not set any provenance.
|
|
||||||
|
|
||||||
### <a name="no-cache"></a> Don't use cache when building the image (--no-cache)
|
### <a name="no-cache"></a> Don't use cache when building the image (--no-cache)
|
||||||
|
|
||||||
Same as `build --no-cache`. Don't use cache when building the image.
|
Same as `build --no-cache`. Don't use cache when building the image.
|
||||||
@@ -233,7 +162,6 @@ You can override the following fields:
|
|||||||
* `context`
|
* `context`
|
||||||
* `dockerfile`
|
* `dockerfile`
|
||||||
* `labels`
|
* `labels`
|
||||||
* `load`
|
|
||||||
* `no-cache`
|
* `no-cache`
|
||||||
* `no-cache-filter`
|
* `no-cache-filter`
|
||||||
* `output`
|
* `output`
|
||||||
|
@@ -24,30 +24,29 @@ Start a build
|
|||||||
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
|
| [`--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-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`) |
|
| [`--cache-to`](#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`](https://docs.docker.com/reference/cli/docker/image/build/#cgroup-parent) | `string` | | Set the parent cgroup for the `RUN` instructions during build |
|
| [`--cgroup-parent`](https://docs.docker.com/reference/cli/docker/image/build/#cgroup-parent) | `string` | | Set the parent cgroup for the `RUN` instructions during build |
|
||||||
| `--check` | | | Shorthand for `--call=check` |
|
| `--detach` | | | Detach buildx server (supported only on linux) |
|
||||||
| `--detach` | | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
|
|
||||||
| [`-f`](https://docs.docker.com/reference/cli/docker/image/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/image/build/#file) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
| [`-f`](https://docs.docker.com/reference/cli/docker/image/build/#file), [`--file`](https://docs.docker.com/reference/cli/docker/image/build/#file) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
|
||||||
| `--iidfile` | `string` | | Write the image ID to a file |
|
| `--iidfile` | `string` | | Write the image ID to the file |
|
||||||
| `--label` | `stringArray` | | Set metadata for an image |
|
| `--label` | `stringArray` | | Set metadata for an image |
|
||||||
| [`--load`](#load) | | | Shorthand for `--output=type=docker` |
|
| [`--load`](#load) | | | Shorthand for `--output=type=docker` |
|
||||||
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file |
|
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to the file |
|
||||||
| `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
|
| `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
|
||||||
| `--no-cache` | | | Do not use cache when building the image |
|
| `--no-cache` | | | Do not use cache when building the image |
|
||||||
| [`--no-cache-filter`](#no-cache-filter) | `stringArray` | | Do not cache specified stages |
|
| `--no-cache-filter` | `stringArray` | | Do not cache specified stages |
|
||||||
| [`-o`](#output), [`--output`](#output) | `stringArray` | | Output destination (format: `type=local,dest=path`) |
|
| [`-o`](#output), [`--output`](#output) | `stringArray` | | Output destination (format: `type=local,dest=path`) |
|
||||||
| [`--platform`](#platform) | `stringArray` | | Set target platform for build |
|
| [`--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 |
|
| `--print` | `string` | | Print result of information request (e.g., outline, targets) |
|
||||||
|
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
|
||||||
| [`--provenance`](#provenance) | `string` | | Shorthand for `--attest=type=provenance` |
|
| [`--provenance`](#provenance) | `string` | | Shorthand for `--attest=type=provenance` |
|
||||||
| `--pull` | | | Always attempt to pull all referenced images |
|
| `--pull` | | | Always attempt to pull all referenced images |
|
||||||
| [`--push`](#push) | | | Shorthand for `--output=type=registry` |
|
| [`--push`](#push) | | | Shorthand for `--output=type=registry` |
|
||||||
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
|
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
|
||||||
| `--root` | `string` | | Specify root directory of server to connect (EXPERIMENTAL) |
|
| `--root` | `string` | | Specify root directory of server to connect |
|
||||||
| [`--sbom`](#sbom) | `string` | | Shorthand for `--attest=type=sbom` |
|
| [`--sbom`](#sbom) | `string` | | Shorthand for `--attest=type=sbom` |
|
||||||
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
|
| [`--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) |
|
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) |
|
||||||
| [`--shm-size`](#shm-size) | `bytes` | `0` | Shared memory size for build containers |
|
| [`--shm-size`](#shm-size) | `bytes` | `0` | Size of `/dev/shm` |
|
||||||
| [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
| [`--ssh`](#ssh) | `stringArray` | | SSH agent socket or keys to expose to the build (format: `default\|<id>[=<socket>\|<key>[,<key>]]`) |
|
||||||
| [`-t`](https://docs.docker.com/reference/cli/docker/image/build/#tag), [`--tag`](https://docs.docker.com/reference/cli/docker/image/build/#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
| [`-t`](https://docs.docker.com/reference/cli/docker/image/build/#tag), [`--tag`](https://docs.docker.com/reference/cli/docker/image/build/#tag) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
|
||||||
| [`--target`](https://docs.docker.com/reference/cli/docker/image/build/#target) | `string` | | Set the target build stage to build |
|
| [`--target`](https://docs.docker.com/reference/cli/docker/image/build/#target) | `string` | | Set the target build stage to build |
|
||||||
@@ -153,9 +152,9 @@ Allow extra privileged entitlement. List of entitlements:
|
|||||||
|
|
||||||
- `network.host` - Allows executions with host networking.
|
- `network.host` - Allows executions with host networking.
|
||||||
- `security.insecure` - Allows executions without sandbox. See
|
- `security.insecure` - Allows executions without sandbox. See
|
||||||
[related Dockerfile extensions](https://docs.docker.com/reference/dockerfile/#run---security).
|
[related Dockerfile extensions](https://docs.docker.com/reference/dockerfile/#run---securitysandbox).
|
||||||
|
|
||||||
For entitlements to be enabled, the BuildKit daemon also needs to allow them
|
For entitlements to be enabled, the `buildkitd` daemon also needs to allow them
|
||||||
with `--allow-insecure-entitlement` (see [`create --buildkitd-flags`](buildx_create.md#buildkitd-flags)).
|
with `--allow-insecure-entitlement` (see [`create --buildkitd-flags`](buildx_create.md#buildkitd-flags)).
|
||||||
|
|
||||||
```console
|
```console
|
||||||
@@ -315,7 +314,7 @@ More info about cache exporters and available attributes: https://github.com/mob
|
|||||||
Shorthand for [`--output=type=docker`](#docker). Will automatically load the
|
Shorthand for [`--output=type=docker`](#docker). Will automatically load the
|
||||||
single-platform build result to `docker images`.
|
single-platform build result to `docker images`.
|
||||||
|
|
||||||
### <a name="metadata-file"></a> Write build result metadata to a file (--metadata-file)
|
### <a name="metadata-file"></a> Write build result metadata to the file (--metadata-file)
|
||||||
|
|
||||||
To output build metadata such as the image digest, pass the `--metadata-file` flag.
|
To output build metadata such as the image digest, pass the `--metadata-file` flag.
|
||||||
The metadata will be written as a JSON object to the specified file. The
|
The metadata will be written as a JSON object to the specified file. The
|
||||||
@@ -328,8 +327,6 @@ $ cat metadata.json
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"buildx.build.provenance": {},
|
|
||||||
"buildx.build.ref": "mybuilder/mybuilder0/0fjb6ubs52xx3vygf6fgdl611",
|
|
||||||
"containerimage.config.digest": "sha256:2937f66a9722f7f4a2df583de2f8cb97fc9196059a410e7f00072fc918930e66",
|
"containerimage.config.digest": "sha256:2937f66a9722f7f4a2df583de2f8cb97fc9196059a410e7f00072fc918930e66",
|
||||||
"containerimage.descriptor": {
|
"containerimage.descriptor": {
|
||||||
"annotations": {
|
"annotations": {
|
||||||
@@ -344,70 +341,6 @@ $ cat metadata.json
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> Build record [provenance](https://docs.docker.com/build/attestations/slsa-provenance/#provenance-attestation-example)
|
|
||||||
> (`buildx.build.provenance`) includes minimal provenance by default. Set the
|
|
||||||
> `BUILDX_METADATA_PROVENANCE` environment variable to customize this behavior:
|
|
||||||
> * `min` sets minimal provenance (default).
|
|
||||||
> * `max` sets full provenance.
|
|
||||||
> * `disabled`, `false` or `0` does not set any provenance.
|
|
||||||
|
|
||||||
### <a name="no-cache-filter"></a> Ignore build cache for specific stages (--no-cache-filter)
|
|
||||||
|
|
||||||
The `--no-cache-filter` lets you specify one or more stages of a multi-stage
|
|
||||||
Dockerfile for which build cache should be ignored. To specify multiple stages,
|
|
||||||
use a comma-separated syntax:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker buildx build --no-cache-filter stage1,stage2,stage3 .
|
|
||||||
```
|
|
||||||
|
|
||||||
For example, the following Dockerfile contains four stages:
|
|
||||||
|
|
||||||
- `base`
|
|
||||||
- `install`
|
|
||||||
- `test`
|
|
||||||
- `release`
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
# syntax=docker/dockerfile:1
|
|
||||||
|
|
||||||
FROM oven/bun:1 as base
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
FROM base AS install
|
|
||||||
WORKDIR /temp/dev
|
|
||||||
RUN --mount=type=bind,source=package.json,target=package.json \
|
|
||||||
--mount=type=bind,source=bun.lockb,target=bun.lockb \
|
|
||||||
bun install --frozen-lockfile
|
|
||||||
|
|
||||||
FROM base AS test
|
|
||||||
COPY --from=install /temp/dev/node_modules node_modules
|
|
||||||
COPY . .
|
|
||||||
RUN bun test
|
|
||||||
|
|
||||||
FROM base AS release
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
COPY --from=install /temp/dev/node_modules node_modules
|
|
||||||
COPY . .
|
|
||||||
ENTRYPOINT ["bun", "run", "index.js"]
|
|
||||||
```
|
|
||||||
|
|
||||||
To ignore the cache for the `install` stage:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker buildx build --no-cache-filter install .
|
|
||||||
```
|
|
||||||
|
|
||||||
To ignore the cache the `install` and `release` stages:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ docker buildx build --no-cache-filter install,release .
|
|
||||||
```
|
|
||||||
|
|
||||||
The arguments for the `--no-cache-filter` flag must be names of stages.
|
|
||||||
|
|
||||||
### <a name="output"></a> Set the export action for the build result (-o, --output)
|
### <a name="output"></a> Set the export action for the build result (-o, --output)
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -429,16 +362,12 @@ exporter and write to `stdout`.
|
|||||||
```console
|
```console
|
||||||
$ docker buildx build -o . .
|
$ docker buildx build -o . .
|
||||||
$ docker buildx build -o outdir .
|
$ docker buildx build -o outdir .
|
||||||
$ docker buildx build -o - . > out.tar
|
$ docker buildx build -o - - > out.tar
|
||||||
$ docker buildx build -o type=docker .
|
$ docker buildx build -o type=docker .
|
||||||
$ docker buildx build -o type=docker,dest=- . > myimage.tar
|
$ docker buildx build -o type=docker,dest=- . > myimage.tar
|
||||||
$ docker buildx build -t tonistiigi/foo -o type=registry
|
$ docker buildx build -t tonistiigi/foo -o type=registry
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note **
|
|
||||||
>
|
|
||||||
> Since BuildKit v0.13.0 multiple outputs can be specified by repeating the flag.
|
|
||||||
|
|
||||||
Supported exported types are:
|
Supported exported types are:
|
||||||
|
|
||||||
#### `local`
|
#### `local`
|
||||||
@@ -551,8 +480,8 @@ $ docker buildx build --platform=darwin .
|
|||||||
--progress=VALUE
|
--progress=VALUE
|
||||||
```
|
```
|
||||||
|
|
||||||
Set type of progress output (`auto`, `plain`, `tty`, `rawjson`). Use `plain` to show container
|
Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container
|
||||||
output (default `auto`).
|
output (default "auto").
|
||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
@@ -575,11 +504,8 @@ $ docker buildx build --load --progress=plain .
|
|||||||
|
|
||||||
> **Note**
|
> **Note**
|
||||||
>
|
>
|
||||||
> Check also the [`BUILDKIT_COLORS`](https://docs.docker.com/build/building/variables/#buildkit_colors)
|
> Check also our [Color output controls guide](https://github.com/docker/buildx/blob/master/docs/guides/color-output.md)
|
||||||
> environment variable for modifying the colors of the terminal output.
|
> for modifying the colors that are used to output information to the terminal.
|
||||||
|
|
||||||
The `rawjson` output marshals the solve status events from BuildKit to JSON lines.
|
|
||||||
This mode is designed to be read by an external program.
|
|
||||||
|
|
||||||
### <a name="provenance"></a> Create provenance attestations (--provenance)
|
### <a name="provenance"></a> Create provenance attestations (--provenance)
|
||||||
|
|
||||||
@@ -629,18 +555,10 @@ For more information, see [here](https://docs.docker.com/build/attestations/sbom
|
|||||||
--secret=[type=TYPE[,KEY=VALUE]
|
--secret=[type=TYPE[,KEY=VALUE]
|
||||||
```
|
```
|
||||||
|
|
||||||
Exposes secrets (authentication credentials, tokens) to the build.
|
Exposes secret to the build. The secret can be used by the build using
|
||||||
A secret can be mounted into the build using a `RUN --mount=type=secret` mount in the
|
[`RUN --mount=type=secret` mount](https://docs.docker.com/reference/dockerfile/#run---mounttypesecret).
|
||||||
[Dockerfile](https://docs.docker.com/reference/dockerfile/#run---mounttypesecret).
|
|
||||||
For more information about how to use build secrets, see
|
|
||||||
[Build secrets](https://docs.docker.com/build/building/secrets/).
|
|
||||||
|
|
||||||
Supported types are:
|
If `type` is unset it will be detected. Supported types are:
|
||||||
|
|
||||||
- [`file`](#file)
|
|
||||||
- [`env`](#env)
|
|
||||||
|
|
||||||
Buildx attempts to detect the `type` automatically if unset.
|
|
||||||
|
|
||||||
#### `file`
|
#### `file`
|
||||||
|
|
||||||
@@ -680,21 +598,12 @@ RUN --mount=type=bind,target=. \
|
|||||||
$ SECRET_TOKEN=token docker buildx build --secret id=SECRET_TOKEN .
|
$ SECRET_TOKEN=token docker buildx build --secret id=SECRET_TOKEN .
|
||||||
```
|
```
|
||||||
|
|
||||||
### <a name="shm-size"></a> Shared memory size for build containers (--shm-size)
|
### <a name="shm-size"></a> Size of /dev/shm (--shm-size)
|
||||||
|
|
||||||
Sets the size of the shared memory allocated for build containers when using
|
|
||||||
`RUN` instructions.
|
|
||||||
|
|
||||||
The format is `<number><unit>`. `number` must be greater than `0`. Unit is
|
The format is `<number><unit>`. `number` must be greater than `0`. Unit is
|
||||||
optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes), or `g`
|
optional and can be `b` (bytes), `k` (kilobytes), `m` (megabytes), or `g`
|
||||||
(gigabytes). If you omit the unit, the system uses bytes.
|
(gigabytes). If you omit the unit, the system uses bytes.
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> In most cases, it is recommended to let the builder automatically determine
|
|
||||||
> the appropriate configurations. Manual adjustments should only be considered
|
|
||||||
> when specific performance tuning is required for complex build scenarios.
|
|
||||||
|
|
||||||
### <a name="ssh"></a> SSH agent socket or keys to expose to the build (--ssh)
|
### <a name="ssh"></a> SSH agent socket or keys to expose to the build (--ssh)
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -728,8 +637,7 @@ $ docker buildx build --ssh default=$SSH_AUTH_SOCK .
|
|||||||
|
|
||||||
### <a name="ulimit"></a> Set ulimits (--ulimit)
|
### <a name="ulimit"></a> Set ulimits (--ulimit)
|
||||||
|
|
||||||
`--ulimit` overrides the default ulimits of build's containers when using `RUN`
|
`--ulimit` is specified with a soft and hard limit as such:
|
||||||
instructions and are specified with a soft and hard limit as such:
|
|
||||||
`<type>=<soft limit>[:<hard limit>]`, for example:
|
`<type>=<soft limit>[:<hard limit>]`, for example:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
@@ -741,9 +649,3 @@ $ docker buildx build --ulimit nofile=1024:1024 .
|
|||||||
> If you don't provide a `hard limit`, the `soft limit` is used
|
> If you don't provide a `hard limit`, the `soft limit` is used
|
||||||
> for both values. If no `ulimits` are set, they're inherited from
|
> for both values. If no `ulimits` are set, they're inherited from
|
||||||
> the default `ulimits` set on the daemon.
|
> the default `ulimits` set on the daemon.
|
||||||
|
|
||||||
> **Note**
|
|
||||||
>
|
|
||||||
> In most cases, it is recommended to let the builder automatically determine
|
|
||||||
> the appropriate configurations. Manual adjustments should only be considered
|
|
||||||
> when specific performance tuning is required for complex build scenarios.
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user