Compare commits

..

25 Commits

Author SHA1 Message Date
CrazyMax
89154c7d33 Merge pull request #2261 from dvdksn/backport_docs-cli-reference-urlscheme
[v0.12 backport] docs: update external links
2024-02-20 09:48:49 +01:00
David Karlsson
1a03187338 docs: update external link paths
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit cdfc35d0b6)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-02-20 09:39:21 +01:00
David Karlsson
b3e7827871 vendor: github.com/docker/cli-docs-tool v0.7.0
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit ce66d8830d)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-02-20 09:35:51 +01:00
CrazyMax
d43cf8c2c6 Merge pull request #2214 from dvdksn/chp-cli-linkupdates
[v0.12 backport]: update doc links to canonical build command
2024-01-26 12:40:58 +01:00
David Karlsson
c954a45352 docs: update link to docker build reference
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit 68c3ac4f66)
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
2024-01-26 12:32:25 +01:00
CrazyMax
ce521a3a85 Merge pull request #2213 from crazy-max/0.12_pin-docs-validate-worklofw
[v0.12] ci(docs-upstream): pin reusable workflow
2024-01-26 12:29:56 +01:00
CrazyMax
29f879990e ci(docs-upstream): pin reusable workflow
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
2024-01-26 10:13:05 +01:00
CrazyMax
5e3bf8713a Merge pull request #2191 from thaJeztah/0.12_backport_bump_golang
[v0.12 backport] update to go1.21.6
2024-01-17 14:42:43 +01:00
Sebastiaan van Stijn
57f7f1becc update to go1.21.6
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
(cherry picked from commit 61dff684ad)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-01-12 23:30:18 +01:00
Tõnis Tiigi
091214d59d Merge pull request #2189 from crazy-max/0.12_backport_ci-dockerhub-creds
[v0.12 backport] ci: use public bot account to push bin image
2024-01-12 11:10:25 -08:00
CrazyMax
5f26f514a1 ci: use org-wide var as username
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
(cherry picked from commit 45deb29f09)
2024-01-12 09:16:07 +01:00
CrazyMax
454306a8ef ci: use public bot account to push bin image
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
(cherry picked from commit 54a1f0f0ea)
2024-01-12 09:15:57 +01:00
Tõnis Tiigi
30feaa1a91 Merge pull request #2178 from crazy-max/0.12_backport_fix-builder-creation
[v0.12 backport] driver(container): fix conditional statement for error handling
2024-01-05 12:47:15 -08:00
CrazyMax
8fb1163577 driver(container): fix conditional statement for error handling
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
(cherry picked from commit 57d737a13c)
2024-01-05 17:39:33 +01:00
CrazyMax
b68ee824c6 Merge pull request #2161 from crazy-max/0.12_backport_docs-annotations
[0.12 backport] docs: annotations
2023-12-14 10:15:05 +01:00
David Karlsson
2175f9ec7c docs: add levels to bake file target.annotations
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit c6535e9675)
2023-12-14 10:08:43 +01:00
David Karlsson
ba1ee7af6e docs: build --annotation
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit d762c76a68)
2023-12-14 10:08:42 +01:00
David Karlsson
565b0b8991 docs: add lang tag for plaintext code blocks
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit 1091707bd5)
2023-12-14 10:08:42 +01:00
David Karlsson
a494e9ccc4 docs: imagetools create --annotation
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit a4c392f4db)
2023-12-14 10:08:42 +01:00
Tõnis Tiigi
542e5d810e Merge pull request #2125 from tonistiigi/v0.12-revert-node-resolution
[v0.12] Revert "build: rework node resolution"
2023-11-16 14:04:54 -08:00
Tonis Tiigi
89fb005922 Revert "build: rework node resolution"
This reverts commit 616fb3e55c.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 3c709640e1)
2023-11-16 13:29:13 -08:00
Tõnis Tiigi
d353f6c426 Merge pull request #2123 from tonistiigi/v0.12.0-cherry-picks-docs
[v0.12] cherry picks docs only
2023-11-16 12:02:04 -08:00
David Karlsson
2271096e46 chore: add docs reminder comments for driver opts
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit 1326634c7d)
2023-11-16 11:47:10 -08:00
David Karlsson
95062ce8df docs: minor cli reference editorial updates
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
(cherry picked from commit 7a724ac445)
2023-11-16 11:47:07 -08:00
CrazyMax
255aff71fb docs: fix imagetools inspect json format
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
(cherry picked from commit 707ae87060)
2023-11-16 11:46:57 -08:00
2661 changed files with 69029 additions and 168470 deletions

View File

@@ -5,11 +5,6 @@ updates:
directory: "/"
schedule:
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:
- "dependencies"
- "bot"

View File

@@ -24,70 +24,57 @@ env:
REPO_SLUG: "docker/buildx-bin"
DESTDIR: "./bin"
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:
test-integration:
prepare-test:
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:
TESTFLAGS: "-v --parallel=6 --timeout=30m"
TESTFLAGS_DOCKER: "-v --parallel=1 --timeout=30m"
GOTESTSUM_FORMAT: "standard-verbose"
TEST_IMAGE_BUILD: "0"
TEST_IMAGE_ID: "buildx-tests"
strategy:
fail-fast: false
matrix:
buildkit:
- master
- latest
- buildx-stable-1
- v0.13.1
- v0.12.5
- v0.11.6
worker:
- docker
- docker\+containerd # same as docker, but with containerd snapshotter
- docker-container
- remote
pkg:
- ./tests
mode:
- ""
- experimental
include:
- worker: docker
pkg: ./tests
- 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
- pkg: ./...
skip-integration-tests: 1
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
uses: actions/checkout@v4
@@ -109,108 +96,38 @@ jobs:
with:
targets: integration-test
set: |
*.cache-from=type=gha,scope=${{ env.TEST_CACHE_SCOPE }}
*.output=type=docker,name=${{ env.TEST_IMAGE_ID }}
-
name: Test
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
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 }}"
SKIP_INTEGRATION_TESTS: "${{ matrix.skip-integration-tests }}"
-
name: Send to Codecov
if: always()
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v3
with:
directory: ./bin/testreports
flags: integration
token: ${{ secrets.CODECOV_TOKEN }}
-
name: Generate annotations
if: always()
uses: crazy-max/.github/.github/actions/gotest-annotations@fa6141aedf23596fb8bdcceab9cce8dadaa31bd9
uses: crazy-max/.github/.github/actions/gotest-annotations@1a64ea6d01db9a48aa61954cb20e265782c167d9
with:
directory: ./bin/testreports
-
name: Upload test reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: test-reports-${{ env.TESTREPORTS_NAME }}
name: test-reports
path: ./bin/testreports
test-unit:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-22.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:
runs-on: ubuntu-22.04
outputs:
@@ -266,17 +183,16 @@ jobs:
CACHE_TO: type=gha,scope=binaries-${{ env.PLATFORM_PAIR }},mode=max
-
name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: buildx-${{ env.PLATFORM_PAIR }}
name: buildx
path: ${{ env.DESTDIR }}/*
if-no-files-found: error
bin-image:
runs-on: ubuntu-22.04
needs:
- test-integration
- test-unit
- test
if: ${{ github.event_name != 'pull_request' && github.repository == 'docker/buildx' }}
steps:
-
@@ -328,8 +244,7 @@ jobs:
release:
runs-on: ubuntu-22.04
needs:
- test-integration
- test-unit
- test
- binaries
steps:
-
@@ -337,11 +252,10 @@ jobs:
uses: actions/checkout@v4
-
name: Download binaries
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: buildx
path: ${{ env.DESTDIR }}
pattern: buildx-*
merge-multiple: true
-
name: Create checksums
run: ./hack/hash-files
@@ -356,9 +270,33 @@ jobs:
-
name: GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v2.0.4
uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
draft: true
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

View File

@@ -13,7 +13,7 @@ permissions:
security-events: write
env:
GO_VERSION: "1.21"
GO_VERSION: 1.21.6
jobs:
codeql:
@@ -24,19 +24,19 @@ jobs:
uses: actions/checkout@v4
-
name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
-
name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v2
with:
languages: go
-
name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v2
-
name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v2
with:
category: "/language:go"

View File

@@ -1,11 +1,6 @@
name: docs-release
on:
workflow_dispatch:
inputs:
tag:
description: 'Git tag'
required: true
release:
types:
- released
@@ -13,7 +8,7 @@ on:
jobs:
open-pr:
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:
-
name: Checkout docs repo
@@ -25,47 +20,39 @@ jobs:
-
name: Prepare
run: |
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
rm -rf ./_data/buildx/*
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Generate yaml
name: Build docs
uses: docker/bake-action@v4
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
provenance: false
set: |
*.output=/tmp/buildx-docs
env:
DOCS_FORMATS: yaml
-
name: Copy yaml
name: Copy files
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: |
make vendor
env:
VENDOR_MODULE: github.com/docker/buildx@${{ env.RELEASE_NAME }}
git add -A .
-
name: Create PR on docs repo
uses: peter-evans/create-pull-request@70a41aba780001da0a30141984ae2a0c95d8704e # v6.0.2
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
with:
token: ${{ secrets.GHPAT_DOCS_DISPATCH }}
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
branch: dispatch/buildx-ref-${{ env.RELEASE_NAME }}
branch: dispatch/buildx-ref-${{ github.event.release.name }}
delete-branch: true
title: Update buildx reference to ${{ env.RELEASE_NAME }}
title: Update buildx reference to ${{ github.event.release.name }}
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

View File

@@ -37,7 +37,6 @@ jobs:
uses: docker/bake-action@v4
with:
targets: update-docs
provenance: false
set: |
*.output=/tmp/buildx-docs
*.cache-from=type=gha,scope=docs-yaml
@@ -46,18 +45,18 @@ jobs:
DOCS_FORMATS: yaml
-
name: Upload reference YAML docs
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: docs-yaml
path: /tmp/buildx-docs/out/reference
retention-days: 1
validate:
uses: docker/docs/.github/workflows/validate-upstream.yml@6b73b05acb21edf7995cc5b3c6672d8e314cee7a # pin for artifact v4 support: https://github.com/docker/docs/pull/19220
uses: docker/docs/.github/workflows/validate-upstream.yml@e864c797e391fa35aecf1ddcecdbdeb683546424 # pin for artifact v3 support
needs:
- docs-yaml
with:
module-name: docker/buildx
data-files-id: docs-yaml
data-files-folder: buildx
create-placeholder-stubs: true
data-files-placeholder-folder: engine/reference/commandline

View File

@@ -46,7 +46,7 @@ jobs:
mv ${{ env.DESTDIR }}/build/buildx ${{ env.DESTDIR }}/build/docker-buildx
-
name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: binary
path: ${{ env.DESTDIR }}/build
@@ -82,8 +82,6 @@ jobs:
driver-opt: qemu.install=true
- driver: remote
endpoint: tcp://localhost:1234
- driver: docker-container
metadata-provenance: max
exclude:
- driver: docker
multi-node: mnode-true
@@ -105,7 +103,7 @@ jobs:
if: matrix.driver == 'docker' || matrix.driver == 'docker-container'
-
name: Install buildx
uses: actions/download-artifact@v4
uses: actions/download-artifact@v3
with:
name: binary
path: /home/runner/.docker/cli-plugins
@@ -131,13 +129,10 @@ jobs:
else
echo "MULTI_NODE=0" >> $GITHUB_ENV
fi
if [ -n "${{ matrix.metadata-provenance }}" ]; then
echo "BUILDX_METADATA_PROVENANCE=${{ matrix.metadata-provenance }}" >> $GITHUB_ENV
fi
-
name: Install k3s
if: matrix.driver == 'kubernetes'
uses: actions/github-script@v7
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');

View File

@@ -28,12 +28,6 @@ jobs:
- validate-docs
- validate-generated-files
steps:
-
name: Prepare
run: |
if [ "$GITHUB_REPOSITORY" = "docker/buildx" ]; then
echo "GOLANGCI_LINT_MULTIPLATFORM=1" >> $GITHUB_ENV
fi
-
name: Checkout
uses: actions/checkout@v4

View File

@@ -1,5 +1,5 @@
run:
timeout: 30m
timeout: 10m
skip-files:
- ".*\\.pb\\.go$"

View File

@@ -1,22 +1,17 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21
ARG XX_VERSION=1.4.0
ARG GO_VERSION=1.21.6
ARG XX_VERSION=1.2.1
# for testing
ARG DOCKER_VERSION=26.0.0
ARG DOCKER_VERSION=24.0.6
ARG GOTESTSUM_VERSION=v1.9.0
ARG REGISTRY_VERSION=2.8.0
ARG BUILDKIT_VERSION=v0.13.1
ARG UNDOCK_VERSION=0.7.0
ARG BUILDKIT_VERSION=v0.11.6
# xx is a helper for cross-compilation
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
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
COPY --from=xx / /
@@ -25,6 +20,26 @@ ENV GOFLAGS=-mod=vendor
ENV CGO_ENABLED=0
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
ARG GOTESTSUM_VERSION
ENV GOFLAGS=
@@ -90,11 +105,9 @@ RUN apk add --no-cache \
xz
COPY --link --from=gotestsum /out/gotestsum /usr/bin/
COPY --link --from=registry /bin/registry /usr/bin/
COPY --link --from=docker-engine / /usr/bin/
COPY --link --from=docker-cli / /usr/bin/
COPY --link --from=docker /opt/docker/* /usr/bin/
COPY --link --from=buildkit /usr/bin/buildkitd /usr/bin/
COPY --link --from=buildkit /usr/bin/buildctl /usr/bin/
COPY --link --from=undock /usr/local/bin/undock /usr/bin/
COPY --link --from=binaries /buildx /usr/bin/
FROM integration-test-base AS integration-test

View File

@@ -13,7 +13,7 @@ import (
"strings"
"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/build"
controllerapi "github.com/docker/buildx/controller/pb"
@@ -21,7 +21,6 @@ import (
"github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/config"
dockeropts "github.com/docker/cli/opts"
hcl "github.com/hashicorp/hcl/v2"
"github.com/moby/buildkit/client"
"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"`
NetworkMode *string `json:"-" hcl:"-" cty:"-"`
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.
// 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.Outputs = removeDupes(t.Outputs)
t.NoCacheFilter = removeDupes(t.NoCacheFilter)
t.Ulimits = removeDupes(t.Ulimits)
for k, v := range t.Contexts {
if v == "" {
@@ -813,12 +809,6 @@ func (t *Target) Merge(t2 *Target) {
if t2.NoCacheFilter != nil { // merge
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...)
}
@@ -883,10 +873,6 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
t.NoCache = &noCache
case "no-cache-filter":
t.NoCacheFilter = o.ArrValue
case "shm-size":
t.ShmSize = &value
case "ulimits":
t.Ulimits = o.ArrValue
case "pull":
pull, err := strconv.ParseBool(value)
if err != nil {
@@ -894,17 +880,19 @@ func (t *Target) AddOverrides(overrides map[string]Override) error {
}
t.Pull = &pull
case "push":
push, err := strconv.ParseBool(value)
_, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key push", value)
}
t.Outputs = setPushOverride(t.Outputs, push)
case "load":
load, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key load", value)
if len(t.Outputs) == 0 {
t.Outputs = append(t.Outputs, "type=image,push=true")
} else {
for i, output := range t.Outputs {
if typ := parseOutputType(output); typ == "image" || typ == "registry" {
t.Outputs[i] = t.Outputs[i] + ",push=" + value
}
}
}
t.Outputs = setLoadOverride(t.Outputs, load)
default:
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) {
// 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))
for k, v := range m {
bo, err := toBuildOpt(v, inp)
if err != nil {
return nil, err
}
bo.Session = append(bo.Session, authProvider)
m2[k] = *bo
}
return m2, nil
@@ -1245,12 +1228,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if t.NetworkMode != nil {
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{
Inputs: bi,
@@ -1262,7 +1239,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
Pull: pull,
NetworkMode: networkMode,
Linked: t.linked,
ShmSize: *shmSize,
}
platforms, err := platformutil.Parse(t.Platforms)
@@ -1271,6 +1247,9 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
}
bo.Platforms = platforms
dockerConfig := config.LoadDefaultConfigFile(os.Stderr)
bo.Session = append(bo.Session, authprovider.NewDockerAuthProvider(dockerConfig, nil))
secrets, err := buildflags.ParseSecretSpecs(t.Secrets)
if err != nil {
return nil, err
@@ -1340,14 +1319,6 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
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
}
@@ -1392,90 +1363,23 @@ func removeAttestDupes(s []string) []string {
return res
}
func parseOutput(str string) map[string]string {
func parseOutputType(str string) string {
csvReader := csv.NewReader(strings.NewReader(str))
fields, err := csvReader.Read()
if err != nil {
return nil
return ""
}
res := map[string]string{}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) == 2 {
res[parts[0]] = parts[1]
}
}
return res
}
func parseOutputType(str string) string {
if out := parseOutput(str); out != nil {
if v, ok := out["type"]; ok {
return v
if parts[0] == "type" {
return parts[1]
}
}
}
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 {
if !targetNamePattern.MatchString(name) {
return errors.Errorf("only %q are allowed", validTargetNameChars)

View File

@@ -22,8 +22,6 @@ target "webDEP" {
VAR_BOTH = "webDEP"
}
no-cache = true
shm-size = "128m"
ulimits = ["nofile=1024:1024"]
}
target "webapp" {
@@ -47,8 +45,6 @@ target "webapp" {
require.Equal(t, ".", *m["webapp"].Context)
require.Equal(t, ptrstr("webDEP"), m["webapp"].Args["VAR_INHERITED"])
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.Equal(t, 1, len(g))
@@ -133,12 +129,6 @@ target "webapp" {
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.Parallel()
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) {
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{"*.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.Parallel()
t.Run("type image", func(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
output = ["type=image,compression=zstd"]
}`),
}
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,compression=zstd,push=true", m["app"].Outputs[0])
})
}
ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
require.NoError(t, err)
t.Run("type image push false", func(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0])
fp = File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
output = ["type=image,compression=zstd"]
}`),
}
m, _, err := ReadTargets(context.TODO(), []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
})
}
ctx = context.TODO()
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
require.NoError(t, err)
t.Run("type registry", func(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
output = ["type=registry"]
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
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=registry", m["app"].Outputs[0])
})
}
ctx = context.TODO()
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
require.NoError(t, err)
t.Run("type registry push false", 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{"*.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)
})
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,push=true", m["app"].Outputs[0])
}
func TestReadTargetsCompose(t *testing.T) {
@@ -511,6 +297,9 @@ services:
ctx := context.TODO()
cwd, err := os.Getwd()
require.NoError(t, err)
m, g, err := ReadTargets(ctx, []File{fp, fp2, fp3}, []string{"default"}, nil, nil)
require.NoError(t, err)
@@ -519,7 +308,7 @@ services:
require.True(t, ok)
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("12"), m["webapp"].Args["buildno2"])
@@ -558,6 +347,9 @@ services:
ctx := context.TODO()
cwd, err := os.Getwd()
require.NoError(t, err)
m, _, err := ReadTargets(ctx, []File{fp}, []string{"web.app"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m))
@@ -580,7 +372,7 @@ services:
_, ok = m["web_app"]
require.True(t, ok)
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("12"), m["web_app"].Args["buildno2"])
@@ -789,6 +581,9 @@ services:
ctx := context.TODO()
cwd, err := os.Getwd()
require.NoError(t, err)
m, _, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app1", "app2"}, nil, nil)
require.NoError(t, err)
@@ -801,7 +596,7 @@ services:
require.Equal(t, "Dockerfile", *m["app1"].Dockerfile)
require.Equal(t, ".", *m["app1"].Context)
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) {

View File

@@ -2,16 +2,13 @@ package bake
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/compose-spec/compose-go/v2/dotenv"
"github.com/compose-spec/compose-go/v2/loader"
composetypes "github.com/compose-spec/compose-go/v2/types"
dockeropts "github.com/docker/cli/opts"
"github.com/docker/go-units"
"github.com/compose-spec/compose-go/dotenv"
"github.com/compose-spec/compose-go/loader"
compose "github.com/compose-spec/compose-go/types"
"github.com/pkg/errors"
"gopkg.in/yaml.v3"
)
@@ -21,9 +18,9 @@ func ParseComposeFiles(fs []File) (*Config, error) {
if err != nil {
return nil, err
}
var cfgs []composetypes.ConfigFile
var cfgs []compose.ConfigFile
for _, f := range fs {
cfgs = append(cfgs, composetypes.ConfigFile{
cfgs = append(cfgs, compose.ConfigFile{
Filename: f.Name,
Content: f.Data,
})
@@ -31,11 +28,11 @@ func ParseComposeFiles(fs []File) (*Config, error) {
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 {
envs = make(map[string]string)
}
cfg, err := loader.LoadWithContext(context.Background(), composetypes.ConfigDetails{
cfg, err := loader.LoadWithContext(context.Background(), compose.ConfigDetails{
ConfigFiles: cfgs,
Environment: envs,
}, func(options *loader.Options) {
@@ -89,24 +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 secrets []string
for _, bs := range s.Build.Secrets {
secret, err := composeToBuildkitSecret(bs, cfg.Secrets[bs.Source])
@@ -143,8 +122,6 @@ func ParseCompose(cfgs []composetypes.ConfigFile, envs map[string]string) (*Conf
CacheTo: s.Build.CacheTo,
NetworkMode: &s.Build.Network,
Secrets: secrets,
ShmSize: shmSize,
Ulimits: ulimits,
}
if err = t.composeExtTarget(s.Build.Extensions); err != nil {
return nil, err
@@ -182,8 +159,8 @@ func validateComposeFile(dt []byte, fn string) (bool, error) {
}
func validateCompose(dt []byte, envs map[string]string) error {
_, err := loader.Load(composetypes.ConfigDetails{
ConfigFiles: []composetypes.ConfigFile{
_, err := loader.Load(compose.ConfigDetails{
ConfigFiles: []compose.ConfigFile{
{
Content: dt,
},
@@ -246,7 +223,7 @@ func loadDotEnv(curenv map[string]string, workingDir string) (map[string]string,
return curenv, nil
}
func flatten(in composetypes.MappingWithEquals) map[string]*string {
func flatten(in compose.MappingWithEquals) map[string]*string {
if len(in) == 0 {
return nil
}
@@ -350,8 +327,8 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
// composeToBuildkitSecret converts secret from compose format to buildkit's
// csv format.
func composeToBuildkitSecret(inp composetypes.ServiceSecretConfig, psecret composetypes.SecretConfig) (string, error) {
if psecret.External {
func composeToBuildkitSecret(inp compose.ServiceSecretConfig, psecret compose.SecretConfig) (string, error) {
if psecret.External.External {
return "", errors.Errorf("unsupported external secret %s", psecret.Name)
}

View File

@@ -6,7 +6,7 @@ import (
"sort"
"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/require"
)
@@ -49,7 +49,10 @@ secrets:
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.Equal(t, 1, len(c.Groups))
@@ -62,12 +65,12 @@ secrets:
return c.Targets[i].Name < c.Targets[j].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, "webapp", c.Targets[1].Name)
require.Equal(t, "dir", *c.Targets[1].Context)
require.Equal(t, map[string]string{"foo": "bar"}, c.Targets[1].Contexts)
require.Equal(t, filepath.Join(cwd, "dir"), *c.Targets[1].Context)
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, 1, len(c.Targets[1].Args))
require.Equal(t, ptrstr("123"), c.Targets[1].Args["buildno"])
@@ -80,7 +83,7 @@ secrets:
}, c.Targets[1].Secrets)
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)
}
@@ -92,7 +95,7 @@ services:
webapp:
build: ./db
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, 1, len(c.Targets))
@@ -111,7 +114,7 @@ services:
target: webapp
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
require.Equal(t, 2, len(c.Targets))
@@ -136,7 +139,7 @@ services:
target: webapp
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
require.Equal(t, 2, len(c.Targets))
sort.Slice(c.Targets, func(i, j int) bool {
@@ -167,7 +170,7 @@ services:
t.Setenv("BAR", "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.Equal(t, ptrstr("bar"), c.Targets[0].Args["FOO"])
require.Equal(t, ptrstr("zzz_foo"), c.Targets[0].Args["BAR"])
@@ -181,7 +184,7 @@ services:
entrypoint: echo 1
`)
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.Error(t, err)
}
@@ -206,7 +209,7 @@ networks:
gateway: 10.5.0.254
`)
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
}
@@ -223,7 +226,7 @@ services:
- bar
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
require.Equal(t, []string{"foo", "bar"}, c.Targets[0].Tags)
}
@@ -260,7 +263,7 @@ networks:
name: test-net
`)
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
}
@@ -303,11 +306,6 @@ services:
args:
CT_ECR: foo
CT_TAG: bar
shm_size: 128m
ulimits:
nofile:
soft: 1024
hard: 1024
x-bake:
secret:
- id=mysecret,src=/local/secret
@@ -318,7 +316,7 @@ services:
no-cache: true
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
require.Equal(t, 2, len(c.Targets))
sort.Slice(c.Targets, func(i, j int) bool {
@@ -337,8 +335,6 @@ services:
require.Equal(t, []string{"linux/arm64"}, c.Targets[1].Platforms)
require.Equal(t, []string{"type=docker"}, c.Targets[1].Outputs)
require.Equal(t, newBool(true), c.Targets[1].NoCache)
require.Equal(t, ptrstr("128MiB"), c.Targets[1].ShmSize)
require.Equal(t, []string{"nofile=1024:1024"}, c.Targets[1].Ulimits)
}
func TestComposeExtDedup(t *testing.T) {
@@ -364,7 +360,7 @@ services:
- type=local,dest=path/to/cache
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, []string{"ct-addon:foo", "ct-addon:baz"}, c.Targets[0].Tags)
@@ -397,7 +393,7 @@ services:
- ` + envf.Name() + `
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
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)
}
@@ -443,7 +439,7 @@ services:
published: "3306"
protocol: tcp
`)
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
}
@@ -489,7 +485,7 @@ func TestServiceName(t *testing.T) {
for _, tt := range cases {
tt := tt
t.Run(tt.svc, func(t *testing.T) {
_, err := ParseCompose([]composetypes.ConfigFile{{Content: []byte(`
_, err := ParseCompose([]compose.ConfigFile{{Content: []byte(`
services:
` + tt.svc + `:
build:
@@ -560,7 +556,7 @@ services:
for _, tt := range cases {
tt := tt
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 {
require.Error(t, err)
} else {
@@ -658,7 +654,7 @@ services:
bar: "baz"
`)
c, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
c, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
require.Equal(t, map[string]*string{"bar": ptrstr("baz")}, c.Targets[0].Args)
}
@@ -677,7 +673,7 @@ services:
build:
context: .
`)
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
}
@@ -708,7 +704,7 @@ services:
chdir(t, tmpdir)
c, err := ParseComposeFiles([]File{{
Name: "composetypes.yml",
Name: "compose.yml",
Data: dt,
}})
require.NoError(t, err)
@@ -738,7 +734,7 @@ services:
- node_modules/
`)
_, err := ParseCompose([]composetypes.ConfigFile{{Content: dt}}, nil)
_, err := ParseCompose([]compose.ConfigFile{{Content: dt}}, nil)
require.NoError(t, err)
}

View File

@@ -1445,39 +1445,6 @@ func TestVarUnsupportedType(t *testing.T) {
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 {
var n *string
if reflect.ValueOf(s).Kind() == reflect.String {

View File

@@ -9,7 +9,6 @@ import (
"github.com/hashicorp/go-cty-funcs/uuid"
"github.com/hashicorp/hcl/v2/ext/tryfunc"
"github.com/hashicorp/hcl/v2/ext/typeexpr"
"github.com/pkg/errors"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/function/stdlib"
@@ -53,7 +52,6 @@ var stdlibFunctions = map[string]function.Function{
"hasindex": stdlib.HasIndexFunc,
"indent": stdlib.IndentFunc,
"index": stdlib.IndexFunc,
"indexof": indexOfFunc,
"int": stdlib.IntFunc,
"join": stdlib.JoinFunc,
"jsondecode": stdlib.JSONDecodeFunc,
@@ -117,51 +115,6 @@ var stdlibFunctions = map[string]function.Function{
"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.
//
// This function was imported from terraform's datetime utilities.

View File

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

View File

@@ -4,8 +4,6 @@ import (
"archive/tar"
"bytes"
"context"
"os"
"strings"
"github.com/docker/buildx/builder"
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) {
var sessions []session.Attachable
var session []session.Attachable
var filename string
st, ok := dockerui.DetectGitContext(url, false)
if ok {
if ssh, err := controllerapi.CreateSSH([]*controllerapi.SSH{{
ID: "default",
Paths: strings.Split(os.Getenv("BUILDX_BAKE_GIT_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)
}
ssh, err := controllerapi.CreateSSH([]*controllerapi.SSH{{ID: "default"}})
if err == nil {
session = append(session, ssh)
}
} else {
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)
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)
if err != nil {
return nil, err

File diff suppressed because it is too large Load Diff

View File

@@ -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")
}

View File

@@ -1,312 +0,0 @@
package build
import (
"context"
"fmt"
"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 nodeResolver struct {
nodes []builder.Node
clients flightcontrol.Group[*client.Client]
opt flightcontrol.Group[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,
}
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,
})
} 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.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (*client.Client, error) {
if r.nodes[idx].Driver == nil {
return nil, nil
}
return driver.Boot(ctx, baseCtx, r.nodes[idx].Driver, pw)
})
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.opt.Do(ctx, fmt.Sprint(idx), func(ctx context.Context) (gateway.BuildOpts, error) {
opt := gateway.BuildOpts{}
_, 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)
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
}

View File

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

View File

@@ -9,18 +9,16 @@ import (
"strings"
"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"
"github.com/pkg/errors"
)
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) {
res := make(map[string]string)
func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath string) (res map[string]string, _ error) {
res = make(map[string]string)
if contextPath == "" {
return nil, nil, nil
return
}
setGitLabels := false
@@ -39,7 +37,7 @@ func getGitAttributes(ctx context.Context, contextPath string, dockerfilePath st
}
if !setGitLabels && !setGitInfo {
return nil, nil, nil
return
}
// 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) {
wd = contextPath
} 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))
if err != nil {
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 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
}
root, err := gitc.RootDir()
if err != nil {
return res, nil, errors.Wrap(err, "failed to get git root dir")
return res, nil
}
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 != "" {
checkDirty := false
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 dockerfilePath == "" {
dockerfilePath = filepath.Join(wd, "Dockerfile")
}
if !filepath.IsAbs(dockerfilePath) {
dockerfilePath = filepath.Join(osutil.GetWd(), dockerfilePath)
}
if r, err := filepath.Rel(root, dockerfilePath); err == nil && !strings.HasPrefix(r, "..") {
res["label:"+DockerfileLabel] = r
if setGitLabels {
if root, err := gitc.RootDir(); err != nil {
return res, errors.Wrap(err, "failed to get git root dir")
} else if root != "" {
if dockerfilePath == "" {
dockerfilePath = filepath.Join(wd, "Dockerfile")
}
if !filepath.IsAbs(dockerfilePath) {
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) {
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
return
}

View File

@@ -9,7 +9,6 @@ import (
"testing"
"github.com/docker/buildx/util/gitutil"
"github.com/moby/buildkit/client"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -31,7 +30,7 @@ func setupTest(tb testing.TB) {
}
func TestGetGitAttributesNotGitRepo(t *testing.T) {
_, _, err := getGitAttributes(context.Background(), t.TempDir(), "Dockerfile")
_, err := getGitAttributes(context.Background(), t.TempDir(), "Dockerfile")
assert.NoError(t, err)
}
@@ -39,14 +38,14 @@ func TestGetGitAttributesBadGitRepo(t *testing.T) {
tmp := t.TempDir()
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)
}
func TestGetGitAttributesNoContext(t *testing.T) {
setupTest(t)
gitattrs, _, err := getGitAttributes(context.Background(), "", "Dockerfile")
gitattrs, err := getGitAttributes(context.Background(), "", "Dockerfile")
assert.NoError(t, err)
assert.Empty(t, gitattrs)
}
@@ -115,7 +114,7 @@ func TestGetGitAttributes(t *testing.T) {
if 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)
for _, e := range tt.expected {
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))
t.Setenv("BUILDX_GIT_LABELS", "true")
gitattrs, _, _ := getGitAttributes(context.Background(), ".", "Dockerfile")
gitattrs, _ := getGitAttributes(context.Background(), ".", "Dockerfile")
assert.Equal(t, 5, len(gitattrs))
assert.Contains(t, gitattrs, "label:"+DockerfileLabel)
@@ -155,55 +154,3 @@ func TestGetGitAttributesDirty(t *testing.T) {
assert.Contains(t, gitattrs, "vcs:revision")
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"])
}

View File

@@ -1,641 +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/docker/docker/builder/remotecontext/urlutil"
"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() {
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
}
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 urlutil.IsURL(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://") {
pathAlone := strings.TrimPrefix(v.Path, "oci-layout://")
localPath := pathAlone
localPath, dig, hasDigest := strings.Cut(localPath, "@")
localPath, tag, hasTag := strings.Cut(localPath, ":")
if !hasTag {
tag = "latest"
hasTag = true
}
idx := ociindex.NewStoreIndex(localPath)
if !hasDigest {
// lookup by name
desc, err := idx.Get(tag)
if err != nil {
return nil, err
}
if desc != nil {
dig = string(desc.Digest)
hasDigest = true
}
}
if !hasDigest {
// lookup single
desc, err := idx.GetSingle()
if err != nil {
return nil, err
}
if desc != nil {
dig = string(desc.Digest)
hasDigest = true
}
}
if !hasDigest {
return nil, errors.Errorf("oci-layout reference %q could not be resolved", v.Path)
}
_, err := digest.Parse(dig)
if err != nil {
return nil, errors.Wrapf(err, "invalid oci-layout digest %s", dig)
}
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
layout := "oci-layout://" + storeName
if hasTag {
layout += ":" + tag
}
if hasDigest {
layout += "@" + dig
}
target.FrontendAttrs["context:"+k] = layout
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 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
}

View File

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

View File

@@ -6,7 +6,6 @@ import (
"context"
"net"
"os"
"strconv"
"strings"
"github.com/docker/buildx/driver"
@@ -14,7 +13,6 @@ import (
"github.com/docker/docker/builder/remotecontext/urlutil"
"github.com/moby/buildkit/util/gitutil"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
@@ -36,6 +34,11 @@ func IsRemoteURL(c string) bool {
return false
}
func isLocalDir(c string) bool {
st, err := os.Stat(c)
return err == nil && st.IsDir()
}
func isArchive(header []byte) bool {
for _, m := range [][]byte{
{0x42, 0x5A, 0x68}, // bzip2
@@ -62,10 +65,7 @@ func toBuildkitExtraHosts(ctx context.Context, inp []string, nodeDriver *driver.
}
hosts := make([]string, 0, len(inp))
for _, h := range inp {
host, ip, ok := strings.Cut(h, "=")
if !ok {
host, ip, ok = strings.Cut(h, ":")
}
host, ip, ok := strings.Cut(h, ":")
if !ok || host == "" || ip == "" {
return "", errors.Errorf("invalid host %s", h)
}
@@ -77,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")
}
ip = hgip.String()
} else {
// If the address is enclosed in square brackets, extract it (for IPv6, but
// 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)
}
} else if net.ParseIP(ip) == nil {
return "", errors.Errorf("invalid host %s", h)
}
hosts = append(hosts, host+"="+ip)
}
@@ -104,21 +96,3 @@ func toBuildkitUlimits(inp *opts.UlimitOpt) (string, error) {
}
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
}

View File

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

View File

@@ -2,31 +2,19 @@ package builder
import (
"context"
"encoding/csv"
"encoding/json"
"net/url"
"os"
"sort"
"strings"
"sync"
"time"
"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/util/confutil"
"github.com/docker/buildx/util/dockerutil"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/buildx/util/progress"
"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/pkg/errors"
"github.com/spf13/pflag"
"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
}
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
func GetBuilders(dockerCli command.Cli, txn *store.Txn) ([]*Builder, error) {
storeng, err := txn.List()
@@ -331,347 +297,3 @@ func GetBuilders(dockerCli command.Cli, txn *store.Txn) ([]*Builder, error) {
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
}

View File

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

View File

@@ -2,11 +2,8 @@ package builder
import (
"context"
"encoding/json"
"sort"
"strings"
"github.com/containerd/containerd/platforms"
"github.com/docker/buildx/driver"
ctxkube "github.com/docker/buildx/driver/kubernetes/context"
"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 {
node.Err = err
return nil
@@ -202,51 +199,6 @@ func (b *Builder) LoadNodes(ctx context.Context, opts ...LoadNodesOption) (_ []N
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 {
if n.Driver == nil {
return nil

View File

@@ -23,6 +23,7 @@ import (
"github.com/docker/buildx/util/tracing"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -41,7 +42,9 @@ type bakeOptions struct {
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")
if err != nil {
return err
@@ -72,10 +75,12 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
overrides := in.overrides
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")
}
if in.exportLoad {
overrides = append(overrides, "*.load=true")
} else if in.exportLoad {
overrides = append(overrides, "*.output=type=docker")
}
if cFlags.noCache != nil {
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 {
err = err1
}
if err == nil && progressMode != progressui.QuietMode && progressMode != progressui.RawJSONMode {
if err == nil && progressMode != progressui.QuietMode {
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
}
// local state group
groupRef := identity.NewID()
var refs []string
for k, b := range bo {
b.Ref = identity.NewID()
b.GroupRef = groupRef
b.WithProvenanceResponse = len(in.metadataFile) > 0
refs = append(refs, b.Ref)
bo[k] = b
}
@@ -261,7 +266,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
options.builder = rootOpts.builder
options.metadataFile = cFlags.metadataFile
// 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),
}
@@ -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) {
var lnames []string // local
var rnames []string // remote
var anames []string // both
var lnames []string
var rnames []string
for _, v := range names {
if strings.HasPrefix(v, "cwd://") {
tname := strings.TrimPrefix(v, "cwd://")
lnames = append(lnames, tname)
anames = append(anames, tname)
lnames = append(lnames, strings.TrimPrefix(v, "cwd://"))
} else {
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 != "" {
lfiles, err = bake.ReadLocalFiles(lnames, stdin, sub)
} else {
lfiles, err = bake.ReadLocalFiles(anames, stdin, sub)
lfiles, err = bake.ReadLocalFiles(append(lnames, rnames...), stdin, sub)
}
return nil
})

View File

@@ -3,10 +3,8 @@ package commands
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/csv"
"encoding/hex"
"encoding/json"
"fmt"
"io"
@@ -15,8 +13,6 @@ import (
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/containerd/console"
"github.com/docker/buildx/build"
@@ -31,12 +27,8 @@ import (
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"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/ioset"
"github.com/docker/buildx/util/metricutil"
"github.com/docker/buildx/util/osutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/buildx/util/tracing"
"github.com/docker/cli-docs-tool/annotation"
@@ -48,10 +40,10 @@ import (
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"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/targets"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/morikuni/aec"
@@ -59,8 +51,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"google.golang.org/grpc/codes"
)
@@ -122,27 +112,26 @@ func (o *buildOptions) toControllerOptions() (*controllerapi.BuildOptions, error
}
opts := controllerapi.BuildOptions{
Allow: o.allow,
Annotations: o.annotations,
BuildArgs: buildArgs,
CgroupParent: o.cgroupParent,
ContextPath: o.contextPath,
DockerfileName: o.dockerfileName,
ExtraHosts: o.extraHosts,
Labels: labels,
NetworkMode: o.networkMode,
NoCacheFilter: o.noCacheFilter,
Platforms: o.platforms,
ShmSize: int64(o.shmSize),
Tags: o.tags,
Target: o.target,
Ulimits: dockerUlimitToControllerUlimit(o.ulimits),
Builder: o.builder,
NoCache: o.noCache,
Pull: o.pull,
ExportPush: o.exportPush,
ExportLoad: o.exportLoad,
WithProvenanceResponse: len(o.metadataFile) > 0,
Allow: o.allow,
Annotations: o.annotations,
BuildArgs: buildArgs,
CgroupParent: o.cgroupParent,
ContextPath: o.contextPath,
DockerfileName: o.dockerfileName,
ExtraHosts: o.extraHosts,
Labels: labels,
NetworkMode: o.networkMode,
NoCacheFilter: o.noCacheFilter,
Platforms: o.platforms,
ShmSize: int64(o.shmSize),
Tags: o.tags,
Target: o.target,
Ulimits: dockerUlimitToControllerUlimit(o.ulimits),
Builder: o.builder,
NoCache: o.noCache,
Pull: o.pull,
ExportPush: o.exportPush,
ExportLoad: o.exportLoad,
}
// TODO: extract env var parsing to a method easily usable by library consumers
@@ -221,56 +210,8 @@ func (o *buildOptions) toDisplayMode() (progressui.DisplayMode, error) {
return progress, nil
}
func buildMetricAttributes(dockerCli command.Cli, b *builder.Builder, options *buildOptions) attribute.Set {
return attribute.NewSet(
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)
defer metricutil.Shutdown(ctx, mp)
func runBuild(dockerCli command.Cli, options buildOptions) (err error) {
ctx := appcontext.Context()
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
if err != nil {
return err
@@ -311,7 +252,6 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
if _, err := console.ConsoleFromFile(os.Stderr); err == nil {
term = true
}
attributes := buildMetricAttributes(dockerCli, b, &options)
ctx2, cancel := context.WithCancel(context.TODO())
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("%s:%s", b.Driver, b.Name),
),
progress.WithMetrics(mp, attributes),
progress.WithOnClose(func() {
printWarnings(os.Stderr, printer.Warnings(), progressMode)
}),
@@ -334,10 +273,9 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
return err
}
done := timeBuildCommand(mp, attributes)
var resp *client.SolveResponse
var retErr error
if confutil.IsExperimental() {
if isExperimental() {
resp, retErr = runControllerBuild(ctx, dockerCli, opts, options, printer)
} else {
resp, retErr = runBasicBuild(ctx, dockerCli, opts, options, printer)
@@ -346,19 +284,14 @@ func runBuild(ctx context.Context, dockerCli command.Cli, options buildOptions)
if err := printer.Wait(); retErr == nil {
retErr = err
}
done(retErr)
if retErr != nil {
return retErr
}
switch progressMode {
case progressui.RawJSONMode:
// no additional display
case progressui.QuietMode:
fmt.Println(getImageID(resp.ExporterResponse))
default:
if progressMode != progressui.QuietMode {
desktop.PrintBuildDetails(os.Stderr, printer.BuildRefs(), term)
} else {
fmt.Println(getImageID(resp.ExporterResponse))
}
if options.imageIDFile != "" {
if err := os.WriteFile(options.imageIDFile, []byte(getImageID(resp.ExporterResponse)), 0644); err != nil {
@@ -420,22 +353,14 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
var ref string
var retErr error
var resp *client.SolveResponse
var f *ioset.SingleForwarder
var pr io.ReadCloser
var pw io.WriteCloser
if options.invokeConfig == nil {
pr = dockerCli.In()
} else {
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
})
}
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)
if err != nil {
@@ -449,13 +374,11 @@ func runControllerBuild(ctx context.Context, dockerCli command.Cli, opts *contro
}
}
if options.invokeConfig != nil {
if err := pw.Close(); err != nil {
logrus.Debug("failed to close stdin pipe writer")
}
if err := pr.Close(); err != nil {
logrus.Debug("failed to close stdin pipe reader")
}
if err := pw.Close(); err != nil {
logrus.Debug("failed to close stdin pipe writer")
}
if err := pr.Close(); err != nil {
logrus.Debug("failed to close stdin pipe reader")
}
if options.invokeConfig != nil && options.invokeConfig.needsDebug(retErr) {
@@ -549,7 +472,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
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) {
return nil, cobra.ShellCompDirectiveFilterDirs
@@ -584,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.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")
@@ -598,9 +521,9 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
flags.StringArrayVar(&options.platforms, "platform", platformsDefault, "Set target platform for build")
if confutil.IsExperimental() {
if isExperimental() {
flags.StringVar(&options.printFunc, "print", "", "Print result of information request (e.g., outline, targets)")
cobrautil.MarkFlagsExperimental(flags, "print")
flags.SetAnnotation("print", "experimentalCLI", nil)
}
flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--output=type=registry"`)
@@ -609,7 +532,7 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
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>]]")`)
@@ -626,12 +549,14 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions, debugConfig *debug.D
flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--attest=type=sbom"`)
flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--attest=type=provenance"`)
if confutil.IsExperimental() {
if isExperimental() {
// TODO: move this to debug command if needed
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.SetAnnotation("detach", "experimentalCLI", nil)
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)
}
// hidden flags
@@ -654,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.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."})
cobrautil.MarkFlagsExperimental(flags, "squash")
flags.SetAnnotation("squash", "experimentalCLI", nil)
flags.StringVarP(&ignore, "memory", "m", "", "Memory limit")
flags.MarkHidden("memory")
@@ -699,7 +624,7 @@ func commonBuildFlags(options *commonFlags, flags *pflag.FlagSet) {
options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image")
flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "tty"). Use plain to show container output`)
options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images")
flags.StringVar(&options.metadataFile, "metadata-file", "", "Write build result metadata to a file")
flags.StringVar(&options.metadataFile, "metadata-file", "", "Write build result metadata to the file")
}
func checkWarnedFlags(f *pflag.Flag) {
@@ -771,6 +696,14 @@ func (w *wrapped) Unwrap() error {
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 {
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
@@ -816,7 +749,7 @@ func dockerUlimitToControllerUlimit(u *dockeropts.UlimitOpt) *controllerapi.Ulim
}
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
}
fmt.Fprintf(w, "\n ")
@@ -863,12 +796,8 @@ func printResult(f *controllerapi.PrintFunc, res map[string]string) error {
return printValue(targets.PrintTargets, targets.SubrequestsTargetsDefinition.Version, f.Format, res)
case "subrequests.describe":
return printValue(subrequests.PrintDescribe, subrequests.SubrequestsDescribeDefinition.Version, f.Format, res)
case "lint":
return printValue(lint.PrintLintViolations, lint.SubrequestLintDefinition.Version, f.Format, res)
default:
if dt, ok := res["result.json"]; ok && f.Format == "json" {
fmt.Println(dt)
} else if dt, ok := res["result.txt"]; ok {
if dt, ok := res["result.txt"]; ok {
fmt.Print(dt)
} else {
log.Printf("%s %+v", f, res)
@@ -997,38 +926,3 @@ func maybeJSONArray(v string) []string {
}
return []string{v}
}
// 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
}

View File

@@ -3,34 +3,71 @@ package commands
import (
"bytes"
"context"
"encoding/csv"
"fmt"
"net/url"
"os"
"strings"
"time"
"github.com/docker/buildx/builder"
"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/util/cobrautil"
"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/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"
)
type createOptions struct {
name string
driver string
nodeName string
platform []string
actionAppend bool
actionLeave bool
use bool
driverOpts []string
buildkitdFlags string
buildkitdConfigFile string
bootstrap bool
name string
driver string
nodeName string
platform []string
actionAppend bool
actionLeave bool
use bool
flags string
configFile string
driverOpts []string
bootstrap bool
// 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)
if err != nil {
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.
defer release()
if in.actionLeave {
return builder.Leave(ctx, txn, dockerCli, builder.LeaveOpts{
Name: in.name,
NodeName: in.nodeName,
})
name := in.name
if name == "" {
name, err = store.GenerateName(txn)
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
if len(args) > 0 {
ep = args[0]
var setEp bool
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{
Name: in.name,
Driver: in.driver,
NodeName: in.nodeName,
Platforms: in.platform,
DriverOpts: in.driverOpts,
BuildkitdFlags: in.buildkitdFlags,
BuildkitdConfigFile: in.buildkitdConfigFile,
Use: in.use,
Endpoint: ep,
Append: in.actionAppend,
})
if err := txn.Save(ng); err != nil {
return err
}
b, err := builder.New(dockerCli,
builder.WithName(ng.Name),
builder.WithStore(txn),
builder.WithSkippedValidation(),
)
if err != nil {
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.
// Release it so we aren't holding the file lock during the boot.
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
}
@@ -96,7 +331,7 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
Short: "Create a new builder instance",
Args: cli.RequiresMaxArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runCreate(cmd.Context(), dockerCli, options, args)
return runCreate(dockerCli, options, args)
},
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.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.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.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.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.use, "use", false, "Set the current builder instance")
@@ -125,3 +356,49 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
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
View 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")
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/docker/buildx/controller/control"
controllerapi "github.com/docker/buildx/controller/pb"
"github.com/docker/buildx/monitor"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/progress/progressui"
@@ -43,6 +42,9 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
Use: "debug",
Short: "Start debugger",
Args: cobra.NoArgs,
Annotations: map[string]string{
"experimentalCLI": "",
},
RunE: func(cmd *cobra.Command, args []string) error {
printer, err := progress.NewPrinter(context.TODO(), os.Stderr, progressui.DisplayMode(progressMode))
if err != nil {
@@ -71,19 +73,21 @@ func RootCmd(dockerCli command.Cli, children ...DebuggableCmd) *cobra.Command {
return err
},
}
cobrautil.MarkCommandExperimental(cmd)
flags := cmd.Flags()
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.SetAnnotation("on", "experimentalCLI", nil)
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.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.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 {
cmd.AddCommand(c.NewDebugger(&options))
}

View File

@@ -1,132 +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()
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).")
return cmd
}

View File

@@ -1,7 +1,6 @@
package commands
import (
"context"
"fmt"
"io"
"os"
@@ -16,6 +15,7 @@ import (
"github.com/docker/cli/opts"
"github.com/docker/go-units"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/appcontext"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
@@ -26,7 +26,9 @@ type duOptions struct {
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())
if err != nil {
return err
@@ -112,7 +114,7 @@ func duCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.builder
return runDiskUsage(cmd.Context(), dockerCli, options)
return runDiskUsage(dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/docker/buildx/util/imagetools"
"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"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
@@ -31,7 +32,7 @@ type createOptions struct {
progress string
}
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 {
return errors.Errorf("no sources specified")
}
@@ -112,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))
if err != nil {
return err
@@ -271,7 +274,7 @@ func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
Short: "Create a new image based on source images",
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = *opts.Builder
return runCreate(cmd.Context(), dockerCli, options, args)
return runCreate(dockerCli, options, args)
},
ValidArgsFunction: completion.Disable,
}

View File

@@ -1,14 +1,13 @@
package commands
import (
"context"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@@ -19,7 +18,9 @@ type inspectOptions struct {
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 {
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),
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = *rootOpts.Builder
return runInspect(cmd.Context(), dockerCli, options, args[0])
return runInspect(dockerCli, options, args[0])
},
ValidArgsFunction: completion.Disable,
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/debug"
"github.com/docker/go-units"
"github.com/moby/buildkit/util/appcontext"
"github.com/spf13/cobra"
)
@@ -25,7 +26,9 @@ type inspectOptions struct {
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,
builder.WithName(in.builder),
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())
} else {
fmt.Fprintf(w, "Status:\t%s\n", nodes[i].DriverInfo.Status)
if len(n.BuildkitdFlags) > 0 {
fmt.Fprintf(w, "BuildKit daemon flags:\t%s\n", strings.Join(n.BuildkitdFlags, " "))
if len(n.Flags) > 0 {
fmt.Fprintf(w, "Flags:\t%s\n", strings.Join(n.Flags, " "))
}
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)
if len(platforms) > 0 {
@@ -147,7 +150,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
if len(args) > 0 {
options.builder = args[0]
}
return runInspect(cmd.Context(), dockerCli, options)
return runInspect(dockerCli, options)
},
ValidArgsFunction: completion.BuilderNames(dockerCli),
}

View File

@@ -2,43 +2,30 @@ package commands
import (
"context"
"encoding/json"
"fmt"
"sort"
"io"
"strings"
"text/tabwriter"
"time"
"github.com/docker/buildx/builder"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/platformutil"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/formatter"
"github.com/moby/buildkit/util/appcontext"
"github.com/spf13/cobra"
"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 {
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)
if err != nil {
return err
@@ -72,9 +59,22 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error {
return err
}
if hasErrors, err := lsPrint(dockerCli, current, builders, in.format); err != nil {
return err
} else if hasErrors {
w := tabwriter.NewWriter(dockerCli.Out(), 0, 0, 1, ' ', 0)
fmt.Fprintf(w, "NAME/NODE\tDRIVER/ENDPOINT\tSTATUS\tBUILDKIT\tPLATFORMS\n")
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")
for _, b := range builders {
if b.Err() != nil {
@@ -92,6 +92,31 @@ func runLs(ctx context.Context, dockerCli command.Cli, in lsOptions) error {
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 {
var options lsOptions
@@ -100,175 +125,13 @@ func lsCmd(dockerCli command.Cli) *cobra.Command {
Short: "List builder instances",
Args: cli.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return runLs(cmd.Context(), dockerCli, options)
return runLs(dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}
flags := cmd.Flags()
flags.StringVar(&options.format, "format", formatter.TableFormatKey, "Format the output")
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
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 ""
}

View File

@@ -1,7 +1,6 @@
package commands
import (
"context"
"fmt"
"os"
"strings"
@@ -16,6 +15,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/go-units"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"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?`
)
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 = command.PruneFilters(dockerCli, pruneFilters)
@@ -49,12 +51,8 @@ func runPrune(ctx context.Context, dockerCli command.Cli, opts pruneOptions) err
warning = allCacheWarning
}
if !opts.force {
if ok, err := prompt(ctx, dockerCli.In(), dockerCli.Out(), warning); err != nil {
return err
} else if !ok {
return nil
}
if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return nil
}
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,
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.builder
return runPrune(cmd.Context(), dockerCli, options)
return runPrune(dockerCli, options)
},
ValidArgsFunction: completion.Disable,
}

View File

@@ -9,14 +9,16 @@ import (
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
type rmOptions struct {
builders []string
builder string
keepState bool
keepDaemon 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?`
)
func runRm(ctx context.Context, dockerCli command.Cli, in rmOptions) error {
if in.allInactive && !in.force {
if ok, err := prompt(ctx, dockerCli.In(), dockerCli.Out(), rmInactiveWarning); err != nil {
return err
} else if !ok {
return nil
}
func runRm(dockerCli command.Cli, in rmOptions) error {
ctx := appcontext.Context()
if in.allInactive && !in.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), rmInactiveWarning) {
return nil
}
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)
}
eg, _ := errgroup.WithContext(ctx)
for _, name := range in.builders {
func(name string) {
eg.Go(func() (err error) {
defer func() {
if err == nil {
_, _ = fmt.Fprintf(dockerCli.Err(), "%s removed\n", name)
} 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)
b, err := builder.New(dockerCli,
builder.WithName(in.builder),
builder.WithStore(txn),
builder.WithSkippedValidation(),
)
if err != nil {
return err
}
if err := eg.Wait(); err != nil {
return errors.New("failed to remove one or more builders")
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
}
_, _ = fmt.Fprintf(dockerCli.Err(), "%s removed\n", b.Name)
return nil
}
@@ -99,24 +80,25 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
var options rmOptions
cmd := &cobra.Command{
Use: "rm [OPTIONS] [NAME] [NAME...]",
Short: "Remove one or more builder instances",
Use: "rm [NAME]",
Short: "Remove a builder instance",
Args: cli.RequiresMaxArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
options.builders = []string{rootOpts.builder}
options.builder = rootOpts.builder
if len(args) > 0 {
if options.allInactive {
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),
}
flags := cmd.Flags()
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.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")

View File

@@ -7,14 +7,12 @@ import (
imagetoolscmd "github.com/docker/buildx/commands/imagetools"
"github.com/docker/buildx/controller/remote"
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/logutil"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/plugin"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/debug"
"github.com/moby/buildkit/util/appcontext"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
@@ -31,15 +29,12 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
CompletionOptions: cobra.CompletionOptions{
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
// https://github.com/docker/cli/blob/6c9eb708fa6d17765d71965f90e1c59cea686ee9/cli-plugins/plugin/plugin.go#L117-L127
cmd.SilenceUsage = true
@@ -64,10 +59,6 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
"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)
return cmd
}
@@ -84,7 +75,6 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
buildCmd(dockerCli, opts, nil),
bakeCmd(dockerCli, opts),
createCmd(dockerCli),
dialStdioCmd(dockerCli, opts),
rmCmd(dockerCli, opts),
lsCmd(dockerCli),
useCmd(dockerCli, opts),
@@ -97,7 +87,7 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
duCmd(dockerCli, opts),
imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: &opts.builder}),
)
if confutil.IsExperimental() {
if isExperimental() {
cmd.AddCommand(debugcmd.RootCmd(dockerCli,
newDebuggableBuild(dockerCli, opts),
))

View File

@@ -7,6 +7,7 @@ import (
"github.com/docker/buildx/util/cobrautil/completion"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
"github.com/spf13/cobra"
)
@@ -14,7 +15,9 @@ type stopOptions struct {
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,
builder.WithName(in.builder),
builder.WithSkippedValidation(),
@@ -42,7 +45,7 @@ func stopCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
if len(args) > 0 {
options.builder = args[0]
}
return runStop(cmd.Context(), dockerCli, options)
return runStop(dockerCli, options)
},
ValidArgsFunction: completion.BuilderNames(dockerCli),
}

View File

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

View File

@@ -53,21 +53,20 @@ func RunBuild(ctx context.Context, dockerCli command.Cli, in controllerapi.Build
InStream: inStream,
NamedContexts: contexts,
},
Ref: in.Ref,
BuildArgs: in.BuildArgs,
CgroupParent: in.CgroupParent,
ExtraHosts: in.ExtraHosts,
Labels: in.Labels,
NetworkMode: in.NetworkMode,
NoCache: in.NoCache,
NoCacheFilter: in.NoCacheFilter,
Pull: in.Pull,
ShmSize: dockeropts.MemBytes(in.ShmSize),
Tags: in.Tags,
Target: in.Target,
Ulimits: controllerUlimitOpt2DockerUlimit(in.Ulimits),
GroupRef: in.GroupRef,
WithProvenanceResponse: in.WithProvenanceResponse,
Ref: in.Ref,
BuildArgs: in.BuildArgs,
CgroupParent: in.CgroupParent,
ExtraHosts: in.ExtraHosts,
Labels: in.Labels,
NetworkMode: in.NetworkMode,
NoCache: in.NoCache,
NoCacheFilter: in.NoCacheFilter,
Pull: in.Pull,
ShmSize: dockeropts.MemBytes(in.ShmSize),
Tags: in.Tags,
Target: in.Target,
Ulimits: controllerUlimitOpt2DockerUlimit(in.Ulimits),
GroupRef: in.GroupRef,
}
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
}
if in.ExportPush {
var pushUsed bool
for i := range outputs {
if outputs[i].Type == client.ExporterImage {
outputs[i].Attrs["push"] = "true"
pushUsed = true
}
if in.ExportLoad {
return nil, nil, errors.Errorf("push and load may not be set together at the moment")
}
if !pushUsed {
outputs = append(outputs, client.ExportEntry{
Type: client.ExporterImage,
if len(outputs) == 0 {
outputs = []client.ExportEntry{{
Type: "image",
Attrs: map[string]string{
"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 {
var loadUsed bool
for i := range outputs {
if outputs[i].Type == client.ExporterDocker {
if _, ok := outputs[i].Attrs["dest"]; !ok {
loadUsed = true
break
}
}
}
if !loadUsed {
outputs = append(outputs, client.ExportEntry{
Type: client.ExporterDocker,
if len(outputs) == 0 {
outputs = []client.ExportEntry{{
Type: "docker",
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)
}
}
}

View File

@@ -271,41 +271,40 @@ func (m *BuildRequest) GetOptions() *BuildOptions {
}
type BuildOptions struct {
ContextPath string `protobuf:"bytes,1,opt,name=ContextPath,proto3" json:"ContextPath,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"`
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"`
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"`
CacheFrom []*CacheOptionsEntry `protobuf:"bytes,8,rep,name=CacheFrom,proto3" json:"CacheFrom,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"`
Exports []*ExportEntry `protobuf:"bytes,11,rep,name=Exports,proto3" json:"Exports,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"`
NetworkMode string `protobuf:"bytes,14,opt,name=NetworkMode,proto3" json:"NetworkMode,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"`
Secrets []*Secret `protobuf:"bytes,17,rep,name=Secrets,proto3" json:"Secrets,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"`
Tags []string `protobuf:"bytes,20,rep,name=Tags,proto3" json:"Tags,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"`
Builder string `protobuf:"bytes,23,opt,name=Builder,proto3" json:"Builder,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"`
ExportPush bool `protobuf:"varint,26,opt,name=ExportPush,proto3" json:"ExportPush,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"`
Ref string `protobuf:"bytes,29,opt,name=Ref,proto3" json:"Ref,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"`
WithProvenanceResponse bool `protobuf:"varint,32,opt,name=WithProvenanceResponse,proto3" json:"WithProvenanceResponse,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
ContextPath string `protobuf:"bytes,1,opt,name=ContextPath,proto3" json:"ContextPath,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"`
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"`
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"`
CacheFrom []*CacheOptionsEntry `protobuf:"bytes,8,rep,name=CacheFrom,proto3" json:"CacheFrom,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"`
Exports []*ExportEntry `protobuf:"bytes,11,rep,name=Exports,proto3" json:"Exports,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"`
NetworkMode string `protobuf:"bytes,14,opt,name=NetworkMode,proto3" json:"NetworkMode,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"`
Secrets []*Secret `protobuf:"bytes,17,rep,name=Secrets,proto3" json:"Secrets,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"`
Tags []string `protobuf:"bytes,20,rep,name=Tags,proto3" json:"Tags,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"`
Builder string `protobuf:"bytes,23,opt,name=Builder,proto3" json:"Builder,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"`
ExportPush bool `protobuf:"varint,26,opt,name=ExportPush,proto3" json:"ExportPush,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"`
Ref string `protobuf:"bytes,29,opt,name=Ref,proto3" json:"Ref,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"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BuildOptions) Reset() { *m = BuildOptions{} }
@@ -549,13 +548,6 @@ func (m *BuildOptions) GetAnnotations() []string {
return nil
}
func (m *BuildOptions) GetWithProvenanceResponse() bool {
if m != nil {
return m.WithProvenanceResponse
}
return false
}
type ExportEntry struct {
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"`
@@ -2086,129 +2078,128 @@ func init() {
func init() { proto.RegisterFile("controller.proto", fileDescriptor_ed7f10298fa1d90f) }
var fileDescriptor_ed7f10298fa1d90f = []byte{
// 1946 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0x5f, 0x53, 0x1b, 0xc9,
0x11, 0xcf, 0x4a, 0x42, 0x7f, 0x5a, 0x08, 0xe3, 0x09, 0x76, 0xc6, 0x6b, 0x9f, 0x8d, 0xd7, 0xf6,
0x45, 0x15, 0xa7, 0xc4, 0x1d, 0x17, 0x1f, 0xe7, 0xf3, 0x5d, 0x55, 0x40, 0xa0, 0xc0, 0x95, 0x0d,
0xd4, 0x0a, 0xdb, 0x95, 0xa4, 0x2a, 0xae, 0x45, 0x1a, 0xc4, 0x16, 0xcb, 0x8e, 0xb2, 0x33, 0x12,
0x28, 0x4f, 0x79, 0x48, 0xde, 0x52, 0xf9, 0x1e, 0xa9, 0x7c, 0x84, 0x3c, 0xe5, 0x2d, 0x1f, 0x27,
0x1f, 0x21, 0x35, 0x3d, 0xb3, 0xab, 0x15, 0xd2, 0x0a, 0xc8, 0x3d, 0x69, 0xba, 0xf7, 0xd7, 0xdd,
0xd3, 0x3d, 0x3d, 0xdd, 0x3d, 0x82, 0xe5, 0x0e, 0x0f, 0x65, 0xc4, 0x83, 0x80, 0x45, 0x8d, 0x7e,
0xc4, 0x25, 0x27, 0x2b, 0xc7, 0x03, 0x3f, 0xe8, 0x5e, 0x36, 0x52, 0x1f, 0x86, 0x5f, 0xda, 0x6f,
0x7a, 0xbe, 0x3c, 0x1d, 0x1c, 0x37, 0x3a, 0xfc, 0x7c, 0xed, 0x9c, 0x1f, 0x8f, 0xd6, 0x10, 0x75,
0xe6, 0xcb, 0x35, 0xaf, 0xef, 0xaf, 0x09, 0x16, 0x0d, 0xfd, 0x0e, 0x13, 0x6b, 0x46, 0x28, 0xfe,
0xd5, 0x2a, 0xed, 0x57, 0x99, 0xc2, 0x82, 0x0f, 0xa2, 0x0e, 0xeb, 0xf3, 0xc0, 0xef, 0x8c, 0xd6,
0xfa, 0xc7, 0x6b, 0x7a, 0xa5, 0xc5, 0x9c, 0x3a, 0xac, 0xbc, 0xf5, 0x85, 0x3c, 0x8c, 0x78, 0x87,
0x09, 0xc1, 0x84, 0xcb, 0xfe, 0x38, 0x60, 0x42, 0x92, 0x65, 0xc8, 0xbb, 0xec, 0x84, 0x5a, 0xab,
0x56, 0xbd, 0xe2, 0xaa, 0xa5, 0x73, 0x08, 0xf7, 0xae, 0x20, 0x45, 0x9f, 0x87, 0x82, 0x91, 0x0d,
0x58, 0xd8, 0x0b, 0x4f, 0xb8, 0xa0, 0xd6, 0x6a, 0xbe, 0x5e, 0x5d, 0x7f, 0xda, 0x98, 0xe5, 0x5c,
0xc3, 0xc8, 0x29, 0xa4, 0xab, 0xf1, 0x8e, 0x80, 0x6a, 0x8a, 0x4b, 0x1e, 0x41, 0x25, 0x26, 0xb7,
0x8d, 0xe1, 0x31, 0x83, 0xb4, 0x60, 0x71, 0x2f, 0x1c, 0xf2, 0x33, 0xd6, 0xe4, 0xe1, 0x89, 0xdf,
0xa3, 0xb9, 0x55, 0xab, 0x5e, 0x5d, 0x77, 0x66, 0x1b, 0x4b, 0x23, 0xdd, 0x09, 0x39, 0xe7, 0x07,
0xa0, 0xdb, 0xbe, 0xe8, 0xf0, 0x30, 0x64, 0x9d, 0xd8, 0x99, 0x4c, 0xa7, 0x27, 0xf7, 0x94, 0xbb,
0xb2, 0x27, 0xe7, 0x21, 0x3c, 0x98, 0xa1, 0x4b, 0x87, 0xc5, 0xf9, 0x03, 0x2c, 0x6e, 0xa9, 0xbd,
0x65, 0x2b, 0xff, 0x0e, 0x4a, 0x07, 0x7d, 0xe9, 0xf3, 0x50, 0xcc, 0xf7, 0x06, 0xd5, 0x18, 0xa4,
0x1b, 0x8b, 0x38, 0xff, 0x59, 0x34, 0x06, 0x0c, 0x83, 0xac, 0x42, 0xb5, 0xc9, 0x43, 0xc9, 0x2e,
0xe5, 0xa1, 0x27, 0x4f, 0x8d, 0xa1, 0x34, 0x8b, 0x7c, 0x0e, 0x4b, 0xdb, 0xbc, 0x73, 0xc6, 0xa2,
0x13, 0x3f, 0x60, 0xfb, 0xde, 0x39, 0x33, 0x2e, 0x5d, 0xe1, 0x92, 0xef, 0x95, 0xd7, 0x7e, 0x28,
0x5b, 0x83, 0xb0, 0x43, 0xf3, 0xb8, 0xb5, 0x27, 0x59, 0xa7, 0x6a, 0x60, 0xee, 0x58, 0x82, 0xfc,
0x1e, 0x6a, 0x4a, 0x4d, 0xd7, 0x98, 0x16, 0xb4, 0x80, 0x89, 0xf1, 0xea, 0x7a, 0xef, 0x1a, 0x13,
0x72, 0x3b, 0xa1, 0x8c, 0x46, 0xee, 0xa4, 0x2e, 0xb2, 0x02, 0x0b, 0x9b, 0x41, 0xc0, 0x2f, 0xe8,
0xc2, 0x6a, 0xbe, 0x5e, 0x71, 0x35, 0x41, 0xbe, 0x86, 0xd2, 0xa6, 0x94, 0x4c, 0x48, 0x41, 0x8b,
0x68, 0xec, 0xd1, 0x6c, 0x63, 0x1a, 0xe4, 0xc6, 0x60, 0x72, 0x00, 0x15, 0xb4, 0xbf, 0x19, 0xf5,
0x04, 0x2d, 0xa1, 0xe4, 0x97, 0x37, 0xd8, 0x66, 0x22, 0xa3, 0xb7, 0x38, 0xd6, 0x41, 0x76, 0xa0,
0xd2, 0xf4, 0x3a, 0xa7, 0xac, 0x15, 0xf1, 0x73, 0x5a, 0x46, 0x85, 0x3f, 0x9f, 0xad, 0x10, 0x61,
0x46, 0xa1, 0x51, 0x93, 0x48, 0x92, 0x4d, 0x28, 0x21, 0x71, 0xc4, 0x69, 0xe5, 0x76, 0x4a, 0x62,
0x39, 0xe2, 0xc0, 0x62, 0xb3, 0x17, 0xf1, 0x41, 0xff, 0xd0, 0x8b, 0x58, 0x28, 0x29, 0xe0, 0x51,
0x4f, 0xf0, 0xc8, 0x1b, 0x28, 0xed, 0x5c, 0xf6, 0x79, 0x24, 0x05, 0xad, 0xce, 0xbb, 0xbc, 0x1a,
0x64, 0x0c, 0x18, 0x09, 0xf2, 0x18, 0x60, 0xe7, 0x52, 0x46, 0xde, 0x2e, 0x57, 0x61, 0x5f, 0xc4,
0xe3, 0x48, 0x71, 0x48, 0x0b, 0x8a, 0x6f, 0xbd, 0x63, 0x16, 0x08, 0x5a, 0x43, 0xdd, 0x8d, 0x1b,
0x04, 0x56, 0x0b, 0x68, 0x43, 0x46, 0x5a, 0xe5, 0xf5, 0x3e, 0x93, 0x17, 0x3c, 0x3a, 0x7b, 0xc7,
0xbb, 0x8c, 0x2e, 0xe9, 0xbc, 0x4e, 0xb1, 0xc8, 0x73, 0xa8, 0xed, 0x73, 0x1d, 0x3c, 0x3f, 0x90,
0x2c, 0xa2, 0x77, 0x70, 0x33, 0x93, 0x4c, 0xbc, 0xcb, 0x81, 0x27, 0x4f, 0x78, 0x74, 0x2e, 0xe8,
0x32, 0x22, 0xc6, 0x0c, 0x95, 0x41, 0x6d, 0xd6, 0x89, 0x98, 0x14, 0xf4, 0xee, 0xbc, 0x0c, 0xd2,
0x20, 0x37, 0x06, 0x13, 0x0a, 0xa5, 0xf6, 0xe9, 0x79, 0xdb, 0xff, 0x13, 0xa3, 0x64, 0xd5, 0xaa,
0xe7, 0xdd, 0x98, 0x24, 0x2f, 0x21, 0xdf, 0x6e, 0xef, 0xd2, 0x9f, 0xa2, 0xb6, 0x07, 0x19, 0xda,
0xda, 0xbb, 0xae, 0x42, 0x11, 0x02, 0x85, 0x23, 0xaf, 0x27, 0xe8, 0x0a, 0xee, 0x0b, 0xd7, 0xe4,
0x3e, 0x14, 0x8f, 0xbc, 0xa8, 0xc7, 0x24, 0xbd, 0x87, 0x3e, 0x1b, 0x8a, 0xbc, 0x86, 0xd2, 0xfb,
0xc0, 0x3f, 0xf7, 0xa5, 0xa0, 0xf7, 0xe7, 0x5d, 0x4e, 0x0d, 0x3a, 0xe8, 0x4b, 0x37, 0xc6, 0xab,
0xdd, 0x62, 0xbc, 0x59, 0x44, 0x7f, 0x86, 0x3a, 0x63, 0x52, 0x7d, 0x31, 0xe1, 0xa2, 0x74, 0xd5,
0xaa, 0x97, 0xdd, 0x98, 0x54, 0x5b, 0x3b, 0x1c, 0x04, 0x01, 0x7d, 0x80, 0x6c, 0x5c, 0xeb, 0xb3,
0x57, 0x69, 0x70, 0x38, 0x10, 0xa7, 0xd4, 0xc6, 0x2f, 0x29, 0xce, 0xf8, 0xfb, 0x5b, 0xee, 0x75,
0xe9, 0xc3, 0xf4, 0x77, 0xc5, 0x21, 0x7b, 0xb0, 0xd8, 0xc6, 0xb6, 0x74, 0x88, 0xcd, 0x88, 0x3e,
0x42, 0x3f, 0x5e, 0x34, 0x54, 0xe7, 0x6a, 0xc4, 0x9d, 0x4b, 0xf9, 0x90, 0x6e, 0x5e, 0x0d, 0x0d,
0x76, 0x27, 0x44, 0xe3, 0xba, 0xfa, 0xd9, 0xb8, 0xae, 0xda, 0x50, 0xfe, 0x8d, 0x4a, 0x72, 0xc5,
0x7e, 0x8c, 0xec, 0x84, 0x56, 0xc9, 0xb4, 0x19, 0x86, 0x5c, 0x7a, 0xba, 0xee, 0x3e, 0xc1, 0x70,
0xa7, 0x59, 0xe4, 0x6b, 0xb8, 0xff, 0xd1, 0x97, 0xa7, 0x87, 0x11, 0x1f, 0xb2, 0xd0, 0x0b, 0x3b,
0x2c, 0xae, 0xe8, 0x74, 0x15, 0xdd, 0xc8, 0xf8, 0x6a, 0xff, 0x1a, 0xc8, 0x74, 0xf5, 0x52, 0xbb,
0x3b, 0x63, 0xa3, 0xb8, 0xea, 0x9f, 0xb1, 0x91, 0x2a, 0x60, 0x43, 0x2f, 0x18, 0xc4, 0xb5, 0x57,
0x13, 0xdf, 0xe6, 0xbe, 0xb1, 0xec, 0xef, 0x60, 0x69, 0xb2, 0xb0, 0xdc, 0x4a, 0xfa, 0x35, 0x54,
0x53, 0xb7, 0xe7, 0x36, 0xa2, 0xce, 0xbf, 0x2d, 0xa8, 0xa6, 0xae, 0x38, 0x26, 0xe3, 0xa8, 0xcf,
0x8c, 0x30, 0xae, 0xc9, 0x16, 0x2c, 0x6c, 0x4a, 0x19, 0xa9, 0x56, 0xa5, 0xf2, 0xf9, 0x97, 0xd7,
0x16, 0x8a, 0x06, 0xc2, 0xf5, 0x55, 0xd6, 0xa2, 0x2a, 0xf8, 0xdb, 0x4c, 0x48, 0x3f, 0xc4, 0x50,
0x63, 0x67, 0xa9, 0xb8, 0x69, 0x96, 0xfd, 0x0d, 0xc0, 0x58, 0xec, 0x56, 0x3e, 0xfc, 0xd3, 0x82,
0xbb, 0x53, 0xd5, 0x70, 0xa6, 0x27, 0xbb, 0x93, 0x9e, 0xac, 0xdf, 0xb0, 0xb2, 0x4e, 0xfb, 0xf3,
0x23, 0x76, 0xbb, 0x0f, 0x45, 0xdd, 0x82, 0x66, 0xee, 0xd0, 0x86, 0xf2, 0xb6, 0x2f, 0xbc, 0xe3,
0x80, 0x75, 0x51, 0xb4, 0xec, 0x26, 0x34, 0xf6, 0x3f, 0xdc, 0xbd, 0x8e, 0x9e, 0x26, 0x1c, 0x5d,
0x6b, 0xc8, 0x12, 0xe4, 0x92, 0xd9, 0x29, 0xb7, 0xb7, 0xad, 0xc0, 0xaa, 0xf1, 0x6b, 0x57, 0x2b,
0xae, 0x26, 0x9c, 0x16, 0x14, 0x75, 0xf5, 0x9a, 0xc2, 0xdb, 0x50, 0x6e, 0xf9, 0x01, 0xc3, 0xf9,
0x41, 0xef, 0x39, 0xa1, 0x95, 0x7b, 0x3b, 0xe1, 0xd0, 0x98, 0x55, 0x4b, 0x67, 0x23, 0x35, 0x26,
0x28, 0x3f, 0x70, 0xa2, 0x30, 0x7e, 0xe0, 0x1c, 0x71, 0x1f, 0x8a, 0x2d, 0x1e, 0x9d, 0x7b, 0xd2,
0x28, 0x33, 0x94, 0xe3, 0xc0, 0xd2, 0x5e, 0x28, 0xfa, 0xac, 0x23, 0xb3, 0xc7, 0xcd, 0x03, 0xb8,
0x93, 0x60, 0xcc, 0xa0, 0x99, 0x9a, 0x97, 0xac, 0xdb, 0xcf, 0x4b, 0xff, 0xb0, 0xa0, 0x92, 0x54,
0x44, 0xd2, 0x84, 0x22, 0x9e, 0x46, 0x3c, 0xb5, 0xbe, 0xbc, 0xa6, 0x84, 0x36, 0x3e, 0x20, 0xda,
0x74, 0x26, 0x2d, 0x6a, 0x7f, 0x84, 0x6a, 0x8a, 0x3d, 0x23, 0x01, 0xd6, 0xd3, 0x09, 0x90, 0xd9,
0x52, 0xb4, 0x91, 0x74, 0x7a, 0x6c, 0x43, 0x51, 0x33, 0x67, 0x86, 0x95, 0x40, 0x61, 0xd7, 0x8b,
0x74, 0x6a, 0xe4, 0x5d, 0x5c, 0x2b, 0x5e, 0x9b, 0x9f, 0x48, 0x3c, 0x9e, 0xbc, 0x8b, 0x6b, 0xe7,
0x5f, 0x16, 0xd4, 0xcc, 0x08, 0x6a, 0x22, 0xc8, 0x60, 0x59, 0xdf, 0x50, 0x16, 0x25, 0x55, 0x4d,
0xfb, 0xff, 0x7a, 0x4e, 0x28, 0x63, 0x68, 0xe3, 0xaa, 0xac, 0x8e, 0xc6, 0x94, 0x4a, 0xbb, 0x09,
0xf7, 0x66, 0x42, 0x6f, 0x75, 0x45, 0x5e, 0xc0, 0xdd, 0xf1, 0x70, 0x9d, 0x9d, 0x27, 0x2b, 0x40,
0xd2, 0x30, 0x33, 0x7c, 0x3f, 0x81, 0xaa, 0x7a, 0xac, 0x64, 0x8b, 0x39, 0xb0, 0xa8, 0x01, 0x26,
0x32, 0x04, 0x0a, 0x67, 0x6c, 0xa4, 0xb3, 0xa1, 0xe2, 0xe2, 0xda, 0xf9, 0xbb, 0xa5, 0xde, 0x1c,
0xfd, 0x81, 0x7c, 0xc7, 0x84, 0xf0, 0x7a, 0x2a, 0x01, 0x0b, 0x7b, 0xa1, 0x2f, 0x4d, 0xf6, 0x7d,
0x9e, 0xf5, 0xf6, 0xe8, 0x0f, 0xa4, 0x82, 0x19, 0xa9, 0xdd, 0x9f, 0xb8, 0x28, 0x45, 0x36, 0xa0,
0xb0, 0xed, 0x49, 0xcf, 0xe4, 0x42, 0xc6, 0xa4, 0xa5, 0x10, 0x29, 0x41, 0x45, 0x6e, 0x95, 0xd4,
0x03, 0xab, 0x3f, 0x90, 0xce, 0x73, 0x58, 0xbe, 0xaa, 0x7d, 0x86, 0x6b, 0x5f, 0x41, 0x35, 0xa5,
0x05, 0xef, 0xed, 0x41, 0x0b, 0x01, 0x65, 0x57, 0x2d, 0x95, 0xaf, 0xc9, 0x46, 0x16, 0xb5, 0x0d,
0xe7, 0x0e, 0xd4, 0x50, 0x75, 0x12, 0xc1, 0x3f, 0xe7, 0xa0, 0x14, 0xab, 0xd8, 0x98, 0xf0, 0xfb,
0x69, 0x96, 0xdf, 0xd3, 0x2e, 0xbf, 0x82, 0x82, 0xaa, 0x1f, 0xc6, 0xe5, 0x8c, 0x31, 0xa5, 0xd5,
0x4d, 0x89, 0x29, 0x38, 0xf9, 0x1e, 0x8a, 0x2e, 0x13, 0x6a, 0xa4, 0xd2, 0x8f, 0x8f, 0x67, 0xb3,
0x05, 0x35, 0x66, 0x2c, 0x6c, 0x84, 0x94, 0x78, 0xdb, 0xef, 0x85, 0x5e, 0x40, 0x0b, 0xf3, 0xc4,
0x35, 0x26, 0x25, 0xae, 0x19, 0xe3, 0x70, 0xff, 0xd5, 0x82, 0xea, 0xdc, 0x50, 0xcf, 0x7f, 0x1e,
0x4e, 0x3d, 0x59, 0xf3, 0xff, 0xe7, 0x93, 0xf5, 0x2f, 0xb9, 0x49, 0x45, 0x38, 0x5d, 0xa9, 0xfb,
0xd4, 0xe7, 0x7e, 0x28, 0x4d, 0xca, 0xa6, 0x38, 0x6a, 0xa3, 0xcd, 0xf3, 0xae, 0x29, 0xfa, 0x6a,
0xa9, 0xae, 0xd9, 0x3e, 0x57, 0xbc, 0x2a, 0xa6, 0x81, 0x26, 0xc6, 0x25, 0x3d, 0x6f, 0x4a, 0xba,
0x4a, 0x8d, 0xf7, 0x82, 0x45, 0x18, 0xb8, 0x8a, 0x8b, 0x6b, 0x55, 0xc5, 0xf7, 0x39, 0x72, 0x17,
0x50, 0xd8, 0x50, 0x68, 0xe5, 0xa2, 0x4b, 0x8b, 0x3a, 0x1c, 0xcd, 0x8b, 0xd8, 0xca, 0x45, 0x97,
0x96, 0x12, 0x2b, 0x17, 0x68, 0xe5, 0x48, 0x8e, 0x68, 0x59, 0x27, 0xe0, 0x91, 0x1c, 0xa9, 0x36,
0xe3, 0xf2, 0x20, 0x38, 0xf6, 0x3a, 0x67, 0xb4, 0xa2, 0xfb, 0x5b, 0x4c, 0xab, 0x39, 0x54, 0xc5,
0xdc, 0xf7, 0x02, 0x7c, 0xb1, 0x94, 0xdd, 0x98, 0x74, 0x36, 0xa1, 0x92, 0xa4, 0x8a, 0xea, 0x5c,
0xad, 0x2e, 0x1e, 0x45, 0xcd, 0xcd, 0xb5, 0xba, 0x71, 0x96, 0xe7, 0xa6, 0xb3, 0x3c, 0x9f, 0xca,
0xf2, 0x0d, 0xa8, 0x4d, 0x24, 0x8d, 0x02, 0xb9, 0xfc, 0x42, 0x18, 0x45, 0xb8, 0x56, 0xbc, 0x26,
0x0f, 0xf4, 0x9b, 0xbc, 0xe6, 0xe2, 0xda, 0x79, 0x06, 0xb5, 0x89, 0x74, 0x99, 0x55, 0x97, 0x9d,
0xa7, 0x50, 0x6b, 0x4b, 0x4f, 0x0e, 0xe6, 0xfc, 0x89, 0xf2, 0x5f, 0x0b, 0x96, 0x62, 0x8c, 0xa9,
0x3c, 0xbf, 0x82, 0xf2, 0x90, 0x45, 0x92, 0x5d, 0x26, 0xbd, 0x88, 0x4e, 0x8f, 0xc1, 0x1f, 0x10,
0xe1, 0x26, 0x48, 0xf2, 0x2d, 0x94, 0x05, 0xea, 0x61, 0xf1, 0x1c, 0xf3, 0x38, 0x4b, 0xca, 0xd8,
0x4b, 0xf0, 0x64, 0x0d, 0x0a, 0x01, 0xef, 0x09, 0x3c, 0xf7, 0xea, 0xfa, 0xc3, 0x2c, 0xb9, 0xb7,
0xbc, 0xe7, 0x22, 0x90, 0xbc, 0x81, 0xf2, 0x85, 0x17, 0x85, 0x7e, 0xd8, 0x8b, 0xdf, 0xf2, 0x4f,
0xb2, 0x84, 0x3e, 0x6a, 0x9c, 0x9b, 0x08, 0x38, 0x35, 0x75, 0x89, 0x4e, 0xb8, 0x89, 0x89, 0xf3,
0x5b, 0x95, 0xcb, 0x8a, 0x34, 0xee, 0xef, 0x41, 0x4d, 0xdf, 0x87, 0x0f, 0x2c, 0x12, 0x6a, 0x2a,
0xb4, 0xe6, 0xdd, 0xd9, 0xad, 0x34, 0xd4, 0x9d, 0x94, 0x74, 0x3e, 0x99, 0x76, 0x17, 0x33, 0x54,
0x2e, 0xf5, 0xbd, 0xce, 0x99, 0xd7, 0x8b, 0xcf, 0x29, 0x26, 0xd5, 0x97, 0xa1, 0xb1, 0xa7, 0xaf,
0x6d, 0x4c, 0xaa, 0xdc, 0x8c, 0xd8, 0xd0, 0x17, 0xe3, 0x01, 0x35, 0xa1, 0xd7, 0xff, 0x56, 0x02,
0x68, 0x26, 0xfb, 0x21, 0x87, 0xb0, 0x80, 0xf6, 0x88, 0x33, 0xb7, 0x79, 0xa2, 0xdf, 0xf6, 0xb3,
0x1b, 0x34, 0x58, 0xf2, 0x41, 0x25, 0x3f, 0x0e, 0x3d, 0xe4, 0x79, 0x56, 0x99, 0x48, 0xcf, 0x4d,
0xf6, 0x8b, 0x6b, 0x50, 0x46, 0xef, 0x7b, 0x28, 0xea, 0x2c, 0x20, 0x59, 0xb5, 0x30, 0x9d, 0xb7,
0xf6, 0xf3, 0xf9, 0x20, 0xad, 0xf4, 0x0b, 0x8b, 0xb8, 0xa6, 0x52, 0x12, 0x67, 0x4e, 0x2b, 0x34,
0x37, 0x26, 0x2b, 0x00, 0x13, 0x5d, 0xa7, 0x6e, 0x91, 0x1f, 0xa0, 0xa8, 0x6b, 0x1d, 0xf9, 0x6c,
0xb6, 0x40, 0xac, 0x6f, 0xfe, 0xe7, 0xba, 0xf5, 0x85, 0x45, 0xde, 0x41, 0x41, 0x35, 0x79, 0x92,
0xd1, 0xb1, 0x52, 0x13, 0x82, 0xed, 0xcc, 0x83, 0x98, 0x28, 0x7e, 0x02, 0x18, 0x8f, 0x1a, 0x24,
0xe3, 0x1f, 0x99, 0xa9, 0x99, 0xc5, 0xae, 0x5f, 0x0f, 0x34, 0x06, 0xde, 0xa9, 0x3e, 0x7b, 0xc2,
0x49, 0x66, 0x87, 0x4d, 0xae, 0x91, 0xed, 0xcc, 0x83, 0x18, 0x75, 0xa7, 0x50, 0x9b, 0xf8, 0xc7,
0x96, 0xfc, 0x22, 0xdb, 0xc9, 0xab, 0x7f, 0x00, 0xdb, 0x2f, 0x6f, 0x84, 0x35, 0x96, 0x64, 0x7a,
0x56, 0x33, 0x9f, 0x49, 0xe3, 0x3a, 0xbf, 0x27, 0xff, 0x7d, 0xb5, 0xd7, 0x6e, 0x8c, 0xd7, 0x56,
0xb7, 0x0a, 0xbf, 0xcb, 0xf5, 0x8f, 0x8f, 0x8b, 0xf8, 0x47, 0xf6, 0x57, 0xff, 0x0b, 0x00, 0x00,
0xff, 0xff, 0xf1, 0x59, 0xad, 0xb5, 0x66, 0x17, 0x00, 0x00,
// 1922 bytes of a gzipped FileDescriptorProto
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,
0x1c, 0x2a, 0x42, 0xc9, 0x77, 0x3e, 0x82, 0x2f, 0x97, 0xbb, 0x2a, 0x6c, 0xd9, 0xc2, 0xbe, 0x4a,
0x6c, 0xd7, 0xca, 0xc9, 0x15, 0x50, 0xc5, 0xd5, 0x5a, 0x1a, 0xcb, 0x5b, 0x5a, 0xed, 0x88, 0x9d,
0x91, 0x6d, 0xf1, 0xc4, 0x03, 0xbc, 0x51, 0x14, 0x5f, 0x83, 0xe2, 0x23, 0xf0, 0xc4, 0x37, 0xe2,
0x23, 0x50, 0xd3, 0x33, 0xbb, 0x5a, 0x59, 0x5a, 0xd9, 0x86, 0x27, 0x4d, 0xf7, 0xfe, 0xba, 0x7b,
0xba, 0xa7, 0xa7, 0xbb, 0x47, 0xb0, 0xda, 0xe1, 0xa1, 0x8c, 0x78, 0x10, 0xb0, 0xa8, 0x31, 0x8c,
0xb8, 0xe4, 0x64, 0xed, 0x74, 0xe4, 0x07, 0xdd, 0xab, 0x46, 0xea, 0xc3, 0xc5, 0x17, 0xf6, 0xdb,
0x9e, 0x2f, 0xcf, 0x47, 0xa7, 0x8d, 0x0e, 0x1f, 0x6c, 0x0c, 0xf8, 0xe9, 0x78, 0x03, 0x51, 0x7d,
0x5f, 0x6e, 0x78, 0x43, 0x7f, 0x43, 0xb0, 0xe8, 0xc2, 0xef, 0x30, 0xb1, 0x61, 0x84, 0xe2, 0x5f,
0xad, 0xd2, 0x7e, 0x9d, 0x29, 0x2c, 0xf8, 0x28, 0xea, 0xb0, 0x21, 0x0f, 0xfc, 0xce, 0x78, 0x63,
0x78, 0xba, 0xa1, 0x57, 0x5a, 0xcc, 0xa9, 0xc3, 0xda, 0x3b, 0x5f, 0xc8, 0xe3, 0x88, 0x77, 0x98,
0x10, 0x4c, 0xb8, 0xec, 0x0f, 0x23, 0x26, 0x24, 0x59, 0x85, 0xbc, 0xcb, 0xce, 0xa8, 0xb5, 0x6e,
0xd5, 0x2b, 0xae, 0x5a, 0x3a, 0xc7, 0x70, 0xff, 0x1a, 0x52, 0x0c, 0x79, 0x28, 0x18, 0xd9, 0x82,
0xa5, 0x83, 0xf0, 0x8c, 0x0b, 0x6a, 0xad, 0xe7, 0xeb, 0xd5, 0xcd, 0x67, 0x8d, 0x79, 0xce, 0x35,
0x8c, 0x9c, 0x42, 0xba, 0x1a, 0xef, 0x08, 0xa8, 0xa6, 0xb8, 0xe4, 0x31, 0x54, 0x62, 0x72, 0xd7,
0x18, 0x9e, 0x30, 0x48, 0x0b, 0x96, 0x0f, 0xc2, 0x0b, 0xde, 0x67, 0x4d, 0x1e, 0x9e, 0xf9, 0x3d,
0x9a, 0x5b, 0xb7, 0xea, 0xd5, 0x4d, 0x67, 0xbe, 0xb1, 0x34, 0xd2, 0x9d, 0x92, 0x73, 0xbe, 0x03,
0xba, 0xeb, 0x8b, 0x0e, 0x0f, 0x43, 0xd6, 0x89, 0x9d, 0xc9, 0x74, 0x7a, 0x7a, 0x4f, 0xb9, 0x6b,
0x7b, 0x72, 0x1e, 0xc1, 0xc3, 0x39, 0xba, 0x74, 0x58, 0x9c, 0xdf, 0xc3, 0xf2, 0x8e, 0xda, 0x5b,
0xb6, 0xf2, 0x6f, 0xa0, 0x74, 0x34, 0x94, 0x3e, 0x0f, 0xc5, 0x62, 0x6f, 0x50, 0x8d, 0x41, 0xba,
0xb1, 0x88, 0xf3, 0xf7, 0x65, 0x63, 0xc0, 0x30, 0xc8, 0x3a, 0x54, 0x9b, 0x3c, 0x94, 0xec, 0x4a,
0x1e, 0x7b, 0xf2, 0xdc, 0x18, 0x4a, 0xb3, 0xc8, 0x67, 0xb0, 0xb2, 0xcb, 0x3b, 0x7d, 0x16, 0x9d,
0xf9, 0x01, 0x3b, 0xf4, 0x06, 0xcc, 0xb8, 0x74, 0x8d, 0x4b, 0xbe, 0x55, 0x5e, 0xfb, 0xa1, 0x6c,
0x8d, 0xc2, 0x0e, 0xcd, 0xe3, 0xd6, 0x9e, 0x66, 0x9d, 0xaa, 0x81, 0xb9, 0x13, 0x09, 0xf2, 0x3b,
0xa8, 0x29, 0x35, 0x5d, 0x63, 0x5a, 0xd0, 0x02, 0x26, 0xc6, 0xeb, 0x9b, 0xbd, 0x6b, 0x4c, 0xc9,
0xed, 0x85, 0x32, 0x1a, 0xbb, 0xd3, 0xba, 0xc8, 0x1a, 0x2c, 0x6d, 0x07, 0x01, 0xbf, 0xa4, 0x4b,
0xeb, 0xf9, 0x7a, 0xc5, 0xd5, 0x04, 0xf9, 0x25, 0x94, 0xb6, 0xa5, 0x64, 0x42, 0x0a, 0x5a, 0x44,
0x63, 0x8f, 0xe7, 0x1b, 0xd3, 0x20, 0x37, 0x06, 0x93, 0x23, 0xa8, 0xa0, 0xfd, 0xed, 0xa8, 0x27,
0x68, 0x09, 0x25, 0xbf, 0xb8, 0xc5, 0x36, 0x13, 0x19, 0xbd, 0xc5, 0x89, 0x0e, 0xb2, 0x07, 0x95,
0xa6, 0xd7, 0x39, 0x67, 0xad, 0x88, 0x0f, 0x68, 0x19, 0x15, 0xfe, 0x74, 0xbe, 0x42, 0x84, 0x19,
0x85, 0x46, 0x4d, 0x22, 0x49, 0xb6, 0xa1, 0x84, 0xc4, 0x09, 0xa7, 0x95, 0xbb, 0x29, 0x89, 0xe5,
0x88, 0x03, 0xcb, 0xcd, 0x5e, 0xc4, 0x47, 0xc3, 0x63, 0x2f, 0x62, 0xa1, 0xa4, 0x80, 0x47, 0x3d,
0xc5, 0x23, 0x6f, 0xa1, 0xb4, 0x77, 0x35, 0xe4, 0x91, 0x14, 0xb4, 0xba, 0xe8, 0xf2, 0x6a, 0x90,
0x31, 0x60, 0x24, 0xc8, 0x13, 0x80, 0xbd, 0x2b, 0x19, 0x79, 0xfb, 0x5c, 0x85, 0x7d, 0x19, 0x8f,
0x23, 0xc5, 0x21, 0x2d, 0x28, 0xbe, 0xf3, 0x4e, 0x59, 0x20, 0x68, 0x0d, 0x75, 0x37, 0x6e, 0x11,
0x58, 0x2d, 0xa0, 0x0d, 0x19, 0x69, 0x95, 0xd7, 0x87, 0x4c, 0x5e, 0xf2, 0xa8, 0xff, 0x9e, 0x77,
0x19, 0x5d, 0xd1, 0x79, 0x9d, 0x62, 0x91, 0x17, 0x50, 0x3b, 0xe4, 0x3a, 0x78, 0x7e, 0x20, 0x59,
0x44, 0x3f, 0xc1, 0xcd, 0x4c, 0x33, 0xf1, 0x2e, 0x07, 0x9e, 0x3c, 0xe3, 0xd1, 0x40, 0xd0, 0x55,
0x44, 0x4c, 0x18, 0x2a, 0x83, 0xda, 0xac, 0x13, 0x31, 0x29, 0xe8, 0xbd, 0x45, 0x19, 0xa4, 0x41,
0x6e, 0x0c, 0x26, 0x14, 0x4a, 0xed, 0xf3, 0x41, 0xdb, 0xff, 0x23, 0xa3, 0x64, 0xdd, 0xaa, 0xe7,
0xdd, 0x98, 0x24, 0xaf, 0x20, 0xdf, 0x6e, 0xef, 0xd3, 0x1f, 0xa3, 0xb6, 0x87, 0x19, 0xda, 0xda,
0xfb, 0xae, 0x42, 0x11, 0x02, 0x85, 0x13, 0xaf, 0x27, 0xe8, 0x1a, 0xee, 0x0b, 0xd7, 0xe4, 0x01,
0x14, 0x4f, 0xbc, 0xa8, 0xc7, 0x24, 0xbd, 0x8f, 0x3e, 0x1b, 0x8a, 0xbc, 0x81, 0xd2, 0x87, 0xc0,
0x1f, 0xf8, 0x52, 0xd0, 0x07, 0x8b, 0x2e, 0xa7, 0x06, 0x1d, 0x0d, 0xa5, 0x1b, 0xe3, 0xd5, 0x6e,
0x31, 0xde, 0x2c, 0xa2, 0x3f, 0x41, 0x9d, 0x31, 0xa9, 0xbe, 0x98, 0x70, 0x51, 0xba, 0x6e, 0xd5,
0xcb, 0x6e, 0x4c, 0xaa, 0xad, 0x1d, 0x8f, 0x82, 0x80, 0x3e, 0x44, 0x36, 0xae, 0xf5, 0xd9, 0xab,
0x34, 0x38, 0x1e, 0x89, 0x73, 0x6a, 0xe3, 0x97, 0x14, 0x67, 0xf2, 0xfd, 0x1d, 0xf7, 0xba, 0xf4,
0x51, 0xfa, 0xbb, 0xe2, 0x90, 0x03, 0x58, 0x6e, 0x63, 0x5b, 0x3a, 0xc6, 0x66, 0x44, 0x1f, 0xa3,
0x1f, 0x2f, 0x1b, 0xaa, 0x73, 0x35, 0xe2, 0xce, 0xa5, 0x7c, 0x48, 0x37, 0xaf, 0x86, 0x06, 0xbb,
0x53, 0xa2, 0x71, 0x5d, 0xfd, 0x74, 0x52, 0x57, 0x6d, 0x28, 0xff, 0x5a, 0x25, 0xb9, 0x62, 0x3f,
0x41, 0x76, 0x42, 0xab, 0x64, 0xda, 0x0e, 0x43, 0x2e, 0x3d, 0x5d, 0x77, 0x9f, 0x62, 0xb8, 0xd3,
0x2c, 0xfb, 0x57, 0x40, 0x66, 0xab, 0x90, 0xb2, 0xd2, 0x67, 0xe3, 0xb8, 0x7a, 0xf7, 0xd9, 0x58,
0x15, 0xa2, 0x0b, 0x2f, 0x18, 0xc5, 0x35, 0x54, 0x13, 0x5f, 0xe7, 0xbe, 0xb2, 0xec, 0x6f, 0x60,
0x65, 0xba, 0x40, 0xdc, 0x49, 0xfa, 0x0d, 0x54, 0x53, 0xb7, 0xe0, 0x2e, 0xa2, 0xce, 0xbf, 0x2d,
0xa8, 0xa6, 0xae, 0x2a, 0x26, 0xd5, 0x78, 0xc8, 0x8c, 0x30, 0xae, 0xc9, 0x0e, 0x2c, 0x6d, 0x4b,
0x19, 0xa9, 0x96, 0xa3, 0xf2, 0xf2, 0xe7, 0x37, 0x5e, 0xf8, 0x06, 0xc2, 0xf5, 0x95, 0xd4, 0xa2,
0x2a, 0x88, 0xbb, 0x4c, 0x48, 0x3f, 0xc4, 0x90, 0x61, 0x87, 0xa8, 0xb8, 0x69, 0x96, 0xfd, 0x15,
0xc0, 0x44, 0xec, 0x4e, 0x3e, 0xfc, 0xd3, 0x82, 0x7b, 0x33, 0x55, 0x6d, 0xae, 0x27, 0xfb, 0xd3,
0x9e, 0x6c, 0xde, 0xb2, 0x42, 0xce, 0xfa, 0xf3, 0x7f, 0xec, 0xf6, 0x10, 0x8a, 0xba, 0x95, 0xcc,
0xdd, 0xa1, 0x0d, 0xe5, 0x5d, 0x5f, 0x78, 0xa7, 0x01, 0xeb, 0xa2, 0x68, 0xd9, 0x4d, 0x68, 0xec,
0x63, 0xb8, 0x7b, 0x1d, 0x3d, 0x4d, 0x38, 0xba, 0x66, 0x90, 0x15, 0xc8, 0x25, 0x33, 0x50, 0xee,
0x60, 0x57, 0x81, 0x55, 0x03, 0xd7, 0xae, 0x56, 0x5c, 0x4d, 0x38, 0x2d, 0x28, 0xea, 0x2a, 0x34,
0x83, 0xb7, 0xa1, 0xdc, 0xf2, 0x03, 0x86, 0x73, 0x80, 0xde, 0x73, 0x42, 0x2b, 0xf7, 0xf6, 0xc2,
0x0b, 0x63, 0x56, 0x2d, 0x9d, 0xad, 0x54, 0xbb, 0x57, 0x7e, 0xe0, 0x64, 0x60, 0xfc, 0xc0, 0x79,
0xe0, 0x01, 0x14, 0x5b, 0x3c, 0x1a, 0x78, 0xd2, 0x28, 0x33, 0x94, 0xe3, 0xc0, 0xca, 0x41, 0x28,
0x86, 0xac, 0x23, 0xb3, 0xc7, 0xc6, 0x23, 0xf8, 0x24, 0xc1, 0x98, 0x81, 0x31, 0x35, 0xf7, 0x58,
0x77, 0x9f, 0x7b, 0xfe, 0x61, 0x41, 0x25, 0xa9, 0x6c, 0xa4, 0x09, 0x45, 0x3c, 0x8d, 0x78, 0xfa,
0x7c, 0x75, 0x43, 0x29, 0x6c, 0x7c, 0x44, 0xb4, 0xe9, 0x30, 0x5a, 0xd4, 0xfe, 0x1e, 0xaa, 0x29,
0xf6, 0x9c, 0x04, 0xd8, 0x4c, 0x27, 0x40, 0x66, 0x6b, 0xd0, 0x46, 0xd2, 0xe9, 0xb1, 0x0b, 0x45,
0xcd, 0x9c, 0x1b, 0x56, 0x02, 0x85, 0x7d, 0x2f, 0xd2, 0xa9, 0x91, 0x77, 0x71, 0xad, 0x78, 0x6d,
0x7e, 0x26, 0xf1, 0x78, 0xf2, 0x2e, 0xae, 0x9d, 0x7f, 0x59, 0x50, 0x33, 0xa3, 0xa4, 0x89, 0x20,
0x83, 0x55, 0x7d, 0x43, 0x59, 0x14, 0xf3, 0x8c, 0xff, 0x6f, 0x16, 0x84, 0x32, 0x86, 0x36, 0xae,
0xcb, 0xea, 0x68, 0xcc, 0xa8, 0xb4, 0x9b, 0x70, 0x7f, 0x2e, 0xf4, 0x4e, 0x57, 0xe4, 0x25, 0xdc,
0x9b, 0x0c, 0xc9, 0xd9, 0x79, 0xb2, 0x06, 0x24, 0x0d, 0x33, 0x43, 0xf4, 0x53, 0xa8, 0xaa, 0x47,
0x47, 0xb6, 0x98, 0x03, 0xcb, 0x1a, 0x60, 0x22, 0x43, 0xa0, 0xd0, 0x67, 0x63, 0x9d, 0x0d, 0x15,
0x17, 0xd7, 0xce, 0xdf, 0x2c, 0xf5, 0x76, 0x18, 0x8e, 0xe4, 0x7b, 0x26, 0x84, 0xd7, 0x53, 0x09,
0x58, 0x38, 0x08, 0x7d, 0x69, 0xb2, 0xef, 0xb3, 0xac, 0x37, 0xc4, 0x70, 0x24, 0x15, 0xcc, 0x48,
0xed, 0xff, 0xc8, 0x45, 0x29, 0xb2, 0x05, 0x85, 0x5d, 0x4f, 0x7a, 0x26, 0x17, 0x32, 0x26, 0x26,
0x85, 0x48, 0x09, 0x2a, 0x72, 0xa7, 0xa4, 0x1e, 0x4a, 0xc3, 0x91, 0x74, 0x5e, 0xc0, 0xea, 0x75,
0xed, 0x73, 0x5c, 0xfb, 0x12, 0xaa, 0x29, 0x2d, 0x78, 0x6f, 0x8f, 0x5a, 0x08, 0x28, 0xbb, 0x6a,
0xa9, 0x7c, 0x4d, 0x36, 0xb2, 0xac, 0x6d, 0x38, 0x9f, 0x40, 0x0d, 0x55, 0x27, 0x11, 0xfc, 0x53,
0x0e, 0x4a, 0xb1, 0x8a, 0xad, 0x29, 0xbf, 0x9f, 0x65, 0xf9, 0x3d, 0xeb, 0xf2, 0x6b, 0x28, 0xa8,
0xfa, 0x61, 0x5c, 0xce, 0x18, 0x37, 0x5a, 0xdd, 0x94, 0x98, 0x82, 0x93, 0x6f, 0xa1, 0xe8, 0x32,
0xa1, 0x46, 0x23, 0xfd, 0x88, 0x78, 0x3e, 0x5f, 0x50, 0x63, 0x26, 0xc2, 0x46, 0x48, 0x89, 0xb7,
0xfd, 0x5e, 0xe8, 0x05, 0xb4, 0xb0, 0x48, 0x5c, 0x63, 0x52, 0xe2, 0x9a, 0x31, 0x09, 0xf7, 0x5f,
0x2c, 0xa8, 0x2e, 0x0c, 0xf5, 0xe2, 0x67, 0xde, 0xcc, 0xd3, 0x33, 0xff, 0x3f, 0x3e, 0x3d, 0xff,
0x9c, 0x9b, 0x56, 0x84, 0x53, 0x92, 0xba, 0x4f, 0x43, 0xee, 0x87, 0xd2, 0xa4, 0x6c, 0x8a, 0xa3,
0x36, 0xda, 0x1c, 0x74, 0x4d, 0xd1, 0x57, 0x4b, 0x75, 0xcd, 0x0e, 0xb9, 0xe2, 0x55, 0x31, 0x0d,
0x34, 0x31, 0x29, 0xe9, 0x79, 0x53, 0xd2, 0x55, 0x6a, 0x7c, 0x10, 0x2c, 0xc2, 0xc0, 0x55, 0x5c,
0x5c, 0xab, 0x2a, 0x7e, 0xc8, 0x91, 0xbb, 0x84, 0xc2, 0x86, 0x42, 0x2b, 0x97, 0x5d, 0x5a, 0xd4,
0xe1, 0x68, 0x5e, 0xc6, 0x56, 0x2e, 0xbb, 0xb4, 0x94, 0x58, 0xb9, 0x44, 0x2b, 0x27, 0x72, 0x4c,
0xcb, 0x3a, 0x01, 0x4f, 0xe4, 0x58, 0xb5, 0x19, 0x97, 0x07, 0xc1, 0xa9, 0xd7, 0xe9, 0xd3, 0x8a,
0xee, 0x6f, 0x31, 0xad, 0xe6, 0x49, 0x15, 0x73, 0xdf, 0x0b, 0xf0, 0xe5, 0x51, 0x76, 0x63, 0xd2,
0xd9, 0x86, 0x4a, 0x92, 0x2a, 0xaa, 0x73, 0xb5, 0xba, 0x78, 0x14, 0x35, 0x37, 0xd7, 0xea, 0xc6,
0x59, 0x9e, 0x9b, 0xcd, 0xf2, 0x7c, 0x2a, 0xcb, 0xb7, 0xa0, 0x36, 0x95, 0x34, 0x0a, 0xe4, 0xf2,
0x4b, 0x61, 0x14, 0xe1, 0x5a, 0xf1, 0x9a, 0x3c, 0xd0, 0x6f, 0xeb, 0x9a, 0x8b, 0x6b, 0xe7, 0x39,
0xd4, 0xa6, 0xd2, 0x65, 0x5e, 0x5d, 0x76, 0x9e, 0x41, 0xad, 0x2d, 0x3d, 0x39, 0x5a, 0xf0, 0x67,
0xc8, 0x7f, 0x2c, 0x58, 0x89, 0x31, 0xa6, 0xf2, 0xfc, 0x02, 0xca, 0x17, 0x2c, 0x92, 0xec, 0x2a,
0xe9, 0x45, 0x74, 0x76, 0x9c, 0xfd, 0x88, 0x08, 0x37, 0x41, 0x92, 0xaf, 0xa1, 0x2c, 0x50, 0x0f,
0x8b, 0xe7, 0x98, 0x27, 0x59, 0x52, 0xc6, 0x5e, 0x82, 0x27, 0x1b, 0x50, 0x08, 0x78, 0x4f, 0xe0,
0xb9, 0x57, 0x37, 0x1f, 0x65, 0xc9, 0xbd, 0xe3, 0x3d, 0x17, 0x81, 0xe4, 0x2d, 0x94, 0x2f, 0xbd,
0x28, 0xf4, 0xc3, 0x5e, 0xfc, 0x26, 0x7f, 0x9a, 0x25, 0xf4, 0xbd, 0xc6, 0xb9, 0x89, 0x80, 0x53,
0x53, 0x97, 0xe8, 0x8c, 0x9b, 0x98, 0x38, 0xbf, 0x51, 0xb9, 0xac, 0x48, 0xe3, 0xfe, 0x01, 0xd4,
0xf4, 0x7d, 0xf8, 0xc8, 0x22, 0xa1, 0xa6, 0x42, 0x6b, 0xd1, 0x9d, 0xdd, 0x49, 0x43, 0xdd, 0x69,
0x49, 0xe7, 0x07, 0xd3, 0xee, 0x62, 0x86, 0xca, 0xa5, 0xa1, 0xd7, 0xe9, 0x7b, 0xbd, 0xf8, 0x9c,
0x62, 0x52, 0x7d, 0xb9, 0x30, 0xf6, 0xf4, 0xb5, 0x8d, 0x49, 0x95, 0x9b, 0x11, 0xbb, 0xf0, 0xc5,
0x64, 0x40, 0x4d, 0xe8, 0xcd, 0xbf, 0x96, 0x00, 0x9a, 0xc9, 0x7e, 0xc8, 0x31, 0x2c, 0xa1, 0x3d,
0xe2, 0x2c, 0x6c, 0x9e, 0xe8, 0xb7, 0xfd, 0xfc, 0x16, 0x0d, 0x96, 0x7c, 0x54, 0xc9, 0x8f, 0x43,
0x0f, 0x79, 0x91, 0x55, 0x26, 0xd2, 0x73, 0x93, 0xfd, 0xf2, 0x06, 0x94, 0xd1, 0xfb, 0x01, 0x8a,
0x3a, 0x0b, 0x48, 0x56, 0x2d, 0x4c, 0xe7, 0xad, 0xfd, 0x62, 0x31, 0x48, 0x2b, 0xfd, 0xdc, 0x22,
0xae, 0xa9, 0x94, 0xc4, 0x59, 0xd0, 0x0a, 0xcd, 0x8d, 0xc9, 0x0a, 0xc0, 0x54, 0xd7, 0xa9, 0x5b,
0xe4, 0x3b, 0x28, 0xea, 0x5a, 0x47, 0x3e, 0x9d, 0x2f, 0x10, 0xeb, 0x5b, 0xfc, 0xb9, 0x6e, 0x7d,
0x6e, 0x91, 0xf7, 0x50, 0x50, 0x4d, 0x9e, 0x64, 0x74, 0xac, 0xd4, 0x84, 0x60, 0x3b, 0x8b, 0x20,
0x26, 0x8a, 0x3f, 0x00, 0x4c, 0x46, 0x0d, 0x92, 0xf1, 0xcf, 0xca, 0xcc, 0xcc, 0x62, 0xd7, 0x6f,
0x06, 0x1a, 0x03, 0xef, 0x55, 0x9f, 0x3d, 0xe3, 0x24, 0xb3, 0xc3, 0x26, 0xd7, 0xc8, 0x76, 0x16,
0x41, 0x8c, 0xba, 0x73, 0xa8, 0x4d, 0xfd, 0xf3, 0x4a, 0x7e, 0x96, 0xed, 0xe4, 0xf5, 0x3f, 0x72,
0xed, 0x57, 0xb7, 0xc2, 0x1a, 0x4b, 0x32, 0x3d, 0xab, 0x99, 0xcf, 0xa4, 0x71, 0x93, 0xdf, 0xd3,
0xff, 0xa2, 0xda, 0x1b, 0xb7, 0xc6, 0x6b, 0xab, 0x3b, 0x85, 0xdf, 0xe6, 0x86, 0xa7, 0xa7, 0x45,
0xfc, 0x43, 0xfa, 0xcb, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x77, 0x0e, 0x2f, 0x2e, 0x17,
0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.

View File

@@ -80,7 +80,6 @@ message BuildOptions {
string Ref = 29;
string GroupRef = 30;
repeated string Annotations = 31;
bool WithProvenanceResponse = 32;
}
message ExportEntry {

View File

@@ -15,7 +15,6 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
if len(entries) == 0 {
return nil, nil
}
var stdoutUsed bool
for _, entry := range entries {
if entry.Type == "" {
return nil, errors.Errorf("type is required for output")
@@ -69,14 +68,10 @@ func CreateExports(entries []*ExportEntry) ([]client.ExportEntry, error) {
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 {
return nil, errors.Errorf("dest file is required for %s exporter. refusing to write to console", out.Type)
}
out.Output = wrapWriteCloser(os.Stdout)
stdoutUsed = true
} else if entry.Destination != "" {
fi, err := os.Stat(entry.Destination)
if err != nil && !os.IsNotExist(err) {

View File

@@ -7,9 +7,6 @@ variable "DOCS_FORMATS" {
variable "DESTDIR" {
default = "./bin"
}
variable "GOLANGCI_LINT_MULTIPLATFORM" {
default = null
}
# Special target: https://github.com/docker/metadata-action#bake-definition
target "meta-helper" {
@@ -35,17 +32,6 @@ target "lint" {
inherits = ["_common"]
dockerfile = "./hack/dockerfiles/lint.Dockerfile"
output = ["type=cacheonly"]
platforms = GOLANGCI_LINT_MULTIPLATFORM != null ? [
"darwin/amd64",
"darwin/arm64",
"linux/amd64",
"linux/arm64",
"linux/s390x",
"linux/ppc64le",
"linux/riscv64",
"windows/amd64",
"windows/arm64"
] : []
}
target "validate-vendor" {
@@ -180,9 +166,6 @@ variable "HTTPS_PROXY" {
variable "NO_PROXY" {
default = ""
}
variable "TEST_BUILDKIT_TAG" {
default = null
}
target "integration-test-base" {
inherits = ["_common"]
@@ -190,7 +173,6 @@ target "integration-test-base" {
HTTP_PROXY = HTTP_PROXY
HTTPS_PROXY = HTTPS_PROXY
NO_PROXY = NO_PROXY
BUILDKIT_VERSION = TEST_BUILDKIT_TAG
}
target = "integration-test-base"
output = ["type=cacheonly"]

View File

@@ -213,7 +213,7 @@ target "webapp" {
The following table shows the complete list of attributes that you can assign to a target:
| Name | Type | Description |
|-------------------------------------------------|---------|----------------------------------------------------------------------|
| ----------------------------------------------- | ------- | -------------------------------------------------------------------- |
| [`args`](#targetargs) | Map | Build arguments |
| [`annotations`](#targetannotations) | List | Exporter annotations |
| [`attest`](#targetattest) | List | Build attestations |
@@ -233,11 +233,9 @@ The following table shows the complete list of attributes that you can assign to
| [`platforms`](#targetplatforms) | List | Target platforms |
| [`pull`](#targetpull) | Boolean | Always pull images |
| [`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 |
| [`tags`](#targettags) | List | Image names and tags |
| [`target`](#targettarget) | String | Target build stage |
| [`ulimits`](#targetulimits) | List | Ulimit options |
### `target.args`
@@ -834,29 +832,6 @@ RUN --mount=type=secret,id=KUBECONFIG \
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`
Defines SSH agent sockets or keys to expose to the build.
@@ -903,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
Groups allow you to invoke multiple builds (targets) at once.

View File

@@ -3,7 +3,6 @@ package main
import (
"log"
"os"
"strings"
"github.com/docker/buildx/commands"
clidocstool "github.com/docker/cli-docs-tool"
@@ -27,28 +26,6 @@ type options struct {
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 {
log.SetFlags(0)
@@ -80,8 +57,6 @@ func gen(opts *options) error {
return err
}
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 {
return err
}

View File

@@ -9,22 +9,21 @@ Extended build capabilities with BuildKit
### Subcommands
| Name | Description |
|:-------------------------------------|:------------------------------------------------|
| [`bake`](buildx_bake.md) | Build from a file |
| [`build`](buildx_build.md) | Start a build |
| [`create`](buildx_create.md) | Create a new builder instance |
| [`debug`](buildx_debug.md) | Start debugger (EXPERIMENTAL) |
| [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance |
| [`du`](buildx_du.md) | Disk usage |
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
| [`ls`](buildx_ls.md) | List builder instances |
| [`prune`](buildx_prune.md) | Remove build cache |
| [`rm`](buildx_rm.md) | Remove one or more builder instances |
| [`stop`](buildx_stop.md) | Stop builder instance |
| [`use`](buildx_use.md) | Set the current builder instance |
| [`version`](buildx_version.md) | Show buildx version information |
| Name | Description |
|:-------------------------------------|:---------------------------------------|
| [`bake`](buildx_bake.md) | Build from a file |
| [`build`](buildx_build.md) | Start a build |
| [`create`](buildx_create.md) | Create a new builder instance |
| [`debug`](buildx_debug.md) | Start debugger |
| [`du`](buildx_du.md) | Disk usage |
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
| [`ls`](buildx_ls.md) | List builder instances |
| [`prune`](buildx_prune.md) | Remove build cache |
| [`rm`](buildx_rm.md) | Remove a builder instance |
| [`stop`](buildx_stop.md) | Stop builder instance |
| [`use`](buildx_use.md) | Set the current builder instance |
| [`version`](buildx_version.md) | Show buildx version information |
### Options

View File

@@ -13,20 +13,20 @@ Build from a file
### Options
| Name | Type | Default | Description |
|:------------------------------------|:--------------|:--------|:-----------------------------------------------------------------------------------------|
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
| `--load` | | | Shorthand for `--set=*.output=type=docker` |
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file |
| [`--no-cache`](#no-cache) | | | Do not use cache when building the image |
| [`--print`](#print) | | | Print the options without building |
| [`--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` |
| [`--pull`](#pull) | | | Always attempt to pull all referenced images |
| `--push` | | | Shorthand for `--set=*.output=type=registry` |
| [`--sbom`](#sbom) | `string` | | Shorthand for `--set=*.attest=type=sbom` |
| [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
| Name | Type | Default | Description |
|:---------------------------------|:--------------|:--------|:-----------------------------------------------------------------------------------------|
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
| `--load` | | | Shorthand for `--set=*.output=type=docker` |
| `--metadata-file` | `string` | | Write build result metadata to the file |
| [`--no-cache`](#no-cache) | | | Do not use cache when building the image |
| [`--print`](#print) | | | Print the options without building |
| [`--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` |
| [`--pull`](#pull) | | | Always attempt to pull all referenced images |
| `--push` | | | Shorthand for `--set=*.output=type=registry` |
| [`--sbom`](#sbom) | `string` | | Shorthand for `--set=*.attest=type=sbom` |
| [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
<!---MARKER_GEN_END-->
@@ -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/)
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)
Same as `build --no-cache`. Don't use cache when building the image.
@@ -233,7 +162,6 @@ You can override the following fields:
* `context`
* `dockerfile`
* `labels`
* `load`
* `no-cache`
* `no-cache-filter`
* `output`

View File

@@ -25,28 +25,28 @@ Start a build
| [`--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`) |
| [`--cgroup-parent`](https://docs.docker.com/reference/cli/docker/image/build/#cgroup-parent) | `string` | | Set the parent cgroup for the `RUN` instructions during build |
| `--detach` | | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
| `--detach` | | | Detach buildx server (supported only on linux) |
| [`-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 |
| [`--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 |
| `--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`) |
| [`--platform`](#platform) | `stringArray` | | Set target platform for build |
| `--print` | `string` | | Print result of information request (e.g., outline, targets) (EXPERIMENTAL) |
| `--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` |
| `--pull` | | | Always attempt to pull all referenced images |
| [`--push`](#push) | | | Shorthand for `--output=type=registry` |
| `-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` |
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
| [`--shm-size`](#shm-size) | `bytes` | `0` | Shared memory size for build containers |
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) |
| [`--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>]]`) |
| [`-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 |
@@ -152,9 +152,9 @@ Allow extra privileged entitlement. List of entitlements:
- `network.host` - Allows executions with host networking.
- `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)).
```console
@@ -314,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
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.
The metadata will be written as a JSON object to the specified file. The
@@ -327,8 +327,6 @@ $ cat metadata.json
```json
{
"buildx.build.provenance": {},
"buildx.build.ref": "mybuilder/mybuilder0/0fjb6ubs52xx3vygf6fgdl611",
"containerimage.config.digest": "sha256:2937f66a9722f7f4a2df583de2f8cb97fc9196059a410e7f00072fc918930e66",
"containerimage.descriptor": {
"annotations": {
@@ -343,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)
```text
@@ -428,16 +362,12 @@ exporter and write to `stdout`.
```console
$ docker buildx build -o . .
$ 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,dest=- . > myimage.tar
$ 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:
#### `local`
@@ -625,18 +555,10 @@ For more information, see [here](https://docs.docker.com/build/attestations/sbom
--secret=[type=TYPE[,KEY=VALUE]
```
Exposes secrets (authentication credentials, tokens) to the build.
A secret can be mounted into the build using a `RUN --mount=type=secret` mount in the
[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/).
Exposes secret to the build. The secret can be used by the build using
[`RUN --mount=type=secret` mount](https://docs.docker.com/reference/dockerfile/#run---mounttypesecret).
Supported types are:
- [`file`](#file)
- [`env`](#env)
Buildx attempts to detect the `type` automatically if unset.
If `type` is unset it will be detected. Supported types are:
#### `file`
@@ -676,21 +598,12 @@ RUN --mount=type=bind,target=. \
$ SECRET_TOKEN=token docker buildx build --secret id=SECRET_TOKEN .
```
### <a name="shm-size"></a> Shared memory size for build containers (--shm-size)
Sets the size of the shared memory allocated for build containers when using
`RUN` instructions.
### <a name="shm-size"></a> Size of /dev/shm (--shm-size)
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.
> **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)
```text
@@ -724,8 +637,7 @@ $ docker buildx build --ssh default=$SSH_AUTH_SOCK .
### <a name="ulimit"></a> Set ulimits (--ulimit)
`--ulimit` overrides the default ulimits of build's containers when using `RUN`
instructions and are specified with a soft and hard limit as such:
`--ulimit` is specified with a soft and hard limit as such:
`<type>=<soft limit>[:<hard limit>]`, for example:
```console
@@ -737,9 +649,3 @@ $ docker buildx build --ulimit nofile=1024:1024 .
> 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
> 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.

View File

@@ -9,19 +9,19 @@ Create a new builder instance
### Options
| Name | Type | Default | Description |
|:------------------------------------------|:--------------|:--------|:----------------------------------------------------------------------|
| [`--append`](#append) | | | Append a node to builder instead of changing it |
| `--bootstrap` | | | Boot builder after creation |
| [`--buildkitd-config`](#buildkitd-config) | `string` | | BuildKit daemon config file |
| [`--buildkitd-flags`](#buildkitd-flags) | `string` | | BuildKit daemon flags |
| [`--driver`](#driver) | `string` | | Driver to use (available: `docker-container`, `kubernetes`, `remote`) |
| [`--driver-opt`](#driver-opt) | `stringArray` | | Options for the driver |
| [`--leave`](#leave) | | | Remove a node from builder instead of changing it |
| [`--name`](#name) | `string` | | Builder instance name |
| [`--node`](#node) | `string` | | Create/modify node with given name |
| [`--platform`](#platform) | `stringArray` | | Fixed platforms for current node |
| [`--use`](#use) | | | Set the current builder instance |
| Name | Type | Default | Description |
|:----------------------------------------|:--------------|:--------|:----------------------------------------------------------------------|
| [`--append`](#append) | | | Append a node to builder instead of changing it |
| `--bootstrap` | | | Boot builder after creation |
| [`--buildkitd-flags`](#buildkitd-flags) | `string` | | Flags for buildkitd daemon |
| [`--config`](#config) | `string` | | BuildKit config file |
| [`--driver`](#driver) | `string` | | Driver to use (available: `docker-container`, `kubernetes`, `remote`) |
| [`--driver-opt`](#driver-opt) | `stringArray` | | Options for the driver |
| [`--leave`](#leave) | | | Remove a node from builder instead of changing it |
| [`--name`](#name) | `string` | | Builder instance name |
| [`--node`](#node) | `string` | | Create/modify node with given name |
| [`--platform`](#platform) | `stringArray` | | Fixed platforms for current node |
| [`--use`](#use) | | | Set the current builder instance |
<!---MARKER_GEN_END-->
@@ -55,15 +55,29 @@ $ docker buildx create --name eager_beaver --append mycontext2
eager_beaver
```
### <a name="buildkitd-config"></a> Specify a configuration file for the BuildKit daemon (--buildkitd-config)
### <a name="buildkitd-flags"></a> Specify options for the buildkitd daemon (--buildkitd-flags)
```text
--buildkitd-config FILE
--buildkitd-flags FLAGS
```
Specifies the configuration file for the BuildKit daemon to use. The
configuration can be overridden by [`--buildkitd-flags`](#buildkitd-flags).
See an [example BuildKit daemon configuration file](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md).
Adds flags when starting the buildkitd daemon. They take precedence over the
configuration file specified by [`--config`](#config). See `buildkitd --help`
for the available flags.
```text
--buildkitd-flags '--debug --debugaddr 0.0.0.0:6666'
```
### <a name="config"></a> Specify a configuration file for the buildkitd daemon (--config)
```text
--config FILE
```
Specifies the configuration file for the buildkitd daemon to use. The configuration
can be overridden by [`--buildkitd-flags`](#buildkitd-flags).
See an [example buildkitd configuration file](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md).
If you don't specify a configuration file, Buildx looks for one by default in:
@@ -76,36 +90,6 @@ certificates for registries in the `buildkitd.toml` configuration, the files
will be copied into the container under `/etc/buildkit/certs` and configuration
will be updated to reflect that.
### <a name="buildkitd-flags"></a> Specify options for the BuildKit daemon (--buildkitd-flags)
```text
--buildkitd-flags FLAGS
```
Adds flags when starting the BuildKit daemon. They take precedence over the
configuration file specified by [`--buildkitd-config`](#buildkitd-config). See
`buildkitd --help` for the available flags.
```text
--buildkitd-flags '--debug --debugaddr 0.0.0.0:6666'
```
#### BuildKit daemon network mode
You can specify the network mode for the BuildKit daemon with either the
configuration file specified by [`--buildkitd-config`](#buildkitd-config) using the
`worker.oci.networkMode` option or `--oci-worker-net` flag here. The default
value is `auto` and can be one of `bridge`, `cni`, `host`:
```text
--buildkitd-flags '--oci-worker-net bridge'
```
> **Note**
>
> Network mode "bridge" is supported since BuildKit v0.13 and will become the
> default in next v0.14.
### <a name="driver"></a> Set the builder driver to use (--driver)
```text
@@ -149,8 +133,8 @@ to achieve that.
#### `remote` driver
Uses a remote instance of BuildKit daemon over an arbitrary connection. With
this driver, you manually create and manage instances of buildkit yourself, and
Uses a remote instance of buildkitd over an arbitrary connection. With this
driver, you manually create and manage instances of buildkit yourself, and
configure buildx to point at it.
Unlike `docker` driver, built images will not automatically appear in

View File

@@ -1,7 +1,7 @@
# docker buildx debug
<!---MARKER_GEN_START-->
Start debugger (EXPERIMENTAL)
Start debugger
### Subcommands
@@ -15,12 +15,12 @@ Start debugger (EXPERIMENTAL)
| Name | Type | Default | Description |
|:------------------|:---------|:--------|:---------------------------------------------------------------------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `--detach` | `bool` | `true` | Detach buildx server for the monitor (supported only on linux) (EXPERIMENTAL) |
| `--invoke` | `string` | | Launch a monitor with executing specified command (EXPERIMENTAL) |
| `--on` | `string` | `error` | When to launch the monitor ([always, error]) (EXPERIMENTAL) |
| `--detach` | `bool` | `true` | Detach buildx server for the monitor (supported only on linux) |
| `--invoke` | `string` | | Launch a monitor with executing specified command |
| `--on` | `string` | `error` | When to launch the monitor ([always, error]) |
| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`) for the monitor. Use plain to show container output |
| `--root` | `string` | | Specify root directory of server to connect for the monitor (EXPERIMENTAL) |
| `--server-config` | `string` | | Specify buildx server config file for the monitor (used only when launching new server) (EXPERIMENTAL) |
| `--root` | `string` | | Specify root directory of server to connect for the monitor |
| `--server-config` | `string` | | Specify buildx server config file for the monitor (used only when launching new server) |
<!---MARKER_GEN_END-->

View File

@@ -21,28 +21,28 @@ Start a build
| `--cache-from` | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
| `--cache-to` | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
| [`--cgroup-parent`](https://docs.docker.com/reference/cli/docker/image/build/#cgroup-parent) | `string` | | Set the parent cgroup for the `RUN` instructions during build |
| `--detach` | | | Detach buildx server (supported only on linux) (EXPERIMENTAL) |
| `--detach` | | | Detach buildx server (supported only on linux) |
| [`-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 |
| `--load` | | | Shorthand for `--output=type=docker` |
| `--metadata-file` | `string` | | Write build result metadata to a file |
| `--metadata-file` | `string` | | Write build result metadata to the file |
| `--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-filter` | `stringArray` | | Do not cache specified stages |
| `-o`, `--output` | `stringArray` | | Output destination (format: `type=local,dest=path`) |
| `--platform` | `stringArray` | | Set target platform for build |
| `--print` | `string` | | Print result of information request (e.g., outline, targets) (EXPERIMENTAL) |
| `--print` | `string` | | Print result of information request (e.g., outline, targets) |
| `--progress` | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
| `--provenance` | `string` | | Shorthand for `--attest=type=provenance` |
| `--pull` | | | Always attempt to pull all referenced images |
| `--push` | | | Shorthand for `--output=type=registry` |
| `-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` | `string` | | Shorthand for `--attest=type=sbom` |
| `--secret` | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) (EXPERIMENTAL) |
| `--shm-size` | `bytes` | `0` | Shared memory size for build containers |
| `--server-config` | `string` | | Specify buildx server config file (used only when launching new server) |
| `--shm-size` | `bytes` | `0` | Size of `/dev/shm` |
| `--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`) |
| [`--target`](https://docs.docker.com/reference/cli/docker/image/build/#target) | `string` | | Set the target build stage to build |

View File

@@ -1,47 +0,0 @@
# docker buildx dial-stdio
<!---MARKER_GEN_START-->
Proxy current stdio streams to builder instance
### Options
| Name | Type | Default | Description |
|:-------------|:---------|:--------|:-------------------------------------------------|
| `--builder` | `string` | | Override the configured builder instance |
| `--platform` | `string` | | Target platform: this is used for node selection |
| `--progress` | `string` | `quiet` | Set type of progress output (auto, plain, tty). |
<!---MARKER_GEN_END-->
## Description
dial-stdio uses the stdin and stdout streams of the command to proxy to the configured builder instance.
It is not intended to be used by humans, but rather by other tools that want to interact with the builder instance via BuildKit API.
## Examples
Example go program that uses the dial-stdio command wire up a buildkit client.
This is for example use only and may not be suitable for production use.
```go
client.New(ctx, "", client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
c1, c2 := net.Pipe()
cmd := exec.Command("docker", "buildx", "dial-stdio")
cmd.Stdin = c1
cmd.Stdout = c1
if err := cmd.Start(); err != nil {
c1.Close()
c2.Close()
return nil, err
}
go func() {
cmd.Wait()
c2.Close()
}()
return c2
}))
```

View File

@@ -13,106 +13,13 @@ Disk usage
|:------------------------|:---------|:--------|:-----------------------------------------|
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `--filter` | `filter` | | Provide filter values |
| [`--verbose`](#verbose) | | | Provide a more verbose output |
| `--verbose` | | | Provide a more verbose output |
<!---MARKER_GEN_END-->
## Examples
### Show disk usage
The `docker buildx du` command shows the disk usage for the currently selected
builder.
```console
$ docker buildx du
ID RECLAIMABLE SIZE LAST ACCESSED
12wgll9os87pazzft8lt0yztp* true 1.704GB 13 days ago
iupsv3it5ubh92aweb7c1wojc* true 1.297GB 36 minutes ago
ek4ve8h4obyv5kld6vicmtqyn true 811.7MB 13 days ago
isovrfnmkelzhtdx942w9vjcb* true 811.7MB 13 days ago
0jty7mjrndi1yo7xkv1baralh true 810.5MB 13 days ago
jyzkefmsysqiaakgwmjgxjpcz* true 810.5MB 13 days ago
z8w1y95jn93gvj92jtaj6uhwk true 318MB 2 weeks ago
rz2zgfcwlfxsxd7d41w2sz2tt true 8.224kB* 43 hours ago
n5bkzpewmk2eiu6hn9tzx18jd true 8.224kB* 43 hours ago
ao94g6vtbzdl6k5zgdmrmnwpt true 8.224kB* 43 hours ago
2pyjep7njm0wh39vcingxb97i true 8.224kB* 43 hours ago
Shared: 115.5MB
Private: 10.25GB
Reclaimable: 10.36GB
Total: 10.36GB
```
If `RECLAIMABLE` is false, the `docker buildx du prune` command won't delete
the record, even if you use `--all`. That's because the record is actively in
use by some component of the builder.
The asterisks (\*) in the default output indicate the following:
- An asterisk next to an ID (`zu7m6evdpebh5h8kfkpw9dlf2*`) indicates that the record
is mutable. The size of the record may change, or another build can take ownership of
it and change or commit to it. If you run the `du` command again, this item may
not be there anymore, or the size might be different.
- An asterisk next to a size (`8.288kB*`) indicates that the record is shared.
Storage of the record is shared with some other resource, typically an image.
If you prune such a record then you will lose build cache but only metadata
will be deleted as the image still needs to actual storage layers.
### <a name="verbose"></a> Use verbose output (--verbose)
The verbose output of the `docker buildx du` command is useful for inspecting
the disk usage records in more detail. The verbose output shows the mutable and
shared states more clearly, as well as additional information about the
corresponding layer.
```console
$ docker buildx du --verbose
...
Last used: 2 days ago
Type: regular
ID: 05d0elirb4mmvpmnzbrp3ssrg
Parent: e8sfdn4mygrg7msi9ak1dy6op
Created at: 2023-11-20 09:53:30.881558721 +0000 UTC
Mutable: false
Reclaimable: true
Shared: false
Size: 0B
Description: [gobase 3/3] WORKDIR /src
Usage count: 3
Last used: 24 hours ago
Type: regular
Reclaimable: 4.453GB
Total: 4.453GB
```
### <a name="builder"></a> Override the configured builder instance (--builder)
Use the `--builder` flag to inspect the disk usage of a particular builder.
```console
$ docker buildx du --builder youthful_shtern
ID RECLAIMABLE SIZE LAST ACCESSED
g41agepgdczekxg2mtw0dujsv* true 1.312GB 47 hours ago
e6ycrsa0bn9akigqgzu0sc6kr true 318MB 47 hours ago
our9zg4ndly65ze1ccczdksiz true 204.9MB 45 hours ago
b7xv3xpxnwupc81tc9ya3mgq6* true 120.6MB 47 hours ago
zihgye15ss6vum3wmck9egdoy* true 79.81MB 2 days ago
aaydharssv1ug98yhuwclkfrh* true 79.81MB 2 days ago
ta1r4vmnjug5dhub76as4kkol* true 74.51MB 47 hours ago
murma9f83j9h8miifbq68udjf* true 74.51MB 47 hours ago
47f961866a49g5y8myz80ixw1* true 74.51MB 47 hours ago
tzh99xtzlaf6txllh3cobag8t true 74.49MB 47 hours ago
ld6laoeuo1kwapysu6afwqybl* true 59.89MB 47 hours ago
yitxizi5kaplpyomqpos2cryp* true 59.83MB 47 hours ago
iy8aa4b7qjn0qmy9wiga9cj8w true 33.65MB 47 hours ago
mci7okeijyp8aqqk16j80dy09 true 19.86MB 47 hours ago
lqvj091he652slxdla4wom3pz true 14.08MB 47 hours ago
fkt31oiv793nd26h42llsjcw7* true 11.87MB 2 days ago
uj802yxtvkcjysnjb4kgwvn2v true 11.68MB 45 hours ago
Reclaimable: 2.627GB
Total: 2.627GB
```
Same as [`buildx --builder`](buildx.md#builder).

View File

@@ -7,78 +7,23 @@ docker buildx ls
<!---MARKER_GEN_START-->
List builder instances
### Options
| Name | Type | Default | Description |
|:----------------------|:---------|:--------|:------------------|
| [`--format`](#format) | `string` | `table` | Format the output |
<!---MARKER_GEN_END-->
## Description
Lists all builder instances and the nodes for each instance.
Lists all builder instances and the nodes for each instance
```console
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
elated_tesla* docker-container
\_ elated_tesla0 \_ unix:///var/run/docker.sock running v0.10.3 linux/amd64
\_ elated_tesla1 \_ ssh://ubuntu@1.2.3.4 running v0.10.3 linux/arm64*, linux/arm/v7, linux/arm/v6
default docker
\_ default \_ default running v0.8.2 linux/amd64
NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
elated_tesla * docker-container
elated_tesla0 unix:///var/run/docker.sock running v0.10.3 linux/amd64
elated_tesla1 ssh://ubuntu@1.2.3.4 running v0.10.3 linux/arm64*, linux/arm/v7, linux/arm/v6
default docker
default default running v0.8.2 linux/amd64
```
Each builder has one or more nodes associated with it. The current builder's
name is marked with a `*` in `NAME/NODE` and explicit node to build against for
the target platform marked with a `*` in the `PLATFORMS` column.
## Examples
### <a name="format"></a> Format the output (--format)
The formatting options (`--format`) pretty-prints builder instances output
using a Go template.
Valid placeholders for the Go template are listed below:
| Placeholder | Description |
|-------------------|---------------------------------------------|
| `.Name` | Builder or node name |
| `.DriverEndpoint` | Driver (for builder) or Endpoint (for node) |
| `.LastActivity` | Builder last activity |
| `.Status` | Builder or node status |
| `.Buildkit` | BuildKit version of the node |
| `.Platforms` | Available node's platforms |
| `.Error` | Error |
| `.Builder` | Builder object |
When using the `--format` option, the `ls` command will either output the data
exactly as the template declares or, when using the `table` directive, includes
column headers as well.
The following example uses a template without headers and outputs the
`Name` and `DriverEndpoint` entries separated by a colon (`:`):
```console
$ docker buildx ls --format "{{.Name}}: {{.DriverEndpoint}}"
elated_tesla: docker-container
elated_tesla0: unix:///var/run/docker.sock
elated_tesla1: ssh://ubuntu@1.2.3.4
default: docker
default: default
```
The `Builder` placeholder can be used to access the builder object and its
fields. For example, the following template outputs the builder's and
nodes' names with their respective endpoints:
```console
$ docker buildx ls --format "{{.Builder.Name}}: {{range .Builder.Nodes}}\n {{.Name}}: {{.Endpoint}}{{end}}"
elated_tesla:
elated_tesla0: unix:///var/run/docker.sock
elated_tesla1: ssh://ubuntu@1.2.3.4
default: docker
default: default
```

View File

@@ -1,11 +1,11 @@
# buildx rm
```text
docker buildx rm [OPTIONS] [NAME] [NAME...]
docker buildx rm [NAME]
```
<!---MARKER_GEN_START-->
Remove one or more builder instances
Remove a builder instance
### Options
@@ -14,7 +14,7 @@ Remove one or more builder instances
| [`--all-inactive`](#all-inactive) | | | Remove all inactive builders |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-f`](#force), [`--force`](#force) | | | Do not prompt for confirmation |
| [`--keep-daemon`](#keep-daemon) | | | Keep the BuildKit daemon running |
| [`--keep-daemon`](#keep-daemon) | | | Keep the buildkitd daemon running |
| [`--keep-state`](#keep-state) | | | Keep BuildKit state |
@@ -48,10 +48,10 @@ Do not prompt for confirmation before removing inactive builders.
$ docker buildx rm --all-inactive --force
```
### <a name="keep-daemon"></a> Keep the BuildKit daemon running (--keep-daemon)
### <a name="keep-daemon"></a> Keep the buildkitd daemon running (--keep-daemon)
Keep the BuildKit daemon running after the buildx context is removed. This is
useful when you manage BuildKit daemons and buildx contexts independently.
useful when you manage buildkitd daemons and buildx contexts independently.
Only supported by the
[`docker-container`](https://docs.docker.com/build/drivers/docker-container/)
and [`kubernetes`](https://docs.docker.com/build/drivers/kubernetes/) drivers.

View File

@@ -20,16 +20,15 @@ import (
"github.com/docker/cli/opts"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
imagetypes "github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/system"
dockerclient "github.com/docker/docker/client"
"github.com/docker/docker/errdefs"
dockerarchive "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/stdcopy"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/tracing/detect"
"github.com/pkg/errors"
)
@@ -44,19 +43,17 @@ type Driver struct {
// if you add fields, remember to update docs:
// https://github.com/docker/docs/blob/main/content/build/drivers/docker-container.md
netMode string
image string
memory opts.MemBytes
memorySwap opts.MemSwapBytes
cpuQuota int64
cpuPeriod int64
cpuShares int64
cpusetCpus string
cpusetMems string
cgroupParent string
restartPolicy container.RestartPolicy
env []string
defaultLoad bool
netMode string
image string
memory opts.MemBytes
memorySwap opts.MemSwapBytes
cpuQuota int64
cpuPeriod int64
cpuShares int64
cpusetCpus string
cpusetMems string
cgroupParent string
env []string
}
func (d *Driver) IsMobyDriver() bool {
@@ -96,7 +93,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
if err != nil {
return err
}
rc, err := d.DockerAPI.ImageCreate(ctx, imageName, imagetypes.CreateOptions{
rc, err := d.DockerAPI.ImageCreate(ctx, imageName, dockertypes.ImageCreateOptions{
RegistryAuth: ra,
})
if err != nil {
@@ -123,8 +120,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
useInit := true // let it cleanup exited processes created by BuildKit's container API
return l.Wrap("creating container "+d.Name, func() error {
hc := &container.HostConfig{
Privileged: true,
RestartPolicy: d.restartPolicy,
Privileged: true,
Mounts: []mount.Mount{
{
Type: mount.TypeVolume,
@@ -168,7 +164,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
}
}
secOpts, err := system.DecodeSecurityOptions(info.SecurityOptions)
secOpts, err := dockertypes.DecodeSecurityOptions(info.SecurityOptions)
if err != nil {
return err
}
@@ -227,7 +223,7 @@ func (d *Driver) wait(ctx context.Context, l progress.SubLogger) error {
}
func (d *Driver) copyLogs(ctx context.Context, l progress.SubLogger) error {
rc, err := d.DockerAPI.ContainerLogs(ctx, d.Name, container.LogsOptions{
rc, err := d.DockerAPI.ContainerLogs(ctx, d.Name, dockertypes.ContainerLogsOptions{
ShowStdout: true, ShowStderr: true,
})
if err != nil {
@@ -305,7 +301,7 @@ func (d *Driver) run(ctx context.Context, cmd []string, stdout, stderr io.Writer
}
func (d *Driver) start(ctx context.Context, l progress.SubLogger) error {
return d.DockerAPI.ContainerStart(ctx, d.Name, container.StartOptions{})
return d.DockerAPI.ContainerStart(ctx, d.Name, dockertypes.ContainerStartOptions{})
}
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
@@ -363,18 +359,18 @@ func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return err
}
if info.Status != driver.Inactive {
ctr, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
container, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
if err != nil {
return err
}
if rmDaemon {
if err := d.DockerAPI.ContainerRemove(ctx, d.Name, container.RemoveOptions{
if err := d.DockerAPI.ContainerRemove(ctx, d.Name, dockertypes.ContainerRemoveOptions{
RemoveVolumes: true,
Force: force,
}); err != nil {
return err
}
for _, v := range ctr.Mounts {
for _, v := range container.Mounts {
if v.Name != d.Name+volumeStateSuffix {
continue
}
@@ -387,30 +383,30 @@ func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return nil
}
func (d *Driver) Dial(ctx context.Context) (net.Conn, error) {
func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
_, conn, err := d.exec(ctx, []string{"buildctl", "dial-stdio"})
if err != nil {
return nil, err
}
conn = demuxConn(conn)
return conn, nil
}
func (d *Driver) Client(ctx context.Context, opts ...client.ClientOpt) (*client.Client, error) {
conn, err := d.Dial(ctx)
conn = demuxConn(conn)
exp, err := detect.Exporter()
if err != nil {
return nil, err
}
var opts []client.ClientOpt
var counter int64
opts = append([]client.ClientOpt{
client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
if atomic.AddInt64(&counter, 1) > 1 {
return nil, net.ErrClosed
}
return conn, nil
}),
}, opts...)
opts = append(opts, client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
if atomic.AddInt64(&counter, 1) > 1 {
return nil, net.ErrClosed
}
return conn, nil
}))
if td, ok := exp.(client.TracerDelegate); ok {
opts = append(opts, client.WithTracerDelegate(td))
}
return client.New(ctx, "", opts...)
}
@@ -424,7 +420,6 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
driver.DockerExporter: true,
driver.CacheExport: true,
driver.MultiPlatform: true,
driver.DefaultLoad: d.defaultLoad,
}
}
@@ -489,7 +484,7 @@ func writeConfigFiles(m map[string][]byte) (_ string, err error) {
}
func getBuildkitFlags(initConfig driver.InitConfig) []string {
flags := initConfig.BuildkitdFlags
flags := initConfig.BuildkitFlags
if _, ok := initConfig.Files[buildkitdConfigFile]; ok {
// There's no way for us to determine the appropriate default configuration
// path and the default path can vary depending on if the image is normal

View File

@@ -7,14 +7,12 @@ import (
"strings"
"github.com/docker/buildx/driver"
dockeropts "github.com/docker/cli/opts"
dockerclient "github.com/docker/docker/client"
"github.com/pkg/errors"
)
const prioritySupported = 30
const priorityUnsupported = 70
const defaultRestartPolicy = "unless-stopped"
func init() {
driver.Register(&factory{})
@@ -42,19 +40,14 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
if cfg.DockerAPI == nil {
return nil, errors.Errorf("%s driver requires docker API access", f.Name())
}
rp, err := dockeropts.ParseRestartPolicy(defaultRestartPolicy)
if err != nil {
return nil, err
}
d := &Driver{
factory: f,
InitConfig: cfg,
restartPolicy: rp,
}
d := &Driver{factory: f, InitConfig: cfg}
for k, v := range cfg.DriverOpts {
switch {
case k == "network":
d.netMode = v
if v == "host" {
d.InitConfig.BuildkitFlags = append(d.InitConfig.BuildkitFlags, "--allow-insecure-entitlement=network.host")
}
case k == "image":
d.image = v
case k == "memory":
@@ -89,16 +82,6 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
d.cpusetMems = v
case k == "cgroup-parent":
d.cgroupParent = v
case k == "restart-policy":
d.restartPolicy, err = dockeropts.ParseRestartPolicy(v)
if err != nil {
return nil, err
}
case k == "default-load":
d.defaultLoad, err = strconv.ParseBool(v)
if err != nil {
return nil, err
}
case strings.HasPrefix(k, "env."):
envName := strings.TrimPrefix(k, "env.")
if envName == "" {

View File

@@ -9,6 +9,7 @@ import (
"github.com/docker/buildx/driver"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/tracing/detect"
"github.com/pkg/errors"
)
@@ -56,18 +57,22 @@ func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return nil
}
func (d *Driver) Dial(ctx context.Context) (net.Conn, error) {
return d.DockerAPI.DialHijack(ctx, "/grpc", "h2c", d.DialMeta)
}
func (d *Driver) Client(ctx context.Context, opts ...client.ClientOpt) (*client.Client, error) {
opts = append([]client.ClientOpt{
func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
opts := []client.ClientOpt{
client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return d.Dial(ctx)
return d.DockerAPI.DialHijack(ctx, "/grpc", "h2c", d.DialMeta)
}), client.WithSessionDialer(func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) {
return d.DockerAPI.DialHijack(ctx, "/session", proto, meta)
}),
}, opts...)
}
exp, err := detect.Exporter()
if err != nil {
return nil, err
}
if td, ok := exp.(client.TracerDelegate); ok {
opts = append(opts, client.WithTracerDelegate(td))
}
return client.New(ctx, "", opts...)
}
@@ -93,7 +98,6 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
driver.DockerExporter: useContainerdSnapshotter,
driver.CacheExport: useContainerdSnapshotter,
driver.MultiPlatform: useContainerdSnapshotter,
driver.DefaultLoad: true,
}
})
return d.features.list

View File

@@ -4,7 +4,6 @@ import (
"context"
"io"
"net"
"strings"
"github.com/docker/buildx/store"
"github.com/docker/buildx/util/progress"
@@ -60,27 +59,13 @@ type Driver interface {
Version(context.Context) (string, error)
Stop(ctx context.Context, force bool) error
Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error
Dial(ctx context.Context) (net.Conn, error)
Client(ctx context.Context, opts ...client.ClientOpt) (*client.Client, error)
Client(ctx context.Context) (*client.Client, error)
Features(ctx context.Context) map[Feature]bool
HostGatewayIP(ctx context.Context) (net.IP, error)
IsMobyDriver() bool
Config() InitConfig
}
const builderNamePrefix = "buildx_buildkit_"
func BuilderName(name string) string {
return builderNamePrefix + name
}
func ParseBuilderName(name string) (string, error) {
if !strings.HasPrefix(name, builderNamePrefix) {
return "", errors.Errorf("invalid builder name %q, must have %q prefix", name, builderNamePrefix)
}
return strings.TrimPrefix(name, builderNamePrefix), nil
}
func Boot(ctx, clientContext context.Context, d *DriverHandle, pw progress.Writer) (*client.Client, error) {
try := 0
for {

View File

@@ -7,5 +7,3 @@ const DockerExporter Feature = "Docker exporter"
const CacheExport Feature = "Cache export"
const MultiPlatform Feature = "Multi-platform build"
const DefaultLoad Feature = "Automatically load images to the Docker Engine image store"

View File

@@ -15,6 +15,7 @@ import (
"github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/tracing/detect"
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
@@ -49,7 +50,6 @@ type Driver struct {
podClient clientcorev1.PodInterface
configMapClient clientcorev1.ConfigMapInterface
podChooser podchooser.PodChooser
defaultLoad bool
}
func (d *Driver) IsMobyDriver() bool {
@@ -189,7 +189,7 @@ func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return nil
}
func (d *Driver) Dial(ctx context.Context) (net.Conn, error) {
func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
restClient := d.clientset.CoreV1().RESTClient()
restClientConfig, err := d.KubeClientConfig.ClientConfig()
if err != nil {
@@ -208,15 +208,19 @@ func (d *Driver) Dial(ctx context.Context) (net.Conn, error) {
if err != nil {
return nil, err
}
return conn, nil
}
func (d *Driver) Client(ctx context.Context, opts ...client.ClientOpt) (*client.Client, error) {
opts = append([]client.ClientOpt{
client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return d.Dial(ctx)
}),
}, opts...)
exp, err := detect.Exporter()
if err != nil {
return nil, err
}
var opts []client.ClientOpt
opts = append(opts, client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return conn, nil
}))
if td, ok := exp.(client.TracerDelegate); ok {
opts = append(opts, client.WithTracerDelegate(td))
}
return client.New(ctx, "", opts...)
}
@@ -230,7 +234,6 @@ func (d *Driver) Features(_ context.Context) map[driver.Feature]bool {
driver.DockerExporter: d.DockerAPI != nil,
driver.CacheExport: true,
driver.MultiPlatform: true, // Untested (needs multiple Driver instances)
driver.DefaultLoad: d.defaultLoad,
}
}

View File

@@ -68,13 +68,11 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
clientset: clientset,
}
deploymentOpt, loadbalance, namespace, defaultLoad, err := f.processDriverOpts(deploymentName, namespace, cfg)
deploymentOpt, loadbalance, namespace, err := f.processDriverOpts(deploymentName, namespace, cfg)
if nil != err {
return nil, err
}
d.defaultLoad = defaultLoad
d.deployment, d.configMaps, err = manifest.NewDeployment(deploymentOpt)
if err != nil {
return nil, err
@@ -102,19 +100,17 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
return d, nil
}
func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg driver.InitConfig) (*manifest.DeploymentOpt, string, string, bool, error) {
func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg driver.InitConfig) (*manifest.DeploymentOpt, string, string, error) {
deploymentOpt := &manifest.DeploymentOpt{
Name: deploymentName,
Image: bkimage.DefaultImage,
Replicas: 1,
BuildkitFlags: cfg.BuildkitdFlags,
BuildkitFlags: cfg.BuildkitFlags,
Rootless: false,
Platforms: cfg.Platforms,
ConfigFiles: cfg.Files,
}
defaultLoad := false
deploymentOpt.Qemu.Image = bkimage.QemuImage
loadbalance := LoadbalanceSticky
@@ -131,24 +127,20 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg
case "replicas":
deploymentOpt.Replicas, err = strconv.Atoi(v)
if err != nil {
return nil, "", "", false, err
return nil, "", "", err
}
case "requests.cpu":
deploymentOpt.RequestsCPU = v
case "requests.memory":
deploymentOpt.RequestsMemory = v
case "requests.ephemeral-storage":
deploymentOpt.RequestsEphemeralStorage = v
case "limits.cpu":
deploymentOpt.LimitsCPU = v
case "limits.memory":
deploymentOpt.LimitsMemory = v
case "limits.ephemeral-storage":
deploymentOpt.LimitsEphemeralStorage = v
case "rootless":
deploymentOpt.Rootless, err = strconv.ParseBool(v)
if err != nil {
return nil, "", "", false, err
return nil, "", "", err
}
if _, isImage := cfg.DriverOpts["image"]; !isImage {
deploymentOpt.Image = bkimage.DefaultRootlessImage
@@ -158,17 +150,17 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg
case "nodeselector":
deploymentOpt.NodeSelector, err = splitMultiValues(v, ",", "=")
if err != nil {
return nil, "", "", false, errors.Wrap(err, "cannot parse node selector")
return nil, "", "", errors.Wrap(err, "cannot parse node selector")
}
case "annotations":
deploymentOpt.CustomAnnotations, err = splitMultiValues(v, ",", "=")
if err != nil {
return nil, "", "", false, errors.Wrap(err, "cannot parse annotations")
return nil, "", "", errors.Wrap(err, "cannot parse annotations")
}
case "labels":
deploymentOpt.CustomLabels, err = splitMultiValues(v, ",", "=")
if err != nil {
return nil, "", "", false, errors.Wrap(err, "cannot parse labels")
return nil, "", "", errors.Wrap(err, "cannot parse labels")
}
case "tolerations":
ts := strings.Split(v, ";")
@@ -193,12 +185,12 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg
case "tolerationSeconds":
c, err := strconv.Atoi(kv[1])
if nil != err {
return nil, "", "", false, err
return nil, "", "", err
}
c64 := int64(c)
t.TolerationSeconds = &c64
default:
return nil, "", "", false, errors.Errorf("invalid tolaration %q", v)
return nil, "", "", errors.Errorf("invalid tolaration %q", v)
}
}
}
@@ -210,29 +202,24 @@ func (f *factory) processDriverOpts(deploymentName string, namespace string, cfg
case LoadbalanceSticky:
case LoadbalanceRandom:
default:
return nil, "", "", false, errors.Errorf("invalid loadbalance %q", v)
return nil, "", "", errors.Errorf("invalid loadbalance %q", v)
}
loadbalance = v
case "qemu.install":
deploymentOpt.Qemu.Install, err = strconv.ParseBool(v)
if err != nil {
return nil, "", "", false, err
return nil, "", "", err
}
case "qemu.image":
if v != "" {
deploymentOpt.Qemu.Image = v
}
case "default-load":
defaultLoad, err = strconv.ParseBool(v)
if err != nil {
return nil, "", "", false, err
}
default:
return nil, "", "", false, errors.Errorf("invalid driver option %s for driver %s", k, DriverName)
return nil, "", "", errors.Errorf("invalid driver option %s for driver %s", k, DriverName)
}
}
return deploymentOpt, loadbalance, namespace, defaultLoad, nil
return deploymentOpt, loadbalance, namespace, nil
}
func splitMultiValues(in string, itemsep string, kvsep string) (map[string]string, error) {
@@ -257,10 +244,10 @@ func (f *factory) AllowsInstances() bool {
// eg. "buildx_buildkit_loving_mendeleev0" -> "loving-mendeleev0"
func buildxNameToDeploymentName(bx string) (string, error) {
// TODO: commands.util.go should not pass "buildx_buildkit_" prefix to drivers
s, err := driver.ParseBuilderName(bx)
if err != nil {
return "", err
if !strings.HasPrefix(bx, "buildx_buildkit_") {
return "", errors.Errorf("expected a string with \"buildx_buildkit_\", got %q", bx)
}
s := strings.TrimPrefix(bx, "buildx_buildkit_")
s = strings.ReplaceAll(s, "_", "-")
return s, nil
}

View File

@@ -29,7 +29,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
}
cfg := driver.InitConfig{
Name: driver.BuilderName("test"),
Name: "buildx_buildkit_test",
KubeClientConfig: &kcc,
}
f := factory{}
@@ -52,9 +52,8 @@ func TestFactory_processDriverOpts(t *testing.T) {
"loadbalance": "random",
"qemu.install": "true",
"qemu.image": "qemu:latest",
"default-load": "true",
}
r, loadbalance, ns, defaultLoad, err := f.processDriverOpts(cfg.Name, "test", cfg)
r, loadbalance, ns, err := f.processDriverOpts(cfg.Name, "test", cfg)
nodeSelectors := map[string]string{
"selector1": "value1",
@@ -103,7 +102,6 @@ func TestFactory_processDriverOpts(t *testing.T) {
require.Equal(t, LoadbalanceRandom, loadbalance)
require.True(t, r.Qemu.Install)
require.Equal(t, "qemu:latest", r.Qemu.Image)
require.True(t, defaultLoad)
},
)
@@ -111,7 +109,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
"NoOptions", func(t *testing.T) {
cfg.DriverOpts = map[string]string{}
r, loadbalance, ns, defaultLoad, err := f.processDriverOpts(cfg.Name, "test", cfg)
r, loadbalance, ns, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.NoError(t, err)
@@ -130,7 +128,6 @@ func TestFactory_processDriverOpts(t *testing.T) {
require.Equal(t, LoadbalanceSticky, loadbalance)
require.False(t, r.Qemu.Install)
require.Equal(t, bkimage.QemuImage, r.Qemu.Image)
require.False(t, defaultLoad)
},
)
@@ -141,7 +138,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
"loadbalance": "sticky",
}
r, loadbalance, ns, defaultLoad, err := f.processDriverOpts(cfg.Name, "test", cfg)
r, loadbalance, ns, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.NoError(t, err)
@@ -160,7 +157,6 @@ func TestFactory_processDriverOpts(t *testing.T) {
require.Equal(t, LoadbalanceSticky, loadbalance)
require.False(t, r.Qemu.Install)
require.Equal(t, bkimage.QemuImage, r.Qemu.Image)
require.False(t, defaultLoad)
},
)
@@ -169,7 +165,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"replicas": "invalid",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -179,7 +175,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"rootless": "invalid",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -189,7 +185,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"tolerations": "key=foo,value=bar,invalid=foo2",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -199,7 +195,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"tolerations": "key=foo,value=bar,tolerationSeconds=invalid",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -209,7 +205,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"annotations": "key,value",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -219,7 +215,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"labels": "key=value=foo",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -229,7 +225,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"loadbalance": "invalid",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -239,7 +235,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"qemu.install": "invalid",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)
@@ -249,7 +245,7 @@ func TestFactory_processDriverOpts(t *testing.T) {
cfg.DriverOpts = map[string]string{
"invalid": "foo",
}
_, _, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
_, _, _, err := f.processDriverOpts(cfg.Name, "test", cfg)
require.Error(t, err)
},
)

View File

@@ -32,18 +32,16 @@ type DeploymentOpt struct {
// files mounted at /etc/buildkitd
ConfigFiles map[string][]byte
Rootless bool
NodeSelector map[string]string
CustomAnnotations map[string]string
CustomLabels map[string]string
Tolerations []corev1.Toleration
RequestsCPU string
RequestsMemory string
RequestsEphemeralStorage string
LimitsCPU string
LimitsMemory string
LimitsEphemeralStorage string
Platforms []v1.Platform
Rootless bool
NodeSelector map[string]string
CustomAnnotations map[string]string
CustomLabels map[string]string
Tolerations []corev1.Toleration
RequestsCPU string
RequestsMemory string
LimitsCPU string
LimitsMemory string
Platforms []v1.Platform
}
const (
@@ -207,14 +205,6 @@ func NewDeployment(opt *DeploymentOpt) (d *appsv1.Deployment, c []*corev1.Config
d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = reqMemory
}
if opt.RequestsEphemeralStorage != "" {
reqEphemeralStorage, err := resource.ParseQuantity(opt.RequestsEphemeralStorage)
if err != nil {
return nil, nil, err
}
d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceEphemeralStorage] = reqEphemeralStorage
}
if opt.LimitsCPU != "" {
limCPU, err := resource.ParseQuantity(opt.LimitsCPU)
if err != nil {
@@ -231,14 +221,6 @@ func NewDeployment(opt *DeploymentOpt) (d *appsv1.Deployment, c []*corev1.Config
d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = limMemory
}
if opt.LimitsEphemeralStorage != "" {
limEphemeralStorage, err := resource.ParseQuantity(opt.LimitsEphemeralStorage)
if err != nil {
return nil, nil, err
}
d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceEphemeralStorage] = limEphemeralStorage
}
return
}

View File

@@ -9,7 +9,6 @@ import (
dockerclient "github.com/docker/docker/client"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/tracing/detect"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"k8s.io/client-go/rest"
@@ -53,7 +52,7 @@ type InitConfig struct {
EndpointAddr string
DockerAPI dockerclient.APIClient
KubeClientConfig KubeClientConfig
BuildkitdFlags []string
BuildkitFlags []string
Files map[string][]byte
DriverOpts map[string]string
Auth Auth
@@ -104,13 +103,13 @@ func GetFactory(name string, instanceRequired bool) (Factory, error) {
return nil, errors.Errorf("failed to find driver %q", name)
}
func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, buildkitdFlags []string, files map[string][]byte, do map[string]string, platforms []specs.Platform, contextPathHash string, dialMeta map[string][]string) (*DriverHandle, error) {
func GetDriver(ctx context.Context, name string, f Factory, endpointAddr string, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, flags []string, files map[string][]byte, do map[string]string, platforms []specs.Platform, contextPathHash string, dialMeta map[string][]string) (*DriverHandle, error) {
ic := InitConfig{
EndpointAddr: endpointAddr,
DockerAPI: api,
KubeClientConfig: kcc,
Name: name,
BuildkitdFlags: buildkitdFlags,
BuildkitFlags: flags,
DriverOpts: do,
Auth: auth,
Platforms: platforms,
@@ -157,28 +156,11 @@ type DriverHandle struct {
func (d *DriverHandle) Client(ctx context.Context) (*client.Client, error) {
d.once.Do(func() {
opts, err := d.getClientOptions()
if err != nil {
d.err = err
return
}
d.client, d.err = d.Driver.Client(ctx, opts...)
d.client, d.err = d.Driver.Client(ctx)
})
return d.client, d.err
}
func (d *DriverHandle) getClientOptions() ([]client.ClientOpt, error) {
exp, _, err := detect.Exporter()
if err != nil {
return nil, err
} else if td, ok := exp.(client.TracerDelegate); ok {
return []client.ClientOpt{
client.WithTracerDelegate(td),
}, nil
}
return nil, nil
}
func (d *DriverHandle) HistoryAPISupported(ctx context.Context) bool {
d.historyAPISupportedOnce.Do(func() {
if c, err := d.Client(ctx); err == nil {

View File

@@ -2,19 +2,13 @@ package remote
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"net"
"os"
"strings"
"time"
"github.com/docker/buildx/driver"
util "github.com/docker/buildx/driver/remote/util"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/connhelper"
"github.com/pkg/errors"
"github.com/moby/buildkit/util/tracing/detect"
)
type Driver struct {
@@ -24,7 +18,6 @@ type Driver struct {
// if you add fields, remember to update docs:
// https://github.com/docker/docs/blob/main/content/build/drivers/remote.md
*tlsOpts
defaultLoad bool
}
type tlsOpts struct {
@@ -39,11 +32,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
if err != nil {
return err
}
return progress.Wrap("[internal] waiting for connection", l, func(_ progress.SubLogger) error {
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
return c.Wait(ctx)
})
return c.Wait(ctx)
}
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
@@ -77,71 +66,25 @@ func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return nil
}
func (d *Driver) Client(ctx context.Context, opts ...client.ClientOpt) (*client.Client, error) {
opts = append([]client.ClientOpt{
client.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return d.Dial(ctx)
}),
}, opts...)
return client.New(ctx, "", opts...)
}
func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
opts := []client.ClientOpt{}
func (d *Driver) Dial(ctx context.Context) (net.Conn, error) {
addr := d.InitConfig.EndpointAddr
ch, err := connhelper.GetConnectionHelper(addr)
exp, err := detect.Exporter()
if err != nil {
return nil, err
}
if ch != nil {
return ch.ContextDialer(ctx, addr)
}
network, addr, ok := strings.Cut(addr, "://")
if !ok {
return nil, errors.Errorf("invalid endpoint address: %s", d.InitConfig.EndpointAddr)
}
conn, err := util.DialContext(ctx, network, addr)
if err != nil {
return nil, errors.WithStack(err)
if td, ok := exp.(client.TracerDelegate); ok {
opts = append(opts, client.WithTracerDelegate(td))
}
if d.tlsOpts != nil {
cfg, err := loadTLS(d.tlsOpts)
if err != nil {
return nil, errors.Wrap(err, "error loading tls config")
}
conn = tls.Client(conn, cfg)
}
return conn, nil
}
func loadTLS(opts *tlsOpts) (*tls.Config, error) {
cfg := &tls.Config{
ServerName: opts.serverName,
RootCAs: x509.NewCertPool(),
opts = append(opts, []client.ClientOpt{
client.WithServerConfig(d.tlsOpts.serverName, d.tlsOpts.caCert),
client.WithCredentials(d.tlsOpts.cert, d.tlsOpts.key),
}...)
}
if opts.caCert != "" {
ca, err := os.ReadFile(opts.caCert)
if err != nil {
return nil, errors.Wrap(err, "could not read ca certificate")
}
if ok := cfg.RootCAs.AppendCertsFromPEM(ca); !ok {
return nil, errors.New("failed to append ca certs")
}
}
if opts.cert != "" || opts.key != "" {
cert, err := tls.LoadX509KeyPair(opts.cert, opts.key)
if err != nil {
return nil, errors.Wrap(err, "could not read certificate/key")
}
cfg.Certificates = append(cfg.Certificates, cert)
}
return cfg, nil
return client.New(ctx, d.InitConfig.EndpointAddr, opts...)
}
func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
@@ -150,7 +93,6 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool {
driver.DockerExporter: true,
driver.CacheExport: true,
driver.MultiPlatform: true,
driver.DefaultLoad: d.defaultLoad,
}
}

View File

@@ -4,7 +4,6 @@ import (
"context"
"net/url"
"path/filepath"
"strconv"
"strings"
// import connhelpers for special url schemes
@@ -47,7 +46,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
if len(cfg.Files) > 0 {
return nil, errors.Errorf("setting config file is not supported for remote driver")
}
if len(cfg.BuildkitdFlags) > 0 {
if len(cfg.BuildkitFlags) > 0 {
return nil, errors.Errorf("setting buildkit flags is not supported for remote driver")
}
@@ -81,12 +80,6 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
}
tls.key = v
tlsEnabled = true
case "default-load":
parsed, err := strconv.ParseBool(v)
if err != nil {
return nil, err
}
d.defaultLoad = parsed
default:
return nil, errors.Errorf("invalid driver option %s for remote driver", k)
}

View File

@@ -1,17 +0,0 @@
//go:build !windows
// +build !windows
package remote
import (
"context"
"net"
)
func DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
dialer := &net.Dialer{}
conn, err := dialer.DialContext(ctx, network, addr)
return conn, err
}

View File

@@ -1,23 +0,0 @@
package remote
import (
"context"
"net"
"github.com/Microsoft/go-winio"
)
func DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
var conn net.Conn
var err error
// dial context doesn't support named pipes
if network == "npipe" {
conn, err = winio.DialPipeContext(ctx, addr)
} else {
dialer := &net.Dialer{}
conn, err = dialer.DialContext(ctx, network, addr)
}
return conn, err
}

View File

@@ -12,7 +12,6 @@ var schemes = map[string]struct{}{
"ssh": {},
"docker-container": {},
"kube-pod": {},
"npipe": {},
}
func IsValidEndpoint(ep string) error {

185
go.mod
View File

@@ -1,177 +1,164 @@
module github.com/docker/buildx
go 1.21
go 1.20
require (
github.com/Masterminds/semver/v3 v3.2.1
github.com/Microsoft/go-winio v0.6.1
github.com/aws/aws-sdk-go-v2/config v1.26.6
github.com/compose-spec/compose-go/v2 v2.0.2
github.com/containerd/console v1.0.4
github.com/containerd/containerd v1.7.15
github.com/containerd/continuity v0.4.3
github.com/aws/aws-sdk-go-v2/config v1.18.16
github.com/compose-spec/compose-go v1.20.0
github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.7.7
github.com/containerd/continuity v0.4.2
github.com/containerd/log v0.1.0
github.com/containerd/typeurl/v2 v2.1.1
github.com/creack/pty v1.1.18
github.com/distribution/reference v0.5.0
github.com/docker/cli v26.0.1-0.20240410153731-b6c552212837+incompatible // v26.1.0-dev
github.com/docker/cli v24.0.5+incompatible
github.com/docker/cli-docs-tool v0.7.0
github.com/docker/docker v26.0.0+incompatible
github.com/docker/docker v24.0.5+incompatible
github.com/docker/go-units v0.5.0
github.com/gofrs/flock v0.8.1
github.com/gogo/protobuf v1.3.2
github.com/golang/protobuf v1.5.4
github.com/golang/protobuf v1.5.3
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-cty-funcs v0.0.0-20230405223818-a090f58aa992
github.com/hashicorp/hcl/v2 v2.20.1
github.com/in-toto/in-toto-golang v0.5.0
github.com/moby/buildkit v0.13.0-rc3.0.20240411143343-549891b34890 // v0.14.0-dev
github.com/moby/sys/mountinfo v0.7.1
github.com/hashicorp/hcl/v2 v2.19.1
github.com/moby/buildkit v0.13.0-beta1.0.20231023114302-d5c1d785b042
github.com/moby/sys/mountinfo v0.6.2
github.com/moby/sys/signal v0.7.0
github.com/morikuni/aec v1.0.0
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc5
github.com/pelletier/go-toml v1.9.5
github.com/pkg/errors v0.9.1
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4
github.com/tonistiigi/fsutil v0.0.0-20240301111122-7525a1af2bb5
github.com/zclconf/go-cty v1.14.4
go.opentelemetry.io/otel v1.21.0
go.opentelemetry.io/otel/metric v1.21.0
go.opentelemetry.io/otel/trace v1.21.0
golang.org/x/mod v0.14.0
golang.org/x/sync v0.6.0
golang.org/x/sys v0.18.0
golang.org/x/term v0.18.0
google.golang.org/grpc v1.59.0
github.com/zclconf/go-cty v1.14.1
go.opentelemetry.io/otel v1.14.0
go.opentelemetry.io/otel/trace v1.14.0
golang.org/x/mod v0.11.0
golang.org/x/sync v0.3.0
golang.org/x/term v0.13.0
google.golang.org/grpc v1.53.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.29.2
k8s.io/apimachinery v0.29.2
k8s.io/apiserver v0.29.2
k8s.io/client-go v0.29.2
k8s.io/api v0.26.7
k8s.io/apimachinery v0.26.7
k8s.io/apiserver v0.26.7
k8s.io/client-go v0.26.7
)
require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.1 // indirect
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-cidr v1.0.1 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.3 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.17.6 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.13.16 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.30 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.31 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.24 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.5 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.18.6 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/cenkalti/backoff/v4 v4.2.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/ttrpc v1.2.3 // indirect
github.com/containerd/ttrpc v1.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.0 // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-metrics v0.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/in-toto/in-toto-golang v0.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/klauspost/compress v1.16.3 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opencontainers/runc v1.1.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.17.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/theupdateframework/notary v0.7.0 // indirect
github.com/tonistiigi/fsutil v0.0.0-20230629203738-36ef4d8c0dbb // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20230623042737-f9a4f7ef6531 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.42.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 // indirect
go.opentelemetry.io/otel/exporters/prometheus v0.42.0 // indirect
go.opentelemetry.io/otel/sdk v1.21.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/oauth2 v0.11.0 // indirect
golang.org/x/text v0.14.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.40.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.40.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.14.0 // indirect
go.opentelemetry.io/otel/metric v0.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.14.0 // indirect
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.17.0 // indirect
golang.org/x/tools v0.7.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/protobuf v1.33.0 // indirect
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
k8s.io/klog/v2 v2.90.1 // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

805
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21
ARG GO_VERSION=1.21.6
ARG FORMATS=md,yaml
FROM golang:${GO_VERSION}-alpine AS docsgen

View File

@@ -5,7 +5,7 @@
# Copyright The Buildx Authors.
# Licensed under the Apache License, Version 2.0
ARG GO_VERSION="1.21"
ARG GO_VERSION="1.21.6"
ARG PROTOC_VERSION="3.11.4"
# protoc is dynamically linked to glibc so can't use alpine base

View File

@@ -1,19 +1,14 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21
ARG XX_VERSION=1.3.0
ARG GO_VERSION=1.21.6
ARG GOLANGCI_LINT_VERSION=1.54.2
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine
FROM golang:${GO_VERSION}-alpine
RUN apk add --no-cache git gcc musl-dev
ENV GOFLAGS="-buildvcs=false"
ARG GOLANGCI_LINT_VERSION
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v${GOLANGCI_LINT_VERSION}
COPY --link --from=xx / /
WORKDIR /go/src/github.com/docker/buildx
ARG TARGETPLATFORM
RUN --mount=target=/go/src/github.com/docker/buildx \
--mount=target=/root/.cache,type=cache,id=lint-cache-$TARGETPLATFORM \
xx-go --wrap && \
--mount=target=/root/.cache,type=cache \
golangci-lint run

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
ARG GO_VERSION=1.21
ARG GO_VERSION=1.21.6
ARG MODOUTDATED_VERSION=v0.8.0
FROM golang:${GO_VERSION}-alpine AS base

View File

@@ -10,9 +10,7 @@ set -eu -o pipefail
: "${TEST_REPORT_SUFFIX=}"
: "${TEST_KEEP_CACHE=}"
: "${TEST_DOCKERD=}"
: "${TEST_BUILDKIT_TAG=}"
: "${TEST_BUILDKIT_IMAGE=}"
: "${TEST_BUILDX_EXPERIMENTAL=}"
if [ "$TEST_IMAGE_BUILD" = "1" ]; then
${BUILDX_CMD} bake integration-test --set "*.output=type=docker,name=$TEST_IMAGE_ID"
@@ -26,11 +24,11 @@ gotestArgs="-mod=vendor -coverprofile=/testreports/coverage-report$TEST_REPORT_S
cacheVolume="buildx-test-cache"
if ! docker container inspect "$cacheVolume" >/dev/null 2>/dev/null; then
docker create -v /root/.cache -v /root/.cache/registry -v /root/.cache/undock -v /go/pkg/mod --name "$cacheVolume" alpine
docker create -v /root/.cache -v /root/.cache/registry -v /go/pkg/mod --name "$cacheVolume" alpine
fi
if [ "$TEST_KEEP_CACHE" != "1" ]; then
trap 'docker rm -v $cacheVolume' EXIT
fi
cid=$(docker create --rm -v /tmp $testReportsVol --volumes-from=$cacheVolume -e GITHUB_REF -e TEST_DOCKERD -e TEST_BUILDKIT_IMAGE -e TEST_BUILDKIT_TAG -e TEST_BUILDX_EXPERIMENTAL -e SKIP_INTEGRATION_TESTS -e GOTESTSUM_FORMAT ${BUILDKIT_INTEGRATION_SNAPSHOTTER:+"-eBUILDKIT_INTEGRATION_SNAPSHOTTER"} -e BUILDKIT_REGISTRY_MIRROR_DIR=/root/.cache/registry --privileged $TEST_IMAGE_ID gotestsum $gotestsumArgs --packages="${TESTPKGS:-./...}" -- $gotestArgs ${TESTFLAGS:--v})
cid=$(docker create --rm -v /tmp $testReportsVol --volumes-from=$cacheVolume -e GITHUB_REF -e TEST_DOCKERD -e TEST_BUILDKIT_IMAGE -e SKIP_INTEGRATION_TESTS -e GOTESTSUM_FORMAT ${BUILDKIT_INTEGRATION_SNAPSHOTTER:+"-eBUILDKIT_INTEGRATION_SNAPSHOTTER"} -e BUILDKIT_REGISTRY_MIRROR_DIR=/root/.cache/registry --privileged $TEST_IMAGE_ID gotestsum $gotestsumArgs --packages="${TESTPKGS:-./...}" -- $gotestArgs ${TESTFLAGS:--v})
docker start -a -i $cid

View File

@@ -49,7 +49,7 @@ if [ "$DRIVER" != "docker" ]; then
for platform in ${PLATFORMS//,/ }; do
createFlags=""
if [ -f "$BUILDKIT_CFG" ]; then
createFlags="$createFlags --buildkitd-config=${BUILDKIT_CFG}"
createFlags="$createFlags --config=${BUILDKIT_CFG}"
fi
if [ "$firstNode" = "0" ]; then
createFlags="$createFlags --append"
@@ -71,7 +71,7 @@ if [ "$DRIVER" != "docker" ]; then
else
createFlags=""
if [ -f "$BUILDKIT_CFG" ]; then
createFlags="$createFlags --buildkitd-config=${BUILDKIT_CFG}"
createFlags="$createFlags --config=${BUILDKIT_CFG}"
fi
buildxCmd create ${createFlags} \
--bootstrap \

View File

@@ -24,11 +24,11 @@ type NodeGroup struct {
}
type Node struct {
Name string
Endpoint string
Platforms []specs.Platform
DriverOpts map[string]string
BuildkitdFlags []string `json:"Flags"` // keep the field name for backward compatibility
Name string
Endpoint string
Platforms []specs.Platform
Flags []string
DriverOpts map[string]string
Files map[string][]byte
}
@@ -48,7 +48,7 @@ func (ng *NodeGroup) Leave(name string) error {
return nil
}
func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpointsSet bool, actionAppend bool, buildkitdFlags []string, buildkitdConfigFile string, do map[string]string) error {
func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpointsSet bool, actionAppend bool, flags []string, configFile string, do map[string]string) error {
if ng.Dynamic {
return errors.New("dynamic node group does not support Update")
}
@@ -66,8 +66,8 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints
}
var files map[string][]byte
if buildkitdConfigFile != "" {
files, err = confutil.LoadConfigFiles(buildkitdConfigFile)
if configFile != "" {
files, err = confutil.LoadConfigFiles(configFile)
if err != nil {
return err
}
@@ -83,15 +83,15 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints
if len(platforms) > 0 {
n.Platforms = pp
}
if buildkitdFlags != nil {
n.BuildkitdFlags = buildkitdFlags
if flags != nil {
n.Flags = flags
needsRestart = true
}
if do != nil {
n.DriverOpts = do
needsRestart = true
}
if buildkitdConfigFile != "" {
if configFile != "" {
for k, v := range files {
n.Files[k] = v
}
@@ -115,12 +115,12 @@ func (ng *NodeGroup) Update(name, endpoint string, platforms []string, endpoints
}
n := Node{
Name: name,
Endpoint: endpoint,
Platforms: pp,
DriverOpts: do,
BuildkitdFlags: buildkitdFlags,
Files: files,
Name: name,
Endpoint: endpoint,
Platforms: pp,
Flags: flags,
DriverOpts: do,
Files: files,
}
ng.Nodes = append(ng.Nodes, n)
@@ -143,8 +143,8 @@ func (ng *NodeGroup) Copy() *NodeGroup {
func (n *Node) Copy() *Node {
platforms := []specs.Platform{}
copy(platforms, n.Platforms)
buildkitdFlags := []string{}
copy(buildkitdFlags, n.BuildkitdFlags)
flags := []string{}
copy(flags, n.Flags)
driverOpts := map[string]string{}
for k, v := range n.DriverOpts {
driverOpts[k] = v
@@ -156,12 +156,12 @@ func (n *Node) Copy() *Node {
files[k] = vv
}
return &Node{
Name: n.Name,
Endpoint: n.Endpoint,
Platforms: platforms,
BuildkitdFlags: buildkitdFlags,
DriverOpts: driverOpts,
Files: files,
Name: n.Name,
Endpoint: n.Endpoint,
Platforms: platforms,
Flags: flags,
DriverOpts: driverOpts,
Files: files,
}
}

View File

@@ -28,8 +28,8 @@ func TestNodeGroupUpdate(t *testing.T) {
require.Equal(t, []string{"linux/arm64"}, platformutil.Format(ng.Nodes[1].Platforms))
require.Equal(t, "foo2", ng.Nodes[0].Endpoint)
require.Equal(t, []string{"--debug"}, ng.Nodes[0].BuildkitdFlags)
require.Equal(t, []string(nil), ng.Nodes[1].BuildkitdFlags)
require.Equal(t, []string{"--debug"}, ng.Nodes[0].Flags)
require.Equal(t, []string(nil), ng.Nodes[1].Flags)
// duplicate endpoint
err = ng.Update("foo1", "foo2", nil, true, false, nil, "", nil)

View File

@@ -1,20 +1,13 @@
package tests
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/containerd/continuity/fs/fstest"
"github.com/docker/buildx/util/gitutil"
"github.com/moby/buildkit/identity"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/util/contentutil"
"github.com/moby/buildkit/util/testutil"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
@@ -29,10 +22,8 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
testBakeLocal,
testBakeLocalMulti,
testBakeRemote,
testBakeRemoteAuth,
testBakeRemoteCmdContext,
testBakeRemoteLocalOverride,
testBakeLocalCwdOverride,
testBakeRemoteCmdContextOverride,
testBakeRemoteContextSubdir,
testBakeRemoteCmdContextEscapeRoot,
@@ -40,11 +31,6 @@ var bakeTests = []func(t *testing.T, sb integration.Sandbox){
testBakeRemoteDockerfileCwd,
testBakeRemoteLocalContextRemoteDockerfile,
testBakeEmpty,
testBakeShmSize,
testBakeUlimits,
testBakeMetadata,
testBakeMultiExporters,
testBakeLoadPush,
}
func testBakeLocal(t *testing.T, sb integration.Sandbox) {
@@ -67,7 +53,7 @@ target "default" {
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--progress=plain", "--set", "*.output=type=local,dest="+dirDest))
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
require.NoError(t, err, out)
require.Contains(t, string(out), `#1 [internal] load local bake definitions`)
require.Contains(t, string(out), `#1 reading docker-bake.hcl`)
@@ -145,41 +131,6 @@ EOT
require.FileExists(t, filepath.Join(dirDest, "foo"))
}
func testBakeRemoteAuth(t *testing.T, sb integration.Sandbox) {
bakefile := []byte(`
target "default" {
dockerfile-inline = <<EOT
FROM scratch
COPY foo /foo
EOT
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
git, err := gitutil.New(gitutil.WithWorkingDir(dir))
require.NoError(t, err)
gitutil.GitInit(git, t)
gitutil.GitAdd(git, t, "docker-bake.hcl", "foo")
gitutil.GitCommit(git, t, "initial commit")
token := identity.NewID()
addr := gitutil.GitServeHTTP(git, t, gitutil.WithAccessToken(token))
out, err := bakeCmd(sb, withDir(dir),
withEnv("BUILDX_BAKE_GIT_AUTH_TOKEN="+token),
withArgs(addr, "--set", "*.output=type=local,dest="+dirDest),
)
require.NoError(t, err, out)
require.FileExists(t, filepath.Join(dirDest, "foo"))
}
func testBakeRemoteLocalOverride(t *testing.T, sb integration.Sandbox) {
remoteBakefile := []byte(`
target "default" {
@@ -222,41 +173,6 @@ EOT
require.FileExists(t, filepath.Join(dirDest, "bar"))
}
func testBakeLocalCwdOverride(t *testing.T, sb integration.Sandbox) {
bakeFile := []byte(`
target "default" {
dockerfile-inline = <<EOT
FROM scratch
COPY foo /foo
EOT
}
`)
cwdBakefile := []byte(`
target "default" {
dockerfile-inline = <<EOT
FROM scratch
COPY bar /bar
EOT
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakeFile, 0600),
fstest.CreateFile("docker-bake-cwd.hcl", cwdBakefile, 0600),
fstest.CreateFile("bar", []byte("bar"), 0600),
)
dirDest := t.TempDir()
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--file", "docker-bake.hcl", "--file", "cwd://docker-bake-cwd.hcl", "--progress=plain", "--set", "*.output=type=local,dest="+dirDest))
dt, err := cmd.CombinedOutput()
require.NoError(t, err, string(dt))
require.Contains(t, string(dt), `#1 [internal] load local bake definitions`)
require.Contains(t, string(dt), `#1 reading docker-bake.hcl`)
require.Contains(t, string(dt), `#1 reading docker-bake-cwd.hcl`)
require.FileExists(t, filepath.Join(dirDest, "bar"))
}
func testBakeRemoteCmdContext(t *testing.T, sb integration.Sandbox) {
bakefile := []byte(`
target "default" {
@@ -568,256 +484,3 @@ func testBakeEmpty(t *testing.T, sb integration.Sandbox) {
require.Error(t, err, out)
require.Contains(t, out, "couldn't find a bake definition")
}
func testBakeShmSize(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM busybox AS build
RUN mount | grep /dev/shm > /shmsize
FROM scratch
COPY --from=build /shmsize /
`)
bakefile := []byte(`
target "default" {
shm-size = "128m"
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
dirDest := t.TempDir()
out, err := bakeCmd(
sb,
withDir(dir),
withArgs("--set", "*.output=type=local,dest="+dirDest),
)
require.NoError(t, err, out)
dt, err := os.ReadFile(filepath.Join(dirDest, "shmsize"))
require.NoError(t, err)
require.Contains(t, string(dt), `size=131072k`)
}
func testBakeUlimits(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM busybox AS build
RUN ulimit -n > first > /ulimit
FROM scratch
COPY --from=build /ulimit /
`)
bakefile := []byte(`
target "default" {
ulimits = ["nofile=1024:1024"]
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
dirDest := t.TempDir()
out, err := bakeCmd(
sb,
withDir(dir),
withArgs("--set", "*.output=type=local,dest="+dirDest),
)
require.NoError(t, err, out)
dt, err := os.ReadFile(filepath.Join(dirDest, "ulimit"))
require.NoError(t, err)
require.Contains(t, string(dt), `1024`)
}
func testBakeMetadata(t *testing.T, sb integration.Sandbox) {
t.Run("max", func(t *testing.T) {
bakeMetadata(t, sb, "max")
})
t.Run("min", func(t *testing.T) {
bakeMetadata(t, sb, "min")
})
t.Run("disabled", func(t *testing.T) {
bakeMetadata(t, sb, "disabled")
})
}
func bakeMetadata(t *testing.T, sb integration.Sandbox, metadataMode string) {
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
dirDest := t.TempDir()
outFlag := "default.output=type=docker"
if sb.DockerAddress() == "" {
// there is no Docker atm to load the image
outFlag += ",dest=" + dirDest + "/image.tar"
}
cmd := buildxCmd(
sb,
withDir(dir),
withArgs("bake", "--metadata-file", filepath.Join(dirDest, "md.json"), "--set", outFlag),
withEnv("BUILDX_METADATA_PROVENANCE="+metadataMode),
)
out, err := cmd.CombinedOutput()
require.NoError(t, err, out)
dt, err := os.ReadFile(filepath.Join(dirDest, "md.json"))
require.NoError(t, err)
type mdT struct {
Default struct {
BuildRef string `json:"buildx.build.ref"`
BuildProvenance map[string]interface{} `json:"buildx.build.provenance"`
} `json:"default"`
}
var md mdT
err = json.Unmarshal(dt, &md)
require.NoError(t, err)
require.NotEmpty(t, md.Default.BuildRef)
if metadataMode == "disabled" {
require.Empty(t, md.Default.BuildProvenance)
return
}
require.NotEmpty(t, md.Default.BuildProvenance)
dtprv, err := json.Marshal(md.Default.BuildProvenance)
require.NoError(t, err)
var prv provenancetypes.ProvenancePredicate
require.NoError(t, json.Unmarshal(dtprv, &prv))
require.Equal(t, provenancetypes.BuildKitBuildType, prv.BuildType)
}
func testBakeMultiExporters(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
skipNoCompatBuildKit(t, sb, ">= 0.13.0-0", "multi exporters")
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
targetReg := registry + "/buildx/registry:latest"
targetStore := "buildx:local-" + identity.NewID()
t.Cleanup(func() {
cmd := dockerCmd(sb, withArgs("image", "rm", targetStore))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
})
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
outputs := []string{
"--set", fmt.Sprintf("*.output=type=image,name=%s,push=true", targetReg),
"--set", fmt.Sprintf("*.output=type=docker,name=%s", targetStore),
"--set", fmt.Sprintf("*.output=type=oci,dest=%s/result", dir),
}
cmd := buildxCmd(sb, withDir(dir), withArgs("bake"), withArgs(outputs...))
outb, err := cmd.CombinedOutput()
require.NoError(t, err, string(outb))
// test registry
desc, provider, err := contentutil.ProviderFromRef(targetReg)
require.NoError(t, err)
_, err = testutil.ReadImages(sb.Context(), provider, desc)
require.NoError(t, err)
// test docker store
cmd = dockerCmd(sb, withArgs("image", "inspect", targetStore))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
// test oci
_, err = os.ReadFile(fmt.Sprintf("%s/result", dir))
require.NoError(t, err)
// TODO: test metadata file when supported by multi exporters https://github.com/docker/buildx/issues/2181
}
func testBakeLoadPush(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
skipNoCompatBuildKit(t, sb, ">= 0.13.0-0", "multi exporters")
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
target := registry + "/buildx/registry:" + identity.NewID()
t.Cleanup(func() {
cmd := dockerCmd(sb, withArgs("image", "rm", target))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
})
dockerfile := []byte(`
FROM scratch
COPY foo /foo
`)
bakefile := []byte(`
target "default" {
}
`)
dir := tmpdir(
t,
fstest.CreateFile("docker-bake.hcl", bakefile, 0600),
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("bake", "--push", "--load", fmt.Sprintf("--set=*.tags=%s", target)))
outb, err := cmd.CombinedOutput()
require.NoError(t, err, string(outb))
// test registry
desc, provider, err := contentutil.ProviderFromRef(target)
require.NoError(t, err)
_, err = testutil.ReadImages(sb.Context(), provider, desc)
require.NoError(t, err)
// test docker store
cmd = dockerCmd(sb, withArgs("image", "inspect", target))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
// TODO: test metadata file when supported by multi exporters https://github.com/docker/buildx/issues/2181
}

View File

@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"path"
"path/filepath"
@@ -16,9 +15,6 @@ import (
"github.com/containerd/containerd/platforms"
"github.com/containerd/continuity/fs/fstest"
"github.com/creack/pty"
"github.com/moby/buildkit/identity"
provenancetypes "github.com/moby/buildkit/solver/llbsolver/provenance/types"
"github.com/moby/buildkit/util/appdefaults"
"github.com/moby/buildkit/util/contentutil"
"github.com/moby/buildkit/util/testutil"
"github.com/moby/buildkit/util/testutil/integration"
@@ -37,7 +33,6 @@ func buildCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
var buildTests = []func(t *testing.T, sb integration.Sandbox){
testBuild,
testBuildStdin,
testImageIDOutput,
testBuildLocalExport,
testBuildRegistryExport,
@@ -53,14 +48,6 @@ var buildTests = []func(t *testing.T, sb integration.Sandbox){
testBuildOCIExportNotSupported,
testBuildMultiPlatformNotSupported,
testDockerHostGateway,
testBuildNetworkModeBridge,
testBuildShmSize,
testBuildUlimit,
testBuildMetadata,
testBuildMultiExporters,
testBuildLoadPush,
testBuildSecret,
testBuildDefaultLoad,
}
func testBuild(t *testing.T, sb integration.Sandbox) {
@@ -69,26 +56,6 @@ func testBuild(t *testing.T, sb integration.Sandbox) {
require.NoError(t, err, string(out))
}
func testBuildStdin(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM busybox:latest AS base
COPY foo /etc/foo
RUN cp /etc/foo /etc/bar
FROM scratch
COPY --from=base /etc/bar /bar
`)
dir := tmpdir(
t,
fstest.CreateFile("foo", []byte("foo"), 0600),
)
cmd := buildxCmd(sb, withDir(dir), withArgs("build", "--progress=quiet", "-f-", dir))
cmd.Stdin = bytes.NewReader(dockerfile)
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
}
func testBuildLocalExport(t *testing.T, sb integration.Sandbox) {
dir := createTestProject(t)
out, err := buildCmd(sb, withArgs(fmt.Sprintf("--output=type=local,dest=%s/result", dir), dir))
@@ -149,13 +116,9 @@ func testBuildRegistryExportAttestations(t *testing.T, sb integration.Sandbox) {
target := registry + "/buildx/registry:latest"
out, err := buildCmd(sb, withArgs(fmt.Sprintf("--output=type=image,name=%s,push=true", target), "--provenance=true", dir))
if isMobyWorker(sb) {
if sb.Name() == "docker" {
require.Error(t, err)
require.Contains(t, out, "Attestation is not supported")
return
} else if !isMobyContainerdSnapWorker(sb) && !matchesBuildKitVersion(t, sb, ">= 0.11.0-0") {
require.Error(t, err)
require.Contains(t, out, "Attestations are not supported by the current BuildKit daemon")
require.Contains(t, out, "attestations are not supported")
return
}
require.NoError(t, err, string(out))
@@ -228,7 +191,7 @@ func testImageIDOutput(t *testing.T, sb integration.Sandbox) {
func testBuildMobyFromLocalImage(t *testing.T, sb integration.Sandbox) {
if !isDockerWorker(sb) {
t.Skip("only testing with docker workers")
t.Skip("skipping test for non-docker workers")
}
// pull image
@@ -275,7 +238,6 @@ RUN busybox | head -1 | grep v1.36.1
}
func testBuildDetailsLink(t *testing.T, sb integration.Sandbox) {
skipNoCompatBuildKit(t, sb, ">= 0.11.0-0", "build details link")
buildDetailsPattern := regexp.MustCompile(`(?m)^View build details: docker-desktop://dashboard/build/[^/]+/[^/]+/[^/]+\n$`)
// build simple dockerfile
@@ -307,11 +269,6 @@ RUN echo foo > /bar`)
require.NoError(t, err, string(out))
require.True(t, buildDetailsPattern.MatchString(string(out)), fmt.Sprintf("expected build details link in output, got %q", out))
if isExperimental() {
// FIXME: https://github.com/docker/buildx/issues/2382
t.Skip("build details link not displayed in experimental mode when build fails: https://github.com/docker/buildx/issues/2382")
}
// build erroneous dockerfile
dockerfile = []byte(`FROM busybox:latest
RUN exit 1`)
@@ -322,9 +279,26 @@ RUN exit 1`)
require.True(t, buildDetailsPattern.MatchString(string(out)), fmt.Sprintf("expected build details link in output, got %q", out))
}
func createTestProject(t *testing.T) string {
dockerfile := []byte(`
FROM busybox:latest AS base
COPY foo /etc/foo
RUN cp /etc/foo /etc/bar
FROM scratch
COPY --from=base /etc/bar /bar
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
return dir
}
func testBuildProgress(t *testing.T, sb integration.Sandbox) {
dir := createTestProject(t)
sbDriver, _ := driverName(sb.Name())
driver, _, _ := strings.Cut(sb.Name(), "+")
name := sb.Address()
// progress=tty
@@ -335,7 +309,7 @@ func testBuildProgress(t *testing.T, sb integration.Sandbox) {
io.Copy(buf, f)
ttyOutput := buf.String()
require.Contains(t, ttyOutput, "[+] Building")
require.Contains(t, ttyOutput, fmt.Sprintf("%s:%s", sbDriver, name))
require.Contains(t, ttyOutput, fmt.Sprintf("%s:%s", driver, name))
require.Contains(t, ttyOutput, "=> [internal] load build definition from Dockerfile")
require.Contains(t, ttyOutput, "=> [base 1/3] FROM docker.io/library/busybox:latest")
@@ -343,16 +317,15 @@ func testBuildProgress(t *testing.T, sb integration.Sandbox) {
cmd = buildxCmd(sb, withArgs("build", "--progress=plain", "--output=type=cacheonly", dir))
plainOutput, err := cmd.CombinedOutput()
require.NoError(t, err)
require.Contains(t, string(plainOutput), fmt.Sprintf(`#0 building with "%s" instance using %s driver`, name, sbDriver))
require.Contains(t, string(plainOutput), fmt.Sprintf(`#0 building with "%s" instance using %s driver`, name, driver))
require.Contains(t, string(plainOutput), "[internal] load build definition from Dockerfile")
require.Contains(t, string(plainOutput), "[base 1/3] FROM docker.io/library/busybox:latest")
}
func testBuildAnnotations(t *testing.T, sb integration.Sandbox) {
if isMobyWorker(sb) {
if sb.Name() == "docker" {
t.Skip("annotations not supported on docker worker")
}
skipNoCompatBuildKit(t, sb, ">= 0.11.0-0", "annotations")
dir := createTestProject(t)
@@ -409,8 +382,8 @@ func testBuildLabelNoKey(t *testing.T, sb integration.Sandbox) {
}
func testBuildCacheExportNotSupported(t *testing.T, sb integration.Sandbox) {
if !isMobyWorker(sb) {
t.Skip("only testing with docker worker")
if sb.Name() != "docker" {
t.Skip("skipping test for non-docker workers")
}
dir := createTestProject(t)
@@ -421,8 +394,8 @@ func testBuildCacheExportNotSupported(t *testing.T, sb integration.Sandbox) {
}
func testBuildOCIExportNotSupported(t *testing.T, sb integration.Sandbox) {
if !isMobyWorker(sb) {
t.Skip("only testing with docker worker")
if sb.Name() != "docker" {
t.Skip("skipping test for non-docker workers")
}
dir := createTestProject(t)
@@ -433,8 +406,8 @@ func testBuildOCIExportNotSupported(t *testing.T, sb integration.Sandbox) {
}
func testBuildMultiPlatformNotSupported(t *testing.T, sb integration.Sandbox) {
if !isMobyWorker(sb) {
t.Skip("only testing with docker worker")
if sb.Name() != "docker" {
t.Skip("skipping test for non-docker workers")
}
dir := createTestProject(t)
@@ -459,353 +432,3 @@ RUN ping -c 1 buildx.host-gateway-ip.local
require.NoError(t, err, string(out))
}
}
func testBuildNetworkModeBridge(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
skipNoCompatBuildKit(t, sb, ">= 0.13.0-0", "network bridge")
var builderName string
t.Cleanup(func() {
if builderName == "" {
return
}
out, err := rmCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
})
out, err := createCmd(sb, withArgs(
"--driver", "docker-container",
"--buildkitd-flags=--oci-worker-net=bridge --allow-insecure-entitlement=network.host",
))
require.NoError(t, err, out)
builderName = strings.TrimSpace(out)
dockerfile := []byte(`
FROM busybox AS build
RUN ip a show eth0 | awk '/inet / {split($2, a, "/"); print a[1]}' > /ip-bridge.txt
RUN --network=host ip a show eth0 | awk '/inet / {split($2, a, "/"); print a[1]}' > /ip-host.txt
FROM scratch
COPY --from=build /ip*.txt /`)
dir := tmpdir(t, fstest.CreateFile("Dockerfile", dockerfile, 0600))
cmd := buildxCmd(sb, withArgs("build", "--allow=network.host", fmt.Sprintf("--output=type=local,dest=%s", dir), dir))
cmd.Env = append(cmd.Env, "BUILDX_BUILDER="+builderName)
outb, err := cmd.CombinedOutput()
require.NoError(t, err, string(outb))
dt, err := os.ReadFile(filepath.Join(dir, "ip-bridge.txt"))
require.NoError(t, err)
ipBridge := net.ParseIP(strings.TrimSpace(string(dt)))
require.NotNil(t, ipBridge)
_, subnet, err := net.ParseCIDR(appdefaults.BridgeSubnet)
require.NoError(t, err)
require.True(t, subnet.Contains(ipBridge))
dt, err = os.ReadFile(filepath.Join(dir, "ip-host.txt"))
require.NoError(t, err)
ip := net.ParseIP(strings.TrimSpace(string(dt)))
require.NotNil(t, ip)
require.NotEqual(t, ip, ipBridge)
}
func testBuildShmSize(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM busybox AS build
RUN mount | grep /dev/shm > /shmsize
FROM scratch
COPY --from=build /shmsize /
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
cmd := buildxCmd(sb, withArgs("build", "--shm-size=128m", fmt.Sprintf("--output=type=local,dest=%s", dir), dir))
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
dt, err := os.ReadFile(filepath.Join(dir, "shmsize"))
require.NoError(t, err)
require.Contains(t, string(dt), `size=131072k`)
}
func testBuildUlimit(t *testing.T, sb integration.Sandbox) {
dockerfile := []byte(`
FROM busybox AS build
RUN ulimit -n > first > /ulimit
FROM scratch
COPY --from=build /ulimit /
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
)
cmd := buildxCmd(sb, withArgs("build", "--ulimit=nofile=1024:1024", fmt.Sprintf("--output=type=local,dest=%s", dir), dir))
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
dt, err := os.ReadFile(filepath.Join(dir, "ulimit"))
require.NoError(t, err)
require.Contains(t, string(dt), `1024`)
}
func testBuildMetadata(t *testing.T, sb integration.Sandbox) {
t.Run("max", func(t *testing.T) {
buildMetadata(t, sb, "max")
})
t.Run("min", func(t *testing.T) {
buildMetadata(t, sb, "min")
})
t.Run("disabled", func(t *testing.T) {
buildMetadata(t, sb, "disabled")
})
}
func buildMetadata(t *testing.T, sb integration.Sandbox, metadataMode string) {
dir := createTestProject(t)
dirDest := t.TempDir()
outFlag := "--output=type=docker"
if sb.DockerAddress() == "" {
// there is no Docker atm to load the image
outFlag += ",dest=" + dirDest + "/image.tar"
}
cmd := buildxCmd(
sb,
withArgs("build", outFlag, "--metadata-file", filepath.Join(dirDest, "md.json"), dir),
withEnv("BUILDX_METADATA_PROVENANCE="+metadataMode),
)
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
dt, err := os.ReadFile(filepath.Join(dirDest, "md.json"))
require.NoError(t, err)
type mdT struct {
BuildRef string `json:"buildx.build.ref"`
BuildProvenance map[string]interface{} `json:"buildx.build.provenance"`
}
var md mdT
err = json.Unmarshal(dt, &md)
require.NoError(t, err)
require.NotEmpty(t, md.BuildRef)
if metadataMode == "disabled" {
require.Empty(t, md.BuildProvenance)
return
}
require.NotEmpty(t, md.BuildProvenance)
dtprv, err := json.Marshal(md.BuildProvenance)
require.NoError(t, err)
var prv provenancetypes.ProvenancePredicate
require.NoError(t, json.Unmarshal(dtprv, &prv))
require.Equal(t, provenancetypes.BuildKitBuildType, prv.BuildType)
}
func testBuildMultiExporters(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
skipNoCompatBuildKit(t, sb, ">= 0.13.0-0", "multi exporters")
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
targetReg := registry + "/buildx/registry:latest"
targetStore := "buildx:local-" + identity.NewID()
t.Cleanup(func() {
cmd := dockerCmd(sb, withArgs("image", "rm", targetStore))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
})
dir := createTestProject(t)
outputs := []string{
"--output", fmt.Sprintf("type=image,name=%s,push=true", targetReg),
"--output", fmt.Sprintf("type=docker,name=%s", targetStore),
"--output", fmt.Sprintf("type=oci,dest=%s/result", dir),
}
cmd := buildxCmd(sb, withArgs("build"), withArgs(outputs...), withArgs(dir))
outb, err := cmd.CombinedOutput()
require.NoError(t, err, string(outb))
// test registry
desc, provider, err := contentutil.ProviderFromRef(targetReg)
require.NoError(t, err)
_, err = testutil.ReadImages(sb.Context(), provider, desc)
require.NoError(t, err)
// test docker store
cmd = dockerCmd(sb, withArgs("image", "inspect", targetStore))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
// test oci
_, err = os.ReadFile(fmt.Sprintf("%s/result", dir))
require.NoError(t, err)
// TODO: test metadata file when supported by multi exporters https://github.com/docker/buildx/issues/2181
}
func testBuildLoadPush(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
skipNoCompatBuildKit(t, sb, ">= 0.13.0-0", "multi exporters")
registry, err := sb.NewRegistry()
if errors.Is(err, integration.ErrRequirements) {
t.Skip(err.Error())
}
require.NoError(t, err)
target := registry + "/buildx/registry:" + identity.NewID()
t.Cleanup(func() {
cmd := dockerCmd(sb, withArgs("image", "rm", target))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
})
dir := createTestProject(t)
cmd := buildxCmd(sb, withArgs(
"build", "--push", "--load",
fmt.Sprintf("-t=%s", target),
dir,
))
outb, err := cmd.CombinedOutput()
require.NoError(t, err, string(outb))
// test registry
desc, provider, err := contentutil.ProviderFromRef(target)
require.NoError(t, err)
_, err = testutil.ReadImages(sb.Context(), provider, desc)
require.NoError(t, err)
// test docker store
cmd = dockerCmd(sb, withArgs("image", "inspect", target))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
// TODO: test metadata file when supported by multi exporters https://github.com/docker/buildx/issues/2181
}
func testBuildSecret(t *testing.T, sb integration.Sandbox) {
token := "abcd1234"
dockerfile := []byte(`
FROM busybox AS build
RUN --mount=type=secret,id=token cat /run/secrets/token | tee /token
FROM scratch
COPY --from=build /token /
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("tokenfile", []byte(token), 0600),
)
t.Run("env", func(t *testing.T) {
t.Cleanup(func() {
_ = os.Remove(filepath.Join(dir, "token"))
})
cmd := buildxCmd(sb, withEnv("TOKEN="+token), withArgs("build", "--secret=id=token,env=TOKEN", fmt.Sprintf("--output=type=local,dest=%s", dir), dir))
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
dt, err := os.ReadFile(filepath.Join(dir, "token"))
require.NoError(t, err)
require.Equal(t, token, string(dt))
})
t.Run("file", func(t *testing.T) {
t.Cleanup(func() {
_ = os.Remove(filepath.Join(dir, "token"))
})
cmd := buildxCmd(sb, withArgs("build", "--secret=id=token,src="+path.Join(dir, "tokenfile"), fmt.Sprintf("--output=type=local,dest=%s", dir), dir))
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
dt, err := os.ReadFile(filepath.Join(dir, "token"))
require.NoError(t, err)
require.Equal(t, token, string(dt))
})
}
func testBuildDefaultLoad(t *testing.T, sb integration.Sandbox) {
if !isDockerWorker(sb) {
t.Skip("only testing with docker workers")
}
tag := "buildx/build:" + identity.NewID()
var builderName string
t.Cleanup(func() {
if builderName == "" {
return
}
cmd := dockerCmd(sb, withArgs("image", "rm", tag))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
out, err := rmCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
})
out, err := createCmd(sb, withArgs(
"--driver", "docker-container",
"--driver-opt", "default-load=true",
))
require.NoError(t, err, out)
builderName = strings.TrimSpace(out)
dir := createTestProject(t)
cmd := buildxCmd(sb, withArgs(
"build",
fmt.Sprintf("-t=%s", tag),
dir,
))
cmd.Env = append(cmd.Env, "BUILDX_BUILDER="+builderName)
outb, err := cmd.CombinedOutput()
require.NoError(t, err, string(outb))
cmd = dockerCmd(sb, withArgs("image", "inspect", tag))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
}
func createTestProject(t *testing.T) string {
dockerfile := []byte(`
FROM busybox:latest AS base
COPY foo /etc/foo
RUN cp /etc/foo /etc/bar
FROM scratch
COPY --from=base /etc/bar /bar
`)
dir := tmpdir(
t,
fstest.CreateFile("Dockerfile", dockerfile, 0600),
fstest.CreateFile("foo", []byte("foo"), 0600),
)
return dir
}

View File

@@ -1,13 +1,9 @@
package tests
import (
"fmt"
"os"
"strings"
"testing"
"github.com/docker/buildx/driver"
"github.com/moby/buildkit/identity"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require"
)
@@ -21,13 +17,11 @@ func createCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
var createTests = []func(t *testing.T, sb integration.Sandbox){
testCreateMemoryLimit,
testCreateRestartAlways,
testCreateRemoteContainer,
}
func testCreateMemoryLimit(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
if sb.Name() != "docker-container" {
t.Skip("only testing for docker-container driver")
}
var builderName string
@@ -43,68 +37,3 @@ func testCreateMemoryLimit(t *testing.T, sb integration.Sandbox) {
require.NoError(t, err, out)
builderName = strings.TrimSpace(out)
}
func testCreateRestartAlways(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
var builderName string
t.Cleanup(func() {
if builderName == "" {
return
}
out, err := rmCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
})
out, err := createCmd(sb, withArgs("--driver", "docker-container", "--driver-opt", "restart-policy=always"))
require.NoError(t, err, out)
builderName = strings.TrimSpace(out)
}
func testCreateRemoteContainer(t *testing.T, sb integration.Sandbox) {
if !isDockerWorker(sb) {
t.Skip("only testing with docker workers")
}
ctnBuilderName := "ctn-builder-" + identity.NewID()
remoteBuilderName := "remote-builder-" + identity.NewID()
var hasCtnBuilder, hasRemoteBuilder bool
t.Cleanup(func() {
if hasCtnBuilder {
out, err := rmCmd(sb, withArgs(ctnBuilderName))
require.NoError(t, err, out)
}
if hasRemoteBuilder {
out, err := rmCmd(sb, withArgs(remoteBuilderName))
require.NoError(t, err, out)
}
})
out, err := createCmd(sb, withArgs("--driver", "docker-container", "--name", ctnBuilderName))
require.NoError(t, err, out)
hasCtnBuilder = true
out, err = inspectCmd(sb, withArgs("--bootstrap", ctnBuilderName))
require.NoError(t, err, out)
cmd := dockerCmd(sb, withArgs("container", "inspect", fmt.Sprintf("%s0", driver.BuilderName(ctnBuilderName))))
cmd.Stderr = os.Stderr
require.NoError(t, cmd.Run())
out, err = createCmd(sb, withArgs("--driver", "remote", "--name", remoteBuilderName, fmt.Sprintf("docker-container://%s0", driver.BuilderName(ctnBuilderName))))
require.NoError(t, err, out)
hasRemoteBuilder = true
out, err = inspectCmd(sb, withArgs(remoteBuilderName))
require.NoError(t, err, out)
for _, line := range strings.Split(out, "\n") {
if v, ok := strings.CutPrefix(line, "Status:"); ok {
require.Equal(t, strings.TrimSpace(v), "running")
return
}
}
require.Fail(t, "remote builder is not running")
}

View File

@@ -1,125 +0,0 @@
package tests
import (
"bytes"
"context"
"net"
"os"
"os/exec"
"path/filepath"
"testing"
"time"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var dialstdioTests = []func(t *testing.T, sb integration.Sandbox){
testDialStdio,
}
func testDialStdio(t *testing.T, sb integration.Sandbox) {
do := func(t *testing.T, pipe func(t *testing.T, cmd *exec.Cmd) net.Conn) {
errBuf := bytes.NewBuffer(nil)
defer func() {
if t.Failed() {
t.Log(errBuf.String())
}
}()
var cmd *exec.Cmd
c, err := client.New(sb.Context(), "", client.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
cmd = buildxCmd(sb, withArgs("dial-stdio", "--progress", "auto"))
conn := pipe(t, cmd)
cmd.Stderr = errBuf
if err := cmd.Start(); err != nil {
return nil, errors.Wrap(err, errBuf.String())
}
return conn, nil
}))
require.NoError(t, err)
defer func() {
c.Close()
// Since the client is closed (and as such the connection shutdown), the buildx command should exit cleanly.
chErr := make(chan error, 1)
go func() {
chErr <- cmd.Wait()
}()
select {
case <-time.After(10 * time.Second):
t.Error("timeout waiting for buildx command to exit")
case <-chErr:
assert.NoError(t, err)
}
}()
skipNoCompatBuildKit(t, sb, ">= 0.11.0-0", "unknown method Info for service moby.buildkit.v1.Control")
_, err = c.Info(sb.Context())
require.NoError(t, err)
require.Contains(t, errBuf.String(), "builder: "+sb.Address())
dir := t.TempDir()
f, err := os.CreateTemp(dir, "log")
require.NoError(t, err)
defer f.Close()
defer func() {
if t.Failed() {
dt, _ := os.ReadFile(f.Name())
t.Log(string(dt))
}
}()
p, err := progress.NewPrinter(sb.Context(), f, progressui.AutoMode)
require.NoError(t, err)
ch, chDone := progress.NewChannel(p)
done := func() {
select {
case <-sb.Context().Done():
case <-chDone:
}
}
_, err = c.Build(sb.Context(), client.SolveOpt{
Exports: []client.ExportEntry{
{Type: "local", OutputDir: dir},
},
}, "", func(ctx context.Context, gwc gwclient.Client) (*gwclient.Result, error) {
def, err := llb.Scratch().File(llb.Mkfile("hello", 0o600, []byte("world"))).Marshal(ctx)
if err != nil {
return nil, err
}
return gwc.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
}, ch)
done()
require.NoError(t, err)
dt, err := os.ReadFile(filepath.Join(dir, "hello"))
require.NoError(t, err)
require.Equal(t, "world", string(dt))
}
t.Run("conn=netpipe", func(t *testing.T) {
t.Parallel()
do(t, func(t *testing.T, cmd *exec.Cmd) net.Conn {
c1, c2 := net.Pipe()
cmd.Stdin = c1
cmd.Stdout = c1
return c2
})
})
}

View File

@@ -22,8 +22,8 @@ var imagetoolsTests = []func(t *testing.T, sb integration.Sandbox){
}
func testImagetoolsCopyManifest(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker, imagetools only runs on docker-container")
if sb.Name() != "docker-container" {
t.Skip("imagetools tests are not driver specific and only run on docker-container")
}
dir := createDockerfile(t)
@@ -81,8 +81,8 @@ func testImagetoolsCopyManifest(t *testing.T, sb integration.Sandbox) {
}
func testImagetoolsCopyIndex(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker, imagetools only runs on docker-container")
if sb.Name() != "docker-container" {
t.Skip("imagetools tests are not driver specific and only run on docker-container")
}
dir := createDockerfile(t)
@@ -130,8 +130,8 @@ func testImagetoolsCopyIndex(t *testing.T, sb integration.Sandbox) {
}
func testImagetoolsInspectAndFilter(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker, imagetools only runs on docker-container")
if sb.Name() != "docker-container" {
t.Skip("imagetools tests are not driver specific and only run on docker-container")
}
dir := createDockerfile(t)
@@ -181,8 +181,8 @@ func testImagetoolsInspectAndFilter(t *testing.T, sb integration.Sandbox) {
}
func testImagetoolsAnnotation(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker, imagetools only runs on docker-container")
if sb.Name() != "docker-container" {
t.Skip("imagetools tests are not driver specific and only run on docker-container")
}
dir := createDockerfile(t)

View File

@@ -17,8 +17,6 @@ func inspectCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
var inspectTests = []func(t *testing.T, sb integration.Sandbox){
testInspect,
testInspectBuildkitdFlags,
testInspectNetworkHostEntitlement,
}
func testInspect(t *testing.T, sb integration.Sandbox) {
@@ -41,7 +39,7 @@ func testInspect(t *testing.T, sb integration.Sandbox) {
}
require.Equal(t, sb.Address(), name)
sbDriver, _ := driverName(sb.Name())
sbDriver, _, _ := strings.Cut(sb.Name(), "+")
require.Equal(t, sbDriver, driver)
if isDockerWorker(sb) {
require.NotEmpty(t, hostGatewayIP, "host-gateway-ip worker label should be set with docker driver")
@@ -49,63 +47,3 @@ func testInspect(t *testing.T, sb integration.Sandbox) {
require.Empty(t, hostGatewayIP, "host-gateway-ip worker label should not be set with non-docker driver")
}
}
func testInspectBuildkitdFlags(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
var builderName string
t.Cleanup(func() {
if builderName == "" {
return
}
out, err := rmCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
})
out, err := createCmd(sb, withArgs("--driver", "docker-container", "--buildkitd-flags=--oci-worker-net=bridge"))
require.NoError(t, err, out)
builderName = strings.TrimSpace(out)
out, err = inspectCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
for _, line := range strings.Split(out, "\n") {
if v, ok := strings.CutPrefix(line, "BuildKit daemon flags:"); ok {
require.Contains(t, v, "--oci-worker-net=bridge")
return
}
}
require.Fail(t, "--oci-worker-net=bridge not found in inspect output")
}
func testInspectNetworkHostEntitlement(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
var builderName string
t.Cleanup(func() {
if builderName == "" {
return
}
out, err := rmCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
})
out, err := createCmd(sb, withArgs("--driver", "docker-container"))
require.NoError(t, err, out)
builderName = strings.TrimSpace(out)
out, err = inspectCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
for _, line := range strings.Split(out, "\n") {
if v, ok := strings.CutPrefix(line, "BuildKit daemon flags:"); ok {
require.Contains(t, v, "--allow-insecure-entitlement=network.host")
return
}
}
require.Fail(t, "network.host insecure entitlement not found in inspect output")
}

View File

@@ -3,22 +3,14 @@ package tests
import (
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"
"github.com/Masterminds/semver/v3"
"github.com/containerd/continuity/fs/fstest"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require"
)
const defaultBuildKitTag = "buildx-stable-1"
var buildkitImage string
func tmpdir(t *testing.T, appliers ...fstest.Applier) string {
t.Helper()
tmpdir := t.TempDir()
@@ -55,17 +47,12 @@ func buildxCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
}
if builder := sb.Address(); builder != "" {
cmd.Env = append(cmd.Env,
"BUILDX_CONFIG=/tmp/buildx-"+builder,
"BUILDX_BUILDER="+builder,
)
cmd.Args = append(cmd.Args, "--builder="+builder)
cmd.Env = append(cmd.Env, "BUILDX_CONFIG=/tmp/buildx-"+builder)
}
if context := sb.DockerAddress(); context != "" {
cmd.Env = append(cmd.Env, "DOCKER_CONTEXT="+context)
}
if isExperimental() {
cmd.Env = append(cmd.Env, "BUILDX_EXPERIMENTAL=1")
}
return cmd
}
@@ -82,118 +69,7 @@ func dockerCmd(sb integration.Sandbox, opts ...cmdOpt) *exec.Cmd {
return cmd
}
func isMobyWorker(sb integration.Sandbox) bool {
name, hasFeature := driverName(sb.Name())
return name == "docker" && !hasFeature
}
func isMobyContainerdSnapWorker(sb integration.Sandbox) bool {
name, hasFeature := driverName(sb.Name())
return name == "docker" && hasFeature
}
func isDockerWorker(sb integration.Sandbox) bool {
name, _ := driverName(sb.Name())
return name == "docker"
}
func isDockerContainerWorker(sb integration.Sandbox) bool {
name, _ := driverName(sb.Name())
return name == "docker-container"
}
func driverName(sbName string) (string, bool) {
name := sbName
var hasFeature bool
if b, _, ok := strings.Cut(name, "+"); ok {
name = b
hasFeature = true
}
return name, hasFeature
}
func isExperimental() bool {
if v, ok := os.LookupEnv("TEST_BUILDX_EXPERIMENTAL"); ok {
vv, _ := strconv.ParseBool(v)
return vv
}
return false
}
func buildkitTag() string {
if v := os.Getenv("TEST_BUILDKIT_TAG"); v != "" {
return v
}
return defaultBuildKitTag
}
var (
bkvers map[string]string
bkversMu sync.Mutex
)
func buildkitVersion(t *testing.T, sb integration.Sandbox) string {
bkversMu.Lock()
defer bkversMu.Unlock()
if bkvers == nil {
bkvers = make(map[string]string)
}
ver, ok := bkvers[sb.Name()]
if !ok {
out, err := inspectCmd(sb, withArgs(sb.Address()))
require.NoError(t, err, out)
for _, line := range strings.Split(out, "\n") {
if v, ok := strings.CutPrefix(line, "BuildKit version:"); ok {
ver = strings.TrimSpace(v)
bkvers[sb.Name()] = ver
}
}
if ver == "" {
t.Logf("BuildKit version not found in inspect output, extract it from the image.\n%s", out)
undockBin, err := exec.LookPath("undock")
require.NoError(t, err, "undock not found")
destDir := t.TempDir()
t.Cleanup(func() {
os.RemoveAll(destDir)
})
cmd := exec.Command(undockBin, "--cachedir", "/root/.cache/undock", "--include", "/usr/bin/buildkitd", "--rm-dist", buildkitImage, destDir)
require.NoErrorf(t, cmd.Run(), "failed to extract buildkitd binary from %q", buildkitImage)
cmd = exec.Command(filepath.Join(destDir, "usr", "bin", "buildkitd"), "--version")
out, err := cmd.CombinedOutput()
require.NoErrorf(t, err, "failed to get BuildKit version from %q: %s", buildkitImage, string(out))
v := strings.Fields(strings.TrimSpace(string(out)))
if len(v) != 4 {
require.Fail(t, "unexpected version format: "+strings.TrimSpace(string(out)))
}
ver = v[2]
bkvers[sb.Name()] = ver
}
}
return ver
}
func matchesBuildKitVersion(t *testing.T, sb integration.Sandbox, constraint string) bool {
c, err := semver.NewConstraint(constraint)
if err != nil {
return false
}
v, err := semver.NewVersion(buildkitVersion(t, sb))
if err != nil {
// if the version is not a valid semver, we assume it matches (master)
return true
}
return c.Check(v)
}
func skipNoCompatBuildKit(t *testing.T, sb integration.Sandbox, constraint string, msg string) {
if !matchesBuildKitVersion(t, sb, constraint) {
t.Skipf("buildkit version %s does not match %s constraint (%s)", buildkitVersion(t, sb), constraint, msg)
}
sbDriver, _, _ := strings.Cut(sb.Name(), "+")
return sbDriver == "docker"
}

View File

@@ -28,14 +28,12 @@ func TestIntegration(t *testing.T) {
tests = append(tests, imagetoolsTests...)
tests = append(tests, versionTests...)
tests = append(tests, createTests...)
tests = append(tests, rmTests...)
tests = append(tests, dialstdioTests...)
testIntegration(t, tests...)
}
func testIntegration(t *testing.T, funcs ...func(t *testing.T, sb integration.Sandbox)) {
mirroredImages := integration.OfficialImages("busybox:latest", "alpine:latest")
buildkitImage = "docker.io/moby/buildkit:" + buildkitTag()
buildkitImage := "docker.io/moby/buildkit:buildx-stable-1"
if bkworkers.IsTestDockerd() {
if img, ok := os.LookupEnv("TEST_BUILDKIT_IMAGE"); ok {
ref, err := reference.ParseNormalizedNamed(img)

View File

@@ -20,37 +20,15 @@ var lsTests = []func(t *testing.T, sb integration.Sandbox){
}
func testLs(t *testing.T, sb integration.Sandbox) {
tests := []struct {
name string
args []string
}{
{
name: "no args",
args: []string{},
},
{
name: "format",
args: []string{"--format", "{{.Name}}: {{.DriverEndpoint}}"},
},
}
out, err := lsCmd(sb)
require.NoError(t, err, string(out))
sbDriver, _ := driverName(sb.Name())
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
out, err := lsCmd(sb, withArgs(tt.args...))
require.NoError(t, err, out)
found := false
for _, line := range strings.Split(out, "\n") {
if strings.Contains(line, sb.Address()) {
found = true
require.Contains(t, line, sbDriver)
break
}
}
if !found {
require.Fail(t, out)
}
})
sbDriver, _, _ := strings.Cut(sb.Name(), "+")
for _, line := range strings.Split(out, "\n") {
if strings.Contains(line, sb.Address()) {
require.Contains(t, line, sbDriver)
return
}
}
require.Fail(t, out)
}

View File

@@ -1,11 +1,7 @@
package tests
import (
"strings"
"testing"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/stretchr/testify/require"
)
func rmCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
@@ -14,47 +10,3 @@ func rmCmd(sb integration.Sandbox, opts ...cmdOpt) (string, error) {
out, err := cmd.CombinedOutput()
return string(out), err
}
var rmTests = []func(t *testing.T, sb integration.Sandbox){
testRm,
testRmMulti,
}
func testRm(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
out, err := rmCmd(sb, withArgs("default"))
require.Error(t, err, out) // can't remove a docker builder
out, err = createCmd(sb, withArgs("--driver", "docker-container"))
require.NoError(t, err, out)
builderName := strings.TrimSpace(out)
out, err = inspectCmd(sb, withArgs(builderName, "--bootstrap"))
require.NoError(t, err, out)
out, err = rmCmd(sb, withArgs(builderName))
require.NoError(t, err, out)
}
func testRmMulti(t *testing.T, sb integration.Sandbox) {
if !isDockerContainerWorker(sb) {
t.Skip("only testing with docker-container worker")
}
var builderNames []string
for i := 0; i < 3; i++ {
out, err := createCmd(sb, withArgs("--driver", "docker-container"))
require.NoError(t, err, out)
builderName := strings.TrimSpace(out)
out, err = inspectCmd(sb, withArgs(builderName, "--bootstrap"))
require.NoError(t, err, out)
builderNames = append(builderNames, builderName)
}
out, err := rmCmd(sb, withArgs(builderNames...))
require.NoError(t, err, out)
}

View File

@@ -35,14 +35,6 @@ func (s *backend) Rootless() bool {
return false
}
func (s *backend) NetNSDetached() bool {
return false
}
func (s *backend) ExtraEnv() []string {
return nil
}
func (s backend) Supports(feature string) bool {
if enabledFeatures := os.Getenv("BUILDKIT_TEST_ENABLE_FEATURES"); enabledFeatures != "" {
for _, enabledFeature := range strings.Split(enabledFeatures, ",") {

View File

@@ -37,10 +37,6 @@ func (w *containerWorker) Rootless() bool {
return false
}
func (w *containerWorker) NetNSDetached() bool {
return false
}
func (w *containerWorker) New(ctx context.Context, cfg *integration.BackendConfig) (integration.Backend, func() error, error) {
w.dockerOnce.Do(func() {
w.docker, w.dockerClose, w.dockerErr = dockerWorker{id: w.id}.New(ctx, cfg)
@@ -58,7 +54,7 @@ func (w *containerWorker) New(ctx context.Context, cfg *integration.BackendConfi
cmd := exec.Command("buildx", "create",
"--bootstrap",
"--name="+name,
"--buildkitd-config="+cfgfile,
"--config="+cfgfile,
"--driver=docker-container",
"--driver-opt=network=host",
)

Some files were not shown because too many files have changed in this diff Show More