Compare commits

..

253 Commits
v0.6 ... v0.8.1

Author SHA1 Message Date
Tõnis Tiigi
5fac64c2c4 Merge pull request #1018 from tonistiigi/v0.8-compose-target-dot
[v0.8] bake: allow dot in target names for compose
2022-03-20 19:23:58 -07:00
Tonis Tiigi
24ad37a5d2 bake: allow dot in target names for compose
This is a hotfix for v0.8 to unblock release and
restore backward compatibility. More proper fix
coming later.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-03-20 18:55:45 -07:00
Tõnis Tiigi
106651877d Merge pull request #1005 from tonistiigi/v0.8-update-fsutil-220315
[v0.8] vendor: update fsutil to 9ed61262
2022-03-16 09:40:28 -07:00
Tonis Tiigi
35bcd88f08 vendor: update fsutil to 9ed61262
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit 111ea95629)
2022-03-15 17:14:16 -07:00
Tõnis Tiigi
c8f7c1e93f Merge pull request #993 from tonistiigi/update-buildkit-220308
vendor: update buildkit
2022-03-08 16:59:14 -08:00
Tõnis Tiigi
b78c680207 Merge pull request #989 from crazy-max/moby-imgdgst
build: set remote digest when pushed with docker driver
2022-03-08 10:55:18 -08:00
Tonis Tiigi
d7412c9420 vendor: update buildkit
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-03-08 10:53:06 -08:00
CrazyMax
a7fba7bf3a Merge pull request #992 from tonistiigi/bake-metadata-fix
bake: restore consistent output for metadata
2022-03-08 19:43:48 +01:00
CrazyMax
19ff7cdadc build: set remote digest when pushed with docker driver
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-03-08 19:31:13 +01:00
Tonis Tiigi
c255c04eed bake: restore consistent output for metadata
Metadata formatting should not depend on the number
of targets.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-03-08 10:06:03 -08:00
Tõnis Tiigi
9fcea76dea Merge pull request #977 from tonistiigi/logs-dupes
progress: avoid double logs when multiple targets build same step
2022-03-04 16:30:53 -08:00
Tõnis Tiigi
1416bc1d83 Merge pull request #972 from crazy-max/imagetools-inspect-order
imagetools inspect: keep platform order
2022-03-04 11:54:15 -08:00
CrazyMax
215a128fc1 imagetools inspect: missing manifest digest for manifest-list (json)
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-03-04 20:36:14 +01:00
CrazyMax
4e4eea7814 imagetools inspect: deterministic platform order
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-03-04 20:36:14 +01:00
Tõnis Tiigi
8079bd2841 Merge pull request #980 from crazy-max/imageid
build: return imageID when loading without docker driver
2022-03-04 10:50:53 -08:00
CrazyMax
2d5368cccc Merge pull request #981 from tonistiigi/target-context-remove
build: remove target context if platform specific used
2022-03-04 16:29:20 +01:00
CrazyMax
a1256c6bb2 Merge pull request #985 from tonistiigi/multi-node-platform
build: fix multi-node builds with mixed platforms
2022-03-04 15:35:55 +01:00
CrazyMax
e7863eb664 build: return imageID when loading without docker driver
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-03-04 15:27:09 +01:00
Tonis Tiigi
171c4375a1 build: fix multi-node builds with mixed platforms
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-03-03 13:15:13 -08:00
Tonis Tiigi
45844805ec build: remove target context if platform specific used
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-03-01 21:25:43 -08:00
Tonis Tiigi
b77d7864fa progress: avoid double logs when multiple targets build same step
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-28 23:32:54 -08:00
Tõnis Tiigi
6efcee28d5 Merge pull request #973 from crazy-max/vendor-docker-cli
vendor: update docker/cli to 8667ccd
2022-02-27 20:29:34 -08:00
Tõnis Tiigi
3ad24524c4 Merge pull request #971 from crazy-max/fix-docs
docs: small fixes
2022-02-27 20:26:27 -08:00
CrazyMax
971b5d2b73 vendor: update docker/cli to 8667ccd
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-27 00:35:39 +01:00
CrazyMax
94c5dde85a docs: small fixes
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-26 05:39:42 +01:00
Tõnis Tiigi
f62c02329e Merge pull request #969 from tonistiigi/update-buildkit-20220225
vendor: update buildkit to 0692ad79
2022-02-25 12:15:13 -08:00
Tonis Tiigi
d2e53f5e05 vendor: update buildkit to 0692ad79
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-25 11:52:45 -08:00
Tõnis Tiigi
7af29802d4 Merge pull request #854 from crazy-max/buildinfo-cmd
imagetools inspect: add --format flag
2022-02-25 11:52:27 -08:00
Tõnis Tiigi
6ac01ec9ac Merge pull request #965 from tonistiigi/bake-context-validation
bake: additional support for named context on remote inputs
2022-02-25 11:36:51 -08:00
CrazyMax
20a55e9184 imagetools inspect: multi-platform support
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-25 20:30:08 +01:00
CrazyMax
6c56109083 imagetools inspect: add --format flag
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-25 17:55:34 +01:00
Tõnis Tiigi
dab3fe71bd Merge pull request #967 from crazy-max/update-clidocstool
docs: update cli-docs-tool to v0.4.0
2022-02-25 08:47:52 -08:00
CrazyMax
9867ca279a docs: update cli-docs-tool to v0.4.0
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-25 15:41:17 +01:00
Tonis Tiigi
91e550b715 bake: add path validation for remote bake invocations
This is a stopgap before proper entitlements support
is implemented.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-24 23:19:18 -08:00
Tonis Tiigi
280c008f81 bake: make named contexts relative to remote bake input
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-24 23:19:12 -08:00
Tõnis Tiigi
5939a23af6 Merge pull request #963 from tonistiigi/bake-contexts-error
bake: use better error in named contexts not supported
2022-02-24 20:30:55 -08:00
CrazyMax
7f1041164e Merge pull request #964 from tonistiigi/update-buildkit-022322
vendor: update buildkit to b124b0c3
2022-02-23 21:35:09 +01:00
Tonis Tiigi
64ce211ba4 vendor: update buildkit to b124b0c3
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-23 12:15:16 -08:00
Tonis Tiigi
b5bf28d722 bake: use better error in named contexts not supported
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-22 21:56:52 -08:00
Tõnis Tiigi
10debb577e Merge pull request #959 from tonistiigi/docker-proxy-config
set build-args from docker proxy configuration
2022-02-22 13:56:11 -08:00
CrazyMax
75cdea48e4 Merge pull request #962 from tonistiigi/bake-deps-error
build: fix deadlock on handling deps errors
2022-02-22 10:50:24 +01:00
CrazyMax
d96d7fb2dc Merge pull request #930 from tylerlwsmith/fix-readme-typo
Fix typo in readme
2022-02-21 10:53:05 +01:00
Tyler Smith
e3245a400a Fix typo in readme
Changed "note tha" to "note that"

Signed-off-by: Tyler Smith <tylerlwsmith@gmail.com>
2022-02-18 19:55:05 -08:00
Tõnis Tiigi
e871c39f05 Merge pull request #908 from crazy-max/inline-buildattrs
build: inline buildinfo attrs
2022-02-18 19:36:25 -08:00
Tõnis Tiigi
9c0a23996d Merge pull request #958 from crazy-max/buildinfo-deps
build: send buildinfo dependencies
2022-02-18 19:35:57 -08:00
Tonis Tiigi
3b2aeb2d5b build: fix deadlock on handling deps errors
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-18 18:41:52 -08:00
Tonis Tiigi
e98a476dc8 set build-args from docker proxy configuration
For backward compatibility with docker build.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-17 22:21:43 -08:00
CrazyMax
7677052cb7 build: send buildinfo dependencies
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-17 21:02:06 +01:00
Tõnis Tiigi
c273e0986c Merge pull request #953 from crazy-max/update-docs
docs: updates and guides
2022-02-17 10:54:31 -08:00
CrazyMax
9ee499ae27 docs: metadata-file usage
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-17 17:52:50 +01:00
CrazyMax
230dfa96a3 docs: built-in build args
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-17 17:52:49 +01:00
CrazyMax
f1a8f54c83 docs: user guides
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-17 17:52:49 +01:00
CrazyMax
2bcf3524e5 docs: ssh usage example
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-16 15:14:45 +01:00
CrazyMax
26918513e3 docs: lint
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-16 15:14:45 +01:00
Tõnis Tiigi
893d505803 Merge pull request #955 from crazy-max/vendor-buildkit
vendor: update buildkit to 1e6032c
2022-02-15 11:32:04 -08:00
CrazyMax
22aaa260e7 vendor: update buildkit to 1e6032c
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-15 19:12:00 +01:00
CrazyMax
1bcc3556fc Merge pull request #949 from docker/dependabot/go_modules/github.com/docker/distribution-2.8.0incompatible
build(deps): bump github.com/docker/distribution from 2.7.1+incompatible to 2.8.0+incompatible
2022-02-14 11:48:21 +01:00
Tõnis Tiigi
eef6deb7c2 Merge pull request #860 from tonistiigi/no-cache-filter
build: add no-cache-filter
2022-02-11 20:09:40 -08:00
dependabot[bot]
542759ea31 build(deps): bump github.com/docker/distribution
Bumps [github.com/docker/distribution](https://github.com/docker/distribution) from 2.7.1+incompatible to 2.8.0+incompatible.
- [Release notes](https://github.com/docker/distribution/releases)
- [Commits](https://github.com/docker/distribution/compare/v2.7.1...v2.8.0)

---
updated-dependencies:
- dependency-name: github.com/docker/distribution
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-11 22:39:59 +00:00
Tõnis Tiigi
e5f590a7fa Merge pull request #946 from crazy-max/metadata-output
build: enhance metadata json output
2022-02-10 23:59:23 -08:00
CrazyMax
ecf215b927 e2e: add bake build and display metadata json
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-11 08:16:47 +01:00
CrazyMax
299fd19c49 build: enhance metadata json output
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-11 08:16:42 +01:00
CrazyMax
4b633c3c7b docs: built-in build args
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-11 07:57:55 +01:00
Tonis Tiigi
eb8057e8e0 forbid setting no-cache and no-cache-filter together
Per review request.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-10 22:01:50 -08:00
Tonis Tiigi
32f6358d78 bake: add no-cache-filter
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-10 22:01:48 -08:00
Tonis Tiigi
3b47722032 build: add no-cache-filter
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-02-10 22:00:02 -08:00
Tõnis Tiigi
e60f0f2c4f Merge pull request #943 from crazy-max/secret-examples
docs: secret usage examples
2022-02-10 17:36:26 -08:00
CrazyMax
b39ebab666 docs: secret usage examples
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-10 15:37:11 +01:00
Akihiro Suda
f891187d8b Merge pull request #948 from crazy-max/vendor-buildkit
vendor: update buildkit to 2f99651
2022-02-10 12:15:39 +09:00
CrazyMax
307c94e5c7 vendor: update buildkit to 2f99651
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-09 21:53:40 +01:00
Tõnis Tiigi
60a025b227 Merge pull request #928 from tonistiigi/bake-named-contexts
bake: add named contexts keys
2022-02-07 10:52:01 -08:00
Tõnis Tiigi
fec415a8e0 Merge pull request #937 from hinshun/printer-writer
Relax to io.Writer requirement for NewPrinter
2022-02-06 23:24:45 -08:00
Edgar Lee
2d7540fb0a Separate io.Writer from console.File for NewPrinter
Signed-off-by: Edgar Lee <edgarl@netflix.com>
2022-02-06 09:11:47 -08:00
Tõnis Tiigi
595285736c Merge pull request #885 from crazy-max/rm-inactive
cli: add --all-inactive for rm command
2022-02-03 19:25:25 -08:00
CrazyMax
378f0b45c6 cli: add --all-inactive and --force flags for rm command
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-02-03 13:39:53 +01:00
Tonis Tiigi
c3dab802d8 docs: add examples for bake named contexts
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-27 18:46:12 -08:00
Tonis Tiigi
fa04611afc bake: connect results between build targets
Build context “target:<name>” will take the contents
from another bake target.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-27 18:46:12 -08:00
Tonis Tiigi
ffa062dc95 util: add waitmap for target synchronization
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-27 18:46:12 -08:00
Tonis Tiigi
0fc2b5ca85 bake: add named contexts keys
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-27 18:46:02 -08:00
Tõnis Tiigi
9a1267cd02 Merge pull request #929 from crazy-max/bake-target-name
bake: restrict target name
2022-01-26 09:06:02 -08:00
CrazyMax
c74b2fe7a4 bake: restrict target name
This fix adds a restriction `[a-zA-Z0-9_-]+`
for target name. This is pretty much the same as the
container name restriction in moby.

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-26 10:29:21 +01:00
Tõnis Tiigi
6c69d970f7 Merge pull request #924 from crazy-max/log
root: filter out useless commandConn.CloseWrite warning message
2022-01-25 08:54:21 -08:00
CrazyMax
d3e56ea9d9 root: simple output format on logrus for parity with cli
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-25 08:54:42 +01:00
Tõnis Tiigi
11b771c789 Merge pull request #904 from tonistiigi/named-contexts
build: support for named contexts(stages)
2022-01-20 19:58:40 -08:00
CrazyMax
278f94a8b6 root: filter out useless commandConn.CloseWrite warning message
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-21 00:36:06 +01:00
Tõnis Tiigi
14b38a9aa8 Merge pull request #914 from crazy-max/fix-target-inherit
bake: keep target inheritance
2022-01-13 18:35:13 -08:00
CrazyMax
0044c28b1f bake: keep target inheritance
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-13 22:23:20 +01:00
Tõnis Tiigi
b568b219bc Merge pull request #920 from crazy-max/update-gocty
bump github.com/zclconf/go-cty from 1.7.1 to 1.10.0
2022-01-13 10:56:54 -08:00
Tõnis Tiigi
aabbe5a56a Merge pull request #919 from crazy-max/mod-outdated-bake
chore: invalidate cache for outdated run stage
2022-01-13 10:56:05 -08:00
Tõnis Tiigi
b038dd063e Merge pull request #921 from crazy-max/readme-install
Update install instructions
2022-01-13 10:55:07 -08:00
CrazyMax
f25d5ff02f Update install instructions
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-13 14:57:25 +01:00
CrazyMax
b67bdedb23 bump github.com/zclconf/go-cty from 1.7.1 to 1.10.0
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-13 14:32:10 +01:00
CrazyMax
3ccb883d95 chore: invalidate cache for outdated run stage
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-13 14:09:47 +01:00
Tõnis Tiigi
785c861233 Merge pull request #915 from crazy-max/ci-buildkit-image
ci: build with stable buildkit image
2022-01-11 22:37:55 -08:00
CrazyMax
1b69919313 ci: build with stable buildkit image
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-12 07:16:58 +01:00
CrazyMax
24db7366ba build: inline buildinfo attrs
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-08 17:53:24 +01:00
Tonis Tiigi
08547827db docs: add docs for -build-context
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-07 18:37:25 -08:00
Tonis Tiigi
f37c253ae4 commands: provide more helpful error when —build-context is not supported
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-07 18:32:37 -08:00
Tonis Tiigi
d77e2453da commands: rename context flag to build-context
Avoid conflicts with docker context

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-07 18:31:32 -08:00
Tõnis Tiigi
2b4d305c58 Merge pull request #905 from crazy-max/compose-go
compose: fix env
2022-01-06 09:02:26 -08:00
CrazyMax
5d715ada96 compose: resolve build args from service environment
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-06 11:59:49 +01:00
CrazyMax
3400fa5628 compose: test env_file
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-06 11:42:16 +01:00
CrazyMax
f04c8c8430 update github.com/compose-spec/compose-go to v1.0.8
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2022-01-06 11:41:45 +01:00
Tonis Tiigi
de6b04d726 build: add support for named contexts
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-01-04 22:55:12 -08:00
Tõnis Tiigi
fe9f9bba87 Merge pull request #884 from crazy-max/fix-bake-resgroup
bake: fix group resolution
2022-01-04 10:50:12 -08:00
Tõnis Tiigi
e482ba2c73 Merge pull request #892 from tonistiigi/warnings-summary
Show summary of build warnings.
2022-01-04 10:48:57 -08:00
Tonis Tiigi
038727477c root: filter out useless debug logs from vendored packages
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-12-19 22:33:05 -08:00
Tonis Tiigi
ed4103ef52 commands: build summary of warnings on build
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-12-19 22:32:59 -08:00
Tõnis Tiigi
54286a0117 Merge pull request #889 from tonistiigi/update-buildkit-20211215
vendor: update buildkit to 539be170
2021-12-17 11:58:40 -08:00
Tonis Tiigi
9c3be32bc9 vendor: update buildkit to 539be170
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-12-16 11:42:02 -08:00
Tõnis Tiigi
59533bbb5c Merge pull request #887 from AkihiroSuda/avoid-unneeded-userns-host
docker-container: set UsernsMode only when needed
2021-12-15 21:00:12 -08:00
Akihiro Suda
5f8600f098 docker-container: set UsernsMode only when needed
Set `UsernsMode="host"` only when the daemon is running in userns-remapping mode.

Fix issue 561

The issue will be also fixed in moby/moby PR 43084 (Docker 20.10.13).
This buildx PR helps users of old releases of Docker.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2021-12-15 18:42:58 +09:00
CrazyMax
d95ebef55c bake: fix group resolution
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-12-14 11:17:37 +01:00
Tõnis Tiigi
33c121df01 Merge pull request #881 from crazy-max/fix-bake-print
bake: fix groups print
2021-12-13 16:09:24 -08:00
Tõnis Tiigi
1dde00c4bc Merge pull request #880 from crazy-max/bake-ignore-field
bake: ignore NetworkMode field for json and hcl
2021-12-13 16:06:41 -08:00
CrazyMax
4466a24f9e bake: fix groups print
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-12-13 19:21:33 +01:00
CrazyMax
ec9daba87e bake: ignore NetworkMode field for json and hcl
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-12-10 19:32:40 +01:00
Sebastiaan van Stijn
202e99695b Merge pull request #823 from crazy-max/docs-git-protoc
docs: fix git protocol
2021-12-09 12:26:17 +01:00
Tõnis Tiigi
7371dda7a2 Merge pull request #863 from zachary-povey/support_host_network_in_compose
Add NetworkMode to bake target
2021-12-04 21:04:05 -08:00
Tõnis Tiigi
62bdf4d85e Merge pull request #868 from crazy-max/discard-containerd-logger
imagetools resolver: discard containerd logger output
2021-12-04 21:03:42 -08:00
Zachary Povey
7f8dbf890d Remove support for network override in bake
Signed-off-by: Zachary Povey <zachary.povey@autotrader.co.uk>
2021-12-01 09:45:32 +00:00
Tõnis Tiigi
bede4ab552 Merge pull request #869 from crazy-max/deprecaded-clientopt
imagetools resolver: fix deprecated client opt
2021-11-30 08:29:40 -08:00
CrazyMax
5c5125f30e imagetools resolver: fix deprecated client opt
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-30 15:34:29 +01:00
CrazyMax
e9cf2cbe32 imagetools resolver: discard containerd logger output
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-30 15:17:56 +01:00
Zachary Povey
4ee7f70400 Remove NetworkMode support for HCL targets
Signed-off-by: Zachary Povey <zachary.povey@autotrader.co.uk>
2021-11-30 11:33:32 +00:00
CrazyMax
0abda783bb Merge pull request #864 from eliottwiener/bake-read-from-stdin
bake: build definition file via stdin
2021-11-30 09:35:03 +01:00
Tõnis Tiigi
aadd118883 Merge pull request #867 from crazy-max/du-last-accessed
disk usage: last accessed not displayed
2021-11-29 09:49:12 -08:00
CrazyMax
9aff9301ce disk usage: last accessed not displayed
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-29 16:21:40 +01:00
CrazyMax
93d6b654ca Merge pull request #866 from Wojciechem/patch-1
add note about variable syntax in json
2021-11-29 09:12:40 +01:00
Wojciech M
42287815b5 add note about variable syntax in json
In addition to HCL, variables can also be defined in json.

Signed-off-by: wojciechem <wmiedzybrodzki@outlook.com>
2021-11-28 22:30:14 +01:00
Eliott Wiener
dcabc22072 bake: build definition file via stdin
closes #833

Accept bake build definition file from stdin with `-f -`.

Signed-off-by: Eliott Wiener <eliottwiener@gmail.com>
2021-11-25 12:43:38 -05:00
Zachary Povey
ae53101e89 Add NetworkMode to bake target
Allows specification of network mode in a bake target.

Fixes #848

Signed-off-by: Zachary Povey <zachary.povey@autotrader.co.uk>
2021-11-25 16:00:42 +00:00
CrazyMax
61627c2ece docs: fix git protocol
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-25 09:07:41 +01:00
Tõnis Tiigi
ab73275f58 Merge pull request #857 from crazy-max/bake-json
bake: fix print output
2021-11-24 15:59:29 -08:00
CrazyMax
316ca972b6 bake: fix print output
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-24 21:54:57 +01:00
CrazyMax
5c2b9bbfc5 Merge pull request #861 from tonistiigi/cleanup
commands: clean up unnecessary code
2021-11-23 09:36:55 +01:00
Tonis Tiigi
cc2a879660 commands: clean up unnecessary code
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-22 23:57:42 -08:00
Tõnis Tiigi
89334a88a9 Merge pull request #853 from crazy-max/fix-flags
cli: fix flags usage
2021-11-22 16:21:48 -08:00
Tõnis Tiigi
1927dba42f Merge pull request #852 from DataDog/mayeul/docker-buildx-rm--keep-buildkitd
Add an flag to buildx rm to keep the buildkitd daemon running
2021-11-22 16:21:30 -08:00
Mayeul Blanzat
72dab552b5 Add an option to buildx rm to keep the buildkitd daemon running
Add --keep-daemon to the `rm` command option to preserve the buildkitd daemon after the buildx context is deleted.

Signed-off-by: Mayeul Blanzat <mayeul.blanzat@datadoghq.com>
2021-11-22 13:24:47 +01:00
CrazyMax
a0a7db127c cli: fix flags usage
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-22 10:51:59 +01:00
Tõnis Tiigi
bcfd434829 Merge pull request #839 from crazy-max/xx-update
dockerfile: update xx to 1.0.0
2021-11-13 19:01:51 -08:00
CrazyMax
d1aaed7a77 dockerfile: update xx to 1.0.0
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-13 18:53:01 +01:00
CrazyMax
f0026081a7 Merge pull request #832 from crazy-max/update-compose
update github.com/compose-spec/compose-go to v1.0.5
2021-11-10 11:03:28 +01:00
CrazyMax
a18829f837 update github.com/compose-spec/compose-go to v1.0.5
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-09 10:16:17 +01:00
CrazyMax
da0eb138d0 Merge pull request #829 from crazy-max/typo
fix typo in docs
2021-11-05 09:17:23 +01:00
CrazyMax
a2c7d43e46 fix typo in docs
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-05 08:48:32 +01:00
Akihiro Suda
f7cba04f5e Merge pull request #828 from tonistiigi/strip-binary
Dockerfile: strip binary by default
2021-11-05 14:25:22 +09:00
Tonis Tiigi
12b5db70e2 Dockerfile: strip binary by default
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-04 18:11:26 -07:00
Tõnis Tiigi
5c4e3fc860 Merge pull request #818 from crazy-max/fix-builder-flag
cli: fix builder persistent flag
2021-11-04 12:00:43 -07:00
CrazyMax
eab0e6a8fe cli: fix builder persistent flag
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-04 18:52:17 +01:00
CrazyMax
4c938c77ba Merge pull request #825 from tonistiigi/multi-node-registry-conf
allow multi-node push and imagetools to use custom registry config
2021-11-04 18:43:31 +01:00
Tonis Tiigi
1cca41b81a build: support insecure export option for multi-node build
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-04 10:02:14 -07:00
Tonis Tiigi
c62472121b allow multi-node push and imagetools to use custom registry config
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-04 10:02:12 -07:00
Tonis Tiigi
88d0775692 refactor accessing registry configs via drivers
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-04 10:01:23 -07:00
Tõnis Tiigi
8afc82b427 Merge pull request #817 from tonistiigi/project-sharedkey
build: set local sharedkey per project basename
2021-11-04 09:52:51 -07:00
Tõnis Tiigi
d311561a8b Merge pull request #824 from tonistiigi/config-files-store2
store snapshot of config files on create
2021-11-04 09:05:13 -07:00
Tõnis Tiigi
44e180b26e Merge pull request #826 from tonistiigi/multi-node-iidfile 2021-11-04 08:24:33 -07:00
Tonis Tiigi
02d29e0af5 build: fix setting iidfile with multi-node push
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-03 23:12:00 -07:00
Tonis Tiigi
40121c671c kubernetes: store config files for k8s
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-03 18:24:16 -07:00
Tonis Tiigi
4c1621cccd store snapshot of config files on create
Files can be reused when container needs to be booted again.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-11-03 16:34:43 -07:00
Tõnis Tiigi
7f0e37531c Merge pull request #822 from crazy-max/fix-bake-git-protoc
bake: fix protocol detection
2021-11-02 16:20:22 -07:00
CrazyMax
82b212bddf bake: fix protocol detection
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-11-02 23:12:27 +01:00
Tonis Tiigi
aa52a5a699 build: set local sharedkey per project basename
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-10-29 21:29:04 -07:00
Tõnis Tiigi
49342dd54d Merge pull request #787 from crazy-max/inject-certs
container driver: copy ca and user tls registries certs
2021-10-28 15:11:57 -07:00
CrazyMax
3f716f00fa container driver: copy ca and user tls registries certs
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-28 22:30:11 +02:00
Tõnis Tiigi
5e25191cb6 Merge pull request #814 from crazy-max/cgroup-parent
build: add cgroup-parent support
2021-10-28 10:53:09 -07:00
CrazyMax
dd15969c93 build: add cgroup-parent support
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-28 19:34:47 +02:00
Tõnis Tiigi
81cf2064c4 Merge pull request #815 from tonistiigi/multi-node-push-names
imagetools: fix pushing same image with multiple names
2021-10-28 10:21:30 -07:00
Tonis Tiigi
b497587f21 imagetools: fix pushing same image with multiple names
containerd pusher can’t handle this case atm so we
need to make sure we always create a new resolver
for each name.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-10-27 17:14:49 -07:00
CrazyMax
2890209a11 refactor: lexical order for build opts
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-27 23:42:16 +02:00
Tõnis Tiigi
4690e14c40 Merge pull request #810 from crazy-max/warn-flags-depre
build: warning msg on deprecated flags
2021-10-26 16:25:19 -07:00
CrazyMax
25d2f73858 build: warning on deprecated flags
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-26 21:36:49 +02:00
CrazyMax
36a37a624e refactor: flags lexical order
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-26 13:11:28 +02:00
CrazyMax
e150d7bdd8 add long description to root command
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-26 13:11:28 +02:00
Tõnis Tiigi
be2c8f71fe Merge pull request #812 from cpuguy83/mark_spans_as_error
Mark span status as error when fatal error occurs.
2021-10-25 20:36:35 -07:00
Akihiro Suda
89f5c1ce51 Merge pull request #804 from tonistiigi/http-hijack-session
docker: dial session directly with http hijack
2021-10-22 13:10:26 +09:00
Brian Goff
b6474d43a9 Mark span status as error when fatal error occurs.
Before this only recorded errors instead of setting the span status,
which makes it harder to dig through.
Now an error that bubbles is reflected in the span status.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2021-10-21 22:24:51 +00:00
Tonis Tiigi
2644d56a6d docker: dial session directly with http hijack
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-10-20 18:54:31 -07:00
CrazyMax
084b6c0a95 Merge pull request #790 from crazy-max/shmsize
build: add shm-size support
2021-10-20 06:24:01 +02:00
CrazyMax
8e5595b7c7 build: add shm-size support
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-19 21:06:01 +02:00
CrazyMax
22500c9929 vendor: update buildkit
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-19 21:03:23 +02:00
Tõnis Tiigi
050f4f9219 Merge pull request #801 from crazy-max/bump-go
update go to 1.17.2
2021-10-18 13:26:07 -07:00
CrazyMax
1a56de8e68 update go to 1.17.2
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-16 15:00:31 +02:00
Tõnis Tiigi
868610e0e9 Merge pull request #800 from crazy-max/ulimit
build: add ulimit support
2021-10-15 11:14:49 -07:00
CrazyMax
b89e2f35df build: add ulimit support
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-15 16:03:49 +02:00
CrazyMax
1b3068df7c vendor: update buildkit
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-10-15 15:01:36 +02:00
Tõnis Tiigi
461369748c Merge pull request #782 from djs55/cgroup-parent
docker-container: place build containers in a separate cgroup
2021-09-30 09:06:29 -07:00
David Scott
d5908cdddf docker-container: use /docker/buildx cgroup by default
This allows resource limits to be applied to all builds on a host.
For example to limit the total amount of CPU used by builds:

https://medium.com/@asishrs/docker-limit-resource-utilization-using-cgroup-parent-72a646651f9d

Signed-off-by: David Scott <dave@recoil.org>
2021-09-29 19:58:22 +01:00
David Scott
b5bc754bad docker-container: support --driver-opt cgroup-parent=...
This allows the parent cgroup to be customised, which allows resource
limits to be imposed on build containers separately from "user"
containers.

Signed-off-by: David Scott <dave@recoil.org>
2021-09-29 19:57:46 +01:00
Tõnis Tiigi
dff7673afb Merge pull request #783 from tonistiigi/override-merge
bake: restore previous override merge behavior
2021-09-29 08:59:54 -07:00
Tõnis Tiigi
3e2fde5639 Merge pull request #785 from crazy-max/fix-docs
Fix --driver flag usage markdown output
2021-09-29 08:59:14 -07:00
Tonis Tiigi
7a7b73c043 bake: restore previous override merge behavior
For array fields, overrides are merged together
but override is not merged with the target. If merging
with target is desired we can add support for
overrides with += operator in the future.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-09-29 08:27:27 -07:00
Tõnis Tiigi
e50c9ae7be Merge pull request #784 from crazy-max/fix-e2e
Driver opt not used in e2e tests
2021-09-29 08:18:49 -07:00
CrazyMax
9e62c9f074 Fix --driver flag usage markdown output
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-29 15:33:44 +02:00
CrazyMax
c82dbafaee Driver opt not used in e2e tests
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-29 10:42:27 +02:00
Tonis Tiigi
0e4d7aa7a9 bake: add test for merging overrides
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-09-28 23:21:07 -07:00
Tõnis Tiigi
c05a6eb2c1 Merge pull request #781 from crazy-max/vendor-buildkit
vendor: update buildkit
2021-09-27 19:01:49 -07:00
CrazyMax
eec1693f30 vendor: update buildkit
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-27 21:54:35 +02:00
CrazyMax
c643c2ca95 Merge pull request #775 from crazy-max/vendor-buildkit
vendor: update buildkit
2021-09-25 18:48:36 +02:00
Tõnis Tiigi
761e22e395 Merge pull request #774 from crazy-max/driver-e2e
Driver e2e tests
2021-09-23 16:49:55 -07:00
CrazyMax
ef8c936b27 Driver e2e tests
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-23 10:49:30 +02:00
Tõnis Tiigi
0cea838344 Merge pull request #773 from tonistiigi/bake-push-fix
bake: fix using push override with output definition
2021-09-21 17:53:45 -07:00
Akihiro Suda
2b18a9b4a5 Merge pull request #761 from morlay/kubeconfig-enhance
util: support load Colon-separated KUBECONFIG
2021-09-21 16:45:55 +09:00
CrazyMax
45e4550c36 vendor: update buildkit
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-21 07:49:45 +02:00
Tonis Tiigi
6fc906532b bake: fix using push override with output definition
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-09-20 16:22:27 -07:00
Tõnis Tiigi
06541ebd0f Merge pull request #765 from thaJeztah/fix_broken_anchors
docs: fix some broken anchors
2021-09-15 16:00:21 -07:00
Sebastiaan van Stijn
773fac9a73 docs: fix some broken anchors
- ./_site/engine/reference/commandline/buildx_build/index.html
      *  linking to internal hash #--buildkitd-flags-flags that does not exist (line 904)
         <a href="/engine/reference/commandline/buildx_create/#--buildkitd-flags-flags"></a>
    - ./_site/engine/reference/commandline/buildx_create/index.html
      *  linking to internal hash #--buildkitd-flags-flags that does not exist (line 350)
         <a href="#--buildkitd-flags-flags"></a>
      *  linking to internal hash #--config-file that does not exist (line 336)
         <a href="#--config-file"></a>
      *  linking to internal hash #--config-file that does not exist (line 336)
         <a href="/engine/reference/commandline/buildx_build/#--load"></a>
      *  linking to internal hash #--load that does not exist (line 369)
         <a href="/engine/reference/commandline/buildx_build/#--load"></a>

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-09-13 17:06:18 +02:00
CrazyMax
7f0e05dfac Merge pull request #762 from crazy-max/remove-yaml
Remove generated YAML docs
2021-09-09 13:39:53 +02:00
CrazyMax
e59aecf034 Remove YAML docs from the repo
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-09 10:29:13 +02:00
CrazyMax
ac9a1612d2 Merge pull request #758 from crazy-max/mod-outdated
Mod outdated
2021-09-08 13:33:26 +02:00
Morlay
c83812144c util: support load Colon-separated KUBECONFIG
Signed-off-by: Morlay <morlay.null@gmail.com>
2021-09-08 17:51:56 +08:00
CrazyMax
df521e4e96 Mod outdated
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-04 18:57:48 +02:00
Tõnis Tiigi
00cb53d0ef Merge pull request #746 from crazy-max/bake-workflow
Bake workflow
2021-09-03 14:11:54 -07:00
CrazyMax
6cfef7fa36 Bake workflow
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-03 22:50:05 +02:00
Tõnis Tiigi
b05c313204 Merge pull request #740 from bossmc/support-quiet
Implement `--quiet` support
2021-09-03 11:00:26 -07:00
CrazyMax
3e8bbbc286 Merge pull request #757 from crazy-max/bump-clidocstool
Fix flags usage markdown output
2021-09-03 14:25:21 +02:00
CrazyMax
8a12884814 Fix flags usage markdown output
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-03 11:52:49 +02:00
Tõnis Tiigi
6cf9fa8261 Merge pull request #739 from crazy-max/go117
Go 1.17
2021-09-02 16:01:45 -07:00
Tõnis Tiigi
fd94fc5fdf Merge pull request #748 from crazy-max/platform-func
Built-in variable `BAKE_LOCAL_PLATFORM`
2021-09-02 16:01:26 -07:00
CrazyMax
45c678ad26 Go 1.17
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-02 23:11:10 +02:00
CrazyMax
55a3ce606f Built-in variable BAKE_LOCAL_PLATFORM
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-09-02 23:06:05 +02:00
CrazyMax
c1c414e4c9 Merge pull request #747 from crazy-max/yaml-docs
Generate YAML doc
2021-09-02 20:16:56 +02:00
Tõnis Tiigi
610601cec0 Merge pull request #753 from tonistiigi/ci-branches
github: fix running ci in version branches
2021-08-28 10:08:06 -07:00
Tonis Tiigi
9833420a03 github: fix running ci in version branches
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-08-27 21:19:11 -07:00
Tõnis Tiigi
7f322caa79 Merge pull request #751 from thaJeztah/fix_mount_path
container-driver: fix volume destination for cache
2021-08-27 21:02:09 -07:00
Sebastiaan van Stijn
93867d02f0 container-driver: fix volume destination for cache
The container-driver creates a Linux container (as there currently isn't a
Windows version of buildkitd). However, the defaults are platform specific.

Buildx was using the defaults from the buildkit `util/appdefault' package,
which resulted in Buildx running on a Windows client to create a Linux
container that used the Windows location, which causes it to fail:

    invalid mount config for type "volume": invalid mount path: 'C:/ProgramData/buildkitd/.buildstate' mount path must be absolute

This patch hard-codes the destination to the default Linux path.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-08-27 09:48:31 +02:00
CrazyMax
b8a602821c Generate YAML doc
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-25 19:10:03 +02:00
Tõnis Tiigi
a8a3b1738e Merge pull request #741 from tonistiigi/client-ctx
use long-running context for client initialization
2021-08-20 09:01:53 -07:00
Andy Caldwell
ef3e46fd62 Move printing to stdout up to the command itself
Signed-off-by: Andy Caldwell <andrew.caldwell@metaswitch.com>
2021-08-20 15:13:23 +01:00
Andy Caldwell
3ab0b6953a Regenerate docs now that --quiet is unmasked
Signed-off-by: Andy Caldwell <andrew.caldwell@metaswitch.com>
2021-08-20 15:13:19 +01:00
Andy Caldwell
c19c018a4c Implement --quiet support
Signed-off-by: Andy Caldwell <andrew.caldwell@metaswitch.com>
2021-08-20 15:13:13 +01:00
Tonis Tiigi
422ba60b04 use long-running context for client initialization
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-08-19 20:36:24 -07:00
Tõnis Tiigi
2d3763990c Merge pull request #731 from crazy-max/dockerfile-13
Update Dockerfile references to use 1.3
2021-08-18 09:05:53 -07:00
CrazyMax
dc6ada9b50 Update Dockerfile references to use 1.3
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-17 08:41:15 +02:00
Tõnis Tiigi
cb185f095f Merge pull request #721 from crazy-max/compose-ext
bake: `x-bake` extension field with compose
2021-08-13 07:51:23 -07:00
CrazyMax
89e126fa60 bake: x-bake extension field with compose
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-13 09:15:15 +02:00
Tõnis Tiigi
04bac63745 Merge pull request #692 from crazy-max/boostrap-cmd
Allow booting builder after creation
2021-08-12 10:35:33 -07:00
Tõnis Tiigi
3594851128 Merge pull request #720 from crazy-max/default-group
bake: print default group
2021-08-12 10:31:16 -07:00
Tõnis Tiigi
58e5a73389 Merge pull request #707 from crazy-max/update-readme
Enhance readme
2021-08-12 10:30:10 -07:00
CrazyMax
c685e46609 bake: print default group
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-12 09:02:36 +02:00
CrazyMax
e3283e6169 Enhance readme
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-12 08:57:09 +02:00
CrazyMax
5d50bd7b43 Allow booting builder after creation
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-12 08:45:55 +02:00
Tõnis Tiigi
3dfbe2c184 Merge pull request #674 from crazy-max/checksum
Create checksums for artifacts
2021-08-10 13:16:13 -07:00
Tõnis Tiigi
06367a120b Merge pull request #682 from morlay/k8s-enhance
[kubernetes] Support --config to mount buildkit.toml and -driver-opt=qemu.install=true,qemu.image=tonistiigi/binfmt:latest for qemu installing
2021-08-10 13:15:32 -07:00
CrazyMax
6149507c7e Merge pull request #670 from crazy-max/cache-gha-doc
Example with gha cache
2021-08-09 13:39:59 +02:00
Morlay
c76b5eac03 feat(driver/kubernetes): support mount buildkit.toml and qemu installing
Signed-off-by: Morlay <morlay.null@gmail.com>
2021-08-04 21:32:27 +08:00
CrazyMax
cd133cee25 Merge pull request #700 from crazy-max/bake-docs
Update bake docs
2021-08-03 14:53:43 +02:00
CrazyMax
eeab638476 Merge pull request #710 from crazy-max/update-ctnd
bump github.com/containerd/containerd from 1.5.4 to 1.5.5
2021-08-02 14:32:44 +02:00
CrazyMax
19b9b86af8 bump github.com/containerd/containerd from 1.5.4 to 1.5.5
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-02 13:47:27 +02:00
CrazyMax
0101c96532 Update cache docs
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-02 11:05:42 +02:00
CrazyMax
85dedf1aea Create checksums for artifacts
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-02 03:37:36 +02:00
CrazyMax
5f05bd9a2b Update bake docs
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-08-01 03:21:20 +02:00
2364 changed files with 205764 additions and 54400 deletions

View File

@@ -1,3 +1,3 @@
bin/ bin/
cross-out/ cross-out/
release-out/ release-out/

View File

@@ -15,158 +15,34 @@ on:
env: env:
REPO_SLUG: "docker/buildx-bin" REPO_SLUG: "docker/buildx-bin"
REPO_SLUG_ORIGIN: "moby/buildkit:master" RELEASE_OUT: "./release-out"
CACHEKEY_BINARIES: "binaries"
PLATFORMS: "linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x,linux/ppc64le,linux/riscv64"
jobs: jobs:
base: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
-
name: Cache ${{ env.CACHEKEY_BINARIES }}
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
key: ${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-
- -
name: Set up QEMU name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1
- -
name: Set up Docker Buildx name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v1
with:
driver-opts: image=${{ env.REPO_SLUG_ORIGIN }}
-
name: Build ${{ env.CACHEKEY_BINARIES }}
run: |
./hack/build_ci_first_pass binaries
env:
CACHEDIR_FROM: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
CACHEDIR_TO: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}-new
-
# FIXME: Temp fix for https://github.com/moby/buildkit/issues/1850
name: Move cache
run: |
rm -rf /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
mv /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}-new /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
test:
runs-on: ubuntu-latest
needs: [base]
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Cache ${{ env.CACHEKEY_BINARIES }}
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
key: ${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
driver-opts: image=${{ env.REPO_SLUG_ORIGIN }}
- -
name: Test name: Test
run: | run: |
make test make test
env:
TEST_COVERAGE: 1
TESTFLAGS: -v --parallel=6 --timeout=20m
CACHEDIR_FROM: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
- -
name: Send to Codecov name: Send to Codecov
uses: codecov/codecov-action@v2 uses: codecov/codecov-action@v2
with: with:
file: ./coverage/coverage.txt file: ./coverage/coverage.txt
cross:
runs-on: ubuntu-latest
needs: [base]
steps:
- -
name: Checkout name: Build binaries
uses: actions/checkout@v2
-
name: Cache ${{ env.CACHEKEY_BINARIES }}
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
key: ${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
driver-opts: image=${{ env.REPO_SLUG_ORIGIN }}
-
name: Cross
run: | run: |
make cross make release
env:
TARGETPLATFORM: ${{ env.PLATFORMS }},darwin/amd64,darwin/arm64,windows/amd64,windows/arm64
CACHEDIR_FROM: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
binaries:
runs-on: ubuntu-latest
needs: [test, cross]
env:
RELEASE_OUT: ./release-out
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Prepare
id: prep
run: |
TAG=pr
if [[ $GITHUB_REF == refs/tags/v* ]]; then
TAG=${GITHUB_REF#refs/tags/}
elif [[ $GITHUB_REF == refs/heads/* ]]; then
TAG=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
fi
echo ::set-output name=tag::${TAG}
-
name: Cache ${{ env.CACHEKEY_BINARIES }}
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
key: ${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-${{ env.CACHEKEY_BINARIES }}-
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
driver-opts: image=${{ env.REPO_SLUG_ORIGIN }}
-
name: Build ${{ steps.prep.outputs.tag }}
run: |
./hack/release ${{ env.RELEASE_OUT }}
env:
PLATFORMS: ${{ env.PLATFORMS }},darwin/amd64,darwin/arm64,windows/amd64,windows/arm64
CACHEDIR_FROM: /tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
- -
name: Upload artifacts name: Upload artifacts
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
@@ -185,6 +61,7 @@ jobs:
type=ref,event=branch type=ref,event=branch
type=ref,event=pr type=ref,event=pr
type=semver,pattern={{version}} type=semver,pattern={{version}}
bake-target: meta-helper
- -
name: Login to DockerHub name: Login to DockerHub
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
@@ -194,15 +71,13 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- -
name: Build and push image name: Build and push image
uses: docker/build-push-action@v2 uses: docker/bake-action@v1
with: with:
context: . files: |
target: binaries ./docker-bake.hcl
${{ steps.meta.outputs.bake-file }}
targets: image-cross
push: ${{ github.event_name != 'pull_request' }} push: ${{ github.event_name != 'pull_request' }}
cache-from: type=local,src=/tmp/.buildx-cache/${{ env.CACHEKEY_BINARIES }}
platforms: ${{ env.PLATFORMS }},darwin/amd64,darwin/arm64,windows/amd64,windows/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- -
name: GitHub Release name: GitHub Release
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
@@ -212,4 +87,26 @@ jobs:
with: with:
draft: true draft: true
files: ${{ env.RELEASE_OUT }}/* files: ${{ env.RELEASE_OUT }}/*
name: ${{ steps.prep.outputs.tag }}
buildkit-edge:
runs-on: ubuntu-latest
continue-on-error: true
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
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@v1
with:
targets: binaries-cross

101
.github/workflows/e2e.yml vendored Normal file
View File

@@ -0,0 +1,101 @@
name: e2e
on:
workflow_dispatch:
push:
branches:
- 'master'
- 'v[0-9]*'
pull_request:
branches:
- 'master'
- 'v[0-9]*'
jobs:
driver:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
driver:
- docker
- docker-container
- kubernetes
buildkit:
- moby/buildkit:buildx-stable-1
- moby/buildkit:master
buildkit-cfg:
- bkcfg-false
- bkcfg-true
multi-node:
- mnode-false
- mnode-true
platforms:
- linux/amd64
- linux/amd64,linux/arm64
include:
- driver: kubernetes
driver-opt: qemu.install=true
exclude:
- driver: docker
multi-node: mnode-true
- driver: docker
buildkit-cfg: bkcfg-true
- driver: docker-container
multi-node: mnode-true
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
if: matrix.driver == 'docker' || matrix.driver == 'docker-container'
-
name: Install buildx
run: |
make install
docker buildx version
-
name: Init env vars
run: |
# BuildKit cfg
if [ "${{ matrix.buildkit-cfg }}" = "bkcfg-true" ]; then
cat > "/tmp/buildkitd.toml" <<EOL
[worker.oci]
max-parallelism = 2
EOL
echo "BUILDKIT_CFG=/tmp/buildkitd.toml" >> $GITHUB_ENV
fi
# Multi node
if [ "${{ matrix.multi-node }}" = "mnode-true" ]; then
echo "MULTI_NODE=1" >> $GITHUB_ENV
else
echo "MULTI_NODE=0" >> $GITHUB_ENV
fi
-
name: Install k3s
if: matrix.driver == 'kubernetes'
uses: debianmaster/actions-k3s@v1.0.3
id: k3s
with:
version: v1.21.2-k3s1
-
name: Config k3s
if: matrix.driver == 'kubernetes'
run: |
(set -x ; cat ${{ steps.k3s.outputs.kubeconfig }})
-
name: Check k3s nodes
if: matrix.driver == 'kubernetes'
run: |
kubectl get nodes
-
name: Test
run: |
make test-driver
env:
BUILDKIT_IMAGE: ${{ matrix.buildkit }}
DRIVER: ${{ matrix.driver }}
DRIVER_OPT: ${{ matrix.driver-opt }}
PLATFORMS: ${{ matrix.platforms }}

View File

@@ -13,9 +13,6 @@ on:
- 'master' - 'master'
- 'v[0-9]*' - 'v[0-9]*'
env:
REPO_SLUG_ORIGIN: "moby/buildkit:master"
jobs: jobs:
validate: validate:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -30,12 +27,22 @@ jobs:
- -
name: Checkout name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
driver-opts: image=${{ env.REPO_SLUG_ORIGIN }}
- -
name: Run name: Run
run: | run: |
make ${{ matrix.target }} make ${{ matrix.target }}
validate-docs-yaml:
runs-on: ubuntu-latest
needs:
- validate
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Run
run: |
make docs
env:
FORMATS: yaml

View File

@@ -1,6 +1,13 @@
# This file lists all individuals having contributed content to the repository. # This file lists all individuals having contributed content to the repository.
# For how it is generated, see `hack/generate-authors`. # For how it is generated, see hack/dockerfiles/authors.Dockerfile.
CrazyMax <github@crazymax.dev>
CrazyMax <github@crazymax.dev> <1951866+crazy-max@users.noreply.github.com>
CrazyMax <github@crazymax.dev> <crazy-max@users.noreply.github.com>
Sebastiaan van Stijn <github@gone.nl>
Sebastiaan van Stijn <github@gone.nl> <thaJeztah@users.noreply.github.com>
Tibor Vass <tibor@docker.com> Tibor Vass <tibor@docker.com>
Tibor Vass <tibor@docker.com> <tiborvass@users.noreply.github.com> Tibor Vass <tibor@docker.com> <tiborvass@users.noreply.github.com>
Tõnis Tiigi <tonistiigi@gmail.com> Tõnis Tiigi <tonistiigi@gmail.com>
Ulysses Souza <ulyssessouza@gmail.com>
Wang Jinglei <morlay.null@gmail.com>

40
AUTHORS
View File

@@ -1,7 +1,45 @@
# This file lists all individuals having contributed content to the repository. # This file lists all individuals having contributed content to the repository.
# For how it is generated, see `scripts/generate-authors.sh`. # For how it is generated, see hack/dockerfiles/authors.Dockerfile.
Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
Alex Couture-Beil <alex@earthly.dev>
Andrew Haines <andrew.haines@zencargo.com>
Andy MacKinlay <admackin@users.noreply.github.com>
Anthony Poschen <zanven42@gmail.com>
Artur Klauser <Artur.Klauser@computer.org>
Batuhan Apaydın <developerguy2@gmail.com>
Bin Du <bindu@microsoft.com> Bin Du <bindu@microsoft.com>
Brandon Philips <brandon@ifup.org>
Brian Goff <cpuguy83@gmail.com> Brian Goff <cpuguy83@gmail.com>
CrazyMax <github@crazymax.dev>
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Devin Bayer <dev@doubly.so>
Djordje Lukic <djordje.lukic@docker.com>
Dmytro Makovey <dmytro.makovey@docker.com>
Donghui Wang <977675308@qq.com>
faust <faustin@fala.red>
Felipe Santos <felipecassiors@gmail.com>
Fernando Miguel <github@FernandoMiguel.net>
gfrancesco <gfrancesco@users.noreply.github.com>
gracenoah <gracenoahgh@gmail.com>
Hollow Man <hollowman@hollowman.ml>
Ilya Dmitrichenko <errordeveloper@gmail.com>
Jack Laxson <jackjrabbit@gmail.com>
Jean-Yves Gastaud <jygastaud@gmail.com>
khs1994 <khs1994@khs1994.com>
Kotaro Adachi <k33asby@gmail.com>
l00397676 <lujingxiao@huawei.com>
Michal Augustyn <michal.augustyn@mail.com>
Patrick Van Stee <patrick@vanstee.me>
Saul Shanabrook <s.shanabrook@gmail.com>
Sebastiaan van Stijn <github@gone.nl>
SHIMA Tatsuya <ts1s1andn@gmail.com>
Silvin Lubecki <silvin.lubecki@docker.com>
Solomon Hykes <sh.github.6811@hykes.org>
Sune Keller <absukl@almbrand.dk>
Tibor Vass <tibor@docker.com> Tibor Vass <tibor@docker.com>
Tõnis Tiigi <tonistiigi@gmail.com> Tõnis Tiigi <tonistiigi@gmail.com>
Ulysses Souza <ulyssessouza@gmail.com>
Wang Jinglei <morlay.null@gmail.com>
Xiang Dai <764524258@qq.com>
zelahi <elahi.zuhayr@gmail.com>

View File

@@ -1,24 +1,16 @@
# syntax=docker/dockerfile:1.2 # syntax=docker/dockerfile:1.3
ARG DOCKERD_VERSION=19.03 ARG GO_VERSION=1.17
ARG CLI_VERSION=19.03 ARG DOCKERD_VERSION=20.10.8
FROM docker:$DOCKERD_VERSION AS dockerd-release FROM docker:$DOCKERD_VERSION AS dockerd-release
# xx is a helper for cross-compilation # xx is a helper for cross-compilation
FROM --platform=$BUILDPLATFORM tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04 AS xx FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.0.0 AS xx
FROM --platform=$BUILDPLATFORM golang:1.16-alpine AS golatest FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS golatest
FROM golatest AS go-linux FROM golatest AS gobase
FROM golatest AS go-darwin
FROM golatest AS go-windows-amd64
FROM golatest AS go-windows-386
FROM golatest AS go-windows-arm
FROM --platform=$BUILDPLATFORM golang:1.17beta1-alpine AS go-windows-arm64
FROM go-windows-${TARGETARCH} AS go-windows
FROM go-${TARGETOS} AS gobase
COPY --from=xx / / COPY --from=xx / /
RUN apk add --no-cache file git RUN apk add --no-cache file git
ENV GOFLAGS=-mod=vendor ENV GOFLAGS=-mod=vendor
@@ -32,25 +24,24 @@ RUN --mount=target=. \
FROM gobase AS buildx-build FROM gobase AS buildx-build
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
ARG LDFLAGS="-w -s"
ARG TARGETPLATFORM ARG TARGETPLATFORM
RUN --mount=target=. --mount=target=/root/.cache,type=cache \ RUN --mount=type=bind,target=. \
--mount=target=/go/pkg/mod,type=cache \ --mount=type=cache,target=/root/.cache \
--mount=source=/tmp/.ldflags,target=/tmp/.ldflags,from=buildx-version \ --mount=type=cache,target=/go/pkg/mod \
set -x; xx-go build -ldflags "$(cat /tmp/.ldflags)" -o /usr/bin/buildx ./cmd/buildx && \ --mount=type=bind,source=/tmp/.ldflags,target=/tmp/.ldflags,from=buildx-version \
set -x; xx-go build -ldflags "$(cat /tmp/.ldflags) ${LDFLAGS}" -o /usr/bin/buildx ./cmd/buildx && \
xx-verify --static /usr/bin/buildx xx-verify --static /usr/bin/buildx
FROM buildx-build AS integration-tests FROM buildx-build AS test
COPY . . RUN --mount=type=bind,target=. \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
go test -v -coverprofile=/tmp/coverage.txt -covermode=atomic ./... && \
go tool cover -func=/tmp/coverage.txt
# FROM golang:1.12-alpine AS docker-cli-build FROM scratch AS test-coverage
# RUN apk add -U git bash coreutils gcc musl-dev COPY --from=test /tmp/coverage.txt /coverage.txt
# ENV CGO_ENABLED=0
# ARG REPO=github.com/tiborvass/cli
# ARG BRANCH=cli-plugin-aliases
# ARG CLI_VERSION
# WORKDIR /go/src/github.com/docker/cli
# RUN git clone git://$REPO . && git checkout $BRANCH
# RUN ./scripts/build/binary
FROM scratch AS binaries-unix FROM scratch AS binaries-unix
COPY --from=buildx-build /usr/bin/buildx / COPY --from=buildx-build /usr/bin/buildx /
@@ -63,28 +54,29 @@ COPY --from=buildx-build /usr/bin/buildx /buildx.exe
FROM binaries-$TARGETOS AS binaries FROM binaries-$TARGETOS AS binaries
# Release
FROM --platform=$BUILDPLATFORM alpine AS releaser FROM --platform=$BUILDPLATFORM alpine AS releaser
WORKDIR /work WORKDIR /work
ARG TARGETPLATFORM ARG TARGETPLATFORM
RUN --mount=from=binaries \ RUN --mount=from=binaries \
--mount=source=/tmp/.version,target=/tmp/.version,from=buildx-version \ --mount=type=bind,source=/tmp/.version,target=/tmp/.version,from=buildx-version \
mkdir -p /out && cp buildx* "/out/buildx-$(cat /tmp/.version).$(echo $TARGETPLATFORM | sed 's/\//-/g')$(ls buildx* | sed -e 's/^buildx//')" mkdir -p /out && cp buildx* "/out/buildx-$(cat /tmp/.version).$(echo $TARGETPLATFORM | sed 's/\//-/g')$(ls buildx* | sed -e 's/^buildx//')"
FROM scratch AS release FROM scratch AS release
COPY --from=releaser /out/ / COPY --from=releaser /out/ /
FROM alpine AS demo-env # Shell
FROM docker:$DOCKERD_VERSION AS dockerd-release
FROM alpine AS shell
RUN apk add --no-cache iptables tmux git vim less openssh RUN apk add --no-cache iptables tmux git vim less openssh
RUN mkdir -p /usr/local/lib/docker/cli-plugins && ln -s /usr/local/bin/buildx /usr/local/lib/docker/cli-plugins/docker-buildx RUN mkdir -p /usr/local/lib/docker/cli-plugins && ln -s /usr/local/bin/buildx /usr/local/lib/docker/cli-plugins/docker-buildx
COPY ./hack/demo-env/entrypoint.sh /usr/local/bin COPY ./hack/demo-env/entrypoint.sh /usr/local/bin
COPY ./hack/demo-env/tmux.conf /root/.tmux.conf COPY ./hack/demo-env/tmux.conf /root/.tmux.conf
COPY --from=dockerd-release /usr/local/bin /usr/local/bin COPY --from=dockerd-release /usr/local/bin /usr/local/bin
#COPY --from=docker-cli-build /go/src/github.com/docker/cli/build/docker /usr/local/bin
WORKDIR /work WORKDIR /work
COPY ./hack/demo-env/examples . COPY ./hack/demo-env/examples .
COPY --from=binaries / /usr/local/bin/ COPY --from=binaries / /usr/local/bin/
VOLUME /var/lib/docker VOLUME /var/lib/docker
ENTRYPOINT ["entrypoint.sh"] ENTRYPOINT ["entrypoint.sh"]
FROM binaries FROM binaries

View File

@@ -1,32 +1,51 @@
ifneq (, $(BUILDX_BIN))
export BUILDX_CMD = $(BUILDX_BIN)
else ifneq (, $(shell docker buildx version))
export BUILDX_CMD = docker buildx
else ifneq (, $(shell which buildx))
export BUILDX_CMD = $(which buildx)
else
$(error "Buildx is required: https://github.com/docker/buildx#installing")
endif
export BIN_OUT = ./bin
export RELEASE_OUT = ./release-out
shell: shell:
./hack/shell ./hack/shell
binaries: binaries:
./hack/binaries $(BUILDX_CMD) bake binaries
binaries-cross: binaries-cross:
EXPORT_LOCAL=cross-out ./hack/cross $(BUILDX_CMD) bake binaries-cross
cross:
./hack/cross
install: binaries install: binaries
mkdir -p ~/.docker/cli-plugins mkdir -p ~/.docker/cli-plugins
install bin/buildx ~/.docker/cli-plugins/docker-buildx install bin/buildx ~/.docker/cli-plugins/docker-buildx
release:
./hack/release
validate-all: lint test validate-vendor validate-docs
lint: lint:
./hack/lint $(BUILDX_CMD) bake lint
test: test:
./hack/test $(BUILDX_CMD) bake test
validate-vendor: validate-vendor:
./hack/validate-vendor $(BUILDX_CMD) bake validate-vendor
validate-docs: validate-docs:
./hack/validate-docs $(BUILDX_CMD) bake validate-docs
validate-all: lint test validate-vendor validate-docs validate-authors:
$(BUILDX_CMD) bake validate-authors
test-driver:
./hack/test-driver
vendor: vendor:
./hack/update-vendor ./hack/update-vendor
@@ -34,7 +53,10 @@ vendor:
docs: docs:
./hack/update-docs ./hack/update-docs
generate-authors: authors:
./hack/generate-authors $(BUILDX_CMD) bake update-authors
.PHONY: vendor lint shell binaries install binaries-cross validate-all generate-authors validate-docs docs mod-outdated:
$(BUILDX_CMD) bake mod-outdated
.PHONY: shell binaries binaries-cross install release validate-all lint validate-vendor validate-docs validate-authors vendor docs authors

312
README.md
View File

@@ -1,11 +1,13 @@
# buildx # buildx
[![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/buildx) [![GitHub release](https://img.shields.io/github/release/docker/buildx.svg?style=flat-square)](https://github.com/docker/buildx/releases/latest)
[![Build Status](https://github.com/docker/buildx/workflows/build/badge.svg)](https://github.com/docker/buildx/actions?query=workflow%3Abuild) [![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?style=flat-square&logo=go&logoColor=white)](https://pkg.go.dev/github.com/docker/buildx)
[![Go Report Card](https://goreportcard.com/badge/github.com/docker/buildx)](https://goreportcard.com/report/github.com/docker/buildx) [![Build Status](https://img.shields.io/github/workflow/status/docker/buildx/build?label=build&logo=github&style=flat-square)](https://github.com/docker/buildx/actions?query=workflow%3Abuild)
[![codecov](https://codecov.io/gh/docker/buildx/branch/master/graph/badge.svg)](https://codecov.io/gh/docker/buildx) [![Go Report Card](https://goreportcard.com/badge/github.com/docker/buildx?style=flat-square)](https://goreportcard.com/report/github.com/docker/buildx)
[![codecov](https://img.shields.io/codecov/c/github/docker/buildx?logo=codecov&style=flat-square)](https://codecov.io/gh/docker/buildx)
`buildx` is a Docker CLI plugin for extended build capabilities with [BuildKit](https://github.com/moby/buildkit). `buildx` is a Docker CLI plugin for extended build capabilities with
[BuildKit](https://github.com/moby/buildkit).
Key features: Key features:
@@ -20,74 +22,134 @@ Key features:
# Table of Contents # Table of Contents
- [Installing](#installing) - [Installing](#installing)
- [Docker](#docker) - [Windows and macOS](#windows-and-macos)
- [Binary release](#binary-release) - [Linux packages](#linux-packages)
- [From `Dockerfile`](#from-dockerfile) - [Manual download](#manual-download)
- [Dockerfile](#dockerfile)
- [Set buildx as the default builder](#set-buildx-as-the-default-builder)
- [Building](#building) - [Building](#building)
- [with Docker 18.09+](#with-docker-1809)
- [with buildx or Docker 19.03](#with-buildx-or-docker-1903)
- [Getting started](#getting-started) - [Getting started](#getting-started)
- [Building with buildx](#building-with-buildx) - [Building with buildx](#building-with-buildx)
- [Working with builder instances](#working-with-builder-instances) - [Working with builder instances](#working-with-builder-instances)
- [Building multi-platform images](#building-multi-platform-images) - [Building multi-platform images](#building-multi-platform-images)
- [High-level build options](#high-level-build-options) - [High-level build options](#high-level-build-options)
- [Documentation](docs/reference) - [Guides](docs/guides)
- [`buildx build [OPTIONS] PATH | URL | -`](docs/reference/buildx_build.md) - [CI/CD](docs/guides/cicd.md)
- [`buildx create [OPTIONS] [CONTEXT|ENDPOINT]`](docs/reference/buildx_create.md) - [CNI networking](docs/guides/cni-networking.md)
- [`buildx use NAME`](docs/reference/buildx_use.md) - [Registry mirror](docs/guides/registry-mirror.md)
- [`buildx inspect [NAME]`](docs/reference/buildx_inspect.md) - [Resource limiting](docs/guides/resource-limiting.md)
- [Using a custom network](docs/guides/custom-network.md)
- [Using a custom registry configuration](docs/guides/custom-registry-config.md)
- [Reference](docs/reference/buildx.md)
- [`buildx bake`](docs/reference/buildx_bake.md)
- [`buildx build`](docs/reference/buildx_build.md)
- [`buildx create`](docs/reference/buildx_create.md)
- [`buildx du`](docs/reference/buildx_du.md)
- [`buildx imagetools`](docs/reference/buildx_imagetools.md)
- [`buildx imagetools create`](docs/reference/buildx_imagetools_create.md)
- [`buildx imagetools inspect`](docs/reference/buildx_imagetools_inspect.md)
- [`buildx inspect`](docs/reference/buildx_inspect.md)
- [`buildx install`](docs/reference/buildx_install.md)
- [`buildx ls`](docs/reference/buildx_ls.md) - [`buildx ls`](docs/reference/buildx_ls.md)
- [`buildx stop [NAME]`](docs/reference/buildx_stop.md) - [`buildx prune`](docs/reference/buildx_prune.md)
- [`buildx rm [NAME]`](docs/reference/buildx_rm.md) - [`buildx rm`](docs/reference/buildx_rm.md)
- [`buildx bake [OPTIONS] [TARGET...]`](docs/reference/buildx_bake.md) - [`buildx stop`](docs/reference/buildx_stop.md)
- [`buildx imagetools create [OPTIONS] [SOURCE] [SOURCE...]`](docs/reference/buildx_imagetools_create.md) - [`buildx uninstall`](docs/reference/buildx_uninstall.md)
- [`buildx imagetools inspect NAME`](docs/reference/buildx_imagetools_inspect.md) - [`buildx use`](docs/reference/buildx_use.md)
- [Setting buildx as default builder in Docker 19.03+](#setting-buildx-as-default-builder-in-docker-1903) - [`buildx version`](docs/reference/buildx_version.md)
- [Contributing](#contributing) - [Contributing](#contributing)
# Installing # Installing
Using `buildx` as a docker CLI plugin requires using Docker 19.03 or newer. A limited set of functionality works with older versions of Docker when invoking the binary directly. Using `buildx` as a docker CLI plugin requires using Docker 19.03 or newer.
A limited set of functionality works with older versions of Docker when
invoking the binary directly.
### Docker ## Windows and macOS
`buildx` comes bundled with Docker Desktop and in latest Docker CE packages, but may not be included in all Linux distros (in which case follow the binary release instructions). Docker Buildx is included in [Docker Desktop](https://docs.docker.com/desktop/)
for Windows and macOS.
### Binary release ## Linux packages
Download the latest binary release from https://github.com/docker/buildx/releases/latest and copy it to `~/.docker/cli-plugins` folder with name `docker-buildx`. Docker Linux packages also include Docker Buildx when installed using the
[DEB or RPM packages](https://docs.docker.com/engine/install/).
Change the permission to execute: ## Manual download
```sh
chmod a+x ~/.docker/cli-plugins/docker-buildx
```
### From `Dockerfile` > **Important**
>
> This section is for unattended installation of the buildx component. These
> instructions are mostly suitable for testing purposes. We do not recommend
> installing buildx using manual download in production environments as they
> will not be updated automatically with security updates.
>
> On Windows and macOS, we recommend that you install [Docker Desktop](https://docs.docker.com/desktop/)
> instead. For Linux, we recommend that you follow the [instructions specific for your distribution](#linux-packages).
Here is how to use buildx inside a Dockerfile through the [`docker/buildx-bin`](https://hub.docker.com/r/docker/buildx-bin) image: You can also download the latest binary from the [GitHub releases page](https://github.com/docker/buildx/releases/latest).
Rename the relevant binary and copy it to the destination matching your OS:
| OS | Binary name | Destination folder |
| -------- | -------------------- | -----------------------------------------|
| Linux | `docker-buildx` | `$HOME/.docker/cli-plugins` |
| macOS | `docker-buildx` | `$HOME/.docker/cli-plugins` |
| Windows | `docker-buildx.exe` | `%USERPROFILE%\.docker\cli-plugins` |
Or copy it into one of these folders for installing it system-wide.
On Unix environments:
* `/usr/local/lib/docker/cli-plugins` OR `/usr/local/libexec/docker/cli-plugins`
* `/usr/lib/docker/cli-plugins` OR `/usr/libexec/docker/cli-plugins`
On Windows:
* `C:\ProgramData\Docker\cli-plugins`
* `C:\Program Files\Docker\cli-plugins`
> **Note**
>
> On Unix environments, it may also be necessary to make it executable with `chmod +x`:
> ```shell
> $ chmod +x ~/.docker/cli-plugins/docker-buildx
> ```
## Dockerfile
Here is how to install and use Buildx inside a Dockerfile through the
[`docker/buildx-bin`](https://hub.docker.com/r/docker/buildx-bin) image:
```Dockerfile ```Dockerfile
FROM docker FROM docker
COPY --from=docker/buildx-bin:latest /buildx /usr/libexec/docker/cli-plugins/docker-buildx COPY --from=docker/buildx-bin /buildx /usr/libexec/docker/cli-plugins/docker-buildx
RUN docker buildx version RUN docker buildx version
``` ```
# Set buildx as the default builder
Running the command [`docker buildx install`](docs/reference/buildx_install.md)
sets up docker builder command as an alias to `docker buildx build`. This
results in the ability to have `docker build` use the current buildx builder.
To remove this alias, run [`docker buildx uninstall`](docs/reference/buildx_uninstall.md).
# Building # Building
```console
# Buildx 0.6+
$ docker buildx bake "https://github.com/docker/buildx.git"
$ mkdir -p ~/.docker/cli-plugins
$ mv ./bin/buildx ~/.docker/cli-plugins/docker-buildx
### with buildx or Docker 19.03+ # Docker 19.03+
``` $ DOCKER_BUILDKIT=1 docker build --platform=local -o . "https://github.com/docker/buildx.git"
$ export DOCKER_BUILDKIT=1
$ docker build --platform=local -o . git://github.com/docker/buildx
$ mkdir -p ~/.docker/cli-plugins $ mkdir -p ~/.docker/cli-plugins
$ mv buildx ~/.docker/cli-plugins/docker-buildx $ mv buildx ~/.docker/cli-plugins/docker-buildx
```
### with Docker 18.09+ # Local
``` $ git clone https://github.com/docker/buildx.git && cd buildx
$ git clone git://github.com/docker/buildx && cd buildx
$ make install $ make install
``` ```
@@ -95,65 +157,151 @@ $ make install
## Building with buildx ## Building with buildx
Buildx is a Docker CLI plugin that extends the `docker build` command with the full support of the features provided by [Moby BuildKit](https://github.com/moby/buildkit) builder toolkit. It provides the same user experience as `docker build` with many new features like creating scoped builder instances and building against multiple nodes concurrently. Buildx is a Docker CLI plugin that extends the `docker build` command with the
full support of the features provided by [Moby BuildKit](https://github.com/moby/buildkit)
builder toolkit. It provides the same user experience as `docker build` with
many new features like creating scoped builder instances and building against
multiple nodes concurrently.
After installation, buildx can be accessed through the `docker buildx` command with Docker 19.03. `docker buildx build` is the command for starting a new build. With Docker versions older than 19.03 buildx binary can be called directly to access the `docker buildx` subcommands. After installation, buildx can be accessed through the `docker buildx` command
with Docker 19.03. `docker buildx build` is the command for starting a new
build. With Docker versions older than 19.03 buildx binary can be called
directly to access the `docker buildx` subcommands.
``` ```console
$ docker buildx build . $ docker buildx build .
[+] Building 8.4s (23/32) [+] Building 8.4s (23/32)
=> ... => ...
``` ```
Buildx will always build using the BuildKit engine and does not require
`DOCKER_BUILDKIT=1` environment variable for starting builds.
Buildx will always build using the BuildKit engine and does not require `DOCKER_BUILDKIT=1` environment variable for starting builds. The `docker buildx build` command supports features available for `docker build`,
including features such as outputs configuration, inline build caching, and
specifying target platform. In addition, Buildx also supports new features that
are not yet available for regular `docker build` like building manifest lists,
distributed caching, and exporting build results to OCI image tarballs.
Buildx build command supports the features available for `docker build` including the new features in Docker 19.03 such as outputs configuration, inline build caching or specifying target platform. In addition, buildx supports new features not yet available for regular `docker build` like building manifest lists, distributed caching, exporting build results to OCI image tarballs etc. Buildx is supposed to be flexible and can be run in different configurations
that are exposed through a driver concept. Currently, we support a
Buildx is supposed to be flexible and can be run in different configurations that are exposed through a driver concept. Currently, we support a "docker" driver that uses the BuildKit library bundled into the Docker daemon binary, and a "docker-container" driver that automatically launches BuildKit inside a Docker container. We plan to add more drivers in the future, for example, one that would allow running buildx inside an (unprivileged) container. [`docker` driver](docs/reference/buildx_create.md#docker-driver) that uses
the BuildKit library bundled into the Docker daemon binary, a
The user experience of using buildx is very similar across drivers, but there are some features that are not currently supported by the "docker" driver, because the BuildKit library bundled into docker daemon currently uses a different storage component. In contrast, all images built with "docker" driver are automatically added to the "docker images" view by default, whereas when using other drivers the method for outputting an image needs to be selected with `--output`. [`docker-container` driver](docs/reference/buildx_create.md#docker-container-driver)
that automatically launches BuildKit inside a Docker container and a
[`kubernetes` driver](docs/reference/buildx_create.md#kubernetes-driver) to
spin up pods with defined BuildKit container image to build your images. We
plan to add more drivers in the future.
The user experience of using buildx is very similar across drivers, but there
are some features that are not currently supported by the `docker` driver,
because the BuildKit library bundled into docker daemon currently uses a
different storage component. In contrast, all images built with `docker` driver
are automatically added to the `docker images` view by default, whereas when
using other drivers the method for outputting an image needs to be selected
with `--output`.
## Working with builder instances ## Working with builder instances
By default, buildx will initially use the "docker" driver if it is supported, providing a very similar user experience to the native `docker build`. But using a local shared daemon is only one way to build your applications. By default, buildx will initially use the `docker` driver if it is supported,
providing a very similar user experience to the native `docker build`. Note that
you must use a local shared daemon to build your applications.
Buildx allows you to create new instances of isolated builders. This can be used for getting a scoped environment for your CI builds that does not change the state of the shared daemon or for isolating the builds for different projects. You can create a new instance for a set of remote nodes, forming a build farm, and quickly switch between them. Buildx allows you to create new instances of isolated builders. This can be
used for getting a scoped environment for your CI builds that does not change
the state of the shared daemon or for isolating the builds for different
projects. You can create a new instance for a set of remote nodes, forming a
build farm, and quickly switch between them.
New instances can be created with `docker buildx create` command. This will create a new builder instance with a single node based on your current configuration. To use a remote node you can specify the `DOCKER_HOST` or remote context name while creating the new builder. After creating a new instance you can manage its lifecycle with the `inspect`, `stop` and `rm` commands and list all available builders with `ls`. After creating a new builder you can also append new nodes to it. You can create new instances using the [`docker buildx create`](docs/reference/buildx_create.md)
command. This creates a new builder instance with a single node based on your
current configuration.
To switch between different builders, use `docker buildx use <name>`. After running this command the build commands would automatically keep using this builder. To use a remote node you can specify the `DOCKER_HOST` or the remote context name
while creating the new builder. After creating a new instance, you can manage its
lifecycle using the [`docker buildx inspect`](docs/reference/buildx_inspect.md),
[`docker buildx stop`](docs/reference/buildx_stop.md), and
[`docker buildx rm`](docs/reference/buildx_rm.md) commands. To list all
available builders, use [`buildx ls`](docs/reference/buildx_ls.md). After
creating a new builder you can also append new nodes to it.
Docker 19.03 also features a new `docker context` command that can be used for giving names for remote Docker API endpoints. Buildx integrates with `docker context` so that all of your contexts automatically get a default builder instance. While creating a new builder instance or when adding a node to it you can also set the context name as the target. To switch between different builders, use [`docker buildx use <name>`](docs/reference/buildx_use.md).
After running this command, the build commands will automatically use this
builder.
Docker also features a [`docker context`](https://docs.docker.com/engine/reference/commandline/context/)
command that can be used for giving names for remote Docker API endpoints.
Buildx integrates with `docker context` so that all of your contexts
automatically get a default builder instance. While creating a new builder
instance or when adding a node to it you can also set the context name as the
target.
## Building multi-platform images ## Building multi-platform images
BuildKit is designed to work well for building for multiple platforms and not only for the architecture and operating system that the user invoking the build happens to run. BuildKit is designed to work well for building for multiple platforms and not
only for the architecture and operating system that the user invoking the build
happens to run.
When invoking a build, the `--platform` flag can be used to specify the target platform for the build output, (e.g. linux/amd64, linux/arm64, darwin/amd64). When the current builder instance is backed by the "docker-container" driver, multiple platforms can be specified together. In this case, a manifest list will be built, containing images for all of the specified architectures. When this image is used in `docker run` or `docker service`, Docker will pick the correct image based on the nodes platform. When you invoke a build, you can set the `--platform` flag to specify the target
platform for the build output, (for example, `linux/amd64`, `linux/arm64`, or
`darwin/amd64`).
Multi-platform images can be built by mainly three different strategies that are all supported by buildx and Dockerfiles. You can use the QEMU emulation support in the kernel, build on multiple native nodes using the same builder instance or use a stage in Dockerfile to cross-compile to different architectures. When the current builder instance is backed by the `docker-container` or
`kubernetes` driver, you can specify multiple platforms together. In this case,
it builds a manifest list which contains images for all specified architectures.
When you use this image in [`docker run`](https://docs.docker.com/engine/reference/commandline/run/)
or [`docker service`](https://docs.docker.com/engine/reference/commandline/service/),
Docker picks the correct image based on the node's platform.
QEMU is the easiest way to get started if your node already supports it (e.g. if you are using Docker Desktop). It requires no changes to your Dockerfile and BuildKit will automatically detect the secondary architectures that are available. When BuildKit needs to run a binary for a different architecture it will automatically load it through a binary registered in the binfmt_misc handler. For QEMU binaries registered with binfmt_misc on the host OS to work transparently inside containers they must be registered with the fix_binary flag. This requires a kernel >= 4.8 and binfmt-support >= 2.1.7. You can check for proper registration by checking if `F` is among the flags in `/proc/sys/fs/binfmt_misc/qemu-*`. While Docker Desktop comes preconfigured with binfmt_misc support for additional platforms, for other installations it likely needs to be installed using [`tonistiigi/binfmt`](https://github.com/tonistiigi/binfmt) image. You can build multi-platform images using three different strategies that are
supported by Buildx and Dockerfiles:
``` 1. Using the QEMU emulation support in the kernel
2. Building on multiple native nodes using the same builder instance
3. Using a stage in Dockerfile to cross-compile to different architectures
QEMU is the easiest way to get started if your node already supports it (for
example. if you are using Docker Desktop). It requires no changes to your
Dockerfile and BuildKit automatically detects the secondary architectures that
are available. When BuildKit needs to run a binary for a different architecture,
it automatically loads it through a binary registered in the `binfmt_misc`
handler.
For QEMU binaries registered with `binfmt_misc` on the host OS to work
transparently inside containers they must be registered with the `fix_binary`
flag. This requires a kernel >= 4.8 and binfmt-support >= 2.1.7. You can check
for proper registration by checking if `F` is among the flags in
`/proc/sys/fs/binfmt_misc/qemu-*`. While Docker Desktop comes preconfigured
with `binfmt_misc` support for additional platforms, for other installations
it likely needs to be installed using [`tonistiigi/binfmt`](https://github.com/tonistiigi/binfmt)
image.
```console
$ docker run --privileged --rm tonistiigi/binfmt --install all $ docker run --privileged --rm tonistiigi/binfmt --install all
``` ```
Using multiple native nodes provides better support for more complicated cases not handled by QEMU and generally have better performance. Additional nodes can be added to the builder instance with `--append` flag. Using multiple native nodes provide better support for more complicated cases
that are not handled by QEMU and generally have better performance. You can
add additional nodes to the builder instance using the `--append` flag.
``` Assuming contexts `node-amd64` and `node-arm64` exist in `docker context ls`;
# assuming contexts node-amd64 and node-arm64 exist in "docker context ls"
```console
$ docker buildx create --use --name mybuild node-amd64 $ docker buildx create --use --name mybuild node-amd64
mybuild mybuild
$ docker buildx create --append --name mybuild node-arm64 $ docker buildx create --append --name mybuild node-arm64
$ docker buildx build --platform linux/amd64,linux/arm64 . $ docker buildx build --platform linux/amd64,linux/arm64 .
``` ```
Finally, depending on your project, the language that you use may have good support for cross-compilation. In that case, multi-stage builds in Dockerfiles can be effectively used to build binaries for the platform specified with `--platform` using the native architecture of the build node. List of build arguments like `BUILDPLATFORM` and `TARGETPLATFORM` are available automatically inside your Dockerfile and can be leveraged by the processes running as part of your build. Finally, depending on your project, the language that you use may have good
support for cross-compilation. In that case, multi-stage builds in Dockerfiles
can be effectively used to build binaries for the platform specified with
`--platform` using the native architecture of the build node. A list of build
arguments like `BUILDPLATFORM` and `TARGETPLATFORM` is available automatically
inside your Dockerfile and can be leveraged by the processes running as part
of your build.
``` ```dockerfile
FROM --platform=$BUILDPLATFORM golang:alpine AS build FROM --platform=$BUILDPLATFORM golang:alpine AS build
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG BUILDPLATFORM ARG BUILDPLATFORM
@@ -162,25 +310,31 @@ FROM alpine
COPY --from=build /log /log COPY --from=build /log /log
``` ```
You can also use [`tonistiigi/xx`](https://github.com/tonistiigi/xx) Dockerfile
cross-compilation helpers for more advanced use-cases.
## High-level build options ## High-level build options
Buildx also aims to provide support for higher level build concepts that go beyond invoking a single build command. We want to support building all the images in your application together and let the users define project specific reusable build flows that can then be easily invoked by anyone. Buildx also aims to provide support for high-level build concepts that go beyond
invoking a single build command. We want to support building all the images in
your application together and let the users define project specific reusable
build flows that can then be easily invoked by anyone.
BuildKit has great support for efficiently handling multiple concurrent build requests and deduplicating work. While build commands can be combined with general-purpose command runners (eg. make), these tools generally invoke builds in sequence and therefore cant leverage the full potential of BuildKit parallelization or combine BuildKits output for the user. For this use case we have added a command called `docker buildx bake`. BuildKit efficiently handles multiple concurrent build requests and
de-duplicating work. The build commands can be combined with general-purpose
command runners (for example, `make`). However, these tools generally invoke
builds in sequence and therefore cannot leverage the full potential of BuildKit
parallelization, or combine BuildKits output for the user. For this use case,
we have added a command called [`docker buildx bake`](docs/reference/buildx_bake.md).
Currently, the bake command supports building images from compose files, similar to `compose build` but allowing all the services to be built concurrently as part of a single request. The `bake` command supports building images from compose files, similar to
[`docker-compose build`](https://docs.docker.com/compose/reference/build/),
There is also support for custom build rules from HCL/JSON files allowing better code reuse and different target groups. The design of bake is in very early stages and we are looking for feedback from users. but allowing all the services to be built concurrently as part of a single
request.
[`buildx bake` Reference Docs](docs/reference/buildx_bake.md)
# Setting buildx as default builder in Docker 19.03+
Running `docker buildx install` sets up `docker builder` command as an alias to `docker buildx`. This results in the ability to have `docker build` use the current buildx builder.
To remove this alias, you can run `docker buildx uninstall`.
There is also support for custom build rules from HCL/JSON files allowing
better code reuse and different target groups. The design of bake is in very
early stages and we are looking for feedback from users.
# Contributing # Contributing

View File

@@ -2,11 +2,14 @@ package bake
import ( import (
"context" "context"
"encoding/csv"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"regexp" "regexp"
"sort"
"strconv" "strconv"
"strings" "strings"
@@ -21,14 +24,26 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
var httpPrefix = regexp.MustCompile(`^https?://`) var (
var gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`) httpPrefix = regexp.MustCompile(`^https?://`)
gitURLPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
validTargetNameChars = `[a-zA-Z0-9_-]+`
validTargetNameCharsCompose = `[a-zA-Z0-9._-]+`
targetNamePattern = regexp.MustCompile(`^` + validTargetNameChars + `$`)
targetNamePatternCompose = regexp.MustCompile(`^` + validTargetNameCharsCompose + `$`)
)
type File struct { type File struct {
Name string Name string
Data []byte Data []byte
} }
type Override struct {
Value string
ArrValue []string
}
func defaultFilenames() []string { func defaultFilenames() []string {
return []string{ return []string{
"docker-compose.yml", // support app "docker-compose.yml", // support app
@@ -49,41 +64,98 @@ func ReadLocalFiles(names []string) ([]File, error) {
out := make([]File, 0, len(names)) out := make([]File, 0, len(names))
for _, n := range names { for _, n := range names {
dt, err := ioutil.ReadFile(n) var dt []byte
if err != nil { var err error
if isDefault && errors.Is(err, os.ErrNotExist) { if n == "-" {
continue dt, err = ioutil.ReadAll(os.Stdin)
if err != nil {
return nil, err
}
} else {
dt, err = ioutil.ReadFile(n)
if err != nil {
if isDefault && errors.Is(err, os.ErrNotExist) {
continue
}
return nil, err
} }
return nil, err
} }
out = append(out, File{Name: n, Data: dt}) out = append(out, File{Name: n, Data: dt})
} }
return out, nil return out, nil
} }
func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string) (map[string]*Target, error) { func ReadTargets(ctx context.Context, files []File, targets, overrides []string, defaults map[string]string) (map[string]*Target, []*Group, error) {
c, err := ParseFiles(files, defaults) c, err := ParseFiles(files, defaults)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
o, err := c.newOverrides(overrides) o, err := c.newOverrides(overrides)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
m := map[string]*Target{} m := map[string]*Target{}
for _, n := range targets { for _, n := range targets {
for _, n := range c.ResolveGroup(n) { for _, n := range c.ResolveGroup(n) {
t, err := c.ResolveTarget(n, o) t, err := c.ResolveTarget(n, o)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
if t != nil { if t != nil {
m[n] = t m[n] = t
} }
} }
} }
return m, nil
var g []*Group
if len(targets) == 0 || (len(targets) == 1 && targets[0] == "default") {
for _, group := range c.Groups {
if group.Name != "default" {
continue
}
g = []*Group{{Targets: group.Targets}}
}
} else {
var gt []string
for _, target := range targets {
isGroup := false
for _, group := range c.Groups {
if target == group.Name {
gt = append(gt, group.Targets...)
isGroup = true
break
}
}
if !isGroup {
gt = append(gt, target)
}
}
g = []*Group{{Targets: dedupString(gt)}}
}
for name, t := range m {
if err := c.loadLinks(name, t, m, o, nil); err != nil {
return nil, nil, err
}
}
return m, g, nil
}
func dedupString(s []string) []string {
if len(s) == 0 {
return s
}
var res []string
seen := make(map[string]struct{})
for _, val := range s {
if _, ok := seen[val]; !ok {
res = append(res, val)
seen[val] = struct{}{}
}
}
return res
} }
func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) { func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error) {
@@ -119,8 +191,9 @@ func ParseFiles(files []File, defaults map[string]string) (_ *Config, err error)
if len(fs) > 0 { if len(fs) > 0 {
if err := hclparser.Parse(hcl.MergeFiles(fs), hclparser.Opt{ if err := hclparser.Parse(hcl.MergeFiles(fs), hclparser.Opt{
LookupVar: os.LookupEnv, LookupVar: os.LookupEnv,
Vars: defaults, Vars: defaults,
ValidateLabel: validateTargetName,
}, &c); err.HasErrors() { }, &c); err.HasErrors() {
return nil, err return nil, err
} }
@@ -240,10 +313,45 @@ func (c Config) expandTargets(pattern string) ([]string, error) {
return names, nil return names, nil
} }
func (c Config) newOverrides(v []string) (map[string]*Target, error) { func (c Config) loadLinks(name string, t *Target, m map[string]*Target, o map[string]map[string]Override, visited []string) error {
m := map[string]*Target{} visited = append(visited, name)
for _, v := range v { for _, v := range t.Contexts {
if strings.HasPrefix(v, "target:") {
target := strings.TrimPrefix(v, "target:")
if target == t.Name {
return errors.Errorf("target %s cannot link to itself", target)
}
for _, v := range visited {
if v == target {
return errors.Errorf("infinite loop from %s to %s", name, target)
}
}
t2, ok := m[target]
if !ok {
var err error
t2, err = c.ResolveTarget(target, o)
if err != nil {
return err
}
t2.Outputs = nil
m[target] = t2
}
if err := c.loadLinks(target, t2, m, o, visited); err != nil {
return err
}
if len(t.Platforms) > 1 && len(t2.Platforms) > 1 {
if !sliceEqual(t.Platforms, t2.Platforms) {
return errors.Errorf("target %s can't be used by %s because it is defined for different platforms %v and %v", target, name, t2.Platforms, t.Platforms)
}
}
}
}
return nil
}
func (c Config) newOverrides(v []string) (map[string]map[string]Override, error) {
m := map[string]map[string]Override{}
for _, v := range v {
parts := strings.SplitN(v, "=", 2) parts := strings.SplitN(v, "=", 2)
keys := strings.SplitN(parts[0], ".", 3) keys := strings.SplitN(parts[0], ".", 3)
if len(keys) < 2 { if len(keys) < 2 {
@@ -260,73 +368,46 @@ func (c Config) newOverrides(v []string) (map[string]*Target, error) {
return nil, err return nil, err
} }
kk := strings.SplitN(parts[0], ".", 2)
for _, name := range names { for _, name := range names {
t, ok := m[name] t, ok := m[name]
if !ok { if !ok {
t = &Target{} t = map[string]Override{}
m[name] = t
} }
o := t[kk[1]]
switch keys[1] { switch keys[1] {
case "context": case "output", "cache-to", "cache-from", "tags", "platform", "secrets", "ssh":
t.Context = &parts[1] if len(parts) == 2 {
case "dockerfile": o.ArrValue = append(o.ArrValue, parts[1])
t.Dockerfile = &parts[1] }
case "args": case "args":
if len(keys) != 3 { if len(keys) != 3 {
return nil, errors.Errorf("invalid key %s, args requires name", parts[0]) return nil, errors.Errorf("invalid key %s, args requires name", parts[0])
} }
if t.Args == nil {
t.Args = map[string]string{}
}
if len(parts) < 2 { if len(parts) < 2 {
v, ok := os.LookupEnv(keys[2]) v, ok := os.LookupEnv(keys[2])
if ok { if !ok {
t.Args[keys[2]] = v continue
} }
} else { o.Value = v
t.Args[keys[2]] = parts[1]
} }
case "labels": fallthrough
case "contexts":
if len(keys) != 3 { if len(keys) != 3 {
return nil, errors.Errorf("invalid key %s, lanels requires name", parts[0]) return nil, errors.Errorf("invalid key %s, contexts requires name", parts[0])
} }
if t.Labels == nil { fallthrough
t.Labels = map[string]string{}
}
t.Labels[keys[2]] = parts[1]
case "tags":
t.Tags = append(t.Tags, parts[1])
case "cache-from":
t.CacheFrom = append(t.CacheFrom, parts[1])
case "cache-to":
t.CacheTo = append(t.CacheTo, parts[1])
case "target":
s := parts[1]
t.Target = &s
case "secrets":
t.Secrets = append(t.Secrets, parts[1])
case "ssh":
t.SSH = append(t.SSH, parts[1])
case "platform":
t.Platforms = append(t.Platforms, parts[1])
case "output":
t.Outputs = append(t.Outputs, parts[1])
case "no-cache":
noCache, err := strconv.ParseBool(parts[1])
if err != nil {
return nil, errors.Errorf("invalid value %s for boolean key no-cache", parts[1])
}
t.NoCache = &noCache
case "pull":
pull, err := strconv.ParseBool(parts[1])
if err != nil {
return nil, errors.Errorf("invalid value %s for boolean key pull", parts[1])
}
t.Pull = &pull
default: default:
return nil, errors.Errorf("unknown key: %s", keys[1]) if len(parts) == 2 {
o.Value = parts[1]
}
} }
m[name] = t
t[kk[1]] = o
} }
} }
return m, nil return m, nil
@@ -353,16 +434,22 @@ func (c Config) group(name string, visited map[string]struct{}) []string {
visited[name] = struct{}{} visited[name] = struct{}{}
targets := make([]string, 0, len(g.Targets)) targets := make([]string, 0, len(g.Targets))
for _, t := range g.Targets { for _, t := range g.Targets {
targets = append(targets, c.group(t, visited)...) tgroup := c.group(t, visited)
if len(tgroup) > 0 {
targets = append(targets, tgroup...)
} else {
targets = append(targets, t)
}
} }
return targets return targets
} }
func (c Config) ResolveTarget(name string, overrides map[string]*Target) (*Target, error) { func (c Config) ResolveTarget(name string, overrides map[string]map[string]Override) (*Target, error) {
t, err := c.target(name, map[string]struct{}{}, overrides) t, err := c.target(name, map[string]*Target{}, overrides)
if err != nil { if err != nil {
return nil, err return nil, err
} }
t.Inherits = nil
if t.Context == nil { if t.Context == nil {
s := "." s := "."
t.Context = &s t.Context = &s
@@ -374,11 +461,11 @@ func (c Config) ResolveTarget(name string, overrides map[string]*Target) (*Targe
return t, nil return t, nil
} }
func (c Config) target(name string, visited map[string]struct{}, overrides map[string]*Target) (*Target, error) { func (c Config) target(name string, visited map[string]*Target, overrides map[string]map[string]Override) (*Target, error) {
if _, ok := visited[name]; ok { if t, ok := visited[name]; ok {
return nil, nil return t, nil
} }
visited[name] = struct{}{} visited[name] = nil
var t *Target var t *Target
for _, target := range c.Targets { for _, target := range c.Targets {
if target.Name == name { if target.Name == name {
@@ -399,15 +486,15 @@ func (c Config) target(name string, visited map[string]struct{}, overrides map[s
tt.Merge(t) tt.Merge(t)
} }
} }
t.Inherits = nil
m := defaultTarget() m := defaultTarget()
m.Merge(tt) m.Merge(tt)
m.Merge(t) m.Merge(t)
tt = m tt = m
if override, ok := overrides[name]; ok { if err := tt.AddOverrides(overrides[name]); err != nil {
tt.Merge(override) return nil, err
} }
tt.normalize() tt.normalize()
visited[name] = tt
return tt, nil return tt, nil
} }
@@ -424,6 +511,7 @@ type Target struct {
Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional"` Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional"`
Context *string `json:"context,omitempty" hcl:"context,optional"` Context *string `json:"context,omitempty" hcl:"context,optional"`
Contexts map[string]string `json:"contexts,omitempty" hcl:"contexts,optional"`
Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional"` Dockerfile *string `json:"dockerfile,omitempty" hcl:"dockerfile,optional"`
DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional"` DockerfileInline *string `json:"dockerfile-inline,omitempty" hcl:"dockerfile-inline,optional"`
Args map[string]string `json:"args,omitempty" hcl:"args,optional"` Args map[string]string `json:"args,omitempty" hcl:"args,optional"`
@@ -438,7 +526,8 @@ type Target struct {
Outputs []string `json:"output,omitempty" hcl:"output,optional"` Outputs []string `json:"output,omitempty" hcl:"output,optional"`
Pull *bool `json:"pull,omitempty" hcl:"pull,optional"` Pull *bool `json:"pull,omitempty" hcl:"pull,optional"`
NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional"` NoCache *bool `json:"no-cache,omitempty" hcl:"no-cache,optional"`
NetworkMode *string `json:"-" hcl:"-"`
NoCacheFilter []string `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional"`
// IMPORTANT: if you add more fields here, do not forget to update newOverrides and README. // IMPORTANT: if you add more fields here, do not forget to update newOverrides and README.
} }
@@ -450,6 +539,16 @@ func (t *Target) normalize() {
t.CacheFrom = removeDupes(t.CacheFrom) t.CacheFrom = removeDupes(t.CacheFrom)
t.CacheTo = removeDupes(t.CacheTo) t.CacheTo = removeDupes(t.CacheTo)
t.Outputs = removeDupes(t.Outputs) t.Outputs = removeDupes(t.Outputs)
t.NoCacheFilter = removeDupes(t.NoCacheFilter)
for k, v := range t.Contexts {
if v == "" {
delete(t.Contexts, k)
}
}
if len(t.Contexts) == 0 {
t.Contexts = nil
}
} }
func (t *Target) Merge(t2 *Target) { func (t *Target) Merge(t2 *Target) {
@@ -468,6 +567,12 @@ func (t *Target) Merge(t2 *Target) {
} }
t.Args[k] = v t.Args[k] = v
} }
for k, v := range t2.Contexts {
if t.Contexts == nil {
t.Contexts = map[string]string{}
}
t.Contexts[k] = v
}
for k, v := range t2.Labels { for k, v := range t2.Labels {
if t.Labels == nil { if t.Labels == nil {
t.Labels = map[string]string{} t.Labels = map[string]string{}
@@ -504,9 +609,99 @@ func (t *Target) Merge(t2 *Target) {
if t2.NoCache != nil { if t2.NoCache != nil {
t.NoCache = t2.NoCache t.NoCache = t2.NoCache
} }
if t2.NetworkMode != nil {
t.NetworkMode = t2.NetworkMode
}
if t2.NoCacheFilter != nil { // merge
t.NoCacheFilter = append(t.NoCacheFilter, t2.NoCacheFilter...)
}
t.Inherits = append(t.Inherits, t2.Inherits...) t.Inherits = append(t.Inherits, t2.Inherits...)
} }
func (t *Target) AddOverrides(overrides map[string]Override) error {
for key, o := range overrides {
value := o.Value
keys := strings.SplitN(key, ".", 2)
switch keys[0] {
case "context":
t.Context = &value
case "dockerfile":
t.Dockerfile = &value
case "args":
if len(keys) != 2 {
return errors.Errorf("args require name")
}
if t.Args == nil {
t.Args = map[string]string{}
}
t.Args[keys[1]] = value
case "contexts":
if len(keys) != 2 {
return errors.Errorf("contexts require name")
}
if t.Contexts == nil {
t.Contexts = map[string]string{}
}
t.Contexts[keys[1]] = value
case "labels":
if len(keys) != 2 {
return errors.Errorf("labels require name")
}
if t.Labels == nil {
t.Labels = map[string]string{}
}
t.Labels[keys[1]] = value
case "tags":
t.Tags = o.ArrValue
case "cache-from":
t.CacheFrom = o.ArrValue
case "cache-to":
t.CacheTo = o.ArrValue
case "target":
t.Target = &value
case "secrets":
t.Secrets = o.ArrValue
case "ssh":
t.SSH = o.ArrValue
case "platform":
t.Platforms = o.ArrValue
case "output":
t.Outputs = o.ArrValue
case "no-cache":
noCache, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key no-cache", value)
}
t.NoCache = &noCache
case "no-cache-filter":
t.NoCacheFilter = o.ArrValue
case "pull":
pull, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key pull", value)
}
t.Pull = &pull
case "push":
_, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key push", 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
}
}
}
default:
return errors.Errorf("unknown key: %s", keys[0])
}
}
return nil
}
func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) { func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) {
m2 := make(map[string]build.Options, len(m)) m2 := make(map[string]build.Options, len(m))
for k, v := range m { for k, v := range m {
@@ -523,6 +718,21 @@ func updateContext(t *build.Inputs, inp *Input) {
if inp == nil || inp.State == nil { if inp == nil || inp.State == nil {
return return
} }
for k, v := range t.NamedContexts {
if v.Path == "." {
t.NamedContexts[k] = build.NamedContext{Path: inp.URL}
}
if strings.HasPrefix(v.Path, "cwd://") || strings.HasPrefix(v.Path, "target:") || strings.HasPrefix(v.Path, "docker-image:") {
continue
}
if IsRemoteURL(v.Path) {
continue
}
st := llb.Scratch().File(llb.Copy(*inp.State, v.Path, "/"), llb.WithCustomNamef("set context %s to %s", k, v.Path))
t.NamedContexts[k] = build.NamedContext{State: &st}
}
if t.ContextPath == "." { if t.ContextPath == "." {
t.ContextPath = inp.URL t.ContextPath = inp.URL
return return
@@ -537,6 +747,59 @@ func updateContext(t *build.Inputs, inp *Input) {
t.ContextState = &st t.ContextState = &st
} }
// validateContextsEntitlements is a basic check to ensure contexts do not
// escape local directories when loaded from remote sources. This is to be
// replaced with proper entitlements support in the future.
func validateContextsEntitlements(t build.Inputs, inp *Input) error {
if inp == nil || inp.State == nil {
return nil
}
if v, ok := os.LookupEnv("BAKE_ALLOW_REMOTE_FS_ACCESS"); ok {
if vv, _ := strconv.ParseBool(v); vv {
return nil
}
}
if t.ContextState == nil {
if err := checkPath(t.ContextPath); err != nil {
return err
}
}
for _, v := range t.NamedContexts {
if v.State != nil {
continue
}
if err := checkPath(v.Path); err != nil {
return err
}
}
return nil
}
func checkPath(p string) error {
if IsRemoteURL(p) || strings.HasPrefix(p, "target:") || strings.HasPrefix(p, "docker-image:") {
return nil
}
p, err := filepath.EvalSymlinks(p)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
wd, err := os.Getwd()
if err != nil {
return err
}
rel, err := filepath.Rel(wd, p)
if err != nil {
return err
}
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return errors.Errorf("path %s is outside of the working directory, please set BAKE_ALLOW_REMOTE_FS_ACCESS=1", p)
}
return nil
}
func toBuildOpt(t *Target, inp *Input) (*build.Options, error) { func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if v := t.Context; v != nil && *v == "-" { if v := t.Context; v != nil && *v == "-" {
return nil, errors.Errorf("context from stdin not allowed in bake") return nil, errors.Errorf("context from stdin not allowed in bake")
@@ -569,10 +832,15 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if t.Pull != nil { if t.Pull != nil {
pull = *t.Pull pull = *t.Pull
} }
networkMode := ""
if t.NetworkMode != nil {
networkMode = *t.NetworkMode
}
bi := build.Inputs{ bi := build.Inputs{
ContextPath: contextPath, ContextPath: contextPath,
DockerfilePath: dockerfilePath, DockerfilePath: dockerfilePath,
NamedContexts: toNamedContexts(t.Contexts),
} }
if t.DockerfileInline != nil { if t.DockerfileInline != nil {
bi.DockerfileInline = *t.DockerfileInline bi.DockerfileInline = *t.DockerfileInline
@@ -581,16 +849,27 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
if strings.HasPrefix(bi.ContextPath, "cwd://") { if strings.HasPrefix(bi.ContextPath, "cwd://") {
bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://")) bi.ContextPath = path.Clean(strings.TrimPrefix(bi.ContextPath, "cwd://"))
} }
for k, v := range bi.NamedContexts {
if strings.HasPrefix(v.Path, "cwd://") {
bi.NamedContexts[k] = build.NamedContext{Path: path.Clean(strings.TrimPrefix(v.Path, "cwd://"))}
}
}
if err := validateContextsEntitlements(bi, inp); err != nil {
return nil, err
}
t.Context = &bi.ContextPath t.Context = &bi.ContextPath
bo := &build.Options{ bo := &build.Options{
Inputs: bi, Inputs: bi,
Tags: t.Tags, Tags: t.Tags,
BuildArgs: t.Args, BuildArgs: t.Args,
Labels: t.Labels, Labels: t.Labels,
NoCache: noCache, NoCache: noCache,
Pull: pull, NoCacheFilter: t.NoCacheFilter,
Pull: pull,
NetworkMode: networkMode,
} }
platforms, err := platformutil.Parse(t.Platforms) platforms, err := platformutil.Parse(t.Platforms)
@@ -666,3 +945,56 @@ func removeDupes(s []string) []string {
func isRemoteResource(str string) bool { func isRemoteResource(str string) bool {
return urlutil.IsGitURL(str) || urlutil.IsURL(str) return urlutil.IsGitURL(str) || urlutil.IsURL(str)
} }
func parseOutputType(str string) string {
csvReader := csv.NewReader(strings.NewReader(str))
fields, err := csvReader.Read()
if err != nil {
return ""
}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) == 2 {
if parts[0] == "type" {
return parts[1]
}
}
}
return ""
}
func validateTargetName(name string) error {
if !targetNamePattern.MatchString(name) {
return errors.Errorf("only %q are allowed", validTargetNameChars)
}
return nil
}
func validateTargetNameCompose(name string) error {
if !targetNamePatternCompose.MatchString(name) {
return errors.Errorf("only %q are allowed", validTargetNameCharsCompose)
}
return nil
}
func sliceEqual(s1, s2 []string) bool {
if len(s1) != len(s2) {
return false
}
sort.Strings(s1)
sort.Strings(s2)
for i := range s1 {
if s1[i] != s2[i] {
return false
}
}
return true
}
func toNamedContexts(m map[string]string) map[string]build.NamedContext {
m2 := make(map[string]build.NamedContext, len(m))
for k, v := range m {
m2[k] = build.NamedContext{Path: v}
}
return m2
}

View File

@@ -3,6 +3,7 @@ package bake
import ( import (
"context" "context"
"os" "os"
"sort"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -34,7 +35,7 @@ target "webapp" {
ctx := context.TODO() ctx := context.TODO()
t.Run("NoOverrides", func(t *testing.T) { t.Run("NoOverrides", func(t *testing.T) {
m, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, nil, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -43,10 +44,13 @@ target "webapp" {
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"]) require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
require.Equal(t, true, *m["webapp"].NoCache) require.Equal(t, true, *m["webapp"].NoCache)
require.Nil(t, m["webapp"].Pull) require.Nil(t, m["webapp"].Pull)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g[0].Targets)
}) })
t.Run("InvalidTargetOverrides", func(t *testing.T) { t.Run("InvalidTargetOverrides", func(t *testing.T) {
_, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"nosuchtarget.context=foo"}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"nosuchtarget.context=foo"}, nil)
require.NotNil(t, err) require.NotNil(t, err)
require.Equal(t, err.Error(), "could not find any target matching 'nosuchtarget'") require.Equal(t, err.Error(), "could not find any target matching 'nosuchtarget'")
}) })
@@ -56,7 +60,7 @@ target "webapp" {
os.Setenv("VAR_FROMENV"+t.Name(), "fromEnv") os.Setenv("VAR_FROMENV"+t.Name(), "fromEnv")
defer os.Unsetenv("VAR_FROM_ENV" + t.Name()) defer os.Unsetenv("VAR_FROM_ENV" + t.Name())
m, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{ m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{
"webapp.args.VAR_UNSET", "webapp.args.VAR_UNSET",
"webapp.args.VAR_EMPTY=", "webapp.args.VAR_EMPTY=",
"webapp.args.VAR_SET=bananas", "webapp.args.VAR_SET=bananas",
@@ -81,58 +85,72 @@ target "webapp" {
require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp") require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp")
require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override") require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override")
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g[0].Targets)
}) })
// building leaf but overriding parent fields // building leaf but overriding parent fields
t.Run("parent", func(t *testing.T) { t.Run("parent", func(t *testing.T) {
m, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{ m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{
"webDEP.args.VAR_INHERITED=override", "webDEP.args.VAR_INHERITED=override",
"webDEP.args.VAR_BOTH=override", "webDEP.args.VAR_BOTH=override",
}, nil) }, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override") require.Equal(t, m["webapp"].Args["VAR_INHERITED"], "override")
require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp") require.Equal(t, m["webapp"].Args["VAR_BOTH"], "webapp")
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g[0].Targets)
}) })
}) })
t.Run("ContextOverride", func(t *testing.T) { t.Run("ContextOverride", func(t *testing.T) {
_, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil) _, _, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context"}, nil)
require.NotNil(t, err) require.NotNil(t, err)
m, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context=foo"}, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.context=foo"}, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "foo", *m["webapp"].Context) require.Equal(t, "foo", *m["webapp"].Context)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g[0].Targets)
}) })
t.Run("NoCacheOverride", func(t *testing.T) { t.Run("NoCacheOverride", func(t *testing.T) {
m, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.no-cache=false"}, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.no-cache=false"}, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, false, *m["webapp"].NoCache) require.Equal(t, false, *m["webapp"].NoCache)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g[0].Targets)
}) })
t.Run("PullOverride", func(t *testing.T) { t.Run("PullOverride", func(t *testing.T) {
m, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"webapp"}, []string{"webapp.pull=false"}, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, false, *m["webapp"].Pull) require.Equal(t, false, *m["webapp"].Pull)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g[0].Targets)
}) })
t.Run("PatternOverride", func(t *testing.T) { t.Run("PatternOverride", func(t *testing.T) {
// same check for two cases // same check for two cases
multiTargetCheck := func(t *testing.T, m map[string]*Target, err error) { multiTargetCheck := func(t *testing.T, m map[string]*Target, g []*Group, err error) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 2, len(m)) require.Equal(t, 2, len(m))
require.Equal(t, "foo", *m["webapp"].Dockerfile) require.Equal(t, "foo", *m["webapp"].Dockerfile)
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"]) require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
require.Equal(t, "foo", *m["webDEP"].Dockerfile) require.Equal(t, "foo", *m["webDEP"].Dockerfile)
require.Equal(t, "webDEP", m["webDEP"].Args["VAR_INHERITED"]) require.Equal(t, "webDEP", m["webDEP"].Args["VAR_INHERITED"])
require.Equal(t, 1, len(g))
sort.Strings(g[0].Targets)
require.Equal(t, []string{"webDEP", "webapp"}, g[0].Targets)
} }
cases := []struct { cases := []struct {
name string name string
targets []string targets []string
overrides []string overrides []string
check func(*testing.T, map[string]*Target, error) check func(*testing.T, map[string]*Target, []*Group, error)
}{ }{
{ {
name: "multi target single pattern", name: "multi target single pattern",
@@ -150,18 +168,20 @@ target "webapp" {
name: "single target", name: "single target",
targets: []string{"webapp"}, targets: []string{"webapp"},
overrides: []string{"web*.dockerfile=foo"}, overrides: []string{"web*.dockerfile=foo"},
check: func(t *testing.T, m map[string]*Target, err error) { check: func(t *testing.T, m map[string]*Target, g []*Group, err error) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
require.Equal(t, "foo", *m["webapp"].Dockerfile) require.Equal(t, "foo", *m["webapp"].Dockerfile)
require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"]) require.Equal(t, "webDEP", m["webapp"].Args["VAR_INHERITED"])
require.Equal(t, 1, len(g))
require.Equal(t, []string{"webapp"}, g[0].Targets)
}, },
}, },
{ {
name: "nomatch", name: "nomatch",
targets: []string{"webapp"}, targets: []string{"webapp"},
overrides: []string{"nomatch*.dockerfile=foo"}, overrides: []string{"nomatch*.dockerfile=foo"},
check: func(t *testing.T, m map[string]*Target, err error) { check: func(t *testing.T, m map[string]*Target, g []*Group, err error) {
// NOTE: I am unsure whether failing to match should always error out // NOTE: I am unsure whether failing to match should always error out
// instead of simply skipping that override. // instead of simply skipping that override.
// Let's enforce the error and we can relax it later if users complain. // Let's enforce the error and we can relax it later if users complain.
@@ -172,13 +192,58 @@ target "webapp" {
} }
for _, test := range cases { for _, test := range cases {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
m, err := ReadTargets(ctx, []File{fp}, test.targets, test.overrides, nil) m, g, err := ReadTargets(ctx, []File{fp}, test.targets, test.overrides, nil)
test.check(t, m, err) test.check(t, m, g, err)
}) })
} }
}) })
} }
func TestPushOverride(t *testing.T) {
t.Parallel()
fp := File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
output = ["type=image,compression=zstd"]
}`),
}
ctx := context.TODO()
m, _, err := ReadTargets(ctx, []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])
fp = File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
output = ["type=image,compression=zstd"]
}`),
}
ctx = context.TODO()
m, _, err = ReadTargets(ctx, []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])
fp = File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
}`),
}
ctx = context.TODO()
m, _, err = ReadTargets(ctx, []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])
}
func TestReadTargetsCompose(t *testing.T) { func TestReadTargetsCompose(t *testing.T) {
t.Parallel() t.Parallel()
@@ -215,22 +280,26 @@ services:
ctx := context.TODO() ctx := context.TODO()
m, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 3, len(m)) require.Equal(t, 3, len(m))
_, ok := m["newservice"] _, ok := m["newservice"]
require.True(t, ok) require.True(t, ok)
require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile) require.Equal(t, "Dockerfile.webapp", *m["webapp"].Dockerfile)
require.Equal(t, ".", *m["webapp"].Context) require.Equal(t, ".", *m["webapp"].Context)
require.Equal(t, "1", m["webapp"].Args["buildno"]) require.Equal(t, "1", m["webapp"].Args["buildno"])
require.Equal(t, "12", m["webapp"].Args["buildno2"]) require.Equal(t, "12", m["webapp"].Args["buildno2"])
require.Equal(t, 1, len(g))
sort.Strings(g[0].Targets)
require.Equal(t, []string{"db", "newservice", "webapp"}, g[0].Targets)
} }
func TestHCLCwdPrefix(t *testing.T) { func TestHCLCwdPrefix(t *testing.T) {
fp := File{ fp := File{
Name: "docker-bake.hc", Name: "docker-bake.hcl",
Data: []byte( Data: []byte(
`target "app" { `target "app" {
context = "cwd://foo" context = "cwd://foo"
@@ -238,7 +307,7 @@ func TestHCLCwdPrefix(t *testing.T) {
}`), }`),
} }
ctx := context.TODO() ctx := context.TODO()
m, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil) m, g, err := ReadTargets(ctx, []File{fp}, []string{"app"}, nil, nil)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(m)) require.Equal(t, 1, len(m))
@@ -250,4 +319,729 @@ func TestHCLCwdPrefix(t *testing.T) {
require.Equal(t, "test", *m["app"].Dockerfile) require.Equal(t, "test", *m["app"].Dockerfile)
require.Equal(t, "foo", *m["app"].Context) require.Equal(t, "foo", *m["app"].Context)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"app"}, g[0].Targets)
}
func TestOverrideMerge(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(
`target "app" {
platforms = ["linux/amd64"]
output = ["foo"]
}`),
}
ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{
"app.platform=linux/arm",
"app.platform=linux/ppc64le",
"app.output=type=registry",
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m))
_, ok := m["app"]
require.True(t, ok)
_, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, []string{"linux/arm", "linux/ppc64le"}, m["app"].Platforms)
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=registry", m["app"].Outputs[0])
}
func TestReadContexts(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "base" {
contexts = {
foo: "bar"
abc: "def"
}
}
target "app" {
inherits = ["base"]
contexts = {
foo: "baz"
}
}
`),
}
ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m))
_, ok := m["app"]
require.True(t, ok)
bo, err := TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
ctxs := bo["app"].Inputs.NamedContexts
require.Equal(t, 2, len(ctxs))
require.Equal(t, "baz", ctxs["foo"].Path)
require.Equal(t, "def", ctxs["abc"].Path)
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo=bay", "base.contexts.ghi=jkl"}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m))
_, ok = m["app"]
require.True(t, ok)
bo, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
ctxs = bo["app"].Inputs.NamedContexts
require.Equal(t, 3, len(ctxs))
require.Equal(t, "bay", ctxs["foo"].Path)
require.Equal(t, "def", ctxs["abc"].Path)
require.Equal(t, "jkl", ctxs["ghi"].Path)
// test resetting base values
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"app.contexts.foo="}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m))
_, ok = m["app"]
require.True(t, ok)
bo, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
ctxs = bo["app"].Inputs.NamedContexts
require.Equal(t, 1, len(ctxs))
require.Equal(t, "def", ctxs["abc"].Path)
}
func TestReadContextFromTargetUnknown(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "base" {
contexts = {
foo: "bar"
abc: "def"
}
}
target "app" {
contexts = {
foo: "baz"
bar: "target:bar"
}
}
`),
}
ctx := context.TODO()
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "failed to find target bar")
}
func TestReadContextFromTargetChain(t *testing.T) {
ctx := context.TODO()
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "base" {
}
target "mid" {
output = ["foo"]
contexts = {
parent: "target:base"
}
}
target "app" {
contexts = {
foo: "baz"
bar: "target:mid"
}
}
target "unused" {}
`),
}
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil)
require.NoError(t, err)
require.Equal(t, 3, len(m))
app, ok := m["app"]
require.True(t, ok)
require.Equal(t, 2, len(app.Contexts))
mid, ok := m["mid"]
require.True(t, ok)
require.Equal(t, 0, len(mid.Outputs))
require.Equal(t, 1, len(mid.Contexts))
base, ok := m["base"]
require.True(t, ok)
require.Equal(t, 0, len(base.Contexts))
}
func TestReadContextFromTargetInfiniteLoop(t *testing.T) {
ctx := context.TODO()
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "mid" {
output = ["foo"]
contexts = {
parent: "target:app"
}
}
target "app" {
contexts = {
foo: "baz"
bar: "target:mid"
}
}
`),
}
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app", "mid"}, []string{}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "infinite loop from")
}
func TestReadContextFromTargetMultiPlatform(t *testing.T) {
ctx := context.TODO()
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "mid" {
output = ["foo"]
platforms = ["linux/amd64", "linux/arm64"]
}
target "app" {
contexts = {
bar: "target:mid"
}
platforms = ["linux/amd64", "linux/arm64"]
}
`),
}
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil)
require.NoError(t, err)
}
func TestReadContextFromTargetInvalidPlatforms(t *testing.T) {
ctx := context.TODO()
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "mid" {
output = ["foo"]
platforms = ["linux/amd64", "linux/riscv64"]
}
target "app" {
contexts = {
bar: "target:mid"
}
platforms = ["linux/amd64", "linux/arm64"]
}
`),
}
_, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{}, nil)
require.Error(t, err)
require.Contains(t, err.Error(), "defined for different platforms")
}
func TestReadTargetsDefault(t *testing.T) {
t.Parallel()
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "default" {
dockerfile = "test"
}`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"default"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 0, len(g))
require.Equal(t, 1, len(m))
require.Equal(t, "test", *m["default"].Dockerfile)
}
func TestReadTargetsSpecified(t *testing.T) {
t.Parallel()
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "image" {
dockerfile = "test"
}`)}
_, _, err := ReadTargets(ctx, []File{f}, []string{"default"}, nil, nil)
require.Error(t, err)
m, g, err := ReadTargets(ctx, []File{f}, []string{"image"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, "test", *m["image"].Dockerfile)
}
func TestReadTargetsGroup(t *testing.T) {
t.Parallel()
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
group "foo" {
targets = ["image"]
}
target "image" {
dockerfile = "test"
}`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, "test", *m["image"].Dockerfile)
}
func TestReadTargetsGroupAndTarget(t *testing.T) {
t.Parallel()
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
group "foo" {
targets = ["image"]
}
target "foo" {
dockerfile = "bar"
}
target "image" {
dockerfile = "test"
}`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, "test", *m["image"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "foo"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, "test", *m["image"].Dockerfile)
}
func TestReadTargetsMixed(t *testing.T) {
t.Parallel()
ctx := context.TODO()
fhcl := File{
Name: "docker-bake.hcl",
Data: []byte(`
group "default" {
targets = ["image"]
}
target "nocache" {
no-cache = true
}
group "release" {
targets = ["image-release"]
}
target "image" {
inherits = ["nocache"]
output = ["type=docker"]
}
target "image-release" {
inherits = ["image"]
output = ["type=image,push=true"]
tags = ["user/app:latest"]
}`)}
fyml := File{
Name: "docker-compose.yml",
Data: []byte(`
services:
addon:
build:
context: .
dockerfile: ./Dockerfile
args:
CT_ECR: foo
CT_TAG: bar
image: ct-addon:bar
environment:
- NODE_ENV=test
- AWS_ACCESS_KEY_ID=dummy
- AWS_SECRET_ACCESS_KEY=dummy
aws:
build:
dockerfile: ./aws.Dockerfile
args:
CT_ECR: foo
CT_TAG: bar
image: ct-fake-aws:bar`)}
fjson := File{
Name: "docker-bake.json",
Data: []byte(`{
"group": {
"default": {
"targets": [
"image"
]
}
},
"target": {
"image": {
"context": ".",
"dockerfile": "Dockerfile",
"output": [
"type=docker"
]
}
}
}`)}
m, g, err := ReadTargets(ctx, []File{fhcl}, []string{"default"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, 1, len(m["image"].Outputs))
require.Equal(t, "type=docker", m["image"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{fhcl}, []string{"image-release"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image-release"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, 1, len(m["image-release"].Outputs))
require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{fhcl}, []string{"image", "image-release"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image", "image-release"}, g[0].Targets)
require.Equal(t, 2, len(m))
require.Equal(t, ".", *m["image"].Context)
require.Equal(t, 1, len(m["image-release"].Outputs))
require.Equal(t, "type=image,push=true", m["image-release"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"default"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, ".", *m["image"].Context)
m, g, err = ReadTargets(ctx, []File{fjson}, []string{"default"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"image"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, ".", *m["image"].Context)
m, g, err = ReadTargets(ctx, []File{fyml}, []string{"default"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
sort.Strings(g[0].Targets)
require.Equal(t, []string{"addon", "aws"}, g[0].Targets)
require.Equal(t, 2, len(m))
require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile)
require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"addon", "aws"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
sort.Strings(g[0].Targets)
require.Equal(t, []string{"addon", "aws"}, g[0].Targets)
require.Equal(t, 2, len(m))
require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile)
require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{fyml, fhcl}, []string{"addon", "aws", "image"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
sort.Strings(g[0].Targets)
require.Equal(t, []string{"addon", "aws", "image"}, g[0].Targets)
require.Equal(t, 3, len(m))
require.Equal(t, ".", *m["image"].Context)
require.Equal(t, "./Dockerfile", *m["addon"].Dockerfile)
require.Equal(t, "./aws.Dockerfile", *m["aws"].Dockerfile)
}
func TestReadTargetsSameGroupTarget(t *testing.T) {
t.Parallel()
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
group "foo" {
targets = ["foo"]
}
target "foo" {
dockerfile = "bar"
}
target "image" {
output = ["type=docker"]
}`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"foo"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, "bar", *m["foo"].Dockerfile)
m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "foo"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"foo"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, "bar", *m["foo"].Dockerfile)
}
func TestReadTargetsSameGroupTargetMulti(t *testing.T) {
t.Parallel()
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
group "foo" {
targets = ["foo", "image"]
}
target "foo" {
dockerfile = "bar"
}
target "image" {
output = ["type=docker"]
}`)}
m, g, err := ReadTargets(ctx, []File{f}, []string{"foo"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"foo", "image"}, g[0].Targets)
require.Equal(t, 2, len(m))
require.Equal(t, "bar", *m["foo"].Dockerfile)
require.Equal(t, "type=docker", m["image"].Outputs[0])
m, g, err = ReadTargets(ctx, []File{f}, []string{"foo", "image"}, nil, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"foo", "image"}, g[0].Targets)
require.Equal(t, 2, len(m))
require.Equal(t, "bar", *m["foo"].Dockerfile)
require.Equal(t, "type=docker", m["image"].Outputs[0])
}
func TestNestedInherits(t *testing.T) {
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "a" {
args = {
foo = "123"
bar = "234"
}
}
target "b" {
inherits = ["a"]
args = {
bar = "567"
}
}
target "c" {
inherits = ["a"]
args = {
baz = "890"
}
}
target "d" {
inherits = ["b", "c"]
}`)}
cases := []struct {
name string
overrides []string
want map[string]string
}{
{
name: "nested simple",
overrides: nil,
want: map[string]string{"bar": "234", "baz": "890", "foo": "123"},
},
{
name: "nested with overrides first",
overrides: []string{"a.args.foo=321", "b.args.bar=432"},
want: map[string]string{"bar": "234", "baz": "890", "foo": "321"},
},
{
name: "nested with overrides last",
overrides: []string{"a.args.foo=321", "c.args.bar=432"},
want: map[string]string{"bar": "432", "baz": "890", "foo": "321"},
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
m, g, err := ReadTargets(ctx, []File{f}, []string{"d"}, tt.overrides, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"d"}, g[0].Targets)
require.Equal(t, 1, len(m))
require.Equal(t, tt.want, m["d"].Args)
})
}
}
func TestNestedInheritsWithGroup(t *testing.T) {
ctx := context.TODO()
f := File{
Name: "docker-bake.hcl",
Data: []byte(`
target "grandparent" {
output = ["type=docker"]
args = {
BAR = "fuu"
}
}
target "parent" {
inherits = ["grandparent"]
args = {
FOO = "bar"
}
}
target "child1" {
inherits = ["parent"]
}
target "child2" {
inherits = ["parent"]
args = {
FOO2 = "bar2"
}
}
group "default" {
targets = [
"child1",
"child2"
]
}`)}
cases := []struct {
name string
overrides []string
wantch1 map[string]string
wantch2 map[string]string
}{
{
name: "nested simple",
overrides: nil,
wantch1: map[string]string{"BAR": "fuu", "FOO": "bar"},
wantch2: map[string]string{"BAR": "fuu", "FOO": "bar", "FOO2": "bar2"},
},
{
name: "nested with overrides first",
overrides: []string{"grandparent.args.BAR=fii", "child1.args.FOO=baaar"},
wantch1: map[string]string{"BAR": "fii", "FOO": "baaar"},
wantch2: map[string]string{"BAR": "fii", "FOO": "bar", "FOO2": "bar2"},
},
{
name: "nested with overrides last",
overrides: []string{"grandparent.args.BAR=fii", "child2.args.FOO=baaar"},
wantch1: map[string]string{"BAR": "fii", "FOO": "bar"},
wantch2: map[string]string{"BAR": "fii", "FOO": "baaar", "FOO2": "bar2"},
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
m, g, err := ReadTargets(ctx, []File{f}, []string{"default"}, tt.overrides, nil)
require.NoError(t, err)
require.Equal(t, 1, len(g))
require.Equal(t, []string{"child1", "child2"}, g[0].Targets)
require.Equal(t, 2, len(m))
require.Equal(t, tt.wantch1, m["child1"].Args)
require.Equal(t, []string{"type=docker"}, m["child1"].Outputs)
require.Equal(t, tt.wantch2, m["child2"].Args)
require.Equal(t, []string{"type=docker"}, m["child2"].Outputs)
})
}
}
func TestTargetName(t *testing.T) {
ctx := context.TODO()
cases := []struct {
target string
wantErr bool
}{
{
target: "a",
wantErr: false,
},
{
target: "abc",
wantErr: false,
},
{
target: "a/b",
wantErr: true,
},
{
target: "a.b",
wantErr: true,
},
{
target: "_a",
wantErr: false,
},
{
target: "a_b",
wantErr: false,
},
{
target: "AbC",
wantErr: false,
},
{
target: "AbC-0123",
wantErr: false,
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.target, func(t *testing.T) {
_, _, err := ReadTargets(ctx, []File{{
Name: "docker-bake.hcl",
Data: []byte(`target "` + tt.target + `" {}`),
}}, []string{tt.target}, nil, nil)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
} }

View File

@@ -8,6 +8,7 @@ import (
"github.com/compose-spec/compose-go/loader" "github.com/compose-spec/compose-go/loader"
compose "github.com/compose-spec/compose-go/types" compose "github.com/compose-spec/compose-go/types"
"github.com/pkg/errors"
) )
func parseCompose(dt []byte) (*compose.Project, error) { func parseCompose(dt []byte) (*compose.Project, error) {
@@ -59,6 +60,10 @@ func ParseCompose(dt []byte) (*Config, error) {
continue continue
} }
if err = validateTargetNameCompose(s.Name); err != nil {
return nil, errors.Wrapf(err, "invalid service name %q", s.Name)
}
var contextPathP *string var contextPathP *string
if s.Build.Context != "" { if s.Build.Context != "" {
contextPath := s.Build.Context contextPath := s.Build.Context
@@ -76,17 +81,23 @@ func ParseCompose(dt []byte) (*Config, error) {
Dockerfile: dockerfilePathP, Dockerfile: dockerfilePathP,
Labels: s.Build.Labels, Labels: s.Build.Labels,
Args: flatten(s.Build.Args.Resolve(func(val string) (string, bool) { Args: flatten(s.Build.Args.Resolve(func(val string) (string, bool) {
if val, ok := s.Environment[val]; ok && val != nil {
return *val, true
}
val, ok := cfg.Environment[val] val, ok := cfg.Environment[val]
return val, ok return val, ok
})), })),
CacheFrom: s.Build.CacheFrom, CacheFrom: s.Build.CacheFrom,
// TODO: add platforms NetworkMode: &s.Build.Network,
}
if err = t.composeExtTarget(s.Build.Extensions); err != nil {
return nil, err
} }
if s.Build.Target != "" { if s.Build.Target != "" {
target := s.Build.Target target := s.Build.Target
t.Target = &target t.Target = &target
} }
if s.Image != "" { if len(t.Tags) == 0 && s.Image != "" {
t.Tags = []string{s.Image} t.Tags = []string{s.Image}
} }
c.Targets = append(c.Targets, t) c.Targets = append(c.Targets, t)
@@ -111,3 +122,90 @@ func flatten(in compose.MappingWithEquals) compose.Mapping {
} }
return out return out
} }
// composeExtTarget converts Compose build extension x-bake to bake Target
// https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension
func (t *Target) composeExtTarget(exts map[string]interface{}) error {
if ext, ok := exts["x-bake"]; ok {
for key, val := range ext.(map[string]interface{}) {
switch key {
case "tags":
if res, k := val.(string); k {
t.Tags = append(t.Tags, res)
} else {
for _, res := range val.([]interface{}) {
t.Tags = append(t.Tags, res.(string))
}
}
case "cache-from":
t.CacheFrom = []string{} // Needed to override the main field
if res, k := val.(string); k {
t.CacheFrom = append(t.CacheFrom, res)
} else {
for _, res := range val.([]interface{}) {
t.CacheFrom = append(t.CacheFrom, res.(string))
}
}
case "cache-to":
if res, k := val.(string); k {
t.CacheTo = append(t.CacheTo, res)
} else {
for _, res := range val.([]interface{}) {
t.CacheTo = append(t.CacheTo, res.(string))
}
}
case "secret":
if res, k := val.(string); k {
t.Secrets = append(t.Secrets, res)
} else {
for _, res := range val.([]interface{}) {
t.Secrets = append(t.Secrets, res.(string))
}
}
case "ssh":
if res, k := val.(string); k {
t.SSH = append(t.SSH, res)
} else {
for _, res := range val.([]interface{}) {
t.SSH = append(t.SSH, res.(string))
}
}
case "platforms":
if res, k := val.(string); k {
t.Platforms = append(t.Platforms, res)
} else {
for _, res := range val.([]interface{}) {
t.Platforms = append(t.Platforms, res.(string))
}
}
case "output":
if res, k := val.(string); k {
t.Outputs = append(t.Outputs, res)
} else {
for _, res := range val.([]interface{}) {
t.Outputs = append(t.Outputs, res.(string))
}
}
case "pull":
if res, ok := val.(bool); ok {
t.Pull = &res
}
case "no-cache":
if res, ok := val.(bool); ok {
t.NoCache = &res
}
case "no-cache-filter":
if res, k := val.(string); k {
t.NoCacheFilter = append(t.NoCacheFilter, res)
} else {
for _, res := range val.([]interface{}) {
t.NoCacheFilter = append(t.NoCacheFilter, res.(string))
}
}
default:
return fmt.Errorf("compose file invalid: unkwown %s field for x-bake", key)
}
}
}
return nil
}

View File

@@ -19,6 +19,8 @@ services:
build: build:
context: ./dir context: ./dir
dockerfile: Dockerfile-alternate dockerfile: Dockerfile-alternate
network:
none
args: args:
buildno: 123 buildno: 123
`) `)
@@ -43,6 +45,7 @@ services:
require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile) require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
require.Equal(t, 1, len(c.Targets[1].Args)) require.Equal(t, 1, len(c.Targets[1].Args))
require.Equal(t, "123", c.Targets[1].Args["buildno"]) require.Equal(t, "123", c.Targets[1].Args["buildno"])
require.Equal(t, "none", *c.Targets[1].NetworkMode)
} }
func TestNoBuildOutOfTreeService(t *testing.T) { func TestNoBuildOutOfTreeService(t *testing.T) {
@@ -214,3 +217,156 @@ networks:
_, err := ParseCompose(dt) _, err := ParseCompose(dt)
require.NoError(t, err) require.NoError(t, err)
} }
func TestComposeExt(t *testing.T) {
var dt = []byte(`
services:
addon:
image: ct-addon:bar
build:
context: .
dockerfile: ./Dockerfile
cache_from:
- user/app:cache
args:
CT_ECR: foo
CT_TAG: bar
x-bake:
tags:
- ct-addon:foo
- ct-addon:alp
platforms:
- linux/amd64
- linux/arm64
cache-from:
- type=local,src=path/to/cache
cache-to: local,dest=path/to/cache
pull: true
aws:
image: ct-fake-aws:bar
build:
dockerfile: ./aws.Dockerfile
args:
CT_ECR: foo
CT_TAG: bar
x-bake:
secret:
- id=mysecret,src=/local/secret
- id=mysecret2,src=/local/secret2
ssh: default
platforms: linux/arm64
output: type=docker
no-cache: true
`)
c, err := ParseCompose(dt)
require.NoError(t, err)
require.Equal(t, 2, len(c.Targets))
sort.Slice(c.Targets, func(i, j int) bool {
return c.Targets[i].Name < c.Targets[j].Name
})
require.Equal(t, c.Targets[0].Args, map[string]string{"CT_ECR": "foo", "CT_TAG": "bar"})
require.Equal(t, c.Targets[0].Tags, []string{"ct-addon:foo", "ct-addon:alp"})
require.Equal(t, c.Targets[0].Platforms, []string{"linux/amd64", "linux/arm64"})
require.Equal(t, c.Targets[0].CacheFrom, []string{"type=local,src=path/to/cache"})
require.Equal(t, c.Targets[0].CacheTo, []string{"local,dest=path/to/cache"})
require.Equal(t, c.Targets[0].Pull, newBool(true))
require.Equal(t, c.Targets[1].Tags, []string{"ct-fake-aws:bar"})
require.Equal(t, c.Targets[1].Secrets, []string{"id=mysecret,src=/local/secret", "id=mysecret2,src=/local/secret2"})
require.Equal(t, c.Targets[1].SSH, []string{"default"})
require.Equal(t, c.Targets[1].Platforms, []string{"linux/arm64"})
require.Equal(t, c.Targets[1].Outputs, []string{"type=docker"})
require.Equal(t, c.Targets[1].NoCache, newBool(true))
}
func TestEnv(t *testing.T) {
envf, err := os.CreateTemp("", "env")
require.NoError(t, err)
defer os.Remove(envf.Name())
_, err = envf.WriteString("FOO=bsdf -csdf\n")
require.NoError(t, err)
var dt = []byte(`
services:
scratch:
build:
context: .
args:
CT_ECR: foo
FOO:
NODE_ENV:
environment:
- NODE_ENV=test
- AWS_ACCESS_KEY_ID=dummy
- AWS_SECRET_ACCESS_KEY=dummy
env_file:
- ` + envf.Name() + `
`)
c, err := ParseCompose(dt)
require.NoError(t, err)
require.Equal(t, c.Targets[0].Args, map[string]string{"CT_ECR": "foo", "FOO": "bsdf -csdf", "NODE_ENV": "test"})
}
func newBool(val bool) *bool {
b := val
return &b
}
func TestServiceName(t *testing.T) {
cases := []struct {
svc string
wantErr bool
}{
{
svc: "a",
wantErr: false,
},
{
svc: "abc",
wantErr: false,
},
{
svc: "a.b",
wantErr: false,
},
{
svc: "a?b",
wantErr: true,
},
{
svc: "_a",
wantErr: false,
},
{
svc: "a_b",
wantErr: false,
},
{
svc: "AbC",
wantErr: false,
},
{
svc: "AbC-0123",
wantErr: false,
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.svc, func(t *testing.T) {
_, err := ParseCompose([]byte(`
services:
` + tt.svc + `:
build:
context: .
`))
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

View File

@@ -3,7 +3,7 @@ package bake
import ( import (
"strings" "strings"
hcl "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclparse" "github.com/hashicorp/hcl/v2/hclparse"
"github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/solver/pb"

View File

@@ -16,8 +16,9 @@ import (
) )
type Opt struct { type Opt struct {
LookupVar func(string) (string, bool) LookupVar func(string) (string, bool)
Vars map[string]string Vars map[string]string
ValidateLabel func(string) error
} }
type variable struct { type variable struct {
@@ -262,6 +263,12 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
} }
} }
if opt.ValidateLabel == nil {
opt.ValidateLabel = func(string) error {
return nil
}
}
p := &parser{ p := &parser{
opt: opt, opt: opt,
@@ -446,6 +453,17 @@ func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
continue continue
} }
if err := opt.ValidateLabel(b.Labels[0]); err != nil {
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid name",
Detail: err.Error(),
Subject: &b.LabelRanges[0],
},
}
}
lblIndex := setLabel(vv, b.Labels[0]) lblIndex := setLabel(vv, b.Labels[0])
oldValue, exists := t.values[b.Labels[0]] oldValue, exists := t.values[b.Labels[0]]

View File

@@ -21,9 +21,10 @@ type Input struct {
} }
func ReadRemoteFiles(ctx context.Context, dis []build.DriverInfo, url string, names []string, pw progress.Writer) ([]File, *Input, error) { func ReadRemoteFiles(ctx context.Context, dis []build.DriverInfo, url string, names []string, pw progress.Writer) ([]File, *Input, error) {
st, filename, ok := detectHTTPContext(url) var filename string
st, ok := detectGitContext(url)
if !ok { if !ok {
st, ok = detectGitContext(url) st, filename, ok = detectHTTPContext(url)
if !ok { if !ok {
return nil, nil, errors.Errorf("not url context") return nil, nil, errors.Errorf("not url context")
} }

View File

@@ -3,6 +3,8 @@ package build
import ( import (
"bufio" "bufio"
"context" "context"
"crypto/rand"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@@ -12,6 +14,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
@@ -19,7 +22,9 @@ import (
"github.com/docker/buildx/driver" "github.com/docker/buildx/driver"
"github.com/docker/buildx/util/imagetools" "github.com/docker/buildx/util/imagetools"
"github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/progress"
clitypes "github.com/docker/cli/cli/config/types" "github.com/docker/buildx/util/resolver"
"github.com/docker/buildx/util/waitmap"
"github.com/docker/cli/opts"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
dockerclient "github.com/docker/docker/client" dockerclient "github.com/docker/docker/client"
@@ -27,9 +32,11 @@ import (
"github.com/docker/docker/pkg/urlutil" "github.com/docker/docker/pkg/urlutil"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
gateway "github.com/moby/buildkit/frontend/gateway/client" gateway "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session" "github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/upload/uploadprovider" "github.com/moby/buildkit/session/upload/uploadprovider"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/apicaps" "github.com/moby/buildkit/util/apicaps"
"github.com/moby/buildkit/util/entitlements" "github.com/moby/buildkit/util/entitlements"
"github.com/moby/buildkit/util/progress/progresswriter" "github.com/moby/buildkit/util/progress/progresswriter"
@@ -48,26 +55,27 @@ var (
) )
type Options struct { type Options struct {
Inputs Inputs Inputs Inputs
Tags []string
Labels map[string]string
BuildArgs map[string]string
Pull bool
ImageIDFile string
ExtraHosts []string
NetworkMode string
NoCache bool Allow []entitlements.Entitlement
Target string BuildArgs map[string]string
Platforms []specs.Platform CacheFrom []client.CacheOptionsEntry
Exports []client.ExportEntry CacheTo []client.CacheOptionsEntry
Session []session.Attachable CgroupParent string
Exports []client.ExportEntry
CacheFrom []client.CacheOptionsEntry ExtraHosts []string
CacheTo []client.CacheOptionsEntry ImageIDFile string
Labels map[string]string
Allow []entitlements.Entitlement NetworkMode string
// DockerTarget NoCache bool
NoCacheFilter []string
Platforms []specs.Platform
Pull bool
Session []session.Attachable
ShmSize opts.MemBytes
Tags []string
Target string
Ulimits *opts.UlimitOpt
} }
type Inputs struct { type Inputs struct {
@@ -76,17 +84,21 @@ type Inputs struct {
InStream io.Reader InStream io.Reader
ContextState *llb.State ContextState *llb.State
DockerfileInline string DockerfileInline string
NamedContexts map[string]NamedContext
}
type NamedContext struct {
Path string
State *llb.State
} }
type DriverInfo struct { type DriverInfo struct {
Driver driver.Driver Driver driver.Driver
Name string Name string
Platform []specs.Platform Platform []specs.Platform
Err error Err error
} ImageOpt imagetools.Opt
ProxyConfig map[string]string
type Auth interface {
GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error)
} }
type DockerAPI interface { type DockerAPI interface {
@@ -177,6 +189,10 @@ func splitToDriverPairs(availablePlatforms map[string]int, opt map[string]Option
pp = append(pp, p) pp = append(pp, p)
mm[idx] = pp mm[idx] = pp
} }
// if no platform is specified, use first driver
if len(mm) == 0 {
mm[0] = nil
}
dps := make([]driverPair, 0, 2) dps := make([]driverPair, 0, 2)
for idx, pp := range mm { for idx, pp := range mm {
dps = append(dps, driverPair{driverIndex: idx, platforms: pp}) dps = append(dps, driverPair{driverIndex: idx, platforms: pp})
@@ -186,8 +202,8 @@ func splitToDriverPairs(availablePlatforms map[string]int, opt map[string]Option
return m return m
} }
func resolveDrivers(ctx context.Context, drivers []DriverInfo, auth Auth, opt map[string]Options, pw progress.Writer) (map[string][]driverPair, []*client.Client, error) { func resolveDrivers(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw progress.Writer) (map[string][]driverPair, []*client.Client, error) {
dps, clients, err := resolveDriversBase(ctx, drivers, auth, opt, pw) dps, clients, err := resolveDriversBase(ctx, drivers, opt, pw)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -214,8 +230,7 @@ func resolveDrivers(ctx context.Context, drivers []DriverInfo, auth Auth, opt ma
} }
err = eg.Wait() err = eg.Wait()
span.RecordError(err) tracing.FinishWithError(span, err)
span.End()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -228,7 +243,7 @@ func resolveDrivers(ctx context.Context, drivers []DriverInfo, auth Auth, opt ma
return dps, clients, nil return dps, clients, nil
} }
func resolveDriversBase(ctx context.Context, drivers []DriverInfo, auth Auth, opt map[string]Options, pw progress.Writer) (map[string][]driverPair, []*client.Client, error) { func resolveDriversBase(ctx context.Context, drivers []DriverInfo, opt map[string]Options, pw progress.Writer) (map[string][]driverPair, []*client.Client, error) {
availablePlatforms := map[string]int{} availablePlatforms := map[string]int{}
for i, d := range drivers { for i, d := range drivers {
for _, p := range d.Platform { for _, p := range d.Platform {
@@ -334,7 +349,8 @@ func toRepoOnly(in string) (string, error) {
return strings.Join(out, ","), nil return strings.Join(out, ","), nil
} }
func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Options, bopts gateway.BuildOpts, pw progress.Writer, dl dockerLoadCallback) (solveOpt *client.SolveOpt, release func(), err error) { func toSolveOpt(ctx context.Context, di DriverInfo, multiDriver bool, opt Options, bopts gateway.BuildOpts, configDir string, pw progress.Writer, dl dockerLoadCallback) (solveOpt *client.SolveOpt, release func(), err error) {
d := di.Driver
defers := make([]func(), 0, 2) defers := make([]func(), 0, 2)
releaseF := func() { releaseF := func() {
for _, f := range defers { for _, f := range defers {
@@ -400,6 +416,10 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
AllowedEntitlements: opt.Allow, AllowedEntitlements: opt.Allow,
} }
if opt.CgroupParent != "" {
so.FrontendAttrs["cgroup-parent"] = opt.CgroupParent
}
if v, ok := opt.BuildArgs["BUILDKIT_MULTI_PLATFORM"]; ok { if v, ok := opt.BuildArgs["BUILDKIT_MULTI_PLATFORM"]; ok {
if v, _ := strconv.ParseBool(v); v { if v, _ := strconv.ParseBool(v); v {
so.FrontendAttrs["multi-platform"] = "true" so.FrontendAttrs["multi-platform"] = "true"
@@ -496,6 +516,12 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
} }
} }
} }
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.Exports = opt.Exports
@@ -507,12 +533,22 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
} }
defers = append(defers, releaseLoad) 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 + ":" + tryNodeIdentifier(configDir)
}
if opt.Pull { if opt.Pull {
so.FrontendAttrs["image-resolve-mode"] = "pull" so.FrontendAttrs["image-resolve-mode"] = "pull"
} }
if opt.Target != "" { if opt.Target != "" {
so.FrontendAttrs["target"] = opt.Target so.FrontendAttrs["target"] = opt.Target
} }
if len(opt.NoCacheFilter) > 0 {
so.FrontendAttrs["no-cache"] = strings.Join(opt.NoCacheFilter, ",")
}
if opt.NoCache { if opt.NoCache {
so.FrontendAttrs["no-cache"] = "" so.FrontendAttrs["no-cache"] = ""
} }
@@ -523,6 +559,12 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
so.FrontendAttrs["label:"+k] = v so.FrontendAttrs["label:"+k] = v
} }
for k, v := range di.ProxyConfig {
if _, ok := opt.BuildArgs[k]; !ok {
so.FrontendAttrs["build-arg:"+k] = v
}
}
// set platforms // set platforms
if len(opt.Platforms) != 0 { if len(opt.Platforms) != 0 {
pp := make([]string, len(opt.Platforms)) pp := make([]string, len(opt.Platforms))
@@ -544,7 +586,7 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
so.FrontendAttrs["force-network-mode"] = opt.NetworkMode so.FrontendAttrs["force-network-mode"] = opt.NetworkMode
case "", "default": case "", "default":
default: default:
return nil, nil, errors.Errorf("network mode %q not supported by buildkit", opt.NetworkMode) 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 // setup extrahosts
@@ -554,10 +596,23 @@ func toSolveOpt(ctx context.Context, d driver.Driver, multiDriver bool, opt Opti
} }
so.FrontendAttrs["add-hosts"] = extraHosts 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 return &so, releaseF, nil
} }
func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, docker DockerAPI, auth Auth, w progress.Writer) (resp map[string]*client.SolveResponse, err error) { func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, docker DockerAPI, configDir string, w progress.Writer) (resp map[string]*client.SolveResponse, err error) {
if len(drivers) == 0 { if len(drivers) == 0 {
return nil, errors.Errorf("driver required for build") return nil, errors.Errorf("driver required for build")
} }
@@ -584,7 +639,7 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
} }
} }
m, clients, err := resolveDrivers(ctx, drivers, auth, opt, w) m, clients, err := resolveDrivers(ctx, drivers, opt, w)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -604,12 +659,12 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
multiDriver := len(m[k]) > 1 multiDriver := len(m[k]) > 1
hasMobyDriver := false hasMobyDriver := false
for i, dp := range m[k] { for i, dp := range m[k] {
d := drivers[dp.driverIndex].Driver di := drivers[dp.driverIndex]
if d.IsMobyDriver() { if di.Driver.IsMobyDriver() {
hasMobyDriver = true hasMobyDriver = true
} }
opt.Platforms = dp.platforms opt.Platforms = dp.platforms
so, release, err := toSolveOpt(ctx, d, multiDriver, opt, dp.bopts, w, func(name string) (io.WriteCloser, func(), error) { so, release, err := toSolveOpt(ctx, di, multiDriver, opt, dp.bopts, configDir, w, func(name string) (io.WriteCloser, func(), error) {
return newDockerLoader(ctx, docker, name, w) return newDockerLoader(ctx, docker, name, w)
}) })
if err != nil { if err != nil {
@@ -642,8 +697,35 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
} }
} }
// validate that all links between targets use same drivers
for name := range opt {
dps := m[name]
for _, dp := range dps {
for k, v := range dp.so.FrontendAttrs {
if strings.HasPrefix(k, "context:") && strings.HasPrefix(v, "target:") {
k2 := strings.TrimPrefix(v, "target:")
dps2, ok := m[k2]
if !ok {
return nil, errors.Errorf("failed to find target %s for context %s", k2, strings.TrimPrefix(k, "context:")) // should be validated before already
}
var found bool
for _, dp2 := range dps2 {
if dp2.driverIndex == dp.driverIndex {
found = true
break
}
}
if !found {
return nil, errors.Errorf("failed to use %s as context %s for %s because targets build with different drivers", k2, strings.TrimPrefix(k, "context:"), name)
}
}
}
}
}
resp = map[string]*client.SolveResponse{} resp = map[string]*client.SolveResponse{}
var respMu sync.Mutex var respMu sync.Mutex
results := waitmap.New()
multiTarget := len(opt) > 1 multiTarget := len(opt) > 1
@@ -664,12 +746,12 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
wg.Add(len(dps)) wg.Add(len(dps))
var pushNames string var pushNames string
var insecurePush bool
eg.Go(func() (err error) { eg.Go(func() (err error) {
defer func() { defer func() {
if span != nil { if span != nil {
span.RecordError(err) tracing.FinishWithError(span, err)
span.End()
} }
}() }()
pw := progress.WithPrefix(w, "default", false) pw := progress.WithPrefix(w, "default", false)
@@ -684,8 +766,12 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
resp[k] = res[0] resp[k] = res[0]
respMu.Unlock() respMu.Unlock()
if len(res) == 1 { if len(res) == 1 {
dgst := res[0].ExporterResponse[exptypes.ExporterImageDigestKey]
if v, ok := res[0].ExporterResponse[exptypes.ExporterImageConfigDigestKey]; ok {
dgst = v
}
if opt.ImageIDFile != "" { if opt.ImageIDFile != "" {
return ioutil.WriteFile(opt.ImageIDFile, []byte(res[0].ExporterResponse["containerimage.digest"]), 0644) return ioutil.WriteFile(opt.ImageIDFile, []byte(dgst), 0644)
} }
return nil return nil
} }
@@ -695,7 +781,7 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
descs := make([]specs.Descriptor, 0, len(res)) descs := make([]specs.Descriptor, 0, len(res))
for _, r := range res { for _, r := range res {
s, ok := r.ExporterResponse["containerimage.digest"] s, ok := r.ExporterResponse[exptypes.ExporterImageDigestKey]
if ok { if ok {
descs = append(descs, specs.Descriptor{ descs = append(descs, specs.Descriptor{
Digest: digest.Digest(s), Digest: digest.Digest(s),
@@ -705,22 +791,41 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
} }
} }
if len(descs) > 0 { if len(descs) > 0 {
itpull := imagetools.New(imagetools.Opt{ var imageopt imagetools.Opt
Auth: auth, for _, dp := range dps {
}) imageopt = drivers[dp.driverIndex].ImageOpt
break
}
names := strings.Split(pushNames, ",") names := strings.Split(pushNames, ",")
if insecurePush {
insecureTrue := true
httpTrue := true
nn, err := reference.ParseNormalizedNamed(names[0])
if err != nil {
return err
}
imageopt.RegistryConfig = map[string]resolver.RegistryConfig{
reference.Domain(nn): {
Insecure: &insecureTrue,
PlainHTTP: &httpTrue,
},
}
}
itpull := imagetools.New(imageopt)
dt, desc, err := itpull.Combine(ctx, names[0], descs) dt, desc, err := itpull.Combine(ctx, names[0], descs)
if err != nil { if err != nil {
return err return err
} }
if opt.ImageIDFile != "" { if opt.ImageIDFile != "" {
return ioutil.WriteFile(opt.ImageIDFile, []byte(desc.Digest), 0644) if err := ioutil.WriteFile(opt.ImageIDFile, []byte(desc.Digest), 0644); err != nil {
return err
}
} }
itpush := imagetools.New(imagetools.Opt{ itpush := imagetools.New(imageopt)
Auth: auth,
})
for _, n := range names { for _, n := range names {
nn, err := reference.ParseNormalizedNamed(n) nn, err := reference.ParseNormalizedNamed(n)
@@ -748,7 +853,6 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
for i, dp := range dps { for i, dp := range dps {
so := *dp.so so := *dp.so
if multiDriver { if multiDriver {
for i, e := range so.Exports { for i, e := range so.Exports {
switch e.Type { switch e.Type {
@@ -765,6 +869,9 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
if err != nil { if err != nil {
return err return err
} }
if ok, _ := strconv.ParseBool(e.Attrs["registry.insecure"]); ok {
insecurePush = true
}
e.Attrs["name"] = names e.Attrs["name"] = names
e.Attrs["push-by-digest"] = "true" e.Attrs["push-by-digest"] = "true"
so.Exports[i].Attrs = e.Attrs so.Exports[i].Attrs = e.Attrs
@@ -778,14 +885,43 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
pw := progress.WithPrefix(w, k, multiTarget) pw := progress.WithPrefix(w, k, multiTarget)
c := clients[dp.driverIndex] c := clients[dp.driverIndex]
pw = progress.ResetTime(pw)
eg.Go(func() error { eg.Go(func() error {
pw = progress.ResetTime(pw)
defer wg.Done() defer wg.Done()
if err := waitContextDeps(ctx, dp.driverIndex, results, &so); err != nil {
return err
}
frontendInputs := make(map[string]*pb.Definition)
for key, st := range so.FrontendInputs {
def, err := st.Marshal(ctx)
if err != nil {
return err
}
frontendInputs[key] = def.ToPB()
}
req := gateway.SolveRequest{
Frontend: so.Frontend,
FrontendOpt: so.FrontendAttrs,
FrontendInputs: frontendInputs,
}
so.Frontend = ""
so.FrontendAttrs = nil
so.FrontendInputs = nil
ch, done := progress.NewChannel(pw) ch, done := progress.NewChannel(pw)
defer func() { <-done }() defer func() { <-done }()
rr, err := c.Solve(ctx, nil, so, ch)
rr, err := c.Build(ctx, so, "buildx", func(ctx context.Context, c gateway.Client) (*gateway.Result, error) {
res, err := c.Solve(ctx, req)
if err != nil {
return nil, err
}
results.Set(resultKey(dp.driverIndex, k), res)
return res, nil
}, ch)
if err != nil { if err != nil {
return err return err
} }
@@ -801,13 +937,27 @@ func Build(ctx context.Context, drivers []DriverInfo, opt map[string]Options, do
return errors.Errorf("tag is needed when pushing to registry") return errors.Errorf("tag is needed when pushing to registry")
} }
pw := progress.ResetTime(pw) pw := progress.ResetTime(pw)
for _, name := range strings.Split(pushNames, ",") { pushList := strings.Split(pushNames, ",")
for _, name := range pushList {
if err := progress.Wrap(fmt.Sprintf("pushing %s with docker", name), pw.Write, func(l progress.SubLogger) error { if err := progress.Wrap(fmt.Sprintf("pushing %s with docker", name), pw.Write, func(l progress.SubLogger) error {
return pushWithMoby(ctx, d, name, l) return pushWithMoby(ctx, d, name, l)
}); err != nil { }); err != nil {
return err return err
} }
} }
remoteDigest, err := remoteDigestWithMoby(ctx, d, pushList[0])
if err == nil && remoteDigest != "" {
// old daemons might not have containerimage.config.digest set
// in response so use containerimage.digest value for it if available
if _, ok := rr.ExporterResponse[exptypes.ExporterImageConfigDigestKey]; !ok {
if v, ok := rr.ExporterResponse[exptypes.ExporterImageDigestKey]; ok {
rr.ExporterResponse[exptypes.ExporterImageConfigDigestKey] = v
}
}
rr.ExporterResponse[exptypes.ExporterImageDigestKey] = remoteDigest
} else if err != nil {
return err
}
} }
} }
} }
@@ -912,6 +1062,29 @@ func pushWithMoby(ctx context.Context, d driver.Driver, name string, l progress.
return nil return nil
} }
func remoteDigestWithMoby(ctx context.Context, d driver.Driver, name string) (string, error) {
api := d.Config().DockerAPI
if api == nil {
return "", errors.Errorf("invalid empty Docker API reference") // should never happen
}
creds, err := imagetools.RegistryAuthForRef(name, d.Config().Auth)
if err != nil {
return "", err
}
image, _, err := api.ImageInspectWithRaw(ctx, name)
if err != nil {
return "", err
}
if len(image.RepoDigests) == 0 {
return "", nil
}
remoteImage, err := api.DistributionInspect(ctx, name, creds)
if err != nil {
return "", err
}
return remoteImage.Descriptor.Digest.String(), nil
}
func createTempDockerfile(r io.Reader) (string, error) { func createTempDockerfile(r io.Reader) (string, error) {
dir, err := ioutil.TempDir("", "dockerfile") dir, err := ioutil.TempDir("", "dockerfile")
if err != nil { if err != nil {
@@ -1034,6 +1207,36 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr
target.FrontendAttrs["filename"] = 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 urlutil.IsGitURL(v.Path) || urlutil.IsURL(v.Path) || strings.HasPrefix(v.Path, "docker-image://") || strings.HasPrefix(v.Path, "target:") {
target.FrontendAttrs["context:"+k] = v.Path
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
}
target.LocalDirs[localName] = v.Path
target.FrontendAttrs["context:"+k] = "local:" + localName
}
release := func() { release := func() {
for _, dir := range toRemove { for _, dir := range toRemove {
os.RemoveAll(dir) os.RemoveAll(dir)
@@ -1042,6 +1245,96 @@ func LoadInputs(ctx context.Context, d driver.Driver, inp Inputs, pw progress.Wr
return release, nil return release, nil
} }
func resultKey(index int, name string) string {
return fmt.Sprintf("%d-%s", index, name)
}
func waitContextDeps(ctx context.Context, index int, results *waitmap.Map, so *client.SolveOpt) error {
m := map[string]string{}
for k, v := range so.FrontendAttrs {
if strings.HasPrefix(k, "context:") && strings.HasPrefix(v, "target:") {
target := resultKey(index, strings.TrimPrefix(v, "target:"))
m[target] = k
}
}
if len(m) == 0 {
return nil
}
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
res, err := results.Get(ctx, keys...)
if err != nil {
return err
}
for k, v := range m {
r, ok := res[k]
if !ok {
continue
}
rr, ok := r.(*gateway.Result)
if !ok {
return errors.Errorf("invalid result type %T", rr)
}
if so.FrontendAttrs == nil {
so.FrontendAttrs = map[string]string{}
}
if so.FrontendInputs == nil {
so.FrontendInputs = map[string]llb.State{}
}
if len(rr.Refs) > 0 {
for platform, r := range rr.Refs {
st, err := r.ToState()
if err != nil {
return err
}
so.FrontendInputs[k+"::"+platform] = st
so.FrontendAttrs[v+"::"+platform] = "input:" + k + "::" + platform
metadata := make(map[string][]byte)
if dt, ok := rr.Metadata[exptypes.ExporterImageConfigKey+"/"+platform]; ok {
metadata[exptypes.ExporterImageConfigKey] = dt
}
if dt, ok := rr.Metadata[exptypes.ExporterBuildInfo+"/"+platform]; ok {
metadata[exptypes.ExporterBuildInfo] = dt
}
if len(metadata) > 0 {
dt, err := json.Marshal(metadata)
if err != nil {
return err
}
so.FrontendAttrs["input-metadata:"+k+"::"+platform] = string(dt)
}
}
delete(so.FrontendAttrs, v)
}
if rr.Ref != nil {
st, err := rr.Ref.ToState()
if err != nil {
return err
}
so.FrontendInputs[k] = st
so.FrontendAttrs[v] = "input:" + k
metadata := make(map[string][]byte)
if dt, ok := rr.Metadata[exptypes.ExporterImageConfigKey]; ok {
metadata[exptypes.ExporterImageConfigKey] = dt
}
if dt, ok := rr.Metadata[exptypes.ExporterBuildInfo]; ok {
metadata[exptypes.ExporterBuildInfo] = dt
}
if len(metadata) > 0 {
dt, err := json.Marshal(metadata)
if err != nil {
return err
}
so.FrontendAttrs["input-metadata:"+k] = string(dt)
}
}
}
return nil
}
func notSupported(d driver.Driver, f driver.Feature) error { func notSupported(d driver.Driver, f driver.Feature) error {
return errors.Errorf("%s feature is currently not supported for %s driver. Please switch to a different driver (eg. \"docker buildx create --use\")", f, d.Factory().Name()) return errors.Errorf("%s feature is currently not supported for %s driver. Please switch to a different driver (eg. \"docker buildx create --use\")", f, d.Factory().Name())
} }
@@ -1158,3 +1451,28 @@ func wrapWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser,
return wc, nil return wc, nil
} }
} }
var nodeIdentifierMu sync.Mutex
func tryNodeIdentifier(configDir string) (out string) {
nodeIdentifierMu.Lock()
defer nodeIdentifierMu.Unlock()
sessionFile := filepath.Join(configDir, ".buildNodeID")
if _, err := os.Lstat(sessionFile); err != nil {
if os.IsNotExist(err) { // create a new file with stored randomness
b := make([]byte, 8)
if _, err := rand.Read(b); err != nil {
return out
}
if err := ioutil.WriteFile(sessionFile, []byte(hex.EncodeToString(b)), 0600); err != nil {
return out
}
}
}
dt, err := ioutil.ReadFile(sessionFile)
if err == nil {
return string(dt)
}
return
}

View File

@@ -7,6 +7,7 @@ import (
"os" "os"
"strings" "strings"
"github.com/docker/cli/opts"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -53,3 +54,15 @@ func toBuildkitExtraHosts(inp []string) (string, error) {
} }
return strings.Join(hosts, ","), nil return strings.Join(hosts, ","), nil
} }
// toBuildkitUlimits converts ulimits from docker type=soft:hard format to buildkit's csv format
func toBuildkitUlimits(inp *opts.UlimitOpt) (string, error) {
if inp == nil || len(inp.GetList()) == 0 {
return "", nil
}
ulimits := make([]string, 0, len(inp.GetList()))
for _, ulimit := range inp.GetList() {
ulimits = append(ulimits, ulimit.String())
}
return strings.Join(ulimits, ","), nil
}

View File

@@ -43,21 +43,19 @@ func init() {
} }
func main() { func main() {
if os.Getenv("DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND") == "" { if plugin.RunningStandalone() {
if len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName { dockerCli, err := command.NewDockerCli()
dockerCli, err := command.NewDockerCli() if err != nil {
if err != nil { fmt.Fprintln(os.Stderr, err)
fmt.Fprintln(os.Stderr, err) os.Exit(1)
os.Exit(1)
}
opts := cliflags.NewClientOptions()
dockerCli.Initialize(opts)
rootCmd := commands.NewRootCmd(os.Args[0], false, dockerCli)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
os.Exit(0)
} }
opts := cliflags.NewClientOptions()
dockerCli.Initialize(opts)
rootCmd := commands.NewRootCmd(os.Args[0], false, dockerCli)
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
os.Exit(0)
} }
dockerCli, err := command.NewDockerCli() dockerCli, err := command.NewDockerCli()

View File

@@ -6,12 +6,13 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/containerd/containerd/platforms"
"github.com/docker/buildx/bake" "github.com/docker/buildx/bake"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/progress"
"github.com/docker/buildx/util/tracing" "github.com/docker/buildx/util/tracing"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/docker/pkg/ioutils"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -19,8 +20,8 @@ import (
type bakeOptions struct { type bakeOptions struct {
files []string files []string
printOnly bool
overrides []string overrides []string
printOnly bool
commonOptions commonOptions
} }
@@ -46,7 +47,6 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
if bake.IsRemoteURL(targets[0]) { if bake.IsRemoteURL(targets[0]) {
cmdContext = targets[0] cmdContext = targets[0]
targets = targets[1:] targets = targets[1:]
} }
} }
} }
@@ -61,7 +61,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
if in.exportLoad { if in.exportLoad {
return errors.Errorf("push and load may not be set together at the moment") return errors.Errorf("push and load may not be set together at the moment")
} }
overrides = append(overrides, "*.output=type=registry") overrides = append(overrides, "*.push=true")
} else if in.exportLoad { } else if in.exportLoad {
overrides = append(overrides, "*.output=type=docker") overrides = append(overrides, "*.output=type=docker")
} }
@@ -75,7 +75,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
ctx2, cancel := context.WithCancel(context.TODO()) ctx2, cancel := context.WithCancel(context.TODO())
defer cancel() defer cancel()
printer := progress.NewPrinter(ctx2, os.Stderr, in.progress) printer := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, in.progress)
defer func() { defer func() {
if printer != nil { if printer != nil {
@@ -103,21 +103,36 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
return err return err
} }
m, err := bake.ReadTargets(ctx, files, targets, overrides, map[string]string{ tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, map[string]string{
"BAKE_CMD_CONTEXT": cmdContext, // Don't forget to update documentation if you add a new
// built-in variable: docs/reference/buildx_bake.md#built-in-variables
"BAKE_CMD_CONTEXT": cmdContext,
"BAKE_LOCAL_PLATFORM": platforms.DefaultString(),
}) })
if err != nil { if err != nil {
return err return err
} }
// this function can update target context string from the input so call before printOnly check // this function can update target context string from the input so call before printOnly check
bo, err := bake.TargetsToBuildOpt(m, inp) bo, err := bake.TargetsToBuildOpt(tgts, inp)
if err != nil { if err != nil {
return err return err
} }
if in.printOnly { if in.printOnly {
dt, err := json.MarshalIndent(map[string]map[string]*bake.Target{"target": m}, "", " ") var defg map[string]*bake.Group
if len(grps) == 1 {
defg = map[string]*bake.Group{
"default": grps[0],
}
}
dt, err := json.MarshalIndent(struct {
Group map[string]*bake.Group `json:"group,omitempty"`
Target map[string]*bake.Target `json:"target"`
}{
defg,
tgts,
}, "", " ")
if err != nil { if err != nil {
return err return err
} }
@@ -130,21 +145,17 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
return nil return nil
} }
resp, err := build.Build(ctx, dis, bo, dockerAPI(dockerCli), dockerCli.ConfigFile(), printer) resp, err := build.Build(ctx, dis, bo, dockerAPI(dockerCli), confutil.ConfigDir(dockerCli), printer)
if err != nil { if err != nil {
return err return wrapBuildError(err, true)
} }
if len(in.metadataFile) > 0 && resp != nil { if len(in.metadataFile) > 0 {
mdata := map[string]map[string]string{} dt := make(map[string]interface{})
for k, r := range resp { for t, r := range resp {
mdata[k] = r.ExporterResponse dt[t] = decodeExporterResponse(r.ExporterResponse)
} }
mdatab, err := json.MarshalIndent(mdata, "", " ") if err := writeMetadataFile(in.metadataFile, dt); err != nil {
if err != nil {
return err
}
if err := ioutils.AtomicWriteFile(in.metadataFile, mdatab, 0644); err != nil {
return err return err
} }
} }
@@ -175,10 +186,10 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
flags := cmd.Flags() flags := cmd.Flags()
flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file") flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Build definition file")
flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--set=*.output=type=docker"`)
flags.BoolVar(&options.printOnly, "print", false, "Print the options without building") flags.BoolVar(&options.printOnly, "print", false, "Print the options without building")
flags.StringArrayVar(&options.overrides, "set", nil, "Override target value (eg: targetpattern.key=value)") flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry"`)
flags.BoolVar(&options.exportPush, "push", false, "Shorthand for --set=*.output=type=registry") flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`)
flags.BoolVar(&options.exportLoad, "load", false, "Shorthand for --set=*.output=type=docker")
commonBuildFlags(&options.commonOptions, flags) commonBuildFlags(&options.commonOptions, flags)

View File

@@ -1,79 +1,78 @@
package commands package commands
import ( import (
"bytes"
"context" "context"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/util/buildflags" "github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/progress"
"github.com/docker/buildx/util/tracing" "github.com/docker/buildx/util/tracing"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
dockeropts "github.com/docker/cli/opts"
"github.com/docker/distribution/reference"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/go-units"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/session/auth/authprovider" "github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/morikuni/aec"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"google.golang.org/grpc/codes"
) )
const defaultTargetName = "default" const defaultTargetName = "default"
type buildOptions struct { type buildOptions struct {
commonOptions
contextPath string contextPath string
dockerfileName string dockerfileName string
tags []string
labels []string
buildArgs []string
cacheFrom []string allow []string
cacheTo []string buildArgs []string
target string cacheFrom []string
platforms []string cacheTo []string
secrets []string cgroupParent string
ssh []string contexts []string
outputs []string extraHosts []string
imageIDFile string imageIDFile string
extraHosts []string labels []string
networkMode string networkMode string
noCacheFilter []string
// unimplemented outputs []string
squash bool platforms []string
quiet bool quiet bool
secrets []string
allow []string shmSize dockeropts.MemBytes
ssh []string
// hidden tags []string
// untrusted bool target string
// ulimits *opts.UlimitOpt ulimits *dockeropts.UlimitOpt
// memory opts.MemBytes commonOptions
// memorySwap opts.MemSwapBytes
// shmSize opts.MemBytes
// cpuShares int64
// cpuPeriod int64
// cpuQuota int64
// cpuSetCpus string
// cpuSetMems string
// cgroupParent string
// isolation string
// compress bool
// securityOpt []string
} }
type commonOptions struct { type commonOptions struct {
builder string builder string
metadataFile string
noCache *bool noCache *bool
progress string progress string
pull *bool pull *bool
metadataFile string
// golangci-lint#826 // golangci-lint#826
// nolint:structcheck // nolint:structcheck
exportPush bool exportPush bool
@@ -82,13 +81,6 @@ type commonOptions struct {
} }
func runBuild(dockerCli command.Cli, in buildOptions) (err error) { func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
if in.squash {
return errors.Errorf("squash currently not implemented")
}
if in.quiet {
logrus.Warnf("quiet currently not implemented")
}
ctx := appcontext.Context() ctx := appcontext.Context()
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build") ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
@@ -108,21 +100,40 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
pull = *in.pull pull = *in.pull
} }
if noCache && len(in.noCacheFilter) > 0 {
return errors.Errorf("--no-cache and --no-cache-filter cannot currently be used together")
}
if in.quiet && in.progress != "auto" && in.progress != "quiet" {
return errors.Errorf("progress=%s and quiet cannot be used together", in.progress)
} else if in.quiet {
in.progress = "quiet"
}
contexts, err := parseContextNames(in.contexts)
if err != nil {
return err
}
opts := build.Options{ opts := build.Options{
Inputs: build.Inputs{ Inputs: build.Inputs{
ContextPath: in.contextPath, ContextPath: in.contextPath,
DockerfilePath: in.dockerfileName, DockerfilePath: in.dockerfileName,
InStream: os.Stdin, InStream: os.Stdin,
NamedContexts: contexts,
}, },
Tags: in.tags, BuildArgs: listToMap(in.buildArgs, true),
Labels: listToMap(in.labels, false), ExtraHosts: in.extraHosts,
BuildArgs: listToMap(in.buildArgs, true), ImageIDFile: in.imageIDFile,
Pull: pull, Labels: listToMap(in.labels, false),
NoCache: noCache, NetworkMode: in.networkMode,
Target: in.target, NoCache: noCache,
ImageIDFile: in.imageIDFile, NoCacheFilter: in.noCacheFilter,
ExtraHosts: in.extraHosts, Pull: pull,
NetworkMode: in.networkMode, ShmSize: in.shmSize,
Tags: in.tags,
Target: in.target,
Ulimits: in.ulimits,
} }
platforms, err := platformutil.Parse(in.platforms) platforms, err := platformutil.Parse(in.platforms)
@@ -214,43 +225,98 @@ func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
contextPathHash = in.contextPath contextPathHash = in.contextPath
} }
return buildTargets(ctx, dockerCli, map[string]build.Options{defaultTargetName: opts}, in.progress, contextPathHash, in.builder, in.metadataFile) imageID, err := buildTargets(ctx, dockerCli, map[string]build.Options{defaultTargetName: opts}, in.progress, contextPathHash, in.builder, in.metadataFile)
} err = wrapBuildError(err, false)
func buildTargets(ctx context.Context, dockerCli command.Cli, opts map[string]build.Options, progressMode, contextPathHash, instance string, metadataFile string) error {
dis, err := getInstanceOrDefault(ctx, dockerCli, instance, contextPathHash)
if err != nil { if err != nil {
return err return err
} }
if in.quiet {
fmt.Println(imageID)
}
return nil
}
func buildTargets(ctx context.Context, dockerCli command.Cli, opts map[string]build.Options, progressMode, contextPathHash, instance string, metadataFile string) (imageID string, err error) {
dis, err := getInstanceOrDefault(ctx, dockerCli, instance, contextPathHash)
if err != nil {
return "", err
}
ctx2, cancel := context.WithCancel(context.TODO()) ctx2, cancel := context.WithCancel(context.TODO())
defer cancel() defer cancel()
printer := progress.NewPrinter(ctx2, os.Stderr, progressMode)
resp, err := build.Build(ctx, dis, opts, dockerAPI(dockerCli), dockerCli.ConfigFile(), printer) printer := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, progressMode)
resp, err := build.Build(ctx, dis, opts, dockerAPI(dockerCli), confutil.ConfigDir(dockerCli), printer)
err1 := printer.Wait() err1 := printer.Wait()
if err == nil { if err == nil {
err = err1 err = err1
} }
if err != nil { if err != nil {
return err return "", err
} }
if len(metadataFile) > 0 && resp != nil { if len(metadataFile) > 0 && resp != nil {
mdatab, err := json.MarshalIndent(resp[defaultTargetName].ExporterResponse, "", " ") if err := writeMetadataFile(metadataFile, decodeExporterResponse(resp[defaultTargetName].ExporterResponse)); err != nil {
if err != nil { return "", err
return err
}
if err := ioutils.AtomicWriteFile(metadataFile, mdatab, 0644); err != nil {
return err
} }
} }
return err printWarnings(os.Stderr, printer.Warnings(), progressMode)
return resp[defaultTargetName].ExporterResponse["containerimage.digest"], err
}
func printWarnings(w io.Writer, warnings []client.VertexWarning, mode string) {
if len(warnings) == 0 || mode == progress.PrinterModeQuiet {
return
}
fmt.Fprintf(w, "\n ")
sb := &bytes.Buffer{}
if len(warnings) == 1 {
fmt.Fprintf(sb, "1 warning found")
} else {
fmt.Fprintf(sb, "%d warnings found", len(warnings))
}
if logrus.GetLevel() < logrus.DebugLevel {
fmt.Fprintf(sb, " (use --debug to expand)")
}
fmt.Fprintf(sb, ":\n")
fmt.Fprint(w, aec.Apply(sb.String(), aec.YellowF))
for _, warn := range warnings {
fmt.Fprintf(w, " - %s\n", warn.Short)
if logrus.GetLevel() < logrus.DebugLevel {
continue
}
for _, d := range warn.Detail {
fmt.Fprintf(w, "%s\n", d)
}
if warn.URL != "" {
fmt.Fprintf(w, "More info: %s\n", warn.URL)
}
if warn.SourceInfo != nil && warn.Range != nil {
src := errdefs.Source{
Info: warn.SourceInfo,
Ranges: warn.Range,
}
src.Print(w)
}
fmt.Fprintf(w, "\n")
}
}
func newBuildOptions() buildOptions {
ulimits := make(map[string]*units.Ulimit)
return buildOptions{
ulimits: dockeropts.NewUlimitOpt(&ulimits),
}
} }
func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
var options buildOptions options := newBuildOptions()
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "build [OPTIONS] PATH | URL | -", Use: "build [OPTIONS] PATH | URL | -",
@@ -260,103 +326,140 @@ func buildCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
options.contextPath = args[0] options.contextPath = args[0]
options.builder = rootOpts.builder options.builder = rootOpts.builder
cmd.Flags().VisitAll(checkWarnedFlags)
return runBuild(dockerCli, options) return runBuild(dockerCli, options)
}, },
} }
var platformsDefault []string
if v := os.Getenv("DOCKER_DEFAULT_PLATFORM"); v != "" {
platformsDefault = []string{v}
}
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVar(&options.exportPush, "push", false, "Shorthand for --output=type=registry") flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`)
flags.BoolVar(&options.exportLoad, "load", false, "Shorthand for --output=type=docker") flags.SetAnnotation("add-host", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host"})
flags.StringSliceVar(&options.allow, "allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`)
flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, "Name and optionally a tag in the 'name:tag' format")
flags.SetAnnotation("tag", "docs.external.url", []string{"https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t"})
flags.StringArrayVar(&options.buildArgs, "build-arg", []string{}, "Set build-time variables") flags.StringArrayVar(&options.buildArgs, "build-arg", []string{}, "Set build-time variables")
flags.SetAnnotation("build-arg", "docs.external.url", []string{"https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg"})
flags.StringVarP(&options.dockerfileName, "file", "f", "", "Name of the Dockerfile (Default is 'PATH/Dockerfile')") flags.StringArrayVar(&options.cacheFrom, "cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`)
flags.SetAnnotation("file", "docs.external.url", []string{"https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f"})
flags.StringArrayVar(&options.cacheTo, "cache-to", []string{}, `Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")`)
flags.StringVar(&options.cgroupParent, "cgroup-parent", "", "Optional parent cgroup for the container")
flags.SetAnnotation("cgroup-parent", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent"})
flags.StringArrayVar(&options.contexts, "build-context", []string{}, "Additional build contexts (e.g., name=path)")
flags.StringVarP(&options.dockerfileName, "file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`)
flags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f"})
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
flags.StringArrayVar(&options.labels, "label", []string{}, "Set metadata for an image") flags.StringArrayVar(&options.labels, "label", []string{}, "Set metadata for an image")
flags.StringArrayVar(&options.cacheFrom, "cache-from", []string{}, "External cache sources (eg. user/app:cache, type=local,src=path/to/dir)") flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--output=type=docker"`)
flags.StringArrayVar(&options.cacheTo, "cache-to", []string{}, "Cache export destinations (eg. user/app:cache, type=local,dest=path/to/dir)")
flags.StringVar(&options.target, "target", "", "Set the target build stage to build.") flags.StringVar(&options.networkMode, "network", "default", `Set the networking mode for the "RUN" instructions during build`)
flags.SetAnnotation("target", "docs.external.url", []string{"https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target"})
flags.StringSliceVar(&options.allow, "allow", []string{}, "Allow extra privileged entitlement, e.g. network.host, security.insecure") flags.StringArrayVar(&options.noCacheFilter, "no-cache-filter", []string{}, "Do not cache specified stages")
flags.StringArrayVarP(&options.outputs, "output", "o", []string{}, `Output destination (format: "type=local,dest=path")`)
flags.StringArrayVar(&options.platforms, "platform", platformsDefault, "Set target platform for build")
flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--output=type=registry"`)
// not implemented
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success") flags.BoolVarP(&options.quiet, "quiet", "q", false, "Suppress the build output and print image ID on success")
flags.StringVar(&options.networkMode, "network", "default", "Set the networking mode for the RUN instructions during build")
flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, "Add a custom host-to-IP mapping (host:ip)") flags.StringArrayVar(&options.secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`)
flags.SetAnnotation("add-host", "docs.external.url", []string{"https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host"})
flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file") flags.Var(&options.shmSize, "shm-size", `Size of "/dev/shm"`)
flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer")
flags.MarkHidden("quiet") flags.StringArrayVar(&options.ssh, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|<id>[=<socket>|<key>[,<key>]]")`)
flags.MarkHidden("squash")
flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, `Name and optionally a tag (format: "name:tag")`)
flags.SetAnnotation("tag", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t"})
flags.StringVar(&options.target, "target", "", "Set the target build stage to build")
flags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target"})
flags.Var(options.ulimits, "ulimit", "Ulimit options")
// hidden flags // hidden flags
var ignore string var ignore string
var ignoreSlice []string var ignoreSlice []string
var ignoreBool bool var ignoreBool bool
var ignoreInt int64 var ignoreInt int64
flags.StringVar(&ignore, "ulimit", "", "Ulimit options")
flags.MarkHidden("ulimit")
flags.StringSliceVar(&ignoreSlice, "security-opt", []string{}, "Security options")
flags.MarkHidden("security-opt")
flags.BoolVar(&ignoreBool, "compress", false, "Compress the build context using gzip") flags.BoolVar(&ignoreBool, "compress", false, "Compress the build context using gzip")
flags.MarkHidden("compress") flags.MarkHidden("compress")
flags.StringVarP(&ignore, "memory", "m", "", "Memory limit")
flags.MarkHidden("memory")
flags.StringVar(&ignore, "memory-swap", "", "Swap limit equal to memory plus swap: '-1' to enable unlimited swap")
flags.MarkHidden("memory-swap")
flags.StringVar(&ignore, "shm-size", "", "Size of /dev/shm")
flags.MarkHidden("shm-size")
flags.Int64VarP(&ignoreInt, "cpu-shares", "c", 0, "CPU shares (relative weight)")
flags.MarkHidden("cpu-shares")
flags.Int64Var(&ignoreInt, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
flags.MarkHidden("cpu-period")
flags.Int64Var(&ignoreInt, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
flags.MarkHidden("cpu-quota")
flags.StringVar(&ignore, "cpuset-cpus", "", "CPUs in which to allow execution (0-3, 0,1)")
flags.MarkHidden("cpuset-cpus")
flags.StringVar(&ignore, "cpuset-mems", "", "MEMs in which to allow execution (0-3, 0,1)")
flags.MarkHidden("cpuset-mems")
flags.StringVar(&ignore, "cgroup-parent", "", "Optional parent cgroup for the container")
flags.MarkHidden("cgroup-parent")
flags.StringVar(&ignore, "isolation", "", "Container isolation technology") flags.StringVar(&ignore, "isolation", "", "Container isolation technology")
flags.MarkHidden("isolation") flags.MarkHidden("isolation")
flags.SetAnnotation("isolation", "flag-warn", []string{"isolation flag is deprecated with BuildKit."})
flags.StringSliceVar(&ignoreSlice, "security-opt", []string{}, "Security options")
flags.MarkHidden("security-opt")
flags.SetAnnotation("security-opt", "flag-warn", []string{`security-opt flag is deprecated. "RUN --security=insecure" should be used with BuildKit.`})
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."})
flags.StringVarP(&ignore, "memory", "m", "", "Memory limit")
flags.MarkHidden("memory")
flags.StringVar(&ignore, "memory-swap", "", `Swap limit equal to memory plus swap: "-1" to enable unlimited swap`)
flags.MarkHidden("memory-swap")
flags.Int64VarP(&ignoreInt, "cpu-shares", "c", 0, "CPU shares (relative weight)")
flags.MarkHidden("cpu-shares")
flags.Int64Var(&ignoreInt, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period")
flags.MarkHidden("cpu-period")
flags.Int64Var(&ignoreInt, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota")
flags.MarkHidden("cpu-quota")
flags.StringVar(&ignore, "cpuset-cpus", "", `CPUs in which to allow execution ("0-3", "0,1")`)
flags.MarkHidden("cpuset-cpus")
flags.StringVar(&ignore, "cpuset-mems", "", `MEMs in which to allow execution ("0-3", "0,1")`)
flags.MarkHidden("cpuset-mems")
flags.BoolVar(&ignoreBool, "rm", true, "Remove intermediate containers after a successful build") flags.BoolVar(&ignoreBool, "rm", true, "Remove intermediate containers after a successful build")
flags.MarkHidden("rm") flags.MarkHidden("rm")
flags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers") flags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers")
flags.MarkHidden("force-rm") flags.MarkHidden("force-rm")
platformsDefault := []string{}
if v := os.Getenv("DOCKER_DEFAULT_PLATFORM"); v != "" {
platformsDefault = []string{v}
}
flags.StringArrayVar(&options.platforms, "platform", platformsDefault, "Set target platform for build")
flags.StringArrayVar(&options.secrets, "secret", []string{}, "Secret file to expose to the build: id=mysecret,src=/local/secret")
flags.StringArrayVar(&options.ssh, "ssh", []string{}, "SSH agent socket or keys to expose to the build (format: default|<id>[=<socket>|<key>[,<key>]])")
flags.StringArrayVarP(&options.outputs, "output", "o", []string{}, "Output destination (format: type=local,dest=path)")
commonBuildFlags(&options.commonOptions, flags) commonBuildFlags(&options.commonOptions, flags)
return cmd return cmd
} }
func commonBuildFlags(options *commonOptions, flags *pflag.FlagSet) { func commonBuildFlags(options *commonOptions, flags *pflag.FlagSet) {
options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image") options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image")
flags.StringVar(&options.progress, "progress", "auto", "Set type of progress output (auto, plain, tty). Use plain to show container output") flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "tty"). Use plain to show container output`)
options.pull = flags.Bool("pull", false, "Always attempt to pull a newer version of the image") options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images")
flags.StringVar(&options.metadataFile, "metadata-file", "", "Write build result metadata to the file") flags.StringVar(&options.metadataFile, "metadata-file", "", "Write build result metadata to the file")
} }
func checkWarnedFlags(f *pflag.Flag) {
if !f.Changed {
return
}
for t, m := range f.Annotations {
switch t {
case "flag-warn":
logrus.Warn(m[0])
}
}
}
func listToMap(values []string, defaultEnv bool) map[string]string { func listToMap(values []string, defaultEnv bool) map[string]string {
result := make(map[string]string, len(values)) result := make(map[string]string, len(values))
for _, value := range values { for _, value := range values {
@@ -376,3 +479,80 @@ func listToMap(values []string, defaultEnv bool) map[string]string {
} }
return result return result
} }
func parseContextNames(values []string) (map[string]build.NamedContext, error) {
if len(values) == 0 {
return nil, nil
}
result := make(map[string]build.NamedContext, len(values))
for _, value := range values {
kv := strings.SplitN(value, "=", 2)
if len(kv) != 2 {
return nil, errors.Errorf("invalid context value: %s, expected key=value", value)
}
named, err := reference.ParseNormalizedNamed(kv[0])
if err != nil {
return nil, errors.Wrapf(err, "invalid context name %s", kv[0])
}
name := strings.TrimSuffix(reference.FamiliarString(named), ":latest")
result[name] = build.NamedContext{Path: kv[1]}
}
return result, nil
}
func writeMetadataFile(filename string, dt interface{}) error {
b, err := json.MarshalIndent(dt, "", " ")
if err != nil {
return err
}
return ioutils.AtomicWriteFile(filename, b, 0644)
}
func decodeExporterResponse(exporterResponse map[string]string) map[string]interface{} {
out := make(map[string]interface{})
for k, v := range exporterResponse {
dt, err := base64.StdEncoding.DecodeString(v)
if err != nil {
out[k] = v
continue
}
var raw map[string]interface{}
if err = json.Unmarshal(dt, &raw); err != nil || len(raw) == 0 {
out[k] = v
continue
}
out[k] = json.RawMessage(dt)
}
return out
}
func wrapBuildError(err error, bake bool) error {
if err == nil {
return nil
}
st, ok := grpcerrors.AsGRPCStatus(err)
if ok {
if st.Code() == codes.Unimplemented && strings.Contains(st.Message(), "unsupported frontend capability moby.buildkit.frontend.contexts") {
msg := "current frontend does not support --build-context."
if bake {
msg = "current frontend does not support defining additional contexts for targets."
}
msg += " Named contexts are supported since Dockerfile v1.4. Use #syntax directive in Dockerfile or update to latest BuildKit."
return &wrapped{err, msg}
}
}
return err
}
type wrapped struct {
err error
msg string
}
func (w *wrapped) Error() string {
return w.msg
}
func (w *wrapped) Unwrap() error {
return w.err
}

View File

@@ -1,14 +1,19 @@
package commands package commands
import ( import (
"bytes"
"context"
"encoding/csv" "encoding/csv"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
"strings" "strings"
"time"
"github.com/docker/buildx/driver" "github.com/docker/buildx/driver"
"github.com/docker/buildx/store" "github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/google/shlex" "github.com/google/shlex"
@@ -29,6 +34,7 @@ type createOptions struct {
flags string flags string
configFile string configFile string
driverOpts []string driverOpts []string
bootstrap bool
// upgrade bool // perform upgrade of the driver // upgrade bool // perform upgrade of the driver
} }
@@ -70,7 +76,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
return errors.Errorf("failed to find driver %q", in.driver) return errors.Errorf("failed to find driver %q", in.driver)
} }
txn, release, err := getStore(dockerCli) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -138,7 +144,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
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>`") 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 = getCurrentEndpoint(dockerCli) ep, err = storeutil.GetCurrentEndpoint(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -170,7 +176,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
} }
if in.use && ep != "" { if in.use && ep != "" {
current, err := getCurrentEndpoint(dockerCli) current, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -179,6 +185,21 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
} }
} }
ngi := &nginfo{ng: ng}
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
if err = loadNodeGroupData(timeoutCtx, dockerCli, ngi); err != nil {
return err
}
if in.bootstrap {
if _, err = boot(ctx, ngi); err != nil {
return err
}
}
fmt.Printf("%s\n", ng.Name) fmt.Printf("%s\n", ng.Name)
return nil return nil
} }
@@ -186,9 +207,12 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
func createCmd(dockerCli command.Cli) *cobra.Command { func createCmd(dockerCli command.Cli) *cobra.Command {
var options createOptions var options createOptions
var drivers []string var drivers bytes.Buffer
for s := range driver.GetFactories() { for _, d := range driver.GetFactories() {
drivers = append(drivers, s) if len(drivers.String()) > 0 {
drivers.WriteString(", ")
}
drivers.WriteString(fmt.Sprintf(`"%s"`, d.Name()))
} }
cmd := &cobra.Command{ cmd := &cobra.Command{
@@ -203,18 +227,20 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags() flags := cmd.Flags()
flags.StringVar(&options.name, "name", "", "Builder instance name") flags.StringVar(&options.name, "name", "", "Builder instance name")
flags.StringVar(&options.driver, "driver", "", fmt.Sprintf("Driver to use (available: %v)", drivers)) flags.StringVar(&options.driver, "driver", "", fmt.Sprintf("Driver to use (available: %s)", drivers.String()))
flags.StringVar(&options.nodeName, "node", "", "Create/modify node with given name") flags.StringVar(&options.nodeName, "node", "", "Create/modify node with given name")
flags.StringVar(&options.flags, "buildkitd-flags", "", "Flags for buildkitd daemon") flags.StringVar(&options.flags, "buildkitd-flags", "", "Flags for buildkitd daemon")
flags.StringVar(&options.configFile, "config", "", "BuildKit config file") flags.StringVar(&options.configFile, "config", "", "BuildKit config file")
flags.StringArrayVar(&options.platform, "platform", []string{}, "Fixed platforms for current node") flags.StringArrayVar(&options.platform, "platform", []string{}, "Fixed platforms for current node")
flags.StringArrayVar(&options.driverOpts, "driver-opt", []string{}, "Options for the driver") flags.StringArrayVar(&options.driverOpts, "driver-opt", []string{}, "Options for the driver")
flags.BoolVar(&options.bootstrap, "bootstrap", false, "Boot builder after creation")
flags.BoolVar(&options.actionAppend, "append", false, "Append a node to builder instead of changing it") flags.BoolVar(&options.actionAppend, "append", false, "Append a node to builder instead of changing it")
flags.BoolVar(&options.actionLeave, "leave", false, "Remove a node from builder instead of changing it") flags.BoolVar(&options.actionLeave, "leave", false, "Remove a node from builder instead of changing it")
flags.BoolVar(&options.use, "use", false, "Set the current builder instance") flags.BoolVar(&options.use, "use", false, "Set the current builder instance")
_ = flags // hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
return cmd return cmd
} }

View File

@@ -4,16 +4,18 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"strings"
"text/tabwriter" "text/tabwriter"
"time"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/opts" "github.com/docker/cli/opts"
"github.com/docker/go-units"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tonistiigi/units"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -125,20 +127,20 @@ func printKV(w io.Writer, k string, v interface{}) {
func printVerbose(tw *tabwriter.Writer, du []*client.UsageInfo) { func printVerbose(tw *tabwriter.Writer, du []*client.UsageInfo) {
for _, di := range du { for _, di := range du {
printKV(tw, "ID", di.ID) printKV(tw, "ID", di.ID)
if di.Parent != "" { if len(di.Parents) != 0 {
printKV(tw, "Parent", di.Parent) printKV(tw, "Parent", strings.Join(di.Parents, ","))
} }
printKV(tw, "Created at", di.CreatedAt) printKV(tw, "Created at", di.CreatedAt)
printKV(tw, "Mutable", di.Mutable) printKV(tw, "Mutable", di.Mutable)
printKV(tw, "Reclaimable", !di.InUse) printKV(tw, "Reclaimable", !di.InUse)
printKV(tw, "Shared", di.Shared) printKV(tw, "Shared", di.Shared)
printKV(tw, "Size", fmt.Sprintf("%.2f", units.Bytes(di.Size))) printKV(tw, "Size", units.HumanSize(float64(di.Size)))
if di.Description != "" { if di.Description != "" {
printKV(tw, "Description", di.Description) printKV(tw, "Description", di.Description)
} }
printKV(tw, "Usage count", di.UsageCount) printKV(tw, "Usage count", di.UsageCount)
if di.LastUsedAt != nil { if di.LastUsedAt != nil {
printKV(tw, "Last used", di.LastUsedAt) printKV(tw, "Last used", units.HumanDuration(time.Since(*di.LastUsedAt))+" ago")
} }
if di.RecordType != "" { if di.RecordType != "" {
printKV(tw, "Type", di.RecordType) printKV(tw, "Type", di.RecordType)
@@ -159,11 +161,15 @@ func printTableRow(tw *tabwriter.Writer, di *client.UsageInfo) {
if di.Mutable { if di.Mutable {
id += "*" id += "*"
} }
size := fmt.Sprintf("%.2f", units.Bytes(di.Size)) size := units.HumanSize(float64(di.Size))
if di.Shared { if di.Shared {
size += "*" size += "*"
} }
fmt.Fprintf(tw, "%-71s\t%-11v\t%s\t\n", id, !di.InUse, size) lastAccessed := ""
if di.LastUsedAt != nil {
lastAccessed = units.HumanDuration(time.Since(*di.LastUsedAt)) + " ago"
}
fmt.Fprintf(tw, "%-40s\t%-5v\t%-10s\t%s\n", id, !di.InUse, size, lastAccessed)
} }
func printSummary(tw *tabwriter.Writer, dus [][]*client.UsageInfo) { func printSummary(tw *tabwriter.Writer, dus [][]*client.UsageInfo) {
@@ -186,11 +192,11 @@ func printSummary(tw *tabwriter.Writer, dus [][]*client.UsageInfo) {
} }
if shared > 0 { if shared > 0 {
fmt.Fprintf(tw, "Shared:\t%.2f\n", units.Bytes(shared)) fmt.Fprintf(tw, "Shared:\t%s\n", units.HumanSize(float64(shared)))
fmt.Fprintf(tw, "Private:\t%.2f\n", units.Bytes(total-shared)) fmt.Fprintf(tw, "Private:\t%s\n", units.HumanSize(float64(total-shared)))
} }
fmt.Fprintf(tw, "Reclaimable:\t%.2f\n", units.Bytes(reclaimable)) fmt.Fprintf(tw, "Reclaimable:\t%s\n", units.HumanSize(float64(reclaimable)))
fmt.Fprintf(tw, "Total:\t%.2f\n", units.Bytes(total)) fmt.Fprintf(tw, "Total:\t%s\n", units.HumanSize(float64(total)))
tw.Flush() tw.Flush()
} }

View File

@@ -6,6 +6,8 @@ import (
"io/ioutil" "io/ioutil"
"strings" "strings"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/imagetools" "github.com/docker/buildx/util/imagetools"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
@@ -18,6 +20,7 @@ import (
) )
type createOptions struct { type createOptions struct {
builder string
files []string files []string
tags []string tags []string
dryrun bool dryrun bool
@@ -101,9 +104,32 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
ctx := appcontext.Context() ctx := appcontext.Context()
r := imagetools.New(imagetools.Opt{ txn, release, err := storeutil.GetStore(dockerCli)
Auth: dockerCli.ConfigFile(), if err != nil {
}) return err
}
defer release()
var ng *store.NodeGroup
if in.builder != "" {
ng, err = storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil {
return err
}
} else {
ng, err = storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil {
return err
}
}
imageopt, err := storeutil.GetImageConfig(dockerCli, ng)
if err != nil {
return err
}
r := imagetools.New(imageopt)
if sourceRefs { if sourceRefs {
eg, ctx2 := errgroup.WithContext(ctx) eg, ctx2 := errgroup.WithContext(ctx)
@@ -152,9 +178,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
} }
// new resolver cause need new auth // new resolver cause need new auth
r = imagetools.New(imagetools.Opt{ r = imagetools.New(imageopt)
Auth: dockerCli.ConfigFile(),
})
for _, t := range tags { for _, t := range tags {
if err := r.Push(ctx, t, desc, dt); err != nil { if err := r.Push(ctx, t, desc, dt); err != nil {
@@ -224,26 +248,24 @@ func parseSource(in string) (*src, error) {
return &s, nil return &s, nil
} }
func createCmd(dockerCli command.Cli) *cobra.Command { func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
var options createOptions var options createOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create [OPTIONS] [SOURCE] [SOURCE...]", Use: "create [OPTIONS] [SOURCE] [SOURCE...]",
Short: "Create a new image based on source images", Short: "Create a new image based on source images",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
options.builder = opts.Builder
return runCreate(dockerCli, options, args) return runCreate(dockerCli, options, args)
}, },
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Read source descriptor from file") flags.StringArrayVarP(&options.files, "file", "f", []string{}, "Read source descriptor from file")
flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, "Set reference for new image") flags.StringArrayVarP(&options.tags, "tag", "t", []string{}, "Set reference for new image")
flags.BoolVar(&options.dryrun, "dry-run", false, "Show final image instead of pushing") flags.BoolVar(&options.dryrun, "dry-run", false, "Show final image instead of pushing")
flags.BoolVar(&options.actionAppend, "append", false, "Append to existing manifest") flags.BoolVar(&options.actionAppend, "append", false, "Append to existing manifest")
_ = flags
return cmd return cmd
} }

View File

@@ -1,68 +1,82 @@
package commands package commands
import ( import (
"fmt" "github.com/docker/buildx/store"
"os" "github.com/docker/buildx/store/storeutil"
"github.com/containerd/containerd/images"
"github.com/docker/buildx/util/imagetools" "github.com/docker/buildx/util/imagetools"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
type inspectOptions struct { type inspectOptions struct {
raw bool builder string
format string
raw bool
} }
func runInspect(dockerCli command.Cli, in inspectOptions, name string) error { func runInspect(dockerCli command.Cli, in inspectOptions, name string) error {
ctx := appcontext.Context() ctx := appcontext.Context()
r := imagetools.New(imagetools.Opt{ if in.format != "" && in.raw {
Auth: dockerCli.ConfigFile(), return errors.Errorf("format and raw cannot be used together")
}) }
dt, desc, err := r.Get(ctx, name) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return err
}
defer release()
var ng *store.NodeGroup
if in.builder != "" {
ng, err = storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil {
return err
}
} else {
ng, err = storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil {
return err
}
}
imageopt, err := storeutil.GetImageConfig(dockerCli, ng)
if err != nil { if err != nil {
return err return err
} }
if in.raw { p, err := imagetools.NewPrinter(ctx, imageopt, name, in.format)
fmt.Printf("%s", dt) // avoid newline to keep digest if err != nil {
return nil return err
} }
switch desc.MediaType { return p.Print(in.raw, dockerCli.Out())
// case images.MediaTypeDockerSchema2Manifest, specs.MediaTypeImageManifest:
// TODO: handle distribution manifest and schema1
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
return imagetools.PrintManifestList(dt, desc, name, os.Stdout)
default:
fmt.Printf("%s\n", dt)
}
return nil
} }
func inspectCmd(dockerCli command.Cli) *cobra.Command { func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options inspectOptions var options inspectOptions
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "inspect [OPTIONS] NAME", Use: "inspect [OPTIONS] NAME",
Short: "Show details of image in the registry", Short: "Show details of an image in the registry",
Args: cli.ExactArgs(1), Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.Builder
return runInspect(dockerCli, options, args[0]) return runInspect(dockerCli, options, args[0])
}, },
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVar(&options.raw, "raw", false, "Show original JSON manifest") flags.StringVar(&options.format, "format", "", "Format the output using the given Go template")
flags.SetAnnotation("format", annotation.DefaultValue, []string{`"{{.Manifest}}"`})
_ = flags flags.BoolVar(&options.raw, "raw", false, "Show original, unformatted JSON manifest")
return cmd return cmd
} }

View File

@@ -5,15 +5,19 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func RootCmd(dockerCli command.Cli) *cobra.Command { type RootOptions struct {
Builder string
}
func RootCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "imagetools", Use: "imagetools",
Short: "Commands to work on images in registry", Short: "Commands to work on images in registry",
} }
cmd.AddCommand( cmd.AddCommand(
inspectCmd(dockerCli), createCmd(dockerCli, opts),
createCmd(dockerCli), inspectCmd(dockerCli, opts),
) )
return cmd return cmd

View File

@@ -8,17 +8,13 @@ import (
"text/tabwriter" "text/tabwriter"
"time" "time"
"github.com/docker/buildx/build"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/store" "github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
) )
type inspectOptions struct { type inspectOptions struct {
@@ -26,23 +22,10 @@ type inspectOptions struct {
builder string builder string
} }
type dinfo struct {
di *build.DriverInfo
info *driver.Info
platforms []specs.Platform
err error
}
type nginfo struct {
ng *store.NodeGroup
drivers []dinfo
err error
}
func runInspect(dockerCli command.Cli, in inspectOptions) error { func runInspect(dockerCli command.Cli, in inspectOptions) error {
ctx := appcontext.Context() ctx := appcontext.Context()
txn, release, err := getStore(dockerCli) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -51,12 +34,12 @@ func runInspect(dockerCli command.Cli, in inspectOptions) error {
var ng *store.NodeGroup var ng *store.NodeGroup
if in.builder != "" { if in.builder != "" {
ng, err = getNodeGroup(txn, dockerCli, in.builder) ng, err = storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
ng, err = getCurrentInstance(txn, dockerCli) ng, err = storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -82,7 +65,7 @@ func runInspect(dockerCli command.Cli, in inspectOptions) error {
var bootNgi *nginfo var bootNgi *nginfo
if in.bootstrap { if in.bootstrap {
var ok bool var ok bool
ok, err = boot(ctx, ngi, dockerCli) ok, err = boot(ctx, ngi)
if err != nil { if err != nil {
return err return err
} }
@@ -149,50 +132,7 @@ func inspectCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVar(&options.bootstrap, "bootstrap", false, "Ensure builder has booted before inspecting") flags.BoolVar(&options.bootstrap, "bootstrap", false, "Ensure builder has booted before inspecting")
_ = flags
return cmd return cmd
} }
func boot(ctx context.Context, ngi *nginfo, dockerCli command.Cli) (bool, error) {
toBoot := make([]int, 0, len(ngi.drivers))
for i, d := range ngi.drivers {
if d.err != nil || d.di.Err != nil || d.di.Driver == nil || d.info == nil {
continue
}
if d.info.Status != driver.Running {
toBoot = append(toBoot, i)
}
}
if len(toBoot) == 0 {
return false, nil
}
printer := progress.NewPrinter(context.TODO(), os.Stderr, "auto")
baseCtx := ctx
eg, _ := errgroup.WithContext(ctx)
for _, idx := range toBoot {
func(idx int) {
eg.Go(func() error {
pw := progress.WithPrefix(printer, ngi.ng.Nodes[idx].Name, len(toBoot) > 1)
_, err := driver.Boot(ctx, baseCtx, ngi.drivers[idx].di.Driver, pw)
if err != nil {
ngi.drivers[idx].err = err
}
return nil
})
}(idx)
}
err := eg.Wait()
err1 := printer.Wait()
if err == nil {
err = err1
}
return true, err
}

View File

@@ -3,6 +3,7 @@ package commands
import ( import (
"os" "os"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
@@ -48,5 +49,8 @@ func installCmd(dockerCli command.Cli) *cobra.Command {
Hidden: true, Hidden: true,
} }
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
return cmd return cmd
} }

View File

@@ -10,6 +10,8 @@ import (
"time" "time"
"github.com/docker/buildx/store" "github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/platformutil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
@@ -24,7 +26,7 @@ type lsOptions struct {
func runLs(dockerCli command.Cli, in lsOptions) error { func runLs(dockerCli command.Cli, in lsOptions) error {
ctx := appcontext.Context() ctx := appcontext.Context()
txn, release, err := getStore(dockerCli) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -79,7 +81,7 @@ func runLs(dockerCli command.Cli, in lsOptions) error {
} }
currentName := "default" currentName := "default"
current, err := getCurrentInstance(txn, dockerCli) current, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -147,5 +149,8 @@ func lsCmd(dockerCli command.Cli) *cobra.Command {
}, },
} }
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
return cmd return cmd
} }

View File

@@ -12,11 +12,11 @@ import (
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/opts" "github.com/docker/cli/opts"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/go-units"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/tonistiigi/units"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -119,7 +119,7 @@ func runPrune(dockerCli command.Cli, opts pruneOptions) error {
<-printed <-printed
tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0) tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0)
fmt.Fprintf(tw, "Total:\t%.2f\n", units.Bytes(total)) fmt.Fprintf(tw, "Total:\t%s\n", units.HumanSize(float64(total)))
tw.Flush() tw.Flush()
return nil return nil
} }
@@ -139,7 +139,7 @@ func pruneCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images, not just dangling ones") flags.BoolVarP(&options.all, "all", "a", false, "Remove all unused images, not just dangling ones")
flags.Var(&options.filter, "filter", "Provide filter values (e.g. 'until=24h')") flags.Var(&options.filter, "filter", `Provide filter values (e.g., "until=24h")`)
flags.Var(&options.keepStorage, "keep-storage", "Amount of disk space to keep for cache") flags.Var(&options.keepStorage, "keep-storage", "Amount of disk space to keep for cache")
flags.BoolVar(&options.verbose, "verbose", false, "Provide a more verbose output") flags.BoolVar(&options.verbose, "verbose", false, "Provide a more verbose output")
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation") flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")

View File

@@ -2,46 +2,65 @@ package commands
import ( import (
"context" "context"
"time"
"github.com/docker/buildx/store" "github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
) )
type rmOptions struct { type rmOptions struct {
builder string builder string
keepState bool keepState bool
keepDaemon bool
allInactive bool
force bool
} }
const (
rmInactiveWarning = `WARNING! This will remove all builders that are not in running state. Are you sure you want to continue?`
)
func runRm(dockerCli command.Cli, in rmOptions) error { func runRm(dockerCli command.Cli, in rmOptions) error {
ctx := appcontext.Context() ctx := appcontext.Context()
txn, release, err := getStore(dockerCli) if in.allInactive && !in.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), rmInactiveWarning) {
return nil
}
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return err return err
} }
defer release() defer release()
if in.allInactive {
return rmAllInactive(ctx, txn, dockerCli, in)
}
if in.builder != "" { if in.builder != "" {
ng, err := getNodeGroup(txn, dockerCli, in.builder) ng, err := storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil { if err != nil {
return err return err
} }
err1 := rm(ctx, dockerCli, ng, in.keepState) err1 := rm(ctx, dockerCli, in, ng)
if err := txn.Remove(ng.Name); err != nil { if err := txn.Remove(ng.Name); err != nil {
return err return err
} }
return err1 return err1
} }
ng, err := getCurrentInstance(txn, dockerCli) ng, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil { if err != nil {
return err return err
} }
if ng != nil { if ng != nil {
err1 := rm(ctx, dockerCli, ng, in.keepState) err1 := rm(ctx, dockerCli, in, ng)
if err := txn.Remove(ng.Name); err != nil { if err := txn.Remove(ng.Name); err != nil {
return err return err
} }
@@ -61,6 +80,9 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.builder options.builder = rootOpts.builder
if len(args) > 0 { if len(args) > 0 {
if options.allInactive {
return errors.New("cannot specify builder name when --all-inactive is set")
}
options.builder = args[0] options.builder = args[0]
} }
return runRm(dockerCli, options) return runRm(dockerCli, options)
@@ -69,23 +91,30 @@ func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVar(&options.keepState, "keep-state", false, "Keep BuildKit state") flags.BoolVar(&options.keepState, "keep-state", false, "Keep BuildKit state")
flags.BoolVar(&options.keepDaemon, "keep-daemon", false, "Keep the 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")
return cmd return cmd
} }
func rm(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, keepState bool) error { func rm(ctx context.Context, dockerCli command.Cli, in rmOptions, ng *store.NodeGroup) error {
dis, err := driversForNodeGroup(ctx, dockerCli, ng, "") dis, err := driversForNodeGroup(ctx, dockerCli, ng, "")
if err != nil { if err != nil {
return err return err
} }
for _, di := range dis { for _, di := range dis {
if di.Driver != nil { if di.Driver == nil {
continue
}
// Do not stop the buildkitd daemon when --keep-daemon is provided
if !in.keepDaemon {
if err := di.Driver.Stop(ctx, true); err != nil { if err := di.Driver.Stop(ctx, true); err != nil {
return err return err
} }
if err := di.Driver.Rm(ctx, true, !keepState); err != nil { }
return err if err := di.Driver.Rm(ctx, true, !in.keepState, !in.keepDaemon); err != nil {
} return err
} }
if di.Err != nil { if di.Err != nil {
err = di.Err err = di.Err
@@ -93,3 +122,42 @@ func rm(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, keepSta
} }
return err return err
} }
func rmAllInactive(ctx context.Context, txn *store.Txn, dockerCli command.Cli, in rmOptions) error {
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
ll, err := txn.List()
if err != nil {
return err
}
builders := make([]*nginfo, len(ll))
for i, ng := range ll {
builders[i] = &nginfo{ng: ng}
}
eg, _ := errgroup.WithContext(ctx)
for _, b := range builders {
func(b *nginfo) {
eg.Go(func() error {
if err := loadNodeGroupData(ctx, dockerCli, b); err != nil {
return errors.Wrapf(err, "cannot load %s", b.ng.Name)
}
if b.ng.Dynamic {
return nil
}
if b.inactive() {
rmerr := rm(ctx, dockerCli, in, b.ng)
if err := txn.Remove(b.ng.Name); err != nil {
return err
}
return rmerr
}
return nil
})
}(b)
}
return eg.Wait()
}

View File

@@ -4,16 +4,23 @@ import (
"os" "os"
imagetoolscmd "github.com/docker/buildx/commands/imagetools" imagetoolscmd "github.com/docker/buildx/commands/imagetools"
"github.com/docker/buildx/util/logutil"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli-plugins/plugin" "github.com/docker/cli/cli-plugins/plugin"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Command { func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Short: "Build with BuildKit", Short: "Docker Buildx",
Long: `Extended build capabilities with BuildKit`,
Use: name, Use: name,
Annotations: map[string]string{
annotation.CodeDelimiter: `"`,
},
} }
if isPlugin { if isPlugin {
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
@@ -21,6 +28,26 @@ func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Comman
} }
} }
logrus.SetFormatter(&logutil.Formatter{})
logrus.AddHook(logutil.NewFilter([]logrus.Level{
logrus.DebugLevel,
},
"serving grpc connection",
"stopping session",
"using default config store",
))
// filter out useless commandConn.CloseWrite warning message that can occur
// when listing builder instances with "buildx ls" for those that are
// unreachable: "commandConn.CloseWrite: commandconn: failed to wait: signal: killed"
// https://github.com/docker/cli/blob/3fb4fb83dfb5db0c0753a8316f21aea54dab32c5/cli/connhelper/commandconn/commandconn.go#L203-L214
logrus.AddHook(logutil.NewFilter([]logrus.Level{
logrus.WarnLevel,
},
"commandConn.CloseWrite:",
))
addCommands(cmd, dockerCli) addCommands(cmd, dockerCli)
return cmd return cmd
} }
@@ -47,7 +74,7 @@ func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
versionCmd(dockerCli), versionCmd(dockerCli),
pruneCmd(dockerCli, opts), pruneCmd(dockerCli, opts),
duCmd(dockerCli, opts), duCmd(dockerCli, opts),
imagetoolscmd.RootCmd(dockerCli), imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: opts.builder}),
) )
} }

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/docker/buildx/store" "github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext" "github.com/moby/buildkit/util/appcontext"
@@ -17,14 +18,14 @@ type stopOptions struct {
func runStop(dockerCli command.Cli, in stopOptions) error { func runStop(dockerCli command.Cli, in stopOptions) error {
ctx := appcontext.Context() ctx := appcontext.Context()
txn, release, err := getStore(dockerCli) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return err return err
} }
defer release() defer release()
if in.builder != "" { if in.builder != "" {
ng, err := getNodeGroup(txn, dockerCli, in.builder) ng, err := storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil { if err != nil {
return err return err
} }
@@ -34,7 +35,7 @@ func runStop(dockerCli command.Cli, in stopOptions) error {
return nil return nil
} }
ng, err := getCurrentInstance(txn, dockerCli) ng, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -61,12 +62,6 @@ func stopCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
}, },
} }
flags := cmd.Flags()
// flags.StringArrayVarP(&options.outputs, "output", "o", []string{}, "Output destination (format: type=local,dest=path)")
_ = flags
return cmd return cmd
} }

View File

@@ -3,6 +3,7 @@ package commands
import ( import (
"os" "os"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
@@ -54,5 +55,8 @@ func uninstallCmd(dockerCli command.Cli) *cobra.Command {
Hidden: true, Hidden: true,
} }
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
return cmd return cmd
} }

View File

@@ -3,6 +3,7 @@ package commands
import ( import (
"os" "os"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -16,7 +17,7 @@ type useOptions struct {
} }
func runUse(dockerCli command.Cli, in useOptions) error { func runUse(dockerCli command.Cli, in useOptions) error {
txn, release, err := getStore(dockerCli) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -28,7 +29,7 @@ func runUse(dockerCli command.Cli, in useOptions) error {
return errors.Errorf("run `docker context use default` to switch to default context") return errors.Errorf("run `docker context use default` to switch to default context")
} }
if in.builder == "default" || in.builder == dockerCli.CurrentContext() { if in.builder == "default" || in.builder == dockerCli.CurrentContext() {
ep, err := getCurrentEndpoint(dockerCli) ep, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -51,7 +52,7 @@ func runUse(dockerCli command.Cli, in useOptions) error {
return errors.Wrapf(err, "failed to find instance %q", in.builder) return errors.Wrapf(err, "failed to find instance %q", in.builder)
} }
ep, err := getCurrentEndpoint(dockerCli) ep, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil { if err != nil {
return err return err
} }
@@ -79,11 +80,8 @@ func useCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
} }
flags := cmd.Flags() flags := cmd.Flags()
flags.BoolVar(&options.isGlobal, "global", false, "Builder persists context changes") flags.BoolVar(&options.isGlobal, "global", false, "Builder persists context changes")
flags.BoolVar(&options.isDefault, "default", false, "Set builder as default for current context") flags.BoolVar(&options.isDefault, "default", false, "Set builder as default for current context")
_ = flags
return cmd return cmd
} }

View File

@@ -4,86 +4,30 @@ import (
"context" "context"
"net/url" "net/url"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/docker/buildx/build" "github.com/docker/buildx/build"
"github.com/docker/buildx/driver" "github.com/docker/buildx/driver"
ctxkube "github.com/docker/buildx/driver/kubernetes/context"
"github.com/docker/buildx/store" "github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context/docker" "github.com/docker/cli/cli/context/docker"
"github.com/docker/cli/cli/context/kubernetes"
ctxstore "github.com/docker/cli/cli/context/store" ctxstore "github.com/docker/cli/cli/context/store"
dopts "github.com/docker/cli/opts" dopts "github.com/docker/cli/opts"
dockerclient "github.com/docker/docker/client" dockerclient "github.com/docker/docker/client"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
) )
// getStore returns current builder instance store
func getStore(dockerCli command.Cli) (*store.Txn, func(), error) {
s, err := store.New(getConfigStorePath(dockerCli))
if err != nil {
return nil, nil, err
}
return s.Txn()
}
// getConfigStorePath will look for correct configuration store path;
// if `$BUILDX_CONFIG` is set - use it, otherwise use parent directory
// of Docker config file (i.e. `${DOCKER_CONFIG}/buildx`)
func getConfigStorePath(dockerCli command.Cli) string {
if buildxConfig := os.Getenv("BUILDX_CONFIG"); buildxConfig != "" {
logrus.Debugf("using config store %q based in \"$BUILDX_CONFIG\" environment variable", buildxConfig)
return buildxConfig
}
buildxConfig := filepath.Join(filepath.Dir(dockerCli.ConfigFile().Filename), "buildx")
logrus.Debugf("using default config store %q", buildxConfig)
return buildxConfig
}
// getCurrentEndpoint returns the current default endpoint value
func getCurrentEndpoint(dockerCli command.Cli) (string, error) {
name := dockerCli.CurrentContext()
if name != "default" {
return name, nil
}
de, err := getDockerEndpoint(dockerCli, name)
if err != nil {
return "", errors.Errorf("docker endpoint for %q not found", name)
}
return de, nil
}
// getDockerEndpoint returns docker endpoint string for given context
func getDockerEndpoint(dockerCli command.Cli, name string) (string, error) {
list, err := dockerCli.ContextStore().List()
if err != nil {
return "", err
}
for _, l := range list {
if l.Name == name {
ep, ok := l.Endpoints["docker"]
if !ok {
return "", errors.Errorf("context %q does not have a Docker endpoint", name)
}
typed, ok := ep.(docker.EndpointMeta)
if !ok {
return "", errors.Errorf("endpoint %q is not of type EndpointMeta, %T", ep, ep)
}
return typed.Host, nil
}
}
return "", nil
}
// validateEndpoint validates that endpoint is either a context or a docker host // validateEndpoint validates that endpoint is either a context or a docker host
func validateEndpoint(dockerCli command.Cli, ep string) (string, error) { func validateEndpoint(dockerCli command.Cli, ep string) (string, error) {
de, err := getDockerEndpoint(dockerCli, ep) de, err := storeutil.GetDockerEndpoint(dockerCli, ep)
if err == nil && de != "" { if err == nil && de != "" {
if ep == "default" { if ep == "default" {
return de, nil return de, nil
@@ -97,60 +41,6 @@ func validateEndpoint(dockerCli command.Cli, ep string) (string, error) {
return h, nil return h, nil
} }
// getCurrentInstance finds the current builder instance
func getCurrentInstance(txn *store.Txn, dockerCli command.Cli) (*store.NodeGroup, error) {
ep, err := getCurrentEndpoint(dockerCli)
if err != nil {
return nil, err
}
ng, err := txn.Current(ep)
if err != nil {
return nil, err
}
if ng == nil {
ng, _ = getNodeGroup(txn, dockerCli, dockerCli.CurrentContext())
}
return ng, nil
}
// getNodeGroup returns nodegroup based on the name
func getNodeGroup(txn *store.Txn, dockerCli command.Cli, name string) (*store.NodeGroup, error) {
ng, err := txn.NodeGroupByName(name)
if err != nil {
if !os.IsNotExist(errors.Cause(err)) {
return nil, err
}
}
if ng != nil {
return ng, nil
}
if name == "default" {
name = dockerCli.CurrentContext()
}
list, err := dockerCli.ContextStore().List()
if err != nil {
return nil, err
}
for _, l := range list {
if l.Name == name {
return &store.NodeGroup{
Name: "default",
Nodes: []store.Node{
{
Name: "default",
Endpoint: name,
},
},
}, nil
}
}
return nil, errors.Errorf("no builder %q found", name)
}
// driversForNodeGroup returns drivers for a nodegroup instance // driversForNodeGroup returns drivers for a nodegroup instance
func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, contextPathHash string) ([]build.DriverInfo, error) { func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, contextPathHash string) ([]build.DriverInfo, error) {
eg, _ := errgroup.WithContext(ctx) eg, _ := errgroup.WithContext(ctx)
@@ -174,13 +64,18 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
} }
ng.Driver = f.Name() ng.Driver = f.Name()
} }
imageopt, err := storeutil.GetImageConfig(dockerCli, ng)
if err != nil {
return nil, err
}
for i, n := range ng.Nodes { for i, n := range ng.Nodes {
func(i int, n store.Node) { func(i int, n store.Node) {
eg.Go(func() error { eg.Go(func() error {
di := build.DriverInfo{ di := build.DriverInfo{
Name: n.Name, Name: n.Name,
Platform: n.Platforms, Platform: n.Platforms,
ProxyConfig: storeutil.GetProxyConfig(dockerCli),
} }
defer func() { defer func() {
dis[i] = di dis[i] = di
@@ -223,12 +118,13 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
} }
} }
d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, dockerapi, dockerCli.ConfigFile(), kcc, n.Flags, n.ConfigFile, n.DriverOpts, n.Platforms, contextPathHash) d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, dockerapi, imageopt.Auth, kcc, n.Flags, n.Files, n.DriverOpts, n.Platforms, contextPathHash)
if err != nil { if err != nil {
di.Err = err di.Err = err
return nil return nil
} }
di.Driver = d di.Driver = d
di.ImageOpt = imageopt
return nil return nil
}) })
}(i, n) }(i, n)
@@ -244,16 +140,17 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
func configFromContext(endpointName string, s ctxstore.Reader) (clientcmd.ClientConfig, error) { func configFromContext(endpointName string, s ctxstore.Reader) (clientcmd.ClientConfig, error) {
if strings.HasPrefix(endpointName, "kubernetes://") { if strings.HasPrefix(endpointName, "kubernetes://") {
u, _ := url.Parse(endpointName) u, _ := url.Parse(endpointName)
if kubeconfig := u.Query().Get("kubeconfig"); kubeconfig != "" { if kubeconfig := u.Query().Get("kubeconfig"); kubeconfig != "" {
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( _ = os.Setenv(clientcmd.RecommendedConfigPathEnvVar, kubeconfig)
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
&clientcmd.ConfigOverrides{},
)
return clientConfig, nil
} }
rules := clientcmd.NewDefaultClientConfigLoadingRules()
apiConfig, err := rules.Load()
if err != nil {
return nil, err
}
return clientcmd.NewDefaultClientConfig(*apiConfig, &clientcmd.ConfigOverrides{}), nil
} }
return kubernetes.ConfigFromContext(endpointName, s) return ctxkube.ConfigFromContext(endpointName, s)
} }
// clientForEndpoint returns a docker client for an endpoint // clientForEndpoint returns a docker client for an endpoint
@@ -325,7 +222,7 @@ func getInstanceOrDefault(ctx context.Context, dockerCli command.Cli, instance,
} }
func getInstanceByName(ctx context.Context, dockerCli command.Cli, instance, contextPathHash string) ([]build.DriverInfo, error) { func getInstanceByName(ctx context.Context, dockerCli command.Cli, instance, contextPathHash string) ([]build.DriverInfo, error) {
txn, release, err := getStore(dockerCli) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -340,14 +237,14 @@ func getInstanceByName(ctx context.Context, dockerCli command.Cli, instance, con
// getDefaultDrivers returns drivers based on current cli config // getDefaultDrivers returns drivers based on current cli config
func getDefaultDrivers(ctx context.Context, dockerCli command.Cli, defaultOnly bool, contextPathHash string) ([]build.DriverInfo, error) { func getDefaultDrivers(ctx context.Context, dockerCli command.Cli, defaultOnly bool, contextPathHash string) ([]build.DriverInfo, error) {
txn, release, err := getStore(dockerCli) txn, release, err := storeutil.GetStore(dockerCli)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer release() defer release()
if !defaultOnly { if !defaultOnly {
ng, err := getCurrentInstance(txn, dockerCli) ng, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -357,14 +254,21 @@ func getDefaultDrivers(ctx context.Context, dockerCli command.Cli, defaultOnly b
} }
} }
d, err := driver.GetDriver(ctx, "buildx_buildkit_default", nil, dockerCli.Client(), dockerCli.ConfigFile(), nil, nil, "", nil, nil, contextPathHash) imageopt, err := storeutil.GetImageConfig(dockerCli, nil)
if err != nil {
return nil, err
}
d, err := driver.GetDriver(ctx, "buildx_buildkit_default", nil, dockerCli.Client(), imageopt.Auth, nil, nil, nil, nil, nil, contextPathHash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return []build.DriverInfo{ return []build.DriverInfo{
{ {
Name: "default", Name: "default",
Driver: d, Driver: d,
ImageOpt: imageopt,
ProxyConfig: storeutil.GetProxyConfig(dockerCli),
}, },
}, nil }, nil
} }
@@ -388,9 +292,7 @@ func loadInfoData(ctx context.Context, d *dinfo) error {
return errors.Wrap(err, "listing workers") return errors.Wrap(err, "listing workers")
} }
for _, w := range workers { for _, w := range workers {
for _, p := range w.Platforms { d.platforms = append(d.platforms, w.Platforms...)
d.platforms = append(d.platforms, p)
}
} }
d.platforms = platformutil.Dedupe(d.platforms) d.platforms = platformutil.Dedupe(d.platforms)
} }
@@ -475,3 +377,67 @@ func (a *api) DockerAPI(name string) (dockerclient.APIClient, error) {
} }
return clientForEndpoint(a.dockerCli, name) return clientForEndpoint(a.dockerCli, name)
} }
type dinfo struct {
di *build.DriverInfo
info *driver.Info
platforms []specs.Platform
err error
}
type nginfo struct {
ng *store.NodeGroup
drivers []dinfo
err error
}
// inactive checks if all nodes are inactive for this builder
func (n *nginfo) inactive() bool {
for idx := range n.ng.Nodes {
d := n.drivers[idx]
if d.info != nil && d.info.Status == driver.Running {
return false
}
}
return true
}
func boot(ctx context.Context, ngi *nginfo) (bool, error) {
toBoot := make([]int, 0, len(ngi.drivers))
for i, d := range ngi.drivers {
if d.err != nil || d.di.Err != nil || d.di.Driver == nil || d.info == nil {
continue
}
if d.info.Status != driver.Running {
toBoot = append(toBoot, i)
}
}
if len(toBoot) == 0 {
return false, nil
}
printer := progress.NewPrinter(context.TODO(), os.Stderr, os.Stderr, "auto")
baseCtx := ctx
eg, _ := errgroup.WithContext(ctx)
for _, idx := range toBoot {
func(idx int) {
eg.Go(func() error {
pw := progress.WithPrefix(printer, ngi.ng.Nodes[idx].Name, len(toBoot) > 1)
_, err := driver.Boot(ctx, baseCtx, ngi.drivers[idx].di.Driver, pw)
if err != nil {
ngi.drivers[idx].err = err
}
return nil
})
}(idx)
}
err := eg.Wait()
err1 := printer.Wait()
if err == nil {
err = err1
}
return true, err
}

View File

@@ -3,6 +3,7 @@ package commands
import ( import (
"fmt" "fmt"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/buildx/version" "github.com/docker/buildx/version"
"github.com/docker/cli/cli" "github.com/docker/cli/cli"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
@@ -23,5 +24,9 @@ func versionCmd(dockerCli command.Cli) *cobra.Command {
return runVersion(dockerCli) return runVersion(dockerCli)
}, },
} }
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
return cmd return cmd
} }

149
docker-bake.hcl Normal file
View File

@@ -0,0 +1,149 @@
variable "GO_VERSION" {
default = "1.17"
}
variable "BIN_OUT" {
default = "./bin"
}
variable "RELEASE_OUT" {
default = "./release-out"
}
variable "DOCS_FORMATS" {
default = "md"
}
// Special target: https://github.com/docker/metadata-action#bake-definition
target "meta-helper" {
tags = ["docker/buildx-bin:local"]
}
target "_common" {
args = {
GO_VERSION = GO_VERSION
BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1
}
}
group "default" {
targets = ["binaries"]
}
group "validate" {
targets = ["lint", "validate-vendor", "validate-docs"]
}
target "lint" {
inherits = ["_common"]
dockerfile = "./hack/dockerfiles/lint.Dockerfile"
output = ["type=cacheonly"]
}
target "validate-vendor" {
inherits = ["_common"]
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
target = "validate"
output = ["type=cacheonly"]
}
target "validate-docs" {
inherits = ["_common"]
args = {
FORMATS = DOCS_FORMATS
}
dockerfile = "./hack/dockerfiles/docs.Dockerfile"
target = "validate"
output = ["type=cacheonly"]
}
target "validate-authors" {
inherits = ["_common"]
dockerfile = "./hack/dockerfiles/authors.Dockerfile"
target = "validate"
output = ["type=cacheonly"]
}
target "update-vendor" {
inherits = ["_common"]
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
target = "update"
output = ["."]
}
target "update-docs" {
inherits = ["_common"]
args = {
FORMATS = DOCS_FORMATS
}
dockerfile = "./hack/dockerfiles/docs.Dockerfile"
target = "update"
output = ["./docs/reference"]
}
target "update-authors" {
inherits = ["_common"]
dockerfile = "./hack/dockerfiles/authors.Dockerfile"
target = "update"
output = ["."]
}
target "mod-outdated" {
inherits = ["_common"]
dockerfile = "./hack/dockerfiles/vendor.Dockerfile"
target = "outdated"
args = {
// used to invalidate cache for outdated run stage
// can be dropped when https://github.com/moby/buildkit/issues/1213 fixed
_RANDOM = uuidv4()
}
output = ["type=cacheonly"]
}
target "test" {
inherits = ["_common"]
target = "test-coverage"
output = ["./coverage"]
}
target "binaries" {
inherits = ["_common"]
target = "binaries"
output = [BIN_OUT]
platforms = ["local"]
}
target "binaries-cross" {
inherits = ["binaries"]
platforms = [
"darwin/amd64",
"darwin/arm64",
"linux/amd64",
"linux/arm/v6",
"linux/arm/v7",
"linux/arm64",
"linux/ppc64le",
"linux/riscv64",
"linux/s390x",
"windows/amd64",
"windows/arm64"
]
}
target "release" {
inherits = ["binaries-cross"]
target = "release"
output = [RELEASE_OUT]
}
target "image" {
inherits = ["meta-helper", "binaries"]
output = ["type=image"]
}
target "image-cross" {
inherits = ["meta-helper", "binaries-cross"]
output = ["type=image"]
}
target "image-local" {
inherits = ["image"]
output = ["type=docker"]
}

View File

@@ -1,198 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"github.com/docker/buildx/commands"
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
const descriptionSourcePath = "docs/reference/"
func generateDocs(opts *options) error {
dockerCLI, err := command.NewDockerCli()
if err != nil {
return err
}
cmd := &cobra.Command{
Use: "docker [OPTIONS] COMMAND [ARG...]",
Short: "The base command for the Docker CLI.",
}
cmd.AddCommand(commands.NewRootCmd("buildx", true, dockerCLI))
return genCmd(cmd, opts.target)
}
func getMDFilename(cmd *cobra.Command) string {
name := cmd.CommandPath()
if i := strings.Index(name, " "); i >= 0 {
name = name[i+1:]
}
return strings.ReplaceAll(name, " ", "_") + ".md"
}
func genCmd(cmd *cobra.Command, dir string) error {
for _, c := range cmd.Commands() {
if err := genCmd(c, dir); err != nil {
return err
}
}
if !cmd.HasParent() {
return nil
}
mdFile := getMDFilename(cmd)
fullPath := filepath.Join(dir, mdFile)
content, err := ioutil.ReadFile(fullPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return errors.Wrapf(err, "%s does not exist", mdFile)
}
}
cs := string(content)
markerStart := "<!---MARKER_GEN_START-->"
markerEnd := "<!---MARKER_GEN_END-->"
start := strings.Index(cs, markerStart)
end := strings.Index(cs, markerEnd)
if start == -1 {
return errors.Errorf("no start marker in %s", mdFile)
}
if end == -1 {
return errors.Errorf("no end marker in %s", mdFile)
}
out, err := cmdOutput(cmd, cs)
if err != nil {
return err
}
cont := cs[:start] + markerStart + "\n" + out + "\n" + cs[end:]
fi, err := os.Stat(fullPath)
if err != nil {
return err
}
if err := ioutil.WriteFile(fullPath, []byte(cont), fi.Mode()); err != nil {
return errors.Wrapf(err, "failed to write %s", fullPath)
}
log.Printf("updated %s", fullPath)
return nil
}
func makeLink(txt, link string, f *pflag.Flag, isAnchor bool) string {
link = "#" + link
annotations, ok := f.Annotations["docs.external.url"]
if ok && len(annotations) > 0 {
link = annotations[0]
} else {
if !isAnchor {
return txt
}
}
return "[" + txt + "](" + link + ")"
}
func cmdOutput(cmd *cobra.Command, old string) (string, error) {
b := &strings.Builder{}
desc := cmd.Short
if cmd.Long != "" {
desc = cmd.Long
}
if desc != "" {
fmt.Fprintf(b, "%s\n\n", desc)
}
if len(cmd.Aliases) != 0 {
fmt.Fprintf(b, "### Aliases\n\n`%s`", cmd.Name())
for _, a := range cmd.Aliases {
fmt.Fprintf(b, ", `%s`", a)
}
fmt.Fprint(b, "\n\n")
}
if len(cmd.Commands()) != 0 {
fmt.Fprint(b, "### Subcommands\n\n")
fmt.Fprint(b, "| Name | Description |\n")
fmt.Fprint(b, "| --- | --- |\n")
for _, c := range cmd.Commands() {
fmt.Fprintf(b, "| [`%s`](%s) | %s |\n", c.Name(), getMDFilename(c), c.Short)
}
fmt.Fprint(b, "\n\n")
}
hasFlags := cmd.Flags().HasAvailableFlags()
cmd.Flags().AddFlagSet(cmd.InheritedFlags())
if hasFlags {
fmt.Fprint(b, "### Options\n\n")
fmt.Fprint(b, "| Name | Description |\n")
fmt.Fprint(b, "| --- | --- |\n")
cmd.Flags().VisitAll(func(f *pflag.Flag) {
if f.Hidden {
return
}
isLink := strings.Contains(old, "<a name=\""+f.Name+"\"></a>")
fmt.Fprint(b, "| ")
if f.Shorthand != "" {
name := "`-" + f.Shorthand + "`"
name = makeLink(name, f.Name, f, isLink)
fmt.Fprintf(b, "%s, ", name)
}
name := "`--" + f.Name
if f.Value.Type() != "bool" {
name += " " + f.Value.Type()
}
name += "`"
name = makeLink(name, f.Name, f, isLink)
fmt.Fprintf(b, "%s | %s |\n", name, f.Usage)
})
fmt.Fprintln(b, "")
}
return b.String(), nil
}
type options struct {
target string
}
func parseArgs() (*options, error) {
opts := &options{}
flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
flags.StringVar(&opts.target, "target", descriptionSourcePath, "Docs directory")
err := flags.Parse(os.Args[1:])
return opts, err
}
func main() {
if err := run(); err != nil {
log.Printf("error: %+v", err)
os.Exit(1)
}
}
func run() error {
opts, err := parseArgs()
if err != nil {
return err
}
if err := generateDocs(opts); err != nil {
return err
}
return nil
}

89
docs/generate.go Normal file
View File

@@ -0,0 +1,89 @@
package main
import (
"log"
"os"
"github.com/docker/buildx/commands"
clidocstool "github.com/docker/cli-docs-tool"
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
// import drivers otherwise factories are empty
// for --driver output flag usage
_ "github.com/docker/buildx/driver/docker"
_ "github.com/docker/buildx/driver/docker-container"
_ "github.com/docker/buildx/driver/kubernetes"
)
const defaultSourcePath = "docs/reference/"
type options struct {
source string
formats []string
}
func gen(opts *options) error {
log.SetFlags(0)
dockerCLI, err := command.NewDockerCli()
if err != nil {
return err
}
cmd := &cobra.Command{
Use: "docker [OPTIONS] COMMAND [ARG...]",
Short: "The base command for the Docker CLI.",
DisableAutoGenTag: true,
}
cmd.AddCommand(commands.NewRootCmd("buildx", true, dockerCLI))
c, err := clidocstool.New(clidocstool.Options{
Root: cmd,
SourceDir: opts.source,
Plugin: true,
})
if err != nil {
return err
}
for _, format := range opts.formats {
switch format {
case "md":
if err = c.GenMarkdownTree(cmd); err != nil {
return err
}
case "yaml":
if err = c.GenYamlTree(cmd); err != nil {
return err
}
default:
return errors.Errorf("unknown format %q", format)
}
}
return nil
}
func run() error {
opts := &options{}
flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
flags.StringVar(&opts.source, "source", defaultSourcePath, "Docs source folder")
flags.StringSliceVar(&opts.formats, "formats", []string{}, "Format (md, yaml)")
if err := flags.Parse(os.Args[1:]); err != nil {
return err
}
if len(opts.formats) == 0 {
return errors.New("Docs format required")
}
return gen(opts)
}
func main() {
if err := run(); err != nil {
log.Printf("ERROR: %+v", err)
os.Exit(1)
}
}

48
docs/guides/cicd.md Normal file
View File

@@ -0,0 +1,48 @@
# CI/CD
## GitHub Actions
Docker provides a [GitHub Action that will build and push your image](https://github.com/docker/build-push-action/#about)
using Buildx. Here is a simple workflow:
```yaml
name: ci
on:
push:
branches:
- 'main'
jobs:
docker:
runs-on: ubuntu-latest
steps:
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v2
with:
push: true
tags: user/app:latest
```
In this example we are also using 3 other actions:
* [`setup-buildx`](https://github.com/docker/setup-buildx-action) action will create and boot a builder using by
default the `docker-container` [builder driver](../reference/buildx_create.md#driver).
This is **not required but recommended** using it to be able to build multi-platform images, export cache, etc.
* [`setup-qemu`](https://github.com/docker/setup-qemu-action) action can be useful if you want
to add emulation support with QEMU to be able to build against more platforms.
* [`login`](https://github.com/docker/login-action) action will take care to log
in against a Docker registry.

View File

@@ -0,0 +1,23 @@
# CNI networking
It can be useful to use a bridge network for your builder if for example you
encounter a network port contention during multiple builds. If you're using
the BuildKit image, CNI is not yet available in it, but you can create
[a custom BuildKit image with CNI support](https://github.com/moby/buildkit/blob/master/docs/cni-networking.md).
Now build this image:
```console
$ docker buildx build --tag buildkit-cni:local --load .
```
Then [create a `docker-container` builder](../reference/buildx_create.md) that
will use this image:
```console
$ docker buildx create --use \
--name mybuilder \
--driver docker-container \
--driver-opt "image=buildkit-cni:local" \
--buildkitd-flags "--oci-worker-net=cni"
```

View File

@@ -0,0 +1,48 @@
# Using a custom network
[Create a network](https://docs.docker.com/engine/reference/commandline/network_create/)
named `foonet`:
```console
$ docker network create foonet
```
[Create a `docker-container` builder](../reference/buildx_create.md) named
`mybuilder` that will use this network:
```console
$ docker buildx create --use \
--name mybuilder \
--driver docker-container \
--driver-opt "network=foonet"
```
Boot and [inspect `mybuilder`](../reference/buildx_inspect.md):
```console
$ docker buildx inspect --bootstrap
```
[Inspect the builder container](https://docs.docker.com/engine/reference/commandline/inspect/)
and see what network is being used:
```console
$ docker inspect buildx_buildkit_mybuilder0 --format={{.NetworkSettings.Networks}}
map[foonet:0xc00018c0c0]
```
## What's `buildx_buildkit_mybuilder0`?
`buildx_buildkit_mybuilder0` is the container name. It can be broken down like this:
* `buildx_buildkit_` is a hardcoded prefix
* `mybuilder0` is the name of the node (defaults to builder name + position in the list of nodes)
```console
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
mybuilder * docker-container
mybuilder0 unix:///var/run/docker.sock running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
default docker
default default running linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
```

View File

@@ -0,0 +1,63 @@
# Using a custom registry configuration
If you [create a `docker-container` or `kubernetes` builder](../reference/buildx_create.md) and
have specified certificates for registries in the [BuildKit daemon configuration](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md),
the files will be copied into the container under `/etc/buildkit/certs` and
configuration will be updated to reflect that.
Take the following `buildkitd.toml` configuration that will be used for
pushing an image to this registry using self-signed certificates:
```toml"
debug = true
[registry."myregistry.com"]
ca=["/etc/certs/myregistry.pem"]
[[registry."myregistry.com".keypair]]
key="/etc/certs/myregistry_key.pem"
cert="/etc/certs/myregistry_cert.pem"
```
> `/etc/buildkitd.toml`
Here we have configured a self-signed certificate for `myregistry.com` registry.
Now [create a `docker-container` builder](../reference/buildx_create.md)
that will use this BuildKit configuration:
```console
$ docker buildx create --use \
--name mybuilder \
--driver docker-container \
--config /etc/buildkitd.toml
```
Inspecting the builder container, you can see that buildkitd configuration
has changed:
```console
$ docker exec -it buildx_buildkit_mybuilder0 cat /etc/buildkit/buildkitd.toml
```
```toml
debug = true
[registry]
[registry."myregistry.com"]
ca = ["/etc/buildkit/certs/myregistry.com/myregistry.pem"]
[[registry."myregistry.com".keypair]]
cert = "/etc/buildkit/certs/myregistry.com/myregistry_cert.pem"
key = "/etc/buildkit/certs/myregistry.com/myregistry_key.pem"
```
And certificates copied inside the container:
```console
$ docker exec -it buildx_buildkit_mybuilder0 ls /etc/buildkit/certs/myregistry.com/
myregistry.pem myregistry_cert.pem myregistry_key.pem
```
Now you should be able to push to the registry with this builder:
```console
$ docker buildx build --push --tag myregistry.com/myimage:latest .
```

View File

@@ -0,0 +1,31 @@
# OpenTelemetry support
To capture the trace to [Jaeger](https://github.com/jaegertracing/jaeger), set
`JAEGER_TRACE` environment variable to the collection address using a `driver-opt`.
First create a Jaeger container:
```console
$ docker run -d --name jaeger -p "6831:6831/udp" -p "16686:16686" jaegertracing/all-in-one
```
Then [create a `docker-container` builder](../reference/buildx_create.md)
that will use the Jaeger instance via the `JAEGER_TRACE` env var:
```console
$ docker buildx create --use \
--name mybuilder \
--driver docker-container \
--driver-opt "network=host" \
--driver-opt "env.JAEGER_TRACE=localhost:6831"
```
Boot and [inspect `mybuilder`](../reference/buildx_inspect.md):
```console
$ docker buildx inspect --bootstrap
```
Buildx commands should be traced at `http://127.0.0.1:16686/`:
![](https://user-images.githubusercontent.com/1951866/124468052-ef085400-dd98-11eb-84ab-7ac8e261dd52.png)

View File

@@ -0,0 +1,60 @@
# Registry mirror
You can define a registry mirror to use for your builds by providing a [BuildKit daemon configuration](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md)
while creating a builder with the [`--config` flags](../reference/buildx_create.md#config).
```toml
debug = true
[registry."docker.io"]
mirrors = ["mirror.gcr.io"]
```
> `/etc/buildkitd.toml`
> :information_source: `debug = true` has been added to be able to debug requests
in the BuildKit daemon and see if the mirror is effectively used.
Then [create a `docker-container` builder](../reference/buildx_create.md)
that will use this BuildKit configuration:
```console
$ docker buildx create --use \
--name mybuilder \
--driver docker-container \
--config /etc/buildkitd.toml
```
Boot and [inspect `mybuilder`](../reference/buildx_inspect.md):
```console
$ docker buildx inspect --bootstrap
```
Build an image:
```console
$ docker buildx build --load . -f-<<EOF
FROM alpine
RUN echo "hello world"
EOF
```
Now let's check the BuildKit logs in the builder container:
```console
$ docker logs buildx_buildkit_mybuilder0
```
```text
...
time="2022-02-06T17:47:48Z" level=debug msg="do request" request.header.accept="application/vnd.docker.container.image.v1+json, */*" request.header.user-agent=containerd/1.5.8+unknown request.method=GET spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg="fetch response received" response.header.accept-ranges=bytes response.header.age=1356 response.header.alt-svc="h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" response.header.cache-control="public, max-age=3600" response.header.content-length=1469 response.header.content-type=application/octet-stream response.header.date="Sun, 06 Feb 2022 17:25:17 GMT" response.header.etag="\"774380abda8f4eae9a149e5d5d3efc83\"" response.header.expires="Sun, 06 Feb 2022 18:25:17 GMT" response.header.last-modified="Wed, 24 Nov 2021 21:07:57 GMT" response.header.server=UploadServer response.header.x-goog-generation=1637788077652182 response.header.x-goog-hash="crc32c=V3DSrg==" response.header.x-goog-hash.1="md5=d0OAq9qPTq6aFJ5dXT78gw==" response.header.x-goog-metageneration=1 response.header.x-goog-storage-class=STANDARD response.header.x-goog-stored-content-encoding=identity response.header.x-goog-stored-content-length=1469 response.header.x-guploader-uploadid=ADPycduqQipVAXc3tzXmTzKQ2gTT6CV736B2J628smtD1iDytEyiYCgvvdD8zz9BT1J1sASUq9pW_ctUyC4B-v2jvhIxnZTlKg response.status="200 OK" spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg="fetch response received" response.header.accept-ranges=bytes response.header.age=760 response.header.alt-svc="h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" response.header.cache-control="public, max-age=3600" response.header.content-length=1471 response.header.content-type=application/octet-stream response.header.date="Sun, 06 Feb 2022 17:35:13 GMT" response.header.etag="\"35d688bd15327daafcdb4d4395e616a8\"" response.header.expires="Sun, 06 Feb 2022 18:35:13 GMT" response.header.last-modified="Wed, 24 Nov 2021 21:07:12 GMT" response.header.server=UploadServer response.header.x-goog-generation=1637788032100793 response.header.x-goog-hash="crc32c=aWgRjA==" response.header.x-goog-hash.1="md5=NdaIvRUyfar8201DleYWqA==" response.header.x-goog-metageneration=1 response.header.x-goog-storage-class=STANDARD response.header.x-goog-stored-content-encoding=identity response.header.x-goog-stored-content-length=1471 response.header.x-guploader-uploadid=ADPycdtR-gJYwC7yHquIkJWFFG8FovDySvtmRnZBqlO3yVDanBXh_VqKYt400yhuf0XbQ3ZMB9IZV2vlcyHezn_Pu3a1SMMtiw response.status="200 OK" spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg=fetch spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg=fetch spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg=fetch spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg=fetch spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg="do request" request.header.accept="application/vnd.docker.image.rootfs.diff.tar.gzip, */*" request.header.user-agent=containerd/1.5.8+unknown request.method=GET spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
time="2022-02-06T17:47:48Z" level=debug msg="fetch response received" response.header.accept-ranges=bytes response.header.age=1356 response.header.alt-svc="h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" response.header.cache-control="public, max-age=3600" response.header.content-length=2818413 response.header.content-type=application/octet-stream response.header.date="Sun, 06 Feb 2022 17:25:17 GMT" response.header.etag="\"1d55e7be5a77c4a908ad11bc33ebea1c\"" response.header.expires="Sun, 06 Feb 2022 18:25:17 GMT" response.header.last-modified="Wed, 24 Nov 2021 21:07:06 GMT" response.header.server=UploadServer response.header.x-goog-generation=1637788026431708 response.header.x-goog-hash="crc32c=ZojF+g==" response.header.x-goog-hash.1="md5=HVXnvlp3xKkIrRG8M+vqHA==" response.header.x-goog-metageneration=1 response.header.x-goog-storage-class=STANDARD response.header.x-goog-stored-content-encoding=identity response.header.x-goog-stored-content-length=2818413 response.header.x-guploader-uploadid=ADPycdsebqxiTBJqZ0bv9zBigjFxgQydD2ESZSkKchpE0ILlN9Ibko3C5r4fJTJ4UR9ddp-UBd-2v_4eRpZ8Yo2llW_j4k8WhQ response.status="200 OK" spanID=9460e5b6e64cec91 traceID=b162d3040ddf86d6614e79c66a01a577
...
```
As you can see, requests come from the GCR registry mirror (`response.header.x-goog*`).

View File

@@ -0,0 +1,33 @@
# Resource limiting
## Max parallelism
You can limit the parallelism of the BuildKit solver, which is particularly useful
for low-powered machines, using a [BuildKit daemon configuration](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md)
while creating a builder with the [`--config` flags](../reference/buildx_create.md#config).
```toml
[worker.oci]
max-parallelism = 4
```
> `/etc/buildkitd.toml`
Now you can [create a `docker-container` builder](../reference/buildx_create.md)
that will use this BuildKit configuration to limit parallelism.
```console
$ docker buildx create --use \
--name mybuilder \
--driver docker-container \
--config /etc/buildkitd.toml
```
## Limit on TCP connections
We are also now limiting TCP connections to **4 per registry** with an additional
connection not used for layer pulls and pushes. This limitation will be able to
manage TCP connection per host to avoid your build being stuck while pulling
images. The additional connection is used for metadata requests
(image config retrieval) to enhance the overall build time.
More info: [moby/buildkit#2259](https://github.com/moby/buildkit/pull/2259)

View File

@@ -5,7 +5,7 @@ docker buildx [OPTIONS] COMMAND
``` ```
<!---MARKER_GEN_START--> <!---MARKER_GEN_START-->
Build with BuildKit Extended build capabilities with BuildKit
### Subcommands ### Subcommands
@@ -27,5 +27,17 @@ Build with BuildKit
| [`version`](buildx_version.md) | Show buildx version information | | [`version`](buildx_version.md) | Show buildx version information |
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
You can also use the `BUILDX_BUILDER` environment variable.

View File

@@ -13,18 +13,18 @@ Build from a file
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| `--builder string` | Override the configured builder instance | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-f`](#file), [`--file stringArray`](#file) | Build definition file | | [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
| `--load` | Shorthand for --set=*.output=type=docker | | `--load` | | | Shorthand for `--set=*.output=type=docker` |
| `--metadata-file string` | Write build result metadata to the file | | `--metadata-file` | `string` | | Write build result metadata to the file |
| [`--no-cache`](#no-cache) | Do not use cache when building the image | | [`--no-cache`](#no-cache) | | | Do not use cache when building the image |
| [`--print`](#print) | Print the options without building | | [`--print`](#print) | | | Print the options without building |
| [`--progress string`](#progress) | Set type of progress output (auto, plain, tty). Use plain to show container output | | [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
| [`--pull`](#pull) | Always attempt to pull a newer version of the image | | [`--pull`](#pull) | | | Always attempt to pull all referenced images |
| `--push` | Shorthand for --set=*.output=type=registry | | `--push` | | | Shorthand for `--set=*.output=type=registry` |
| [`--set stringArray`](#set) | Override target value (eg: targetpattern.key=value) | | [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -34,16 +34,23 @@ Build from a file
Bake is a high-level build command. Each specified target will run in parallel Bake is a high-level build command. Each specified target will run in parallel
as part of the build. as part of the build.
Read [High-level build options](https://github.com/docker/buildx#high-level-build-options) for introduction. Read [High-level build options](https://github.com/docker/buildx#high-level-build-options)
for introduction.
Please note that `buildx bake` command may receive backwards incompatible features in the future if needed. We are looking for feedback on improving the command and extending the functionality further. Please note that `buildx bake` command may receive backwards incompatible
features in the future if needed. We are looking for feedback on improving the
command and extending the functionality further.
## Examples ## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).
### <a name="file"></a> Specify a build definition file (-f, --file) ### <a name="file"></a> Specify a build definition file (-f, --file)
By default, `buildx bake` looks for build definition files in the current directory, By default, `buildx bake` looks for build definition files in the current
the following are parsed: directory, the following are parsed:
- `docker-compose.yml` - `docker-compose.yml`
- `docker-compose.yaml` - `docker-compose.yaml`
@@ -89,27 +96,148 @@ $ docker buildx bake -f docker-compose.dev.yaml backend database
... ...
``` ```
You can also use a remote `git` bake definition:
```console
$ docker buildx bake "https://github.com/docker/cli.git#v20.10.11" --print
#1 [internal] load git source https://github.com/docker/cli.git#v20.10.11
#1 0.745 e8f1871b077b64bcb4a13334b7146492773769f7 refs/tags/v20.10.11
#1 2.022 From https://github.com/docker/cli
#1 2.022 * [new tag] v20.10.11 -> v20.10.11
#1 DONE 2.9s
{
"group": {
"default": {
"targets": [
"binary"
]
}
},
"target": {
"binary": {
"context": "https://github.com/docker/cli.git#v20.10.11",
"dockerfile": "Dockerfile",
"args": {
"BASE_VARIANT": "alpine",
"GO_STRIP": "",
"VERSION": ""
},
"target": "binary",
"platforms": [
"local"
],
"output": [
"build"
]
}
}
}
```
As you can see the context is fixed to `https://github.com/docker/cli.git` even if
[no context is actually defined](https://github.com/docker/cli/blob/2776a6d694f988c0c1df61cad4bfac0f54e481c8/docker-bake.hcl#L17-L26)
in the definition.
If you want to access the main context for bake command from a bake file
that has been imported remotely, you can use the `BAKE_CMD_CONTEXT` builtin var:
```console
$ cat https://raw.githubusercontent.com/tonistiigi/buildx/remote-test/docker-bake.hcl
target "default" {
context = BAKE_CMD_CONTEXT
dockerfile-inline = <<EOT
FROM alpine
WORKDIR /src
COPY . .
RUN ls -l && stop
EOT
}
```
```console
$ docker buildx bake "https://github.com/tonistiigi/buildx.git#remote-test" --print
{
"target": {
"default": {
"context": ".",
"dockerfile": "Dockerfile",
"dockerfile-inline": "FROM alpine\nWORKDIR /src\nCOPY . .\nRUN ls -l \u0026\u0026 stop\n"
}
}
}
```
```console
$ touch foo bar
$ docker buildx bake "https://github.com/tonistiigi/buildx.git#remote-test"
...
> [4/4] RUN ls -l && stop:
#8 0.101 total 0
#8 0.102 -rw-r--r-- 1 root root 0 Jul 27 18:47 bar
#8 0.102 -rw-r--r-- 1 root root 0 Jul 27 18:47 foo
#8 0.102 /bin/sh: stop: not found
```
```console
$ docker buildx bake "https://github.com/tonistiigi/buildx.git#remote-test" "https://github.com/docker/cli.git#v20.10.11" --print
#1 [internal] load git source https://github.com/tonistiigi/buildx.git#remote-test
#1 0.429 577303add004dd7efeb13434d69ea030d35f7888 refs/heads/remote-test
#1 CACHED
{
"target": {
"default": {
"context": "https://github.com/docker/cli.git#v20.10.11",
"dockerfile": "Dockerfile",
"dockerfile-inline": "FROM alpine\nWORKDIR /src\nCOPY . .\nRUN ls -l \u0026\u0026 stop\n"
}
}
}
```
```console
$ docker buildx bake "https://github.com/tonistiigi/buildx.git#remote-test" "https://github.com/docker/cli.git#v20.10.11"
...
> [4/4] RUN ls -l && stop:
#8 0.136 drwxrwxrwx 5 root root 4096 Jul 27 18:31 kubernetes
#8 0.136 drwxrwxrwx 3 root root 4096 Jul 27 18:31 man
#8 0.136 drwxrwxrwx 2 root root 4096 Jul 27 18:31 opts
#8 0.136 -rw-rw-rw- 1 root root 1893 Jul 27 18:31 poule.yml
#8 0.136 drwxrwxrwx 7 root root 4096 Jul 27 18:31 scripts
#8 0.136 drwxrwxrwx 3 root root 4096 Jul 27 18:31 service
#8 0.136 drwxrwxrwx 2 root root 4096 Jul 27 18:31 templates
#8 0.136 drwxrwxrwx 10 root root 4096 Jul 27 18:31 vendor
#8 0.136 -rwxrwxrwx 1 root root 9620 Jul 27 18:31 vendor.conf
#8 0.136 /bin/sh: stop: not found
```
### <a name="no-cache"></a> Do not use cache when building the image (--no-cache) ### <a name="no-cache"></a> Do not use cache when building the image (--no-cache)
Same as `build --no-cache`. Do not use cache when building the image. Same as `build --no-cache`. Do not use cache when building the image.
### <a name="print"></a> Print the options without building (--print) ### <a name="print"></a> Print the options without building (--print)
Prints the resulting options of the targets desired to be built, in a JSON format, Prints the resulting options of the targets desired to be built, in a JSON
without starting a build. format, without starting a build.
```console ```console
$ docker buildx bake -f docker-bake.hcl --print db $ docker buildx bake -f docker-bake.hcl --print db
{ {
"target": { "group": {
"db": { "default": {
"context": "./", "targets": [
"dockerfile": "Dockerfile", "db"
"tags": [ ]
"docker.io/tiborvass/db" }
] },
} "target": {
} "db": {
"context": "./",
"dockerfile": "Dockerfile",
"tags": [
"docker.io/tiborvass/db"
]
}
}
} }
``` ```
@@ -136,7 +264,6 @@ $ docker buildx bake --progress=plain
... ...
``` ```
### <a name="pull"></a> Always attempt to pull a newer version of the image (--pull) ### <a name="pull"></a> Always attempt to pull a newer version of the image (--pull)
Same as `build --pull`. Same as `build --pull`.
@@ -147,10 +274,8 @@ Same as `build --pull`.
--set targetpattern.key[.subkey]=value --set targetpattern.key[.subkey]=value
``` ```
Override target configurations from command line. The pattern matching syntax is Override target configurations from command line. The pattern matching syntax
defined in https://golang.org/pkg/path/#Match. is defined in https://golang.org/pkg/path/#Match.
**Examples**
```console ```console
$ docker buildx bake --set target.args.mybuildarg=value $ docker buildx bake --set target.args.mybuildarg=value
@@ -161,8 +286,8 @@ $ docker buildx bake --set foo*.no-cache # bypass caching only for
``` ```
Complete list of overridable fields: Complete list of overridable fields:
args, cache-from, cache-to, context, dockerfile, labels, no-cache, output, platform, `args`, `cache-from`, `cache-to`, `context`, `dockerfile`, `labels`, `no-cache`,
pull, secrets, ssh, tags, target `output`, `platform`, `pull`, `secrets`, `ssh`, `tags`, `target`
### File definition ### File definition
@@ -184,8 +309,7 @@ groups to inherit from.
Note: Design of bake command is work in progress, the user experience may change Note: Design of bake command is work in progress, the user experience may change
based on feedback. based on feedback.
HCL definition example:
**Example HCL definition**
```hcl ```hcl
group "default" { group "default" {
@@ -210,21 +334,104 @@ target "db" {
Complete list of valid target fields: Complete list of valid target fields:
`args`, `cache-from`, `cache-to`, `context`, `dockerfile`, `inherits`, `labels`, `args`, `cache-from`, `cache-to`, `context`, `contexts`, `dockerfile`, `inherits`, `labels`,
`no-cache`, `output`, `platform`, `pull`, `secrets`, `ssh`, `tags`, `target` `no-cache`, `no-cache-filter`, `output`, `platform`, `pull`, `secrets`, `ssh`, `tags`, `target`
### Global scope attributes
You can define global scope attributes in HCL/JSON and use them for code reuse
and setting values for variables. This means you can do a "data-only" HCL file
with the values you want to set/override and use it in the list of regular
output files.
```hcl
# docker-bake.hcl
variable "FOO" {
default = "abc"
}
target "app" {
args = {
v1 = "pre-${FOO}"
}
}
```
You can use this file directly:
```console
$ docker buildx bake --print app
{
"group": {
"default": {
"targets": [
"app"
]
}
},
"target": {
"app": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"v1": "pre-abc"
}
}
}
}
```
Or create an override configuration file:
```hcl
# env.hcl
WHOAMI="myuser"
FOO="def-${WHOAMI}"
```
And invoke bake together with both of the files:
```console
$ docker buildx bake -f docker-bake.hcl -f env.hcl --print app
{
"group": {
"default": {
"targets": [
"app"
]
}
},
"target": {
"app": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"v1": "pre-def-myuser"
}
}
}
}
```
### HCL variables and functions ### HCL variables and functions
Similar to how Terraform provides a way to [define variables](https://www.terraform.io/docs/configuration/variables.html#declaring-an-input-variable), Similar to how Terraform provides a way to [define variables](https://www.terraform.io/docs/configuration/variables.html#declaring-an-input-variable),
the HCL file format also supports variable block definitions. These can be used the HCL file format also supports variable block definitions. These can be used
to define variables with values provided by the current environment, or a default to define variables with values provided by the current environment, or a
value when unset. default value when unset.
A [set of generally useful functions](https://github.com/docker/buildx/blob/master/bake/hclparser/stdlib.go)
provided by [go-cty](https://github.com/zclconf/go-cty/tree/main/cty/function/stdlib)
are available for use in HCL files. In addition, [user defined functions](https://github.com/hashicorp/hcl/tree/main/ext/userfunc)
are also supported.
Example of using interpolation to tag an image with the git sha: #### Using interpolation to tag an image with the git sha
```console Bake supports variable blocks which are assigned to matching environment
$ cat <<'EOF' > docker-bake.hcl variables or default values.
```hcl
# docker-bake.hcl
variable "TAG" { variable "TAG" {
default = "latest" default = "latest"
} }
@@ -236,45 +443,81 @@ group "default" {
target "webapp" { target "webapp" {
tags = ["docker.io/username/webapp:${TAG}"] tags = ["docker.io/username/webapp:${TAG}"]
} }
EOF ```
$ docker buildx bake --print webapp alternatively, in json format:
{
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"tags": [
"docker.io/username/webapp:latest"
]
}
}
}
$ TAG=$(git rev-parse --short HEAD) docker buildx bake --print webapp ```json
{ {
"target": { "variable": {
"webapp": { "TAG": {
"context": ".", "default": "latest"
"dockerfile": "Dockerfile", }
"tags": [ }
"docker.io/username/webapp:985e9e9" "group": {
] "default": {
} "targets": ["webapp"]
} }
},
"target": {
"webapp": {
"tags": ["docker.io/username/webapp:${TAG}"]
}
}
} }
``` ```
```console
A [set of generally useful functions](https://github.com/docker/buildx/blob/master/bake/hclparser/stdlib.go) $ docker buildx bake --print webapp
provided by [go-cty](https://github.com/zclconf/go-cty/tree/main/cty/function/stdlib) {
are available for use in HCL files. In addition, [user defined functions](https://github.com/hashicorp/hcl/tree/main/ext/userfunc) "group": {
are also supported. "default": {
"targets": [
Example of using the `add` function: "webapp"
]
}
},
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"tags": [
"docker.io/username/webapp:latest"
]
}
}
}
```
```console ```console
$ cat <<'EOF' > docker-bake.hcl $ TAG=$(git rev-parse --short HEAD) docker buildx bake --print webapp
{
"group": {
"default": {
"targets": [
"webapp"
]
}
},
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"tags": [
"docker.io/username/webapp:985e9e9"
]
}
}
}
```
#### Using the `add` function
You can use [`go-cty` stdlib functions](https://github.com/zclconf/go-cty/tree/main/cty/function/stdlib).
Here we are using the `add` function.
```hcl
# docker-bake.hcl
variable "TAG" { variable "TAG" {
default = "latest" default = "latest"
} }
@@ -288,26 +531,37 @@ target "webapp" {
buildno = "${add(123, 1)}" buildno = "${add(123, 1)}"
} }
} }
EOF ```
```console
$ docker buildx bake --print webapp $ docker buildx bake --print webapp
{ {
"target": { "group": {
"webapp": { "default": {
"context": ".", "targets": [
"dockerfile": "Dockerfile", "webapp"
"args": { ]
"buildno": "124" }
} },
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"buildno": "124"
} }
} }
}
} }
``` ```
Example of defining an `increment` function: #### Defining an `increment` function
```console It also supports [user defined functions](https://github.com/hashicorp/hcl/tree/main/ext/userfunc).
$ cat <<'EOF' > docker-bake.hcl The following example defines a simple an `increment` function.
```hcl
# docker-bake.hcl
function "increment" { function "increment" {
params = [number] params = [number]
result = number + 1 result = number + 1
@@ -322,27 +576,37 @@ target "webapp" {
buildno = "${increment(123)}" buildno = "${increment(123)}"
} }
} }
EOF ```
```console
$ docker buildx bake --print webapp $ docker buildx bake --print webapp
{ {
"target": { "group": {
"webapp": { "default": {
"context": ".", "targets": [
"dockerfile": "Dockerfile", "webapp"
"args": { ]
"buildno": "124" }
} },
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"buildno": "124"
} }
} }
}
} }
``` ```
Example of only adding tags if a variable is not empty using an `notequal` #### Only adding tags if a variable is not empty using an `notequal`
function:
```console Here we are using the conditional `notequal` function which is just for
$ cat <<'EOF' > docker-bake.hcl symmetry with the `equal` one.
```hcl
# docker-bake.hcl
variable "TAG" {default="" } variable "TAG" {default="" }
group "default" { group "default" {
@@ -359,18 +623,364 @@ target "webapp" {
notequal("",TAG) ? "my-image:${TAG}": "", notequal("",TAG) ? "my-image:${TAG}": "",
] ]
} }
EOF ```
```console
$ docker buildx bake --print webapp $ docker buildx bake --print webapp
{ {
"target": { "group": {
"webapp": { "default": {
"context": ".", "targets": [
"dockerfile": "Dockerfile", "webapp"
"tags": [ ]
"my-image:latest" }
] },
} "target": {
} "webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"tags": [
"my-image:latest"
]
}
}
} }
``` ```
#### Using variables in functions
You can refer variables to other variables like the target blocks can. Stdlib
functions can also be called but user functions can't at the moment.
```hcl
# docker-bake.hcl
variable "REPO" {
default = "user/repo"
}
function "tag" {
params = [tag]
result = ["${REPO}:${tag}"]
}
target "webapp" {
tags = tag("v1")
}
```
```console
$ docker buildx bake --print webapp
{
"group": {
"default": {
"targets": [
"webapp"
]
}
},
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"tags": [
"user/repo:v1"
]
}
}
}
```
#### Using variables in variables across files
When multiple files are specified, one file can use variables defined in
another file.
```hcl
# docker-bake1.hcl
variable "FOO" {
default = upper("${BASE}def")
}
variable "BAR" {
default = "-${FOO}-"
}
target "app" {
args = {
v1 = "pre-${BAR}"
}
}
```
```hcl
# docker-bake2.hcl
variable "BASE" {
default = "abc"
}
target "app" {
args = {
v2 = "${FOO}-post"
}
}
```
```console
$ docker buildx bake -f docker-bake1.hcl -f docker-bake2.hcl --print app
{
"group": {
"default": {
"targets": [
"app"
]
}
},
"target": {
"app": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"v1": "pre--ABCDEF-",
"v2": "ABCDEF-post"
}
}
}
}
```
#### Using typed variables
Non-string variables are also accepted. The value passed with env is parsed
into suitable type first.
```hcl
# docker-bake.hcl
variable "FOO" {
default = 3
}
variable "IS_FOO" {
default = true
}
target "app" {
args = {
v1 = FOO > 5 ? "higher" : "lower"
v2 = IS_FOO ? "yes" : "no"
}
}
```
```console
$ docker buildx bake --print app
{
"group": {
"default": {
"targets": [
"app"
]
}
},
"target": {
"app": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"v1": "lower",
"v2": "yes"
}
}
}
}
```
### Defining additional build contexts and linking targets
In addition to the main `context` key that defines the build context each target can also define additional named contexts with a map defined with key `contexts`. These values map to the `--build-context` flag in the [build command](buildx_build.md#build-context).
Inside the Dockerfile these contexts can be used with the `FROM` instruction or `--from` flag.
The value can be a local source directory, container image (with docker-image:// prefix), Git URL, HTTP URL or a name of another target in the Bake file (with target: prefix).
#### Pinning alpine image
```Dockerfile
# Dockerfile
FROM alpine
RUN echo "Hello world"
```
```hcl
# docker-bake.hcl
target "app" {
contexts = {
alpine = "docker-image://alpine:3.13"
}
}
```
#### Using a secondary source directory
```Dockerfile
# Dockerfile
FROM scratch AS src
FROM golang
COPY --from=src . .
```
```hcl
# docker-bake.hcl
target "app" {
contexts = {
src = "../path/to/source"
}
}
```
#### Using a result of one target as a base image in another target
To use a result of one target as a build context of another, specity the target name with `target:` prefix.
```Dockerfile
# Dockerfile
FROM baseapp
RUN echo "Hello world"
```
```hcl
# docker-bake.hcl
target "base" {
dockerfile = "baseapp.Dockerfile"
}
target "app" {
contexts = {
baseapp = "target:base"
}
}
```
Please note that in most cases you should just use a single multi-stage Dockerfile with multiple targets for similar behavior. This case is recommended when you have multiple Dockerfiles that can't be easily merged into one.
### Extension field with Compose
[Special extension](https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension)
field `x-bake` can be used in your compose file to evaluate fields that are not
(yet) available in the [build definition](https://github.com/compose-spec/compose-spec/blob/master/build.md#build-definition).
```yaml
# docker-compose.yml
services:
addon:
image: ct-addon:bar
build:
context: .
dockerfile: ./Dockerfile
args:
CT_ECR: foo
CT_TAG: bar
x-bake:
tags:
- ct-addon:foo
- ct-addon:alp
platforms:
- linux/amd64
- linux/arm64
cache-from:
- user/app:cache
- type=local,src=path/to/cache
cache-to: type=local,dest=path/to/cache
pull: true
aws:
image: ct-fake-aws:bar
build:
dockerfile: ./aws.Dockerfile
args:
CT_ECR: foo
CT_TAG: bar
x-bake:
secret:
- id=mysecret,src=./secret
- id=mysecret2,src=./secret2
platforms: linux/arm64
output: type=docker
no-cache: true
```
```console
$ docker buildx bake --print
{
"group": {
"default": {
"targets": [
"aws",
"addon"
]
}
},
"target": {
"addon": {
"context": ".",
"dockerfile": "./Dockerfile",
"args": {
"CT_ECR": "foo",
"CT_TAG": "bar"
},
"tags": [
"ct-addon:foo",
"ct-addon:alp"
],
"cache-from": [
"user/app:cache",
"type=local,src=path/to/cache"
],
"cache-to": [
"type=local,dest=path/to/cache"
],
"platforms": [
"linux/amd64",
"linux/arm64"
],
"pull": true
},
"aws": {
"context": ".",
"dockerfile": "./aws.Dockerfile",
"args": {
"CT_ECR": "foo",
"CT_TAG": "bar"
},
"tags": [
"ct-fake-aws:bar"
],
"secret": [
"id=mysecret,src=./secret",
"id=mysecret2,src=./secret2"
],
"platforms": [
"linux/arm64"
],
"output": [
"type=docker"
],
"no-cache": true
}
}
}
```
Complete list of valid fields for `x-bake`:
`tags`, `cache-from`, `cache-to`, `secret`, `ssh`, `platforms`, `output`,
`pull`, `no-cache`, `no-cache-filter`
### Built-in variables
* `BAKE_CMD_CONTEXT` can be used to access the main `context` for bake command
from a bake file that has been [imported remotely](#file).
* `BAKE_LOCAL_PLATFORM` returns the current platform's default platform
specification (e.g. `linux/amd64`).

View File

@@ -13,30 +13,36 @@ Start a build
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| [`--add-host stringSlice`](https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host) | Add a custom host-to-IP mapping (host:ip) | | [`--add-host`](https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
| [`--allow stringSlice`](#allow) | Allow extra privileged entitlement, e.g. network.host, security.insecure | | [`--allow`](#allow) | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
| [`--build-arg stringArray`](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg) | Set build-time variables | | [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
| `--builder string` | Override the configured builder instance | | [`--build-context`](#build-context) | `stringArray` | | Additional build contexts (e.g., name=path) |
| [`--cache-from stringArray`](#cache-from) | External cache sources (eg. user/app:cache, type=local,src=path/to/dir) | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--cache-to stringArray`](#cache-to) | Cache export destinations (eg. user/app:cache, type=local,dest=path/to/dir) | | [`--cache-from`](#cache-from) | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f), [`--file string`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f) | Name of the Dockerfile (Default is 'PATH/Dockerfile') | | [`--cache-to`](#cache-to) | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
| `--iidfile string` | Write the image ID to the file | | [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent) | `string` | | Optional parent cgroup for the container |
| `--label stringArray` | Set metadata for an image | | [`-f`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
| [`--load`](#load) | Shorthand for --output=type=docker | | `--iidfile` | `string` | | Write the image ID to the file |
| `--metadata-file string` | Write build result metadata to the file | | `--label` | `stringArray` | | Set metadata for an image |
| `--network string` | Set the networking mode for the RUN instructions during build | | [`--load`](#load) | | | Shorthand for `--output=type=docker` |
| `--no-cache` | Do not use cache when building the image | | [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to the file |
| [`-o`](#output), [`--output stringArray`](#output) | Output destination (format: type=local,dest=path) | | `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
| [`--platform stringArray`](#platform) | Set target platform for build | | `--no-cache` | | | Do not use cache when building the image |
| [`--progress string`](#progress) | Set type of progress output (auto, plain, tty). Use plain to show container output | | `--no-cache-filter` | `stringArray` | | Do not cache specified stages |
| `--pull` | Always attempt to pull a newer version of the image | | [`-o`](#output), [`--output`](#output) | `stringArray` | | Output destination (format: `type=local,dest=path`) |
| [`--push`](#push) | Shorthand for --output=type=registry | | [`--platform`](#platform) | `stringArray` | | Set target platform for build |
| `--secret stringArray` | Secret file to expose to the build: id=mysecret,src=/local/secret | | [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
| `--ssh stringArray` | SSH agent socket or keys to expose to the build (format: default|<id>[=<socket>|<key>[,<key>]]) | | `--pull` | | | Always attempt to pull all referenced images |
| [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t), [`--tag stringArray`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t) | Name and optionally a tag in the 'name:tag' format | | [`--push`](#push) | | | Shorthand for `--output=type=registry` |
| [`--target string`](https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target) | Set the target build stage to build. | | `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
| [`--secret`](#secret) | `stringArray` | | Secret to expose to the build (format: `id=mysecret[,src=/local/secret]`) |
| [`--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/engine/reference/commandline/build/#tag-an-image--t), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
| [`--target`](https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target) | `string` | | Set the target build stage to build |
| [`--ulimit`](#ulimit) | `ulimit` | | Ulimit options |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -48,76 +54,200 @@ to the UI of `docker build` command and takes the same flags and arguments.
For documentation on most of these flags, refer to the [`docker build` For documentation on most of these flags, refer to the [`docker build`
documentation](https://docs.docker.com/engine/reference/commandline/build/). In documentation](https://docs.docker.com/engine/reference/commandline/build/). In
here well document a subset of the new flags. here we'll document a subset of the new flags.
## Examples ## Examples
### <a name="platform"></a> Set the target platforms for the build (--platform) ### <a name="allow"></a> Allow extra privileged entitlement (--allow)
``` ```
--platform=value[,value] --allow=ENTITLEMENT
``` ```
Set the target platform for the build. All `FROM` commands inside the Dockerfile Allow extra privileged entitlement. List of entitlements:
without their own `--platform` flag will pull base images for this platform and
this value will also be the platform of the resulting image. The default value
will be the current platform of the buildkit daemon.
When using `docker-container` driver with `buildx`, this flag can accept multiple - `network.host` - Allows executions with host networking.
values as an input separated by a comma. With multiple values the result will be - `security.insecure` - Allows executions without sandbox. See
built for all of the specified platforms and joined together into a single manifest [related Dockerfile extensions](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md#run---securityinsecuresandbox).
list.
If the `Dockerfile` needs to invoke the `RUN` command, the builder needs runtime For entitlements to be enabled, the `buildkitd` daemon also needs to allow them
support for the specified platform. In a clean setup, you can only execute `RUN` with `--allow-insecure-entitlement` (see [`create --buildkitd-flags`](buildx_create.md#buildkitd-flags))
commands for your system architecture.
If your kernel supports [`binfmt_misc`](https://en.wikipedia.org/wiki/Binfmt_misc)
launchers for secondary architectures, buildx will pick them up automatically.
Docker desktop releases come with `binfmt_misc` automatically configured for `arm64`
and `arm` architectures. You can see what runtime platforms your current builder
instance supports by running `docker buildx inspect --bootstrap`.
Inside a `Dockerfile`, you can access the current platform value through
`TARGETPLATFORM` build argument. Please refer to the [`docker build`
documentation](https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope)
for the full description of automatic platform argument variants .
The formatting for the platform specifier is defined in the [containerd source
code](https://github.com/containerd/containerd/blob/v1.4.3/platforms/platforms.go#L63).
**Examples** **Examples**
```console ```console
$ docker buildx build --platform=linux/arm64 . $ docker buildx create --use --name insecure-builder --buildkitd-flags '--allow-insecure-entitlement security.insecure'
$ docker buildx build --platform=linux/amd64,linux/arm64,linux/arm/v7 . $ docker buildx build --allow security.insecure .
$ docker buildx build --platform=darwin .
``` ```
### <a name="progress"></a> Set type of progress output (--progress) ### <a name="build-arg"></a> Set build-time variables (--build-arg)
``` Same as [`docker build` command](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg).
--progress=VALUE
```
Set type of progress output (auto, plain, tty). Use plain to show container There are also useful built-in build args like:
output (default "auto").
> You can also use the `BUILDKIT_PROGRESS` environment variable to set * `BUILDKIT_CONTEXT_KEEP_GIT_DIR=<bool>` trigger git context to keep the `.git` directory
> its value. * `BUILDKIT_INLINE_BUILDINFO_ATTRS=<bool>` inline build info attributes in image config or not
* `BUILDKIT_INLINE_CACHE=<bool>` inline cache metadata to image config or not
The following example uses `plain` output during the build: * `BUILDKIT_MULTI_PLATFORM=<bool>` opt into determnistic output regardless of multi-platform output or not
```console ```console
$ docker buildx build --load --progress=plain . $ docker buildx build --build-arg BUILDKIT_MULTI_PLATFORM=1 .
```
#1 [internal] load build definition from Dockerfile More built-in build args can be found in [dockerfile frontend docs](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#built-in-build-args).
#1 transferring dockerfile: 227B 0.0s done
#1 DONE 0.1s
#2 [internal] load .dockerignore ### <a name="build-context"></a> Additional build contexts (--build-context)
#2 transferring context: 129B 0.0s done
#2 DONE 0.0s ```
... --build-context=name=VALUE
```
Define additional build context with specified contents. In Dockerfile the context can be accessed when `FROM name` or `--from=name` is used.
When Dockerfile defines a stage with the same name it is overwritten.
The value can be a local source directory, container image (with docker-image:// prefix), Git or HTTP URL.
Replace `alpine:latest` with a pinned one:
```console
$ docker buildx build --build-context alpine=docker-image://alpine@sha256:0123456789 .
```
Expose a secondary local source directory:
```console
$ docker buildx build --build-context project=path/to/project/source .
# docker buildx build --build-context project=https://github.com/myuser/project.git .
```
```Dockerfile
FROM alpine
COPY --from=project myfile /
```
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).
### <a name="cache-from"></a> Use an external cache source for a build (--cache-from)
```
--cache-from=[NAME|type=TYPE[,KEY=VALUE]]
```
Use an external cache source for a build. Supported types are `registry`,
`local` and `gha`.
- [`registry` source](https://github.com/moby/buildkit#registry-push-image-and-cache-separately)
can import cache from a cache manifest or (special) image configuration on the
registry.
- [`local` source](https://github.com/moby/buildkit#local-directory-1) can
import cache from local files previously exported with `--cache-to`.
- [`gha` source](https://github.com/moby/buildkit#github-actions-cache-experimental)
can import cache from a previously exported cache with `--cache-to` in your
GitHub repository
If no type is specified, `registry` exporter is used with a specified reference.
`docker` driver currently only supports importing build cache from the registry.
```console
$ docker buildx build --cache-from=user/app:cache .
$ docker buildx build --cache-from=user/app .
$ docker buildx build --cache-from=type=registry,ref=user/app .
$ docker buildx build --cache-from=type=local,src=path/to/cache .
$ docker buildx build --cache-from=type=gha .
```
More info about cache exporters and available attributes: https://github.com/moby/buildkit#export-cache
### <a name="cache-to"></a> Export build cache to an external cache destination (--cache-to)
```
--cache-to=[NAME|type=TYPE[,KEY=VALUE]]
```
Export build cache to an external cache destination. Supported types are
`registry`, `local`, `inline` and `gha`.
- [`registry` type](https://github.com/moby/buildkit#registry-push-image-and-cache-separately) exports build cache to a cache manifest in the registry.
- [`local` type](https://github.com/moby/buildkit#local-directory-1) type
exports cache to a local directory on the client.
- [`inline` type](https://github.com/moby/buildkit#inline-push-image-and-cache-together)
type writes the cache metadata into the image configuration.
- [`gha` type](https://github.com/moby/buildkit#github-actions-cache-experimental)
type exports cache through the [Github Actions Cache service API](https://github.com/tonistiigi/go-actions-cache/blob/master/api.md#authentication).
`docker` driver currently only supports exporting inline cache metadata to image
configuration. Alternatively, `--build-arg BUILDKIT_INLINE_CACHE=1` can be used
to trigger inline cache exporter.
Attribute key:
- `mode` - Specifies how many layers are exported with the cache. `min` on only
exports layers already in the final build stage, `max` exports layers for
all stages. Metadata is always exported for the whole build.
```console
$ docker buildx build --cache-to=user/app:cache .
$ docker buildx build --cache-to=type=inline .
$ docker buildx build --cache-to=type=registry,ref=user/app .
$ docker buildx build --cache-to=type=local,dest=path/to/cache .
$ docker buildx build --cache-to=type=gha .
```
More info about cache exporters and available attributes: https://github.com/moby/buildkit#export-cache
### <a name="load"></a> Load the single-platform build result to `docker images` (--load)
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 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
directory of the specified file must already exist and be writable.
```console
$ docker buildx build --load --metadata-file metadata.json .
$ cat metadata.json
```
```json
{
"containerimage.buildinfo": {
"frontend": "dockerfile.v0",
"attrs": {
"context": "https://github.com/crazy-max/buildkit-buildsources-test.git#master",
"filename": "Dockerfile",
"source": "docker/dockerfile:master"
},
"sources": [
{
"type": "docker-image",
"ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0",
"pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0"
},
{
"type": "docker-image",
"ref": "docker.io/library/alpine:3.13",
"pin": "sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c"
}
]
},
"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"
}
``` ```
### <a name="output"></a> Set the export action for the build result (-o, --output) ### <a name="output"></a> Set the export action for the build result (-o, --output)
@@ -138,8 +268,6 @@ If just the path is specified as a value, `buildx` will use the local exporter
with this path as the destination. If the value is "-", `buildx` will use `tar` with this path as the destination. If the value is "-", `buildx` will use `tar`
exporter and write to `stdout`. exporter and write to `stdout`.
**Examples**
```console ```console
$ docker buildx build -o . . $ docker buildx build -o . .
$ docker buildx build -o outdir . $ docker buildx build -o outdir .
@@ -193,7 +321,7 @@ The most common usecase for multi-platform images is to directly push to a regis
Attribute keys: Attribute keys:
- `dest` - destination path where tarball will be written. If not specified the - `dest` - destination path where tarball will be written. If not specified the
tar will be loaded automatically to the current docker instance. tar will be loaded automatically to the current docker instance.
- `context` - name for the docker context where to import the result - `context` - name for the docker context where to import the result
#### `image` #### `image`
@@ -211,89 +339,174 @@ Attribute keys:
The `registry` exporter is a shortcut for `type=image,push=true`. The `registry` exporter is a shortcut for `type=image,push=true`.
### <a name="platform"></a> Set the target platforms for the build (--platform)
```
--platform=value[,value]
```
Set the target platform for the build. All `FROM` commands inside the Dockerfile
without their own `--platform` flag will pull base images for this platform and
this value will also be the platform of the resulting image. The default value
will be the current platform of the buildkit daemon.
When using `docker-container` driver with `buildx`, this flag can accept multiple
values as an input separated by a comma. With multiple values the result will be
built for all of the specified platforms and joined together into a single manifest
list.
If the `Dockerfile` needs to invoke the `RUN` command, the builder needs runtime
support for the specified platform. In a clean setup, you can only execute `RUN`
commands for your system architecture.
If your kernel supports [`binfmt_misc`](https://en.wikipedia.org/wiki/Binfmt_misc)
launchers for secondary architectures, buildx will pick them up automatically.
Docker desktop releases come with `binfmt_misc` automatically configured for `arm64`
and `arm` architectures. You can see what runtime platforms your current builder
instance supports by running `docker buildx inspect --bootstrap`.
Inside a `Dockerfile`, you can access the current platform value through
`TARGETPLATFORM` build argument. Please refer to the [`docker build`
documentation](https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope)
for the full description of automatic platform argument variants .
The formatting for the platform specifier is defined in the [containerd source
code](https://github.com/containerd/containerd/blob/v1.4.3/platforms/platforms.go#L63).
```console
$ docker buildx build --platform=linux/arm64 .
$ docker buildx build --platform=linux/amd64,linux/arm64,linux/arm/v7 .
$ docker buildx build --platform=darwin .
```
### <a name="progress"></a> Set type of progress output (--progress)
```
--progress=VALUE
```
Set type of progress output (auto, plain, tty). Use plain to show container
output (default "auto").
> You can also use the `BUILDKIT_PROGRESS` environment variable to set
> its value.
The following example uses `plain` output during the build:
```console
$ docker buildx build --load --progress=plain .
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 227B 0.0s done
#1 DONE 0.1s
#2 [internal] load .dockerignore
#2 transferring context: 129B 0.0s done
#2 DONE 0.0s
...
```
### <a name="push"></a> Push the build result to a registry (--push) ### <a name="push"></a> Push the build result to a registry (--push)
Shorthand for [`--output=type=registry`](#registry). Will automatically push the Shorthand for [`--output=type=registry`](#registry). Will automatically push the
build result to registry. build result to registry.
### <a name="load"></a> Load the single-platform build result to `docker images` (--load) ### <a name="secret"></a> Secret to expose to the build (--secret)
Shorthand for [`--output=type=docker`](#docker). Will automatically load the
single-platform build result to `docker images`.
### <a name="cache-from"></a> Use an external cache source for a build (--cache-from)
``` ```
--cache-from=[NAME|type=TYPE[,KEY=VALUE]] --secret=[type=TYPE[,KEY=VALUE]
``` ```
Use an external cache source for a build. Supported types are `registry` and `local`. Exposes secret to the build. The secret can be used by the build using
The `registry` source can import cache from a cache manifest or (special) image [`RUN --mount=type=secret` mount](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#run---mounttypesecret).
configuration on the registry. The `local` source can import cache from local
files previously exported with `--cache-to`.
If no type is specified, `registry` exporter is used with a specified reference. If `type` is unset it will be detected. Supported types are:
`docker` driver currently only supports importing build cache from the registry. #### `file`
**Examples** Attribute keys:
- `id` - ID of the secret. Defaults to basename of the `src` path.
- `src`, `source` - Secret filename. `id` used if unset.
```dockerfile
# syntax=docker/dockerfile:1.3
FROM python:3
RUN pip install awscli
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
aws s3 cp s3://... ...
```
```console ```console
$ docker buildx build --cache-from=user/app:cache . $ docker buildx build --secret id=aws,src=$HOME/.aws/credentials .
$ docker buildx build --cache-from=user/app .
$ docker buildx build --cache-from=type=registry,ref=user/app .
$ docker buildx build --cache-from=type=local,src=path/to/cache .
``` ```
### <a name="cache-to"></a> Export build cache to an external cache destination (--cache-to) #### `env`
Attribute keys:
- `id` - ID of the secret. Defaults to `env` name.
- `env` - Secret environment variable. `id` used if unset, otherwise will look for `src`, `source` if `id` unset.
```dockerfile
# syntax=docker/dockerfile:1.3
FROM node:alpine
RUN --mount=type=bind,target=. \
--mount=type=secret,id=SECRET_TOKEN \
SECRET_TOKEN=$(cat /run/secrets/SECRET_TOKEN) yarn run test
``` ```
--cache-to=[NAME|type=TYPE[,KEY=VALUE]]
```
Export build cache to an external cache destination. Supported types are `registry`,
`local` and `inline`. Registry exports build cache to a cache manifest in the
registry, local exports cache to a local directory on the client and inline writes
the cache metadata into the image configuration.
`docker` driver currently only supports exporting inline cache metadata to image
configuration. Alternatively, `--build-arg BUILDKIT_INLINE_CACHE=1` can be used
to trigger inline cache exporter.
Attribute key:
- `mode` - Specifies how many layers are exported with the cache. “min” on only
exports layers already in the final build stage, “max” exports layers for
all stages. Metadata is always exported for the whole build.
**Examples**
```console ```console
$ docker buildx build --cache-to=user/app:cache . $ SECRET_TOKEN=token docker buildx build --secret id=SECRET_TOKEN .
$ docker buildx build --cache-to=type=inline .
$ docker buildx build --cache-to=type=registry,ref=user/app .
$ docker buildx build --cache-to=type=local,dest=path/to/cache .
``` ```
### <a name="allow"></a> Allow extra privileged entitlement (--allow) ### <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.
### <a name="ssh"></a> SSH agent socket or keys to expose to the build (--ssh)
``` ```
--allow=ENTITLEMENT --ssh=default|<id>[=<socket>|<key>[,<key>]]
``` ```
Allow extra privileged entitlement. List of entitlements: This can be useful when some commands in your Dockerfile need specific SSH
authentication (e.g., cloning a private repository).
- `network.host` - Allows executions with host networking. `--ssh` exposes SSH agent socket or keys to the build and can be used with the
- `security.insecure` - Allows executions without sandbox. See [`RUN --mount=type=ssh` mount](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#run---mounttypessh).
[related Dockerfile extensions](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md#run---securityinsecuresandbox).
For entitlements to be enabled, the `buildkitd` daemon also needs to allow them Example to access Gitlab using an SSH agent socket:
with `--allow-insecure-entitlement` (see [`create --buildkitd-flags`](buildx_create.md#--buildkitd-flags-flags))
**Examples** ```dockerfile
# syntax=docker/dockerfile:1.3
FROM alpine
RUN apk add --no-cache openssh-client
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
RUN --mount=type=ssh ssh -q -T git@gitlab.com 2>&1 | tee /hello
# "Welcome to GitLab, @GITLAB_USERNAME_ASSOCIATED_WITH_SSHKEY" should be printed here
# with the type of build progress is defined as `plain`.
```
```console ```console
$ docker buildx create --use --name insecure-builder --buildkitd-flags '--allow-insecure-entitlement security.insecure' $ eval $(ssh-agent)
$ docker buildx build --allow security.insecure . $ ssh-add ~/.ssh/id_rsa
(Input your passphrase here)
$ docker buildx build --ssh default=$SSH_AUTH_SOCK .
``` ```
### <a name="ulimit"></a> Set ulimits (--ulimit)
`--ulimit` is specified with a soft and hard limit as such:
`<type>=<soft limit>[:<hard limit>]`, for example:
```console
$ docker buildx build --ulimit 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.

View File

@@ -9,19 +9,19 @@ Create a new builder instance
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| [`--append`](#append) | Append a node to builder instead of changing it | | [`--append`](#append) | | | Append a node to builder instead of changing it |
| `--builder string` | Override the configured builder instance | | `--bootstrap` | | | Boot builder after creation |
| [`--buildkitd-flags string`](#buildkitd-flags) | Flags for buildkitd daemon | | [`--buildkitd-flags`](#buildkitd-flags) | `string` | | Flags for buildkitd daemon |
| [`--config string`](#config) | BuildKit config file | | [`--config`](#config) | `string` | | BuildKit config file |
| [`--driver string`](#driver) | Driver to use (available: []) | | [`--driver`](#driver) | `string` | | Driver to use (available: `docker`, `docker-container`, `kubernetes`) |
| [`--driver-opt stringArray`](#driver-opt) | Options for the driver | | [`--driver-opt`](#driver-opt) | `stringArray` | | Options for the driver |
| [`--leave`](#leave) | Remove a node from builder instead of changing it | | [`--leave`](#leave) | | | Remove a node from builder instead of changing it |
| [`--name string`](#name) | Builder instance name | | [`--name`](#name) | `string` | | Builder instance name |
| [`--node string`](#node) | Create/modify node with given name | | [`--node`](#node) | `string` | | Create/modify node with given name |
| [`--platform stringArray`](#platform) | Fixed platforms for current node | | [`--platform`](#platform) | `stringArray` | | Fixed platforms for current node |
| [`--use`](#use) | Set the current builder instance | | [`--use`](#use) | | | Set the current builder instance |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -47,8 +47,6 @@ The `--append` flag changes the action of the command to append a new node to an
existing builder specified by `--name`. Buildx will choose an appropriate node existing builder specified by `--name`. Buildx will choose an appropriate node
for a build based on the platforms it supports. for a build based on the platforms it supports.
**Examples**
```console ```console
$ docker buildx create mycontext1 $ docker buildx create mycontext1
eager_beaver eager_beaver
@@ -64,11 +62,9 @@ eager_beaver
``` ```
Adds flags when starting the buildkitd daemon. They take precedence over the Adds flags when starting the buildkitd daemon. They take precedence over the
configuration file specified by [`--config`](#--config-file). See `buildkitd --help` configuration file specified by [`--config`](#config). See `buildkitd --help`
for the available flags. for the available flags.
**Example**
``` ```
--buildkitd-flags '--debug --debugaddr 0.0.0.0:6666' --buildkitd-flags '--debug --debugaddr 0.0.0.0:6666'
``` ```
@@ -80,9 +76,14 @@ for the available flags.
``` ```
Specifies the configuration file for the buildkitd daemon to use. The configuration Specifies the configuration file for the buildkitd daemon to use. The configuration
can be overridden by [`--buildkitd-flags`](#--buildkitd-flags-flags). 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). See an [example buildkitd configuration file](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md).
Note that if you create a `docker-container` builder and have specified
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="driver"></a> Set the builder driver to use (--driver) ### <a name="driver"></a> Set the builder driver to use (--driver)
``` ```
@@ -92,17 +93,30 @@ See an [example buildkitd configuration file](https://github.com/moby/buildkit/b
Sets the builder driver to be used. There are two available drivers, each have Sets the builder driver to be used. There are two available drivers, each have
their own specificities. their own specificities.
- `docker` - Uses the builder that is built into the docker daemon. With this #### `docker` driver
driver, the [`--load`](buildx_build.md#--load) flag is implied by default on
`buildx build`. However, building multi-platform images or exporting cache is
not currently supported.
- `docker-container` - Uses a buildkit container that will be spawned via docker.
With this driver, both building multi-platform images and exporting cache are
supported. However, images built will not automatically appear in `docker images`
(see [`build --load`](buildx_build.md#--load)).
- `kubernetes` - Uses a kubernetes pods. With this driver, you can spin up pods
with defined buildkit container image to build your images.
Uses the builder that is built into the docker daemon. With this driver,
the [`--load`](buildx_build.md#load) flag is implied by default on
`buildx build`. However, building multi-platform images or exporting cache is
not currently supported.
#### `docker-container` driver
Uses a BuildKit container that will be spawned via docker. With this driver,
both building multi-platform images and exporting cache are supported.
Unlike `docker` driver, built images will not automatically appear in
`docker images` and [`build --load`](buildx_build.md#load) needs to be used
to achieve that.
#### `kubernetes` driver
Uses a kubernetes pods. With this driver, you can spin up pods with defined
BuildKit container image to build your images.
Unlike `docker` driver, built images will not automatically appear in
`docker images` and [`build --load`](buildx_build.md#load) needs to be used
to achieve that.
### <a name="driver-opt"></a> Set additional driver-specific options (--driver-opt) ### <a name="driver-opt"></a> Set additional driver-specific options (--driver-opt)
@@ -114,24 +128,22 @@ Passes additional driver-specific options. Details for each driver:
- `docker` - No driver options - `docker` - No driver options
- `docker-container` - `docker-container`
- `image=IMAGE` - Sets the container image to be used for running buildkit. - `image=IMAGE` - Sets the container image to be used for running buildkit.
- `network=NETMODE` - Sets the network mode for running the buildkit container. - `network=NETMODE` - Sets the network mode for running the buildkit container.
- Example: - `cgroup-parent=CGROUP` - Sets the cgroup parent of the buildkit container if docker is using the "cgroupfs" driver. Defaults to `/docker/buildx`.
```console
--driver docker-container --driver-opt image=moby/buildkit:master,network=host
```
- `kubernetes` - `kubernetes`
- `image=IMAGE` - Sets the container image to be used for running buildkit. - `image=IMAGE` - Sets the container image to be used for running buildkit.
- `namespace=NS` - Sets the Kubernetes namespace. Defaults to the current namespace. - `namespace=NS` - Sets the Kubernetes namespace. Defaults to the current namespace.
- `replicas=N` - Sets the number of `Pod` replicas. Defaults to 1. - `replicas=N` - Sets the number of `Pod` replicas. Defaults to 1.
- `requests.cpu` - Sets the request CPU value specified in units of Kubernetes CPU. Example `requests.cpu=100m`, `requests.cpu=2` - `requests.cpu` - Sets the request CPU value specified in units of Kubernetes CPU. Example `requests.cpu=100m`, `requests.cpu=2`
- `requests.memory` - Sets the request memory value specified in bytes or with a valid suffix. Example `requests.memory=500Mi`, `requests.memory=4G` - `requests.memory` - Sets the request memory value specified in bytes or with a valid suffix. Example `requests.memory=500Mi`, `requests.memory=4G`
- `limits.cpu` - Sets the limit CPU value specified in units of Kubernetes CPU. Example `limits.cpu=100m`, `limits.cpu=2` - `limits.cpu` - Sets the limit CPU value specified in units of Kubernetes CPU. Example `limits.cpu=100m`, `limits.cpu=2`
- `limits.memory` - Sets the limit memory value specified in bytes or with a valid suffix. Example `limits.memory=500Mi`, `limits.memory=4G` - `limits.memory` - Sets the limit memory value specified in bytes or with a valid suffix. Example `limits.memory=500Mi`, `limits.memory=4G`
- `nodeselector="label1=value1,label2=value2"` - Sets the kv of `Pod` nodeSelector. No Defaults. Example `nodeselector=kubernetes.io/arch=arm64` - `nodeselector="label1=value1,label2=value2"` - Sets the kv of `Pod` nodeSelector. No Defaults. Example `nodeselector=kubernetes.io/arch=arm64`
- `rootless=(true|false)` - Run the container as a non-root user without `securityContext.privileged`. [Using Ubuntu host kernel is recommended](https://github.com/moby/buildkit/blob/master/docs/rootless.md). Defaults to false. - `rootless=(true|false)` - Run the container as a non-root user without `securityContext.privileged`. [Using Ubuntu host kernel is recommended](https://github.com/moby/buildkit/blob/master/docs/rootless.md). Defaults to false.
- `loadbalance=(sticky|random)` - Load-balancing strategy. If set to "sticky", the pod is chosen using the hash of the context path. Defaults to "sticky" - `loadbalance=(sticky|random)` - Load-balancing strategy. If set to "sticky", the pod is chosen using the hash of the context path. Defaults to "sticky"
- `qemu.install=(true|false)` - Install QEMU emulation for multi platforms support.
- `qemu.image=IMAGE` - Sets the QEMU emulation image. Defaults to `tonistiigi/binfmt:latest`
### <a name="leave"></a> Remove a node from a builder (--leave) ### <a name="leave"></a> Remove a node from a builder (--leave)
@@ -139,8 +151,6 @@ The `--leave` flag changes the action of the command to remove a node from a
builder. The builder needs to be specified with `--name` and node that is removed builder. The builder needs to be specified with `--name` and node that is removed
is set with `--node`. is set with `--node`.
**Examples**
```console ```console
$ docker buildx create --name mybuilder --node mybuilder0 --leave $ docker buildx create --name mybuilder --node mybuilder0 --leave
``` ```
@@ -164,7 +174,7 @@ The `--node` flag specifies the name of the node to be created or modified. If
none is specified, it is the name of the builder it belongs to, with an index none is specified, it is the name of the builder it belongs to, with an index
number suffix. number suffix.
### <a name="platform"></a> Set the platforms supported by the node ### <a name="platform"></a> Set the platforms supported by the node (--platform)
``` ```
--platform PLATFORMS --platform PLATFORMS
@@ -176,14 +186,12 @@ will also automatically detect the platforms it supports, but manual values take
priority over the detected ones and can be used when multiple nodes support priority over the detected ones and can be used when multiple nodes support
building for the same platform. building for the same platform.
**Examples**
```console ```console
$ docker buildx create --platform linux/amd64 $ docker buildx create --platform linux/amd64
$ docker buildx create --platform linux/arm64,linux/arm/v8 $ docker buildx create --platform linux/arm64,linux/arm/v8
``` ```
### <a name="use"></a> Automatically switch to the newly created builder ### <a name="use"></a> Automatically switch to the newly created builder (--use)
The `--use` flag automatically switches the current builder to the newly created The `--use` flag automatically switches the current builder to the newly created
one. Equivalent to running `docker buildx use $(docker buildx create ...)`. one. Equivalent to running `docker buildx use $(docker buildx create ...)`.

View File

@@ -9,11 +9,17 @@ Disk usage
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| `--builder string` | Override the configured builder instance | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `--filter filter` | Provide filter values | | `--filter` | `filter` | | Provide filter values |
| `--verbose` | Provide a more verbose output | | `--verbose` | | | Provide a more verbose output |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).

View File

@@ -12,9 +12,15 @@ Commands to work on images in registry
| Name | Description | | Name | Description |
| --- | --- | | --- | --- |
| [`create`](buildx_imagetools_create.md) | Create a new image based on source images | | [`create`](buildx_imagetools_create.md) | Create a new image based on source images |
| [`inspect`](buildx_imagetools_inspect.md) | Show details of image in the registry | | [`inspect`](buildx_imagetools_inspect.md) | Show details of an image in the registry |
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -22,3 +28,9 @@ Commands to work on images in registry
Imagetools contains commands for working with manifest lists in the registry. Imagetools contains commands for working with manifest lists in the registry.
These commands are useful for inspecting multi-platform build results. These commands are useful for inspecting multi-platform build results.
## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).

View File

@@ -9,22 +9,19 @@ Create a new image based on source images
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| [`--append`](#append) | Append to existing manifest | | [`--append`](#append) | | | Append to existing manifest |
| `--builder string` | Override the configured builder instance | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--dry-run`](#dry-run) | Show final image instead of pushing | | [`--dry-run`](#dry-run) | | | Show final image instead of pushing |
| [`-f`](#file), [`--file stringArray`](#file) | Read source descriptor from file | | [`-f`](#file), [`--file`](#file) | `stringArray` | | Read source descriptor from file |
| [`-t`](#tag), [`--tag stringArray`](#tag) | Set reference for new image | | [`-t`](#tag), [`--tag`](#tag) | `stringArray` | | Set reference for new image |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
## Description ## Description
Imagetools contains commands for working with manifest lists in the registry.
These commands are useful for inspecting multi-platform build results.
Create a new manifest list based on source manifests. The source manifests can Create a new manifest list based on source manifests. The source manifests can
be manifest lists or single platform distribution manifests and must already be manifest lists or single platform distribution manifests and must already
exist in the registry where the new manifest is created. If only one source is exist in the registry where the new manifest is created. If only one source is
@@ -37,6 +34,10 @@ specified, create performs a carbon copy.
Use the `--append` flag to append the new sources to an existing manifest list Use the `--append` flag to append the new sources to an existing manifest list
in the destination. in the destination.
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).
### <a name="dry-run"></a> Show final image instead of pushing (--dry-run) ### <a name="dry-run"></a> Show final image instead of pushing (--dry-run)
Use the `--dry-run` flag to not push the image, just show it. Use the `--dry-run` flag to not push the image, just show it.
@@ -53,16 +54,15 @@ or a JSON of OCI descriptor object.
In order to define annotations or additional platform properties like `os.version` and In order to define annotations or additional platform properties like `os.version` and
`os.features` you need to add them in the OCI descriptor object encoded in JSON. `os.features` you need to add them in the OCI descriptor object encoded in JSON.
``` ```console
docker buildx imagetools inspect --raw alpine | jq '.manifests[0] | .platform."os.version"="10.1"' > descr.json $ docker buildx imagetools inspect --raw alpine | jq '.manifests[0] | .platform."os.version"="10.1"' > descr.json
docker buildx imagetools create -f descr.json myuser/image $ docker buildx imagetools create -f descr.json myuser/image
``` ```
The descriptor in the file is merged with existing descriptor in the registry if it exists. The descriptor in the file is merged with existing descriptor in the registry if it exists.
The supported fields for the descriptor are defined in [OCI spec](https://github.com/opencontainers/image-spec/blob/master/descriptor.md#properties) . The supported fields for the descriptor are defined in [OCI spec](https://github.com/opencontainers/image-spec/blob/master/descriptor.md#properties) .
### <a name="tag"></a> Set reference for new image (-t, --tag) ### <a name="tag"></a> Set reference for new image (-t, --tag)
``` ```
@@ -71,10 +71,7 @@ The supported fields for the descriptor are defined in [OCI spec](https://github
Use the `-t` or `--tag` flag to set the name of the image to be created. Use the `-t` or `--tag` flag to set the name of the image to be created.
**Examples**
```console ```console
$ docker buildx imagetools create --dry-run alpine@sha256:5c40b3c27b9f13c873fefb2139765c56ce97fd50230f1f2d5c91e55dec171907 sha256:c4ba6347b0e4258ce6a6de2401619316f982b7bcc529f73d2a410d0097730204 $ docker buildx imagetools create --dry-run alpine@sha256:5c40b3c27b9f13c873fefb2139765c56ce97fd50230f1f2d5c91e55dec171907 sha256:c4ba6347b0e4258ce6a6de2401619316f982b7bcc529f73d2a410d0097730204
$ docker buildx imagetools create -t tonistiigi/myapp -f image1 -f image2 $ docker buildx imagetools create -t tonistiigi/myapp -f image1 -f image2
``` ```

View File

@@ -5,43 +5,628 @@ docker buildx imagetools inspect [OPTIONS] NAME
``` ```
<!---MARKER_GEN_START--> <!---MARKER_GEN_START-->
Show details of image in the registry Show details of an image in the registry
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| `--builder string` | Override the configured builder instance | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--raw`](#raw) | Show original JSON manifest | | [`--format`](#format) | `string` | `{{.Manifest}}` | Format the output using the given Go template |
| [`--raw`](#raw) | | | Show original, unformatted JSON manifest |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
## Description ## Description
Show details of image in the registry. Show details of an image in the registry.
Example:
```console ```console
$ docker buildx imagetools inspect alpine $ docker buildx imagetools inspect alpine
Name: docker.io/library/alpine:latest Name: docker.io/library/alpine:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913 Digest: sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300
Manifests: Manifests:
Name: docker.io/library/alpine:latest@sha256:5c40b3c27b9f13c873fefb2139765c56ce97fd50230f1f2d5c91e55dec171907 Name: docker.io/library/alpine:latest@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3
MediaType: application/vnd.docker.distribution.manifest.v2+json MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64 Platform: linux/amd64
Name: docker.io/library/alpine:latest@sha256:c4ba6347b0e4258ce6a6de2401619316f982b7bcc529f73d2a410d0097730204 Name: docker.io/library/alpine:latest@sha256:e047bc2af17934d38c5a7fa9f46d443f1de3a7675546402592ef805cfa929f9d
MediaType: application/vnd.docker.distribution.manifest.v2+json MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm/v6 Platform: linux/arm/v6
...
Name: docker.io/library/alpine:latest@sha256:8483ecd016885d8dba70426fda133c30466f661bb041490d525658f1aac73822
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm/v7
Name: docker.io/library/alpine:latest@sha256:c74f1b1166784193ea6c8f9440263b9be6cae07dfe35e32a5df7a31358ac2060
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm64/v8
Name: docker.io/library/alpine:latest@sha256:2689e157117d2da668ad4699549e55eba1ceb79cb7862368b30919f0488213f4
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/386
Name: docker.io/library/alpine:latest@sha256:2042a492bcdd847a01cd7f119cd48caa180da696ed2aedd085001a78664407d6
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/ppc64le
Name: docker.io/library/alpine:latest@sha256:49e322ab6690e73a4909f787bcbdb873631264ff4a108cddfd9f9c249ba1d58e
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/s390x
```
## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).
### <a name="format"></a> Format the output (--format)
Format the output using the given Go template. Defaults to `{{.Manifest}}` if
unset. Following fields are available:
* `.Name`: provides the reference of the image
* `.Manifest`: provides the manifest or manifest list
* `.Image`: provides the image config
* `.BuildInfo`: provides [build info from image config](https://github.com/moby/buildkit/blob/master/docs/build-repro.md#image-config)
#### `.Name`
```console
$ docker buildx imagetools inspect alpine --format "{{.Name}}"
Name: docker.io/library/alpine:latest
```
#### `.Manifest`
```console
$ docker buildx imagetools inspect crazymax/loop --format "{{.Manifest}}"
Name: docker.io/crazymax/loop:latest
MediaType: application/vnd.docker.distribution.manifest.v2+json
Digest: sha256:08602e7340970e92bde5e0a2e887c1fde4d9ae753d1e05efb4c8ef3b609f97f1
```
```console
$ docker buildx imagetools inspect moby/buildkit:master --format "{{.Manifest}}"
Name: docker.io/moby/buildkit:master
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:3183f7ce54d1efb44c34b84f428ae10aaf141e553c6b52a7ff44cc7083a05a66
Manifests:
Name: docker.io/moby/buildkit:master@sha256:667d28c9fb33820ce686887a717a148e89fa77f9097f9352996bbcce99d352b1
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64
Name: docker.io/moby/buildkit:master@sha256:71789527b64ab3d7b3de01d364b449cd7f7a3da758218fbf73b9c9aae05a6775
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm/v7
Name: docker.io/moby/buildkit:master@sha256:fb64667e1ce6ab0d05478f3a8402af07b27737598dcf9a510fb1d792b13a66be
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm64
Name: docker.io/moby/buildkit:master@sha256:1c3ddf95a0788e23f72f25800c05abc4458946685e2b66788c3d978cde6da92b
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/s390x
Name: docker.io/moby/buildkit:master@sha256:05bcde6d460a284e5bc88026cd070277e8380355de3126cbc8fe8a452708c6b1
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/ppc64le
Name: docker.io/moby/buildkit:master@sha256:c04c57765304ab84f4f9807fff3e11605c3a60e16435c734b02c723680f6bd6e
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/riscv64
```
#### `.BuildInfo`
```console
$ docker buildx imagetools inspect crazymax/buildx:buildinfo --format "{{.BuildInfo}}"
Name: docker.io/crazymax/buildx:buildinfo
Frontend: dockerfile.v0
Attrs:
filename: Dockerfile
source: docker/dockerfile-upstream:master-labs
build-arg:bar: foo
build-arg:foo: bar
Sources:
Type: docker-image
Ref: docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0
Pin: sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0
Type: docker-image
Ref: docker.io/library/alpine:3.13
Pin: sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c
Type: docker-image
Ref: docker.io/moby/buildkit:v0.9.0
Pin: sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab
Type: docker-image
Ref: docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04
Pin: sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04
Type: http
Ref: https://raw.githubusercontent.com/moby/moby/master/README.md
Pin: sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c
```
#### JSON output
A `json` go template func is also available if you want to render fields as
JSON bytes:
```console
$ docker buildx imagetools inspect crazymax/loop --format "{{json .Manifest}}"
```
```json
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:08602e7340970e92bde5e0a2e887c1fde4d9ae753d1e05efb4c8ef3b609f97f1",
"size": 949
}
```
```console
$ docker buildx imagetools inspect moby/buildkit:master --format "{{json .Manifest}}"
```
```json
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"digest": "sha256:79d97f205e2799d99a3a8ae2a1ef17acb331e11784262c3faada847dc6972c52",
"size": 2010,
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:bd1e78f06de26610fadf4eb9d04b1a45a545799d6342701726e952cc0c11c912",
"size": 1158,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:d37dcced63ec0965824fca644f0ac9efad8569434ec15b4c83adfcb3dcfc743b",
"size": 1158,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:ce142eb2255e6af46f2809e159fd03081697c7605a3de03b9cbe9a52ddb244bf",
"size": 1158,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:f59bfb5062fff76ce464bfa4e25ebaaaac887d6818238e119d68613c456d360c",
"size": 1158,
"platform": {
"architecture": "s390x",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:cc96426e0c50a78105d5637d31356db5dd6ec594f21b24276e534a32da09645c",
"size": 1159,
"platform": {
"architecture": "ppc64le",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:39f9c1e2878e6c333acb23187d6b205ce82ed934c60da326cb2c698192631478",
"size": 1158,
"platform": {
"architecture": "riscv64",
"os": "linux"
}
}
]
}
```
```console
$ docker buildx imagetools inspect crazymax/buildx:buildinfo --format "{{json .BuildInfo}}"
```
```json
{
"frontend": "dockerfile.v0",
"attrs": {
"build-arg:bar": "foo",
"build-arg:foo": "bar",
"filename": "Dockerfile",
"source": "crazymax/dockerfile:buildattrs"
},
"sources": [
{
"type": "docker-image",
"ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0",
"pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0"
},
{
"type": "docker-image",
"ref": "docker.io/library/alpine:3.13@sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c",
"pin": "sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c"
},
{
"type": "docker-image",
"ref": "docker.io/moby/buildkit:v0.9.0@sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab",
"pin": "sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab"
},
{
"type": "docker-image",
"ref": "docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04",
"pin": "sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04"
},
{
"type": "http",
"ref": "https://raw.githubusercontent.com/moby/moby/master/README.md",
"pin": "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c"
}
]
}
```
```console
$ docker buildx imagetools inspect crazymax/buildx:buildinfo --format "{{json .}}"
```
```json
{
"name": "crazymax/buildx:buildinfo",
"manifest": {
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:899d2c7acbc124d406820857bb51d9089717bbe4e22b97eb4bc5789e99f09f83",
"size": 2628
},
"image": {
"created": "2022-02-24T12:27:43.627154558Z",
"architecture": "amd64",
"os": "linux",
"config": {
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"DOCKER_TLS_CERTDIR=/certs",
"DOCKER_CLI_EXPERIMENTAL=enabled"
],
"Entrypoint": [
"docker-entrypoint.sh"
],
"Cmd": [
"sh"
]
},
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:7fcb75871b2101082203959c83514ac8a9f4ecfee77a0fe9aa73bbe56afdf1b4",
"sha256:d3c0b963ff5684160641f936d6a4aa14efc8ff27b6edac255c07f2d03ff92e82",
"sha256:3f8d78f13fa9b1f35d3bc3f1351d03a027c38018c37baca73f93eecdea17f244",
"sha256:8e6eb1137b182ae0c3f5d40ca46341fda2eaeeeb5fa516a9a2bf96171238e2e0",
"sha256:fde4c869a56b54dd76d7352ddaa813fd96202bda30b9dceb2c2f2ad22fa2e6ce",
"sha256:52025823edb284321af7846419899234b3c66219bf06061692b709875ed0760f",
"sha256:50adb5982dbf6126c7cf279ac3181d1e39fc9116b610b947a3dadae6f7e7c5bc",
"sha256:9801c319e1c66c5d295e78b2d3e80547e73c7e3c63a4b71e97c8ca357224af24",
"sha256:dfbfac44d5d228c49b42194c8a2f470abd6916d072f612a6fb14318e94fde8ae",
"sha256:3dfb74e19dedf61568b917c19b0fd3ee4580870027ca0b6054baf239855d1322",
"sha256:b182e707c23e4f19be73f9022a99d2d1ca7bf1ca8f280d40e4d1c10a6f51550e"
]
},
"history": [
{
"created": "2021-11-12T17:19:58.698676655Z",
"created_by": "/bin/sh -c #(nop) ADD file:5a707b9d6cb5fff532e4c2141bc35707593f21da5528c9e71ae2ddb6ba4a4eb6 in / "
},
{
"created": "2021-11-12T17:19:58.948920855Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
},
{
"created": "2022-02-24T12:27:38.285594601Z",
"created_by": "RUN /bin/sh -c apk --update --no-cache add bash ca-certificates openssh-client \u0026\u0026 rm -rf /tmp/* /var/cache/apk/* # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:41.061874167Z",
"created_by": "COPY /opt/docker/ /usr/local/bin/ # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:41.174098947Z",
"created_by": "COPY /usr/bin/buildctl /usr/local/bin/buildctl # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:41.320343683Z",
"created_by": "COPY /usr/bin/buildkit* /usr/local/bin/ # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:41.447149933Z",
"created_by": "COPY /buildx /usr/libexec/docker/cli-plugins/docker-buildx # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:43.057722191Z",
"created_by": "COPY /opt/docker-compose /usr/libexec/docker/cli-plugins/docker-compose # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:43.145224134Z",
"created_by": "ADD https://raw.githubusercontent.com/moby/moby/master/README.md / # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:43.422212427Z",
"created_by": "ENV DOCKER_TLS_CERTDIR=/certs",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
},
{
"created": "2022-02-24T12:27:43.422212427Z",
"created_by": "ENV DOCKER_CLI_EXPERIMENTAL=enabled",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
},
{
"created": "2022-02-24T12:27:43.422212427Z",
"created_by": "RUN /bin/sh -c docker --version \u0026\u0026 buildkitd --version \u0026\u0026 buildctl --version \u0026\u0026 docker buildx version \u0026\u0026 docker compose version \u0026\u0026 mkdir /certs /certs/client \u0026\u0026 chmod 1777 /certs /certs/client # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:43.514320155Z",
"created_by": "COPY rootfs/modprobe.sh /usr/local/bin/modprobe # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:43.627154558Z",
"created_by": "COPY rootfs/docker-entrypoint.sh /usr/local/bin/ # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T12:27:43.627154558Z",
"created_by": "ENTRYPOINT [\"docker-entrypoint.sh\"]",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
},
{
"created": "2022-02-24T12:27:43.627154558Z",
"created_by": "CMD [\"sh\"]",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
}
]
},
"buildinfo": {
"frontend": "dockerfile.v0",
"attrs": {
"build-arg:bar": "foo",
"build-arg:foo": "bar",
"filename": "Dockerfile",
"source": "docker/dockerfile-upstream:master-labs"
},
"sources": [
{
"type": "docker-image",
"ref": "docker.io/docker/buildx-bin:0.6.1@sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0",
"pin": "sha256:a652ced4a4141977c7daaed0a074dcd9844a78d7d2615465b12f433ae6dd29f0"
},
{
"type": "docker-image",
"ref": "docker.io/library/alpine:3.13",
"pin": "sha256:026f721af4cf2843e07bba648e158fb35ecc876d822130633cc49f707f0fc88c"
},
{
"type": "docker-image",
"ref": "docker.io/moby/buildkit:v0.9.0",
"pin": "sha256:8dc668e7f66db1c044aadbed306020743516a94848793e0f81f94a087ee78cab"
},
{
"type": "docker-image",
"ref": "docker.io/tonistiigi/xx@sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04",
"pin": "sha256:21a61be4744f6531cb5f33b0e6f40ede41fa3a1b8c82d5946178f80cc84bfc04"
},
{
"type": "http",
"ref": "https://raw.githubusercontent.com/moby/moby/master/README.md",
"pin": "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c"
}
]
}
}
```
#### Multi-platform
Multi-platform images are supported for `.Image` and `.BuildInfo` fields. If
you want to pick up a specific platform, you can specify it using the `index`
go template function:
```console
$ docker buildx imagetools inspect --format '{{json (index .Image "linux/s390x")}}' moby/buildkit:master
```
```json
{
"created": "2022-02-25T17:13:27.89891722Z",
"architecture": "s390x",
"os": "linux",
"config": {
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Entrypoint": [
"buildkitd"
],
"Volumes": {
"/var/lib/buildkit": {}
}
},
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:41048e32d0684349141cf05f629c5fc3c5915d1f3426b66dbb8953a540e01e1e",
"sha256:2651209b9208fff6c053bc3c17353cb07874e50f1a9bc96d6afd03aef63de76a",
"sha256:6741ed7e73039d853fa8902246a4c7e8bf9dd09652fd1b08251bc5f9e8876a7f",
"sha256:92ac046adeeb65c86ae3f0b458dee04ad4a462e417661c04d77642c66494f69b"
]
},
"history": [
{
"created": "2021-11-24T20:41:23.709681315Z",
"created_by": "/bin/sh -c #(nop) ADD file:cd24c711a2ef431b3ff94f9a02bfc42f159bc60de1d0eceecafea4e8af02441d in / "
},
{
"created": "2021-11-24T20:41:23.94211262Z",
"created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
"empty_layer": true
},
{
"created": "2022-01-26T18:15:21.449825391Z",
"created_by": "RUN /bin/sh -c apk add --no-cache fuse3 git openssh pigz xz \u0026\u0026 ln -s fusermount3 /usr/bin/fusermount # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-24T00:34:00.924540012Z",
"created_by": "COPY examples/buildctl-daemonless/buildctl-daemonless.sh /usr/bin/ # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-25T17:13:27.89891722Z",
"created_by": "VOLUME [/var/lib/buildkit]",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
},
{
"created": "2022-02-25T17:13:27.89891722Z",
"created_by": "COPY / /usr/bin/ # buildkit",
"comment": "buildkit.dockerfile.v0"
},
{
"created": "2022-02-25T17:13:27.89891722Z",
"created_by": "ENTRYPOINT [\"buildkitd\"]",
"comment": "buildkit.dockerfile.v0",
"empty_layer": true
}
]
}
``` ```
### <a name="raw"></a> Show original, unformatted JSON manifest (--raw) ### <a name="raw"></a> Show original, unformatted JSON manifest (--raw)
Use the `--raw` option to print the original JSON bytes instead of the formatted Use the `--raw` option to print the unformatted JSON manifest bytes.
output.
> `jq` is used here to get a better rendering of the output result.
```console
$ docker buildx imagetools inspect --raw crazymax/loop | jq
```
```json
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"digest": "sha256:7ace7d324e79b360b2db8b820d83081863d96d22e734cdf297a8e7fd83f6ceb3",
"size": 2298
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:5843afab387455b37944e709ee8c78d7520df80f8d01cf7f861aae63beeddb6b",
"size": 2811478
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:726d3732a87e1c430d67e8969de6b222a889d45e045ebae1a008a37ba38f3b1f",
"size": 1776812
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:5d7cf9b33148a8f220c84f27dd2cfae46aca019a3ea3fbf7274f6d6dbfae8f3b",
"size": 382855
}
]
}
```
```console
$ docker buildx imagetools inspect --raw moby/buildkit:master | jq
```
```json
{
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:667d28c9fb33820ce686887a717a148e89fa77f9097f9352996bbcce99d352b1",
"size": 1158,
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:71789527b64ab3d7b3de01d364b449cd7f7a3da758218fbf73b9c9aae05a6775",
"size": 1158,
"platform": {
"architecture": "arm",
"os": "linux",
"variant": "v7"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:fb64667e1ce6ab0d05478f3a8402af07b27737598dcf9a510fb1d792b13a66be",
"size": 1158,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:1c3ddf95a0788e23f72f25800c05abc4458946685e2b66788c3d978cde6da92b",
"size": 1158,
"platform": {
"architecture": "s390x",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:05bcde6d460a284e5bc88026cd070277e8380355de3126cbc8fe8a452708c6b1",
"size": 1159,
"platform": {
"architecture": "ppc64le",
"os": "linux"
}
},
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"digest": "sha256:c04c57765304ab84f4f9807fff3e11605c3a60e16435c734b02c723680f6bd6e",
"size": 1158,
"platform": {
"architecture": "riscv64",
"os": "linux"
}
}
]
}
```

View File

@@ -9,10 +9,10 @@ Inspect current builder instance
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| [`--bootstrap`](#bootstrap) | Ensure builder has booted before inspecting | | [`--bootstrap`](#bootstrap) | | | Ensure builder has booted before inspecting |
| `--builder string` | Override the configured builder instance | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -23,6 +23,19 @@ Shows information about the current or specified builder.
## Examples ## Examples
### <a name="bootstrap"></a> Ensure that the builder is running before inspecting (--bootstrap)
Use the `--bootstrap` option to ensure that the builder is running before
inspecting it. If the driver is `docker-container`, then `--bootstrap` starts
the buildkit container and waits until it is operational. Bootstrapping is
automatically done during build, and therefore not necessary. The same BuildKit
container is used during the lifetime of the associated builder node (as
displayed in `buildx ls`).
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).
### Get information about a builder instance ### Get information about a builder instance
By default, `inspect` shows information about the current builder. Specify the By default, `inspect` shows information about the current builder. Specify the
@@ -47,12 +60,3 @@ Endpoint: ssh://ubuntu@1.2.3.4
Status: running Status: running
Platforms: linux/arm64, linux/arm/v7, linux/arm/v6 Platforms: linux/arm64, linux/arm/v7, linux/arm/v6
``` ```
### <a name="bootstrap"></a> Ensure that the builder is running before inspecting (--bootstrap)
Use the `--bootstrap` option to ensure that the builder is running before
inspecting it. If the driver is `docker-container`, then `--bootstrap` starts
the buildkit container and waits until it is operational. Bootstrapping is
automatically done during build, and therefore not necessary. The same BuildKit
container is used during the lifetime of the associated builder node (as
displayed in `buildx ls`).

View File

@@ -14,18 +14,17 @@ List builder instances
Lists all builder instances and the nodes for each instance Lists all builder instances and the nodes for each instance
**Example**
```console ```console
$ docker buildx ls $ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
elated_tesla * docker-container elated_tesla * docker-container
elated_tesla0 unix:///var/run/docker.sock running linux/amd64 elated_tesla0 unix:///var/run/docker.sock running linux/amd64
elated_tesla1 ssh://ubuntu@1.2.3.4 running linux/arm64, linux/arm/v7, linux/arm/v6 elated_tesla1 ssh://ubuntu@1.2.3.4 running linux/arm64*, linux/arm/v7, linux/arm/v6
default docker default docker
default default running linux/amd64 default default running linux/amd64
``` ```
Each builder has one or more nodes associated with it. The current builder's Each builder has one or more nodes associated with it. The current builder's
name is marked with a `*`. 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.

View File

@@ -9,15 +9,20 @@ Remove build cache
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| `-a`, `--all` | Remove all unused images, not just dangling ones | | `-a`, `--all` | | | Remove all unused images, not just dangling ones |
| `--builder string` | Override the configured builder instance | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `--filter filter` | Provide filter values (e.g. 'until=24h') | | `--filter` | `filter` | | Provide filter values (e.g., `until=24h`) |
| `-f`, `--force` | Do not prompt for confirmation | | `-f`, `--force` | | | Do not prompt for confirmation |
| `--keep-storage bytes` | Amount of disk space to keep for cache | | `--keep-storage` | `bytes` | `0` | Amount of disk space to keep for cache |
| `--verbose` | Provide a more verbose output | | `--verbose` | | | Provide a more verbose output |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).

View File

@@ -9,10 +9,13 @@ Remove a builder instance
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| `--builder string` | Override the configured builder instance | | [`--all-inactive`](#all-inactive) | | | Remove all inactive builders |
| [`--keep-state`](#keep-state) | Keep BuildKit state | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-f`](#force), [`--force`](#force) | | | Do not prompt for confirmation |
| [`--keep-daemon`](#keep-daemon) | | | Keep the buildkitd daemon running |
| [`--keep-state`](#keep-state) | | | Keep BuildKit state |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -24,6 +27,32 @@ default builder.
## Examples ## Examples
### <a name="all-inactive"></a> Remove all inactive builders (--all-inactive)
Remove builders that are not in running state.
```console
$ docker buildx rm --all-inactive
WARNING! This will remove all builders that are not in running state. Are you sure you want to continue? [y/N] y
```
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).
### <a name="force"></a> Do not prompt for confirmation (--force)
Do not prompt for confirmation before removing inactive builders.
```console
$ docker buildx rm --all-inactive --force
```
### <a name="keep-daemon"></a> Keep the buildkitd daemon running (--keep-daemon)
Keep the buildkitd daemon running after the buildx context is removed. This is useful when you manage buildkitd daemons and buildx contexts independently.
Currently, only supported by the [`docker-container` and `kubernetes` drivers](buildx_create.md#driver).
### <a name="keep-state"></a> Keep BuildKit state (--keep-state) ### <a name="keep-state"></a> Keep BuildKit state (--keep-state)
Keep BuildKit state, so it can be reused by a new builder with the same name. Keep BuildKit state, so it can be reused by a new builder with the same name.

View File

@@ -7,6 +7,12 @@ docker buildx stop [NAME]
<!---MARKER_GEN_START--> <!---MARKER_GEN_START-->
Stop builder instance Stop builder instance
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -14,3 +20,9 @@ Stop builder instance
Stops the specified or current builder. This will not prevent buildx build to Stops the specified or current builder. This will not prevent buildx build to
restart the builder. The implementation of stop depends on the driver. restart the builder. The implementation of stop depends on the driver.
## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).

View File

@@ -9,11 +9,11 @@ Set the current builder instance
### Options ### Options
| Name | Description | | Name | Type | Default | Description |
| --- | --- | | --- | --- | --- | --- |
| `--builder string` | Override the configured builder instance | | [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `--default` | Set builder as default for current context | | `--default` | | | Set builder as default for current context |
| `--global` | Builder persists context changes | | `--global` | | | Builder persists context changes |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
@@ -23,3 +23,9 @@ Set the current builder instance
Switches the current builder instance. Build commands invoked after this command Switches the current builder instance. Build commands invoked after this command
will run on a specified builder. Alternatively, a context name can be used to will run on a specified builder. Alternatively, a context name can be used to
switch to the default builder of that context. switch to the default builder of that context.
## Examples
### <a name="builder"></a> Override the configured builder instance (--builder)
Same as [`buildx --builder`](buildx.md#builder).

View File

@@ -10,10 +10,9 @@ Show buildx version information
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->
## Examples ## Description
### View version information
View version information
```console ```console
$ docker buildx version $ docker buildx version

View File

@@ -2,5 +2,6 @@ package bkimage
const ( const (
DefaultImage = "moby/buildkit:buildx-stable-1" // TODO: make this verified DefaultImage = "moby/buildkit:buildx-stable-1" // TODO: make this verified
QemuImage = "tonistiigi/binfmt:latest" // TODO: make this verified
DefaultRootlessImage = DefaultImage + "-rootless" DefaultRootlessImage = DefaultImage + "-rootless"
) )

View File

@@ -1,17 +1,19 @@
package docker package docker
import ( import (
"archive/tar"
"bytes" "bytes"
"context" "context"
"io" "io"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
"path"
"path/filepath"
"time" "time"
"github.com/docker/buildx/driver" "github.com/docker/buildx/driver"
"github.com/docker/buildx/driver/bkimage" "github.com/docker/buildx/driver/bkimage"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/imagetools" "github.com/docker/buildx/util/imagetools"
"github.com/docker/buildx/util/progress" "github.com/docker/buildx/util/progress"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
@@ -20,6 +22,8 @@ import (
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/network"
dockerclient "github.com/docker/docker/client" dockerclient "github.com/docker/docker/client"
dockerarchive "github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/stdcopy"
"github.com/moby/buildkit/client" "github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/tracing/detect" "github.com/moby/buildkit/util/tracing/detect"
@@ -28,20 +32,16 @@ import (
const ( const (
volumeStateSuffix = "_state" volumeStateSuffix = "_state"
// containerStateDir is the location where buildkitd inside the container
// stores its state. The container driver creates a Linux container, so
// this should match the location for Linux, as defined in:
// https://github.com/moby/buildkit/blob/v0.9.0/util/appdefaults/appdefaults_unix.go#L11-L15
containerBuildKitRootDir = "/var/lib/buildkit"
) )
type Driver struct { type Driver struct {
driver.InitConfig driver.InitConfig
factory driver.Factory factory driver.Factory
netMode string userNSRemap bool // true if dockerd is running with userns-remap mode
image string netMode string
env []string image string
cgroupParent string
env []string
} }
func (d *Driver) IsMobyDriver() bool { func (d *Driver) IsMobyDriver() bool {
@@ -113,30 +113,34 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
if err := l.Wrap("creating container "+d.Name, func() error { if err := l.Wrap("creating container "+d.Name, func() error {
hc := &container.HostConfig{ hc := &container.HostConfig{
Privileged: true, Privileged: true,
UsernsMode: "host",
Mounts: []mount.Mount{ Mounts: []mount.Mount{
{ {
Type: mount.TypeVolume, Type: mount.TypeVolume,
Source: d.Name + volumeStateSuffix, Source: d.Name + volumeStateSuffix,
Target: containerBuildKitRootDir, Target: confutil.DefaultBuildKitStateDir,
}, },
}, },
} }
if d.userNSRemap {
hc.UsernsMode = "host"
}
if d.netMode != "" { if d.netMode != "" {
hc.NetworkMode = container.NetworkMode(d.netMode) hc.NetworkMode = container.NetworkMode(d.netMode)
} }
if info, err := d.DockerAPI.Info(ctx); err == nil && info.CgroupDriver == "cgroupfs" {
// Place all buildkit containers inside this cgroup by default so limits can be attached
// to all build activity on the host.
hc.CgroupParent = "/docker/buildx"
if d.cgroupParent != "" {
hc.CgroupParent = d.cgroupParent
}
}
_, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, nil, d.Name) _, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, nil, d.Name)
if err != nil { if err != nil {
return err return err
} }
if f := d.InitConfig.ConfigFile; f != "" { if err := d.copyToContainer(ctx, d.InitConfig.Files); err != nil {
buf, err := readFileToTar(f) return err
if err != nil {
return err
}
if err := d.DockerAPI.CopyToContainer(ctx, d.Name, "/", buf, dockertypes.CopyToContainerOptions{}); err != nil {
return err
}
} }
if err := d.start(ctx, l); err != nil { if err := d.start(ctx, l); err != nil {
return err return err
@@ -196,6 +200,24 @@ func (d *Driver) copyLogs(ctx context.Context, l progress.SubLogger) error {
return rc.Close() return rc.Close()
} }
func (d *Driver) copyToContainer(ctx context.Context, files map[string][]byte) error {
srcPath, err := writeConfigFiles(files)
if err != nil {
return err
}
if srcPath != "" {
defer os.RemoveAll(srcPath)
}
srcArchive, err := dockerarchive.TarWithOptions(srcPath, &dockerarchive.TarOptions{
ChownOpts: &idtools.Identity{UID: 0, GID: 0},
})
if err != nil {
return err
}
defer srcArchive.Close()
return d.DockerAPI.CopyToContainer(ctx, d.Name, "/", srcArchive, dockertypes.CopyToContainerOptions{})
}
func (d *Driver) exec(ctx context.Context, cmd []string) (string, net.Conn, error) { func (d *Driver) exec(ctx context.Context, cmd []string) (string, net.Conn, error) {
execConfig := types.ExecConfig{ execConfig := types.ExecConfig{
Cmd: cmd, Cmd: cmd,
@@ -276,7 +298,7 @@ func (d *Driver) Stop(ctx context.Context, force bool) error {
return nil return nil
} }
func (d *Driver) Rm(ctx context.Context, force bool, rmVolume bool) error { func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
info, err := d.Info(ctx) info, err := d.Info(ctx)
if err != nil { if err != nil {
return err return err
@@ -286,20 +308,22 @@ func (d *Driver) Rm(ctx context.Context, force bool, rmVolume bool) error {
if err != nil { if err != nil {
return err return err
} }
if err := d.DockerAPI.ContainerRemove(ctx, d.Name, dockertypes.ContainerRemoveOptions{ if rmDaemon {
RemoveVolumes: true, if err := d.DockerAPI.ContainerRemove(ctx, d.Name, dockertypes.ContainerRemoveOptions{
Force: force, RemoveVolumes: true,
}); err != nil { Force: force,
return err }); err != nil {
} return err
for _, v := range container.Mounts { }
if v.Name == d.Name+volumeStateSuffix { for _, v := range container.Mounts {
if v.Name != d.Name+volumeStateSuffix {
continue
}
if rmVolume { if rmVolume {
return d.DockerAPI.VolumeRemove(ctx, d.Name+volumeStateSuffix, false) return d.DockerAPI.VolumeRemove(ctx, d.Name+volumeStateSuffix, false)
} }
} }
} }
} }
return nil return nil
} }
@@ -357,29 +381,6 @@ func (d *demux) Read(dt []byte) (int, error) {
return d.Reader.Read(dt) return d.Reader.Read(dt)
} }
func readFileToTar(fn string) (*bytes.Buffer, error) {
buf := bytes.NewBuffer(nil)
tw := tar.NewWriter(buf)
dt, err := ioutil.ReadFile(fn)
if err != nil {
return nil, err
}
if err := tw.WriteHeader(&tar.Header{
Name: "/etc/buildkit/buildkitd.toml",
Size: int64(len(dt)),
Mode: 0644,
}); err != nil {
return nil, err
}
if _, err := tw.Write(dt); err != nil {
return nil, err
}
if err := tw.Close(); err != nil {
return nil, err
}
return buf, nil
}
type logWriter struct { type logWriter struct {
logger progress.SubLogger logger progress.SubLogger
stream int stream int
@@ -389,3 +390,27 @@ func (l *logWriter) Write(dt []byte) (int, error) {
l.logger.Log(l.stream, dt) l.logger.Log(l.stream, dt)
return len(dt), nil return len(dt), nil
} }
func writeConfigFiles(m map[string][]byte) (_ string, err error) {
// Temp dir that will be copied to the container
tmpDir, err := os.MkdirTemp("", "buildkitd-config")
if err != nil {
return "", err
}
defer func() {
if err != nil {
os.RemoveAll(tmpDir)
}
}()
for f, dt := range m {
f = path.Join(confutil.DefaultBuildKitConfigDir, f)
p := filepath.Join(tmpDir, f)
if err := os.MkdirAll(filepath.Dir(p), 0700); err != nil {
return "", err
}
if err := os.WriteFile(p, dt, 0600); err != nil {
return "", err
}
}
return tmpDir, nil
}

View File

@@ -6,6 +6,7 @@ import (
"strings" "strings"
"github.com/docker/buildx/driver" "github.com/docker/buildx/driver"
dockertypes "github.com/docker/docker/api/types"
dockerclient "github.com/docker/docker/client" dockerclient "github.com/docker/docker/client"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -40,6 +41,20 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
return nil, errors.Errorf("%s driver requires docker API access", f.Name()) return nil, errors.Errorf("%s driver requires docker API access", f.Name())
} }
d := &Driver{factory: f, InitConfig: cfg} d := &Driver{factory: f, InitConfig: cfg}
dockerInfo, err := cfg.DockerAPI.Info(ctx)
if err != nil {
return nil, err
}
secOpts, err := dockertypes.DecodeSecurityOptions(dockerInfo.SecurityOptions)
if err != nil {
return nil, err
}
for _, f := range secOpts {
if f.Name == "userns" {
d.userNSRemap = true
break
}
}
for k, v := range cfg.DriverOpts { for k, v := range cfg.DriverOpts {
switch { switch {
case k == "network": case k == "network":
@@ -49,6 +64,8 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
} }
case k == "image": case k == "image":
d.image = v d.image = v
case k == "cgroup-parent":
d.cgroupParent = v
case strings.HasPrefix(k, "env."): case strings.HasPrefix(k, "env."):
envName := strings.TrimPrefix(k, "env.") envName := strings.TrimPrefix(k, "env.")
if envName == "" { if envName == "" {

View File

@@ -33,13 +33,15 @@ func (d *Driver) Stop(ctx context.Context, force bool) error {
return nil return nil
} }
func (d *Driver) Rm(ctx context.Context, force bool, rmVolume bool) error { func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return nil return nil
} }
func (d *Driver) Client(ctx context.Context) (*client.Client, error) { func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
return client.New(ctx, "", client.WithContextDialer(func(context.Context, string) (net.Conn, error) { return client.New(ctx, "", client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return d.DockerAPI.DialHijack(ctx, "/grpc", "h2c", nil) return d.DockerAPI.DialHijack(ctx, "/grpc", "h2c", nil)
}), client.WithSessionDialer(func(ctx context.Context, proto string, meta map[string][]string) (net.Conn, error) {
return d.DockerAPI.DialHijack(ctx, "/session", proto, meta)
})) }))
} }

View File

@@ -44,7 +44,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
if cfg.DockerAPI == nil { if cfg.DockerAPI == nil {
return nil, errors.Errorf("docker driver requires docker API access") return nil, errors.Errorf("docker driver requires docker API access")
} }
if cfg.ConfigFile != "" { if len(cfg.Files) > 0 {
return nil, errors.Errorf("setting config file is not supported for docker driver, use dockerd configuration file") return nil, errors.Errorf("setting config file is not supported for docker driver, use dockerd configuration file")
} }

View File

@@ -54,7 +54,7 @@ type Driver interface {
Bootstrap(context.Context, progress.Logger) error Bootstrap(context.Context, progress.Logger) error
Info(context.Context) (*Info, error) Info(context.Context) (*Info, error)
Stop(ctx context.Context, force bool) error Stop(ctx context.Context, force bool) error
Rm(ctx context.Context, force bool, rmVolume bool) error Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error
Client(ctx context.Context) (*client.Client, error) Client(ctx context.Context) (*client.Client, error)
Features() map[Feature]bool Features() map[Feature]bool
IsMobyDriver() bool IsMobyDriver() bool

View File

@@ -1,4 +1,4 @@
package kubernetes package context
const ( const (
// KubernetesEndpoint is the kubernetes endpoint name in a stored context // KubernetesEndpoint is the kubernetes endpoint name in a stored context

View File

@@ -0,0 +1,225 @@
package context
import (
"io/ioutil"
"os"
"testing"
"github.com/docker/cli/cli/context"
"github.com/docker/cli/cli/context/store"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
func testEndpoint(server, defaultNamespace string, ca, cert, key []byte, skipTLSVerify bool) Endpoint {
var tlsData *context.TLSData
if ca != nil || cert != nil || key != nil {
tlsData = &context.TLSData{
CA: ca,
Cert: cert,
Key: key,
}
}
return Endpoint{
EndpointMeta: EndpointMeta{
EndpointMetaBase: context.EndpointMetaBase{
Host: server,
SkipTLSVerify: skipTLSVerify,
},
DefaultNamespace: defaultNamespace,
},
TLSData: tlsData,
}
}
var testStoreCfg = store.NewConfig(
func() interface{} {
return &map[string]interface{}{}
},
store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }),
)
func TestSaveLoadContexts(t *testing.T) {
storeDir, err := ioutil.TempDir("", "test-load-save-k8-context")
require.NoError(t, err)
defer os.RemoveAll(storeDir)
store := store.New(storeDir, testStoreCfg)
require.NoError(t, save(store, testEndpoint("https://test", "test", nil, nil, nil, false), "raw-notls"))
require.NoError(t, save(store, testEndpoint("https://test", "test", nil, nil, nil, true), "raw-notls-skip"))
require.NoError(t, save(store, testEndpoint("https://test", "test", []byte("ca"), []byte("cert"), []byte("key"), true), "raw-tls"))
kcFile, err := ioutil.TempFile(os.TempDir(), "test-load-save-k8-context")
require.NoError(t, err)
defer os.Remove(kcFile.Name())
defer kcFile.Close()
cfg := clientcmdapi.NewConfig()
cfg.AuthInfos["user"] = clientcmdapi.NewAuthInfo()
cfg.Contexts["context1"] = clientcmdapi.NewContext()
cfg.Clusters["cluster1"] = clientcmdapi.NewCluster()
cfg.Contexts["context2"] = clientcmdapi.NewContext()
cfg.Clusters["cluster2"] = clientcmdapi.NewCluster()
cfg.AuthInfos["user"].ClientCertificateData = []byte("cert")
cfg.AuthInfos["user"].ClientKeyData = []byte("key")
cfg.Clusters["cluster1"].Server = "https://server1"
cfg.Clusters["cluster1"].InsecureSkipTLSVerify = true
cfg.Clusters["cluster2"].Server = "https://server2"
cfg.Clusters["cluster2"].CertificateAuthorityData = []byte("ca")
cfg.Contexts["context1"].AuthInfo = "user"
cfg.Contexts["context1"].Cluster = "cluster1"
cfg.Contexts["context1"].Namespace = "namespace1"
cfg.Contexts["context2"].AuthInfo = "user"
cfg.Contexts["context2"].Cluster = "cluster2"
cfg.Contexts["context2"].Namespace = "namespace2"
cfg.CurrentContext = "context1"
cfgData, err := clientcmd.Write(*cfg)
require.NoError(t, err)
_, err = kcFile.Write(cfgData)
require.NoError(t, err)
kcFile.Close()
epDefault, err := FromKubeConfig(kcFile.Name(), "", "")
require.NoError(t, err)
epContext2, err := FromKubeConfig(kcFile.Name(), "context2", "namespace-override")
require.NoError(t, err)
require.NoError(t, save(store, epDefault, "embed-default-context"))
require.NoError(t, save(store, epContext2, "embed-context2"))
rawNoTLSMeta, err := store.GetMetadata("raw-notls")
require.NoError(t, err)
rawNoTLSSkipMeta, err := store.GetMetadata("raw-notls-skip")
require.NoError(t, err)
rawTLSMeta, err := store.GetMetadata("raw-tls")
require.NoError(t, err)
embededDefaultMeta, err := store.GetMetadata("embed-default-context")
require.NoError(t, err)
embededContext2Meta, err := store.GetMetadata("embed-context2")
require.NoError(t, err)
rawNoTLS := EndpointFromContext(rawNoTLSMeta)
rawNoTLSSkip := EndpointFromContext(rawNoTLSSkipMeta)
rawTLS := EndpointFromContext(rawTLSMeta)
embededDefault := EndpointFromContext(embededDefaultMeta)
embededContext2 := EndpointFromContext(embededContext2Meta)
rawNoTLSEP, err := rawNoTLS.WithTLSData(store, "raw-notls")
require.NoError(t, err)
checkClientConfig(t, rawNoTLSEP, "https://test", "test", nil, nil, nil, false)
rawNoTLSSkipEP, err := rawNoTLSSkip.WithTLSData(store, "raw-notls-skip")
require.NoError(t, err)
checkClientConfig(t, rawNoTLSSkipEP, "https://test", "test", nil, nil, nil, true)
rawTLSEP, err := rawTLS.WithTLSData(store, "raw-tls")
require.NoError(t, err)
checkClientConfig(t, rawTLSEP, "https://test", "test", []byte("ca"), []byte("cert"), []byte("key"), true)
embededDefaultEP, err := embededDefault.WithTLSData(store, "embed-default-context")
require.NoError(t, err)
checkClientConfig(t, embededDefaultEP, "https://server1", "namespace1", nil, []byte("cert"), []byte("key"), true)
embededContext2EP, err := embededContext2.WithTLSData(store, "embed-context2")
require.NoError(t, err)
checkClientConfig(t, embededContext2EP, "https://server2", "namespace-override", []byte("ca"), []byte("cert"), []byte("key"), false)
}
func checkClientConfig(t *testing.T, ep Endpoint, server, namespace string, ca, cert, key []byte, skipTLSVerify bool) {
config := ep.KubernetesConfig()
cfg, err := config.ClientConfig()
require.NoError(t, err)
ns, _, _ := config.Namespace()
assert.Equal(t, server, cfg.Host)
assert.Equal(t, namespace, ns)
assert.Equal(t, ca, cfg.CAData)
assert.Equal(t, cert, cfg.CertData)
assert.Equal(t, key, cfg.KeyData)
assert.Equal(t, skipTLSVerify, cfg.Insecure)
}
func save(s store.Writer, ep Endpoint, name string) error {
meta := store.Metadata{
Endpoints: map[string]interface{}{
KubernetesEndpoint: ep.EndpointMeta,
},
Name: name,
}
if err := s.CreateOrUpdate(meta); err != nil {
return err
}
return s.ResetEndpointTLSMaterial(name, KubernetesEndpoint, ep.TLSData.ToStoreTLSData())
}
func TestSaveLoadGKEConfig(t *testing.T) {
storeDir, err := ioutil.TempDir("", t.Name())
require.NoError(t, err)
defer os.RemoveAll(storeDir)
store := store.New(storeDir, testStoreCfg)
cfg, err := clientcmd.LoadFromFile("fixtures/gke-kubeconfig")
require.NoError(t, err)
clientCfg := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{})
expectedCfg, err := clientCfg.ClientConfig()
require.NoError(t, err)
ep, err := FromKubeConfig("fixtures/gke-kubeconfig", "", "")
require.NoError(t, err)
require.NoError(t, save(store, ep, "gke-context"))
persistedMetadata, err := store.GetMetadata("gke-context")
require.NoError(t, err)
persistedEPMeta := EndpointFromContext(persistedMetadata)
assert.True(t, persistedEPMeta != nil)
persistedEP, err := persistedEPMeta.WithTLSData(store, "gke-context")
require.NoError(t, err)
persistedCfg := persistedEP.KubernetesConfig()
actualCfg, err := persistedCfg.ClientConfig()
require.NoError(t, err)
assert.Equal(t, expectedCfg.AuthProvider, actualCfg.AuthProvider)
}
func TestSaveLoadEKSConfig(t *testing.T) {
storeDir, err := ioutil.TempDir("", t.Name())
require.NoError(t, err)
defer os.RemoveAll(storeDir)
store := store.New(storeDir, testStoreCfg)
cfg, err := clientcmd.LoadFromFile("fixtures/eks-kubeconfig")
require.NoError(t, err)
clientCfg := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{})
expectedCfg, err := clientCfg.ClientConfig()
require.NoError(t, err)
ep, err := FromKubeConfig("fixtures/eks-kubeconfig", "", "")
require.NoError(t, err)
require.NoError(t, save(store, ep, "eks-context"))
persistedMetadata, err := store.GetMetadata("eks-context")
require.NoError(t, err)
persistedEPMeta := EndpointFromContext(persistedMetadata)
assert.True(t, persistedEPMeta != nil)
persistedEP, err := persistedEPMeta.WithTLSData(store, "eks-context")
require.NoError(t, err)
persistedCfg := persistedEP.KubernetesConfig()
actualCfg, err := persistedCfg.ClientConfig()
require.NoError(t, err)
assert.Equal(t, expectedCfg.ExecProvider, actualCfg.ExecProvider)
}
func TestSaveLoadK3SConfig(t *testing.T) {
storeDir, err := ioutil.TempDir("", t.Name())
require.NoError(t, err)
defer os.RemoveAll(storeDir)
store := store.New(storeDir, testStoreCfg)
cfg, err := clientcmd.LoadFromFile("fixtures/k3s-kubeconfig")
require.NoError(t, err)
clientCfg := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{})
expectedCfg, err := clientCfg.ClientConfig()
require.NoError(t, err)
ep, err := FromKubeConfig("fixtures/k3s-kubeconfig", "", "")
require.NoError(t, err)
require.NoError(t, save(store, ep, "k3s-context"))
persistedMetadata, err := store.GetMetadata("k3s-context")
require.NoError(t, err)
persistedEPMeta := EndpointFromContext(persistedMetadata)
assert.True(t, persistedEPMeta != nil)
persistedEP, err := persistedEPMeta.WithTLSData(store, "k3s-context")
require.NoError(t, err)
persistedCfg := persistedEP.KubernetesConfig()
actualCfg, err := persistedCfg.ClientConfig()
require.NoError(t, err)
assert.True(t, len(actualCfg.Username) > 0)
assert.True(t, len(actualCfg.Password) > 0)
assert.Equal(t, expectedCfg.Username, actualCfg.Username)
assert.Equal(t, expectedCfg.Password, actualCfg.Password)
}

View File

@@ -0,0 +1,23 @@
apiVersion: v1
clusters:
- cluster:
server: https://some-server
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: aws
name: aws
current-context: aws
kind: Config
preferences: {}
users:
- name: aws
user:
exec:
apiVersion: client.authentication.k8s.io/v1alpha1
command: heptio-authenticator-aws
args:
- "token"
- "-i"
- "eks-cf"

View File

@@ -0,0 +1,23 @@
apiVersion: v1
clusters:
- cluster:
server: https://some-server
name: gke_sample
contexts:
- context:
cluster: gke_sample
user: gke_sample
name: gke_sample
current-context: gke_sample
kind: Config
preferences: {}
users:
- name: gke_sample
user:
auth-provider:
config:
cmd-args: config config-helper --format=json
cmd-path: /google/google-cloud-sdk/bin/gcloud
expiry-key: '{.credential.token_expiry}'
token-key: '{.credential.access_token}'
name: gcp

View File

@@ -0,0 +1,20 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: dGhlLWNh
server: https://someserver
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: test-user
namespace: zoinx
name: test
current-context: test
kind: Config
preferences: {}
users:
- name: test-user
user:
username: admin
password: testpwd

View File

@@ -0,0 +1,20 @@
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: dGhlLWNh
server: https://someserver
name: test-cluster
contexts:
- context:
cluster: test-cluster
user: test-user
namespace: zoinx
name: test
current-context: test
kind: Config
preferences: {}
users:
- name: test-user
user:
client-certificate-data: dGhlLWNlcnQ=
client-key-data: dGhlLWtleQ==

View File

@@ -1,4 +1,4 @@
package kubernetes package context
import ( import (
"os" "os"
@@ -7,9 +7,7 @@ import (
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context" "github.com/docker/cli/cli/context"
"github.com/docker/cli/cli/context/store" "github.com/docker/cli/cli/context/store"
api "github.com/docker/compose-on-kubernetes/api"
"github.com/docker/docker/pkg/homedir" "github.com/docker/docker/pkg/homedir"
"github.com/pkg/errors"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api" clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
) )
@@ -88,23 +86,18 @@ func (c *Endpoint) KubernetesConfig() clientcmd.ClientConfig {
// ResolveDefault returns endpoint metadata for the default Kubernetes // ResolveDefault returns endpoint metadata for the default Kubernetes
// endpoint, which is derived from the env-based kubeconfig. // endpoint, which is derived from the env-based kubeconfig.
func (c *EndpointMeta) ResolveDefault(stackOrchestrator command.Orchestrator) (interface{}, *store.EndpointTLSData, error) { func (c *EndpointMeta) ResolveDefault() (interface{}, *store.EndpointTLSData, error) {
kubeconfig := os.Getenv("KUBECONFIG") kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" { if kubeconfig == "" {
kubeconfig = filepath.Join(homedir.Get(), ".kube/config") kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
} }
kubeEP, err := FromKubeConfig(kubeconfig, "", "") kubeEP, err := FromKubeConfig(kubeconfig, "", "")
if err != nil { if err != nil {
if stackOrchestrator == command.OrchestratorKubernetes || stackOrchestrator == command.OrchestratorAll {
return nil, nil, errors.Wrapf(err, "default orchestrator is %s but unable to resolve kubernetes endpoint", stackOrchestrator)
}
// We deliberately quash the error here, returning nil // We deliberately quash the error here, returning nil
// for the first argument is sufficient to indicate we weren't able to // for the first argument is sufficient to indicate we weren't able to
// provide a default // provide a default
return nil, nil, nil return nil, nil, nil
} }
var tls *store.EndpointTLSData var tls *store.EndpointTLSData
if kubeEP.TLSData != nil { if kubeEP.TLSData != nil {
tls = kubeEP.TLSData.ToStoreTLSData() tls = kubeEP.TLSData.ToStoreTLSData()
@@ -142,5 +135,21 @@ func ConfigFromContext(name string, s store.Reader) (clientcmd.ClientConfig, err
return ep.KubernetesConfig(), nil return ep.KubernetesConfig(), nil
} }
// context has no kubernetes endpoint // context has no kubernetes endpoint
return api.NewKubernetesConfig(""), nil return NewKubernetesConfig(""), nil
}
// NewKubernetesConfig resolves the path to the desired Kubernetes configuration
// file based on the KUBECONFIG environment variable and command line flags.
func NewKubernetesConfig(configPath string) clientcmd.ClientConfig {
kubeConfig := configPath
if kubeConfig == "" {
if config := os.Getenv("KUBECONFIG"); config != "" {
kubeConfig = config
} else {
kubeConfig = filepath.Join(homedir.Get(), ".kube/config")
}
}
return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfig},
&clientcmd.ConfigOverrides{})
} }

View File

@@ -0,0 +1,23 @@
package context
import (
"os"
"testing"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config/configfile"
cliflags "github.com/docker/cli/cli/flags"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDefaultContextInitializer(t *testing.T) {
cli, err := command.NewDockerCli()
require.NoError(t, err)
os.Setenv("KUBECONFIG", "./fixtures/test-kubeconfig")
defer os.Unsetenv("KUBECONFIG")
ctx, err := command.ResolveDefaultContext(&cliflags.CommonOptions{}, &configfile.ConfigFile{}, command.DefaultContextStoreConfig(), cli.Err())
require.NoError(t, err)
assert.Equal(t, "default", ctx.Meta.Name)
assert.Equal(t, "zoinx", ctx.Meta.Endpoints[KubernetesEndpoint].(EndpointMeta).DefaultNamespace)
}

View File

@@ -1,4 +1,4 @@
package kubernetes package context
import ( import (
"io/ioutil" "io/ioutil"

View File

@@ -18,6 +18,8 @@ import (
"github.com/moby/buildkit/util/tracing/detect" "github.com/moby/buildkit/util/tracing/detect"
"github.com/pkg/errors" "github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
clientappsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" clientappsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
@@ -39,15 +41,18 @@ type Driver struct {
factory driver.Factory factory driver.Factory
minReplicas int minReplicas int
deployment *appsv1.Deployment deployment *appsv1.Deployment
configMaps []*corev1.ConfigMap
clientset *kubernetes.Clientset clientset *kubernetes.Clientset
deploymentClient clientappsv1.DeploymentInterface deploymentClient clientappsv1.DeploymentInterface
podClient clientcorev1.PodInterface podClient clientcorev1.PodInterface
configMapClient clientcorev1.ConfigMapInterface
podChooser podchooser.PodChooser podChooser podchooser.PodChooser
} }
func (d *Driver) IsMobyDriver() bool { func (d *Driver) IsMobyDriver() bool {
return false return false
} }
func (d *Driver) Config() driver.InitConfig { func (d *Driver) Config() driver.InitConfig {
return d.InitConfig return d.InitConfig
} }
@@ -56,7 +61,24 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error { return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
_, err := d.deploymentClient.Get(ctx, d.deployment.Name, metav1.GetOptions{}) _, err := d.deploymentClient.Get(ctx, d.deployment.Name, metav1.GetOptions{})
if err != nil { if err != nil {
// TODO: return err if err != ErrNotFound if !apierrors.IsNotFound(err) {
return errors.Wrapf(err, "error for bootstrap %q", d.deployment.Name)
}
for _, cfg := range d.configMaps {
// create ConfigMap first if exists
_, err = d.configMapClient.Create(ctx, cfg, metav1.CreateOptions{})
if err != nil {
if !apierrors.IsAlreadyExists(err) {
return errors.Wrapf(err, "error while calling configMapClient.Create for %q", cfg.Name)
}
_, err = d.configMapClient.Update(ctx, cfg, metav1.UpdateOptions{})
if err != nil {
return errors.Wrapf(err, "error while calling configMapClient.Update for %q", cfg.Name)
}
}
}
_, err = d.deploymentClient.Create(ctx, d.deployment, metav1.CreateOptions{}) _, err = d.deploymentClient.Create(ctx, d.deployment, metav1.CreateOptions{})
if err != nil { if err != nil {
return errors.Wrapf(err, "error while calling deploymentClient.Create for %q", d.deployment.Name) return errors.Wrapf(err, "error while calling deploymentClient.Create for %q", d.deployment.Name)
@@ -143,9 +165,22 @@ func (d *Driver) Stop(ctx context.Context, force bool) error {
return nil return nil
} }
func (d *Driver) Rm(ctx context.Context, force bool, rmVolume bool) error { func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
if !rmDaemon {
return nil
}
if err := d.deploymentClient.Delete(ctx, d.deployment.Name, metav1.DeleteOptions{}); err != nil { if err := d.deploymentClient.Delete(ctx, d.deployment.Name, metav1.DeleteOptions{}); err != nil {
return errors.Wrapf(err, "error while calling deploymentClient.Delete for %q", d.deployment.Name) if !apierrors.IsNotFound(err) {
return errors.Wrapf(err, "error while calling deploymentClient.Delete for %q", d.deployment.Name)
}
}
for _, cfg := range d.configMaps {
if err := d.configMapClient.Delete(ctx, cfg.Name, metav1.DeleteOptions{}); err != nil {
if !apierrors.IsNotFound(err) {
return errors.Wrapf(err, "error while calling configMapClient.Delete for %q", cfg.Name)
}
}
} }
return nil return nil
} }

View File

@@ -59,11 +59,13 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
if err != nil { if err != nil {
return nil, err return nil, err
} }
d := &Driver{ d := &Driver{
factory: f, factory: f,
InitConfig: cfg, InitConfig: cfg,
clientset: clientset, clientset: clientset,
} }
deploymentOpt := &manifest.DeploymentOpt{ deploymentOpt := &manifest.DeploymentOpt{
Name: deploymentName, Name: deploymentName,
Image: bkimage.DefaultImage, Image: bkimage.DefaultImage,
@@ -71,13 +73,19 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
BuildkitFlags: cfg.BuildkitFlags, BuildkitFlags: cfg.BuildkitFlags,
Rootless: false, Rootless: false,
Platforms: cfg.Platforms, Platforms: cfg.Platforms,
ConfigFiles: cfg.Files,
} }
deploymentOpt.Qemu.Image = bkimage.QemuImage
loadbalance := LoadbalanceSticky loadbalance := LoadbalanceSticky
imageOverride := ""
for k, v := range cfg.DriverOpts { for k, v := range cfg.DriverOpts {
switch k { switch k {
case "image": case "image":
imageOverride = v if v != "" {
deploymentOpt.Image = v
}
case "namespace": case "namespace":
namespace = v namespace = v
case "replicas": case "replicas":
@@ -117,20 +125,31 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
return nil, errors.Errorf("invalid loadbalance %q", v) return nil, errors.Errorf("invalid loadbalance %q", v)
} }
loadbalance = v loadbalance = v
case "qemu.install":
deploymentOpt.Qemu.Install, err = strconv.ParseBool(v)
if err != nil {
return nil, err
}
case "qemu.image":
if v != "" {
deploymentOpt.Qemu.Image = v
}
default: default:
return nil, errors.Errorf("invalid driver option %s for driver %s", k, DriverName) return nil, errors.Errorf("invalid driver option %s for driver %s", k, DriverName)
} }
} }
if imageOverride != "" {
deploymentOpt.Image = imageOverride d.deployment, d.configMaps, err = manifest.NewDeployment(deploymentOpt)
}
d.deployment, err = manifest.NewDeployment(deploymentOpt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
d.minReplicas = deploymentOpt.Replicas d.minReplicas = deploymentOpt.Replicas
d.deploymentClient = clientset.AppsV1().Deployments(namespace) d.deploymentClient = clientset.AppsV1().Deployments(namespace)
d.podClient = clientset.CoreV1().Pods(namespace) d.podClient = clientset.CoreV1().Pods(namespace)
d.configMapClient = clientset.CoreV1().ConfigMaps(namespace)
switch loadbalance { switch loadbalance {
case LoadbalanceSticky: case LoadbalanceSticky:
d.podChooser = &podchooser.StickyPodChooser{ d.podChooser = &podchooser.StickyPodChooser{

View File

@@ -1,6 +1,8 @@
package manifest package manifest
import ( import (
"fmt"
"path"
"strings" "strings"
"github.com/docker/buildx/util/platformutil" "github.com/docker/buildx/util/platformutil"
@@ -12,11 +14,22 @@ import (
) )
type DeploymentOpt struct { type DeploymentOpt struct {
Namespace string Namespace string
Name string Name string
Image string Image string
Replicas int Replicas int
BuildkitFlags []string
// Qemu
Qemu struct {
// when true, will install binfmt
Install bool
Image string
}
BuildkitFlags []string
// files mounted at /etc/buildkitd
ConfigFiles map[string][]byte
Rootless bool Rootless bool
NodeSelector map[string]string NodeSelector map[string]string
RequestsCPU string RequestsCPU string
@@ -31,7 +44,7 @@ const (
AnnotationPlatform = "buildx.docker.com/platform" AnnotationPlatform = "buildx.docker.com/platform"
) )
func NewDeployment(opt *DeploymentOpt) (*appsv1.Deployment, error) { func NewDeployment(opt *DeploymentOpt) (d *appsv1.Deployment, c []*corev1.ConfigMap, err error) {
labels := map[string]string{ labels := map[string]string{
"app": opt.Name, "app": opt.Name,
} }
@@ -44,7 +57,7 @@ func NewDeployment(opt *DeploymentOpt) (*appsv1.Deployment, error) {
annotations[AnnotationPlatform] = strings.Join(platformutil.Format(opt.Platforms), ",") annotations[AnnotationPlatform] = strings.Join(platformutil.Format(opt.Platforms), ",")
} }
d := &appsv1.Deployment{ d = &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{ TypeMeta: metav1.TypeMeta{
APIVersion: appsv1.SchemeGroupVersion.String(), APIVersion: appsv1.SchemeGroupVersion.String(),
Kind: "Deployment", Kind: "Deployment",
@@ -91,9 +104,54 @@ func NewDeployment(opt *DeploymentOpt) (*appsv1.Deployment, error) {
}, },
}, },
} }
for _, cfg := range splitConfigFiles(opt.ConfigFiles) {
cc := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
APIVersion: corev1.SchemeGroupVersion.String(),
Kind: "ConfigMap",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: opt.Namespace,
Name: opt.Name + "-" + cfg.name,
Annotations: annotations,
},
Data: cfg.files,
}
d.Spec.Template.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{{
Name: cfg.name,
MountPath: path.Join("/etc/buildkit", cfg.path),
}}
d.Spec.Template.Spec.Volumes = []corev1.Volume{{
Name: "config",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: cc.Name,
},
},
},
}}
c = append(c, cc)
}
if opt.Qemu.Install {
d.Spec.Template.Spec.InitContainers = []corev1.Container{
{
Name: "qemu",
Image: opt.Qemu.Image,
Args: []string{"--install", "all"},
SecurityContext: &corev1.SecurityContext{
Privileged: &privileged,
},
},
}
}
if opt.Rootless { if opt.Rootless {
if err := toRootless(d); err != nil { if err := toRootless(d); err != nil {
return nil, err return nil, nil, err
} }
} }
@@ -104,7 +162,7 @@ func NewDeployment(opt *DeploymentOpt) (*appsv1.Deployment, error) {
if opt.RequestsCPU != "" { if opt.RequestsCPU != "" {
reqCPU, err := resource.ParseQuantity(opt.RequestsCPU) reqCPU, err := resource.ParseQuantity(opt.RequestsCPU)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = reqCPU d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = reqCPU
} }
@@ -112,7 +170,7 @@ func NewDeployment(opt *DeploymentOpt) (*appsv1.Deployment, error) {
if opt.RequestsMemory != "" { if opt.RequestsMemory != "" {
reqMemory, err := resource.ParseQuantity(opt.RequestsMemory) reqMemory, err := resource.ParseQuantity(opt.RequestsMemory)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = reqMemory d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = reqMemory
} }
@@ -120,7 +178,7 @@ func NewDeployment(opt *DeploymentOpt) (*appsv1.Deployment, error) {
if opt.LimitsCPU != "" { if opt.LimitsCPU != "" {
limCPU, err := resource.ParseQuantity(opt.LimitsCPU) limCPU, err := resource.ParseQuantity(opt.LimitsCPU)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] = limCPU d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] = limCPU
} }
@@ -128,12 +186,12 @@ func NewDeployment(opt *DeploymentOpt) (*appsv1.Deployment, error) {
if opt.LimitsMemory != "" { if opt.LimitsMemory != "" {
limMemory, err := resource.ParseQuantity(opt.LimitsMemory) limMemory, err := resource.ParseQuantity(opt.LimitsMemory)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = limMemory d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = limMemory
} }
return d, nil return
} }
func toRootless(d *appsv1.Deployment) error { func toRootless(d *appsv1.Deployment) error {
@@ -149,3 +207,35 @@ func toRootless(d *appsv1.Deployment) error {
d.Spec.Template.ObjectMeta.Annotations["container.seccomp.security.alpha.kubernetes.io/"+containerName] = "unconfined" d.Spec.Template.ObjectMeta.Annotations["container.seccomp.security.alpha.kubernetes.io/"+containerName] = "unconfined"
return nil return nil
} }
type config struct {
name string
path string
files map[string]string
}
func splitConfigFiles(m map[string][]byte) []config {
var c []config
idx := map[string]int{}
nameIdx := 0
for k, v := range m {
dir := path.Dir(k)
i, ok := idx[dir]
if !ok {
idx[dir] = len(c)
i = len(c)
name := "config"
if dir != "." {
nameIdx++
name = fmt.Sprintf("%s-%d", name, nameIdx)
}
c = append(c, config{
path: dir,
name: name,
files: map[string]string{},
})
}
c[i].files[path.Base(k)] = string(v)
}
return c
}

View File

@@ -53,7 +53,7 @@ type InitConfig struct {
DockerAPI dockerclient.APIClient DockerAPI dockerclient.APIClient
KubeClientConfig KubeClientConfig KubeClientConfig KubeClientConfig
BuildkitFlags []string BuildkitFlags []string
ConfigFile string Files map[string][]byte
DriverOpts map[string]string DriverOpts map[string]string
Auth Auth Auth Auth
Platforms []specs.Platform Platforms []specs.Platform
@@ -103,17 +103,17 @@ func GetFactory(name string, instanceRequired bool) Factory {
return nil return nil
} }
func GetDriver(ctx context.Context, name string, f Factory, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, flags []string, config string, do map[string]string, platforms []specs.Platform, contextPathHash string) (Driver, error) { func GetDriver(ctx context.Context, name string, f Factory, api dockerclient.APIClient, auth Auth, kcc KubeClientConfig, flags []string, files map[string][]byte, do map[string]string, platforms []specs.Platform, contextPathHash string) (Driver, error) {
ic := InitConfig{ ic := InitConfig{
DockerAPI: api, DockerAPI: api,
KubeClientConfig: kcc, KubeClientConfig: kcc,
Name: name, Name: name,
BuildkitFlags: flags, BuildkitFlags: flags,
ConfigFile: config,
DriverOpts: do, DriverOpts: do,
Auth: auth, Auth: auth,
Platforms: platforms, Platforms: platforms,
ContextPathHash: contextPathHash, ContextPathHash: contextPathHash,
Files: files,
} }
if f == nil { if f == nil {
var err error var err error
@@ -129,8 +129,15 @@ func GetDriver(ctx context.Context, name string, f Factory, api dockerclient.API
return &cachedDriver{Driver: d}, nil return &cachedDriver{Driver: d}, nil
} }
func GetFactories() map[string]Factory { func GetFactories() []Factory {
return drivers ds := make([]Factory, 0, len(drivers))
for _, d := range drivers {
ds = append(ds, d)
}
sort.Slice(ds, func(i, j int) bool {
return ds[i].Name() < ds[j].Name()
})
return ds
} }
type cachedDriver struct { type cachedDriver struct {

53
go.mod
View File

@@ -1,28 +1,24 @@
module github.com/docker/buildx module github.com/docker/buildx
go 1.13 go 1.16
require ( require (
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect
github.com/bugsnag/bugsnag-go v1.4.1 // indirect github.com/bugsnag/bugsnag-go v1.4.1 // indirect
github.com/bugsnag/panicwrap v1.2.0 // indirect github.com/bugsnag/panicwrap v1.2.0 // indirect
github.com/cenkalti/backoff v2.1.1+incompatible // indirect github.com/cenkalti/backoff v2.1.1+incompatible // indirect
github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e // indirect github.com/cloudflare/cfssl v0.0.0-20181213083726-b94e044bb51e // indirect
github.com/compose-spec/compose-go v0.0.0-20210729195839-de56f4f0cb3c github.com/compose-spec/compose-go v1.0.8
github.com/containerd/console v1.0.2 github.com/containerd/console v1.0.3
github.com/containerd/containerd v1.5.4 github.com/containerd/containerd v1.6.1
github.com/denisenkom/go-mssqldb v0.0.0-20190315220205-a8ed825ac853 // indirect github.com/docker/cli v20.10.12+incompatible
github.com/docker/cli v20.10.7+incompatible github.com/docker/cli-docs-tool v0.4.0
github.com/docker/compose-on-kubernetes v0.4.19-0.20190128150448-356b2919c496 // indirect github.com/docker/distribution v2.8.0+incompatible
github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v20.10.7+incompatible github.com/docker/docker v20.10.7+incompatible
github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a // indirect
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
github.com/docker/go-units v0.4.0
github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4 // indirect github.com/docker/libtrust v0.0.0-20150526203908-9cbd2a1374f4 // indirect
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c // indirect
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 // indirect github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484 // indirect
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect
github.com/fvbommel/sortorder v1.0.1 // indirect github.com/fvbommel/sortorder v1.0.1 // indirect
github.com/gofrs/flock v0.7.3 github.com/gofrs/flock v0.7.3
github.com/gofrs/uuid v3.3.0+incompatible // indirect github.com/gofrs/uuid v3.3.0+incompatible // indirect
@@ -33,34 +29,37 @@ require (
github.com/hashicorp/hcl/v2 v2.8.2 github.com/hashicorp/hcl/v2 v2.8.2
github.com/jinzhu/gorm v1.9.2 // indirect github.com/jinzhu/gorm v1.9.2 // indirect
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
github.com/jinzhu/now v1.0.0 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/lib/pq v1.10.0 // indirect github.com/moby/buildkit v0.10.0-rc2.0.20220308185020-fdecd0ae108b
github.com/mattn/go-sqlite3 v1.10.0 // indirect github.com/morikuni/aec v1.0.0
github.com/moby/buildkit v0.8.2-0.20210702160134-1a7543a10527
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5
github.com/pelletier/go-toml v1.9.4
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/theupdateframework/notary v0.6.1 // indirect github.com/theupdateframework/notary v0.6.1 // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea github.com/tonistiigi/fsutil v0.0.0-20220315205639-9ed612626da3 // indirect
github.com/zclconf/go-cty v1.7.1 github.com/zclconf/go-cty v1.10.0
go.opentelemetry.io/otel v1.0.0-RC1 go.opentelemetry.io/otel v1.4.1
go.opentelemetry.io/otel/trace v1.0.0-RC1 go.opentelemetry.io/otel/trace v1.4.1
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/grpc v1.44.0
gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect gopkg.in/dancannon/gorethink.v3 v3.0.5 // indirect
gopkg.in/fatih/pool.v2 v2.0.0 // indirect gopkg.in/fatih/pool.v2 v2.0.0 // indirect
gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect gopkg.in/gorethink/gorethink.v3 v3.0.5 // indirect
k8s.io/api v0.20.6 k8s.io/api v0.23.4
k8s.io/apimachinery v0.20.6 k8s.io/apimachinery v0.23.4
k8s.io/client-go v0.20.6 k8s.io/client-go v0.23.4
) )
replace ( replace (
github.com/docker/cli => github.com/docker/cli v20.10.3-0.20210702143511-f782d1355eff+incompatible github.com/docker/cli => github.com/docker/cli v20.10.3-0.20220226190722-8667ccd1124c+incompatible
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20210609100121-ef4d47340142+incompatible github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220121014307-40bb9831756f+incompatible
k8s.io/api => k8s.io/api v0.22.4
k8s.io/apimachinery => k8s.io/apimachinery v0.22.4
k8s.io/client-go => k8s.io/client-go v0.22.4
) )

1191
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +0,0 @@
#!/usr/bin/env bash
. $(dirname $0)/util
set -eu
: ${TARGETPLATFORM=$CLI_PLATFORM}
platformFlag=""
if [ -n "$TARGETPLATFORM" ]; then
platformFlag="--platform $TARGETPLATFORM"
fi
buildxCmd build $platformFlag \
--target "binaries" \
--output "type=local,dest=./bin/" \
.

View File

@@ -1,38 +0,0 @@
#!/usr/bin/env bash
TYP=$1
. $(dirname $0)/util
set -e
usage() {
echo "usage: ./hack/build_ci_first_pass <binaries>"
exit 1
}
if [ -z "$TYP" ]; then
usage
fi
importCacheFlags=""
exportCacheFlags=""
if [ "$GITHUB_ACTIONS" = "true" ]; then
if [ -n "$cacheRefFrom" ]; then
importCacheFlags="--cache-from=type=local,src=$cacheRefFrom"
fi
if [ -n "$cacheRefTo" ]; then
exportCacheFlags="--cache-to=type=local,dest=$cacheRefTo"
fi
fi
case $TYP in
"binaries")
buildxCmd build $importCacheFlags $exportCacheFlags \
--target "binaries" \
$currentcontext
;;
*)
echo >&2 "Unknown type $TYP"
exit 1
;;
esac

View File

@@ -1,24 +0,0 @@
#!/usr/bin/env bash
. $(dirname $0)/util
set -e
: ${TARGETPLATFORM=linux/amd64,linux/arm/v7,linux/arm64,darwin/amd64,windows/amd64,linux/ppc64le,linux/s390x,linux/riscv64}
: ${EXPORT_LOCAL=}
importCacheFlags=""
if [ "$GITHUB_ACTIONS" = "true" ]; then
if [ -n "$cacheRefFrom" ]; then
importCacheFlags="--cache-from=type=local,src=$cacheRefFrom"
fi
fi
exportFlag=""
if [ -n "$EXPORT_LOCAL" ]; then
exportFlag="--output=type=local,dest=$EXPORT_LOCAL"
fi
buildxCmd build $importCacheFlags $exportFlag \
--target "binaries" \
--platform "$TARGETPLATFORM" \
$currentcontext

View File

@@ -13,7 +13,3 @@ else
( $dockerdCmd &>/var/log/dockerd.log & ) ( $dockerdCmd &>/var/log/dockerd.log & )
exec ash exec ash
fi fi

View File

@@ -0,0 +1,33 @@
# syntax=docker/dockerfile:1.3-labs
FROM alpine:3.14 AS gen
RUN apk add --no-cache git
WORKDIR /src
RUN --mount=type=bind,target=. <<EOT
#!/usr/bin/env bash
set -e
mkdir /out
# see also ".mailmap" for how email addresses and names are deduplicated
{
echo "# This file lists all individuals having contributed content to the repository."
echo "# For how it is generated, see hack/dockerfiles/authors.Dockerfile."
echo
git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf
} > /out/AUTHORS
cat /out/AUTHORS
EOT
FROM scratch AS update
COPY --from=gen /out /
FROM gen AS validate
RUN --mount=type=bind,target=.,rw <<EOT
set -e
git add -A
cp -rf /out/* .
if [ -n "$(git status --porcelain -- AUTHORS)" ]; then
echo >&2 'ERROR: Authors result differs. Please update with "make authors"'
git status --porcelain -- AUTHORS
exit 1
fi
EOT

View File

@@ -1,29 +1,42 @@
# syntax = docker/dockerfile:1.2 # syntax=docker/dockerfile:1.3-labs
FROM golang:1.16-alpine AS docsgen ARG GO_VERSION=1.17
ARG FORMATS=md,yaml
FROM golang:${GO_VERSION}-alpine AS docsgen
WORKDIR /src WORKDIR /src
RUN --mount=target=. \ RUN --mount=target=. \
--mount=target=/root/.cache,type=cache \ --mount=target=/root/.cache,type=cache \
go build -mod=vendor -o /out/docsgen ./docs/docsgen go build -mod=vendor -o /out/docsgen ./docs/generate.go
FROM alpine AS gen FROM alpine AS gen
RUN apk add --no-cache rsync git RUN apk add --no-cache rsync git
WORKDIR /src WORKDIR /src
COPY --from=docsgen /out/docsgen /usr/bin COPY --from=docsgen /out/docsgen /usr/bin
ARG FORMATS
RUN --mount=target=/context \ RUN --mount=target=/context \
--mount=target=.,type=tmpfs,readwrite \ --mount=target=.,type=tmpfs <<EOT
rsync -a /context/. . && \ set -e
docsgen && \ rsync -a /context/. .
mkdir /out && cp -r docs/reference /out docsgen --formats "$FORMATS" --source "docs/reference"
mkdir /out
cp -r docs/reference /out
EOT
FROM scratch AS update FROM scratch AS update
COPY --from=gen /out /out COPY --from=gen /out /out
FROM gen AS validate FROM gen AS validate
RUN --mount=target=/context \ RUN --mount=target=/context \
--mount=target=.,type=tmpfs,readwrite \ --mount=target=.,type=tmpfs <<EOT
rsync -a /context/. . && \ set -e
git add -A && \ rsync -a /context/. .
rm -rf docs/reference/* && \ git add -A
cp -rf /out/* ./docs/ && \ rm -rf docs/reference/*
./hack/validate-docs check cp -rf /out/* ./docs/
if [ -n "$(git status --porcelain -- docs/reference)" ]; then
echo >&2 'ERROR: Docs result differs. Please update with "make docs"'
git status --porcelain -- docs/reference
exit 1
fi
EOT

View File

@@ -1,10 +1,12 @@
# syntax=docker/dockerfile:1.2 # syntax=docker/dockerfile:1.3
FROM golang:1.16-alpine ARG GO_VERSION=1.17
FROM golang:${GO_VERSION}-alpine
RUN apk add --no-cache gcc musl-dev yamllint RUN apk add --no-cache gcc musl-dev yamllint
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.36.0 RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.36.0
WORKDIR /go/src/github.com/docker/buildx WORKDIR /go/src/github.com/docker/buildx
RUN --mount=target=/go/src/github.com/docker/buildx --mount=target=/root/.cache,type=cache \ RUN --mount=target=/go/src/github.com/docker/buildx --mount=target=/root/.cache,type=cache \
golangci-lint run golangci-lint run
RUN --mount=target=/go/src/github.com/docker/buildx --mount=target=/root/.cache,type=cache \ RUN --mount=target=/go/src/github.com/docker/buildx --mount=target=/root/.cache,type=cache \
yamllint -c .yamllint.yml --strict . yamllint -c .yamllint.yml --strict .

View File

@@ -1,23 +1,46 @@
# syntax = docker/dockerfile:1.2 # syntax=docker/dockerfile:1.3-labs
FROM golang:1.16-alpine AS vendored ARG GO_VERSION=1.17
RUN apk add --no-cache git rsync ARG MODOUTDATED_VERSION=v0.8.0
FROM golang:${GO_VERSION}-alpine AS base
RUN apk add --no-cache git rsync
WORKDIR /src WORKDIR /src
FROM base AS vendored
RUN --mount=target=/context \ RUN --mount=target=/context \
--mount=target=.,type=tmpfs,readwrite \ --mount=target=.,type=tmpfs \
--mount=target=/go/pkg/mod,type=cache \ --mount=target=/go/pkg/mod,type=cache <<EOT
rsync -a /context/. . && \ set -e
go mod tidy && go mod vendor && \ rsync -a /context/. .
mkdir /out && cp -r go.mod go.sum vendor /out go mod tidy
go mod vendor
mkdir /out
cp -r go.mod go.sum vendor /out
EOT
FROM scratch AS update FROM scratch AS update
COPY --from=vendored /out /out COPY --from=vendored /out /out
FROM vendored AS validate FROM vendored AS validate
RUN --mount=target=/context \ RUN --mount=target=/context \
--mount=target=.,type=tmpfs,readwrite \ --mount=target=.,type=tmpfs <<EOT
rsync -a /context/. . && \ set -e
git add -A && \ rsync -a /context/. .
rm -rf vendor && \ git add -A
cp -rf /out/* . && \ rm -rf vendor
./hack/validate-vendor check cp -rf /out/* .
if [ -n "$(git status --porcelain -- go.mod go.sum vendor)" ]; then
echo >&2 'ERROR: Vendor result differs. Please vendor your package with "make vendor"'
git status --porcelain -- go.mod go.sum vendor
exit 1
fi
EOT
FROM psampaz/go-mod-outdated:${MODOUTDATED_VERSION} AS go-mod-outdated
FROM base AS outdated
ARG _RANDOM
RUN --mount=target=.,ro \
--mount=target=/go/pkg/mod,type=cache \
--mount=from=go-mod-outdated,source=/home/go-mod-outdated,target=/usr/bin/go-mod-outdated \
go list -mod=readonly -u -m -json all | go-mod-outdated -update -direct

View File

@@ -1,21 +0,0 @@
#!/usr/bin/env bash
set -eu -o pipefail -x
if [ -x "$(command -v greadlink)" ]; then
# on macOS, GNU readlink is ava (greadlink) can be installed through brew install coreutils
cd "$(dirname "$(greadlink -f "$BASH_SOURCE")")/.."
else
cd "$(dirname "$(readlink -f "$BASH_SOURCE")")/.."
fi
# see also ".mailmap" for how email addresses and names are deduplicated
{
cat <<-'EOH'
# This file lists all individuals having contributed content to the repository.
# For how it is generated, see `scripts/generate-authors.sh`.
EOH
echo
git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf
} > AUTHORS

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
. $(dirname $0)/util
set -eu
buildxCmd build --file ./hack/dockerfiles/lint.Dockerfile .

View File

@@ -1,28 +1,20 @@
#!/usr/bin/env bash #!/usr/bin/env bash
OUT=${1:-release-out}
. $(dirname $0)/util
set -eu -o pipefail set -eu -o pipefail
: ${PLATFORMS=linux/amd64} : ${BUILDX_CMD=docker buildx}
: ${RELEASE_OUT=./release-out}
importCacheFlags="" # release
if [[ -n "$cacheRefFrom" ]] && [[ "$cacheType" = "local" ]]; then (set -x ; ${BUILDX_CMD} bake --set "*.output=$RELEASE_OUT" release)
for ref in $cacheRefFrom; do
importCacheFlags="$importCacheFlags--cache-from=type=local,src=$ref "
done
fi
buildxCmd build $importCacheFlags \
--target "release" \
--platform "$PLATFORMS" \
--output "type=local,dest=$OUT" \
$currentcontext
# wrap binaries # wrap binaries
{ set +x; } 2>/dev/null mv -f ./${RELEASE_OUT}/**/* ./${RELEASE_OUT}/
if [[ $PLATFORMS =~ "," ]]; then find ./${RELEASE_OUT} -type d -empty -delete
mv -f ./$OUT/**/* ./$OUT/
find ./$OUT -type d -empty -delete # checksums
fi if ! type shasum > /dev/null 2>&1; then
echo >&2 "ERROR: shasum is required"
exit 1
fi
find ./${RELEASE_OUT}/ -type f \( -iname "buildx-*" ! -iname "*darwin*" \) -print0 | sort -z | xargs -r0 shasum -a 256 -b | sed 's# .*/# #' > ./${RELEASE_OUT}/checksums.txt

View File

@@ -2,17 +2,18 @@
set -e set -e
: ${BUILDX_CMD=docker buildx}
: ${TMUX=} : ${TMUX=}
function clean { function clean {
docker rmi $(cat $iidfile) docker rmi $iid
} }
iidfile=$(mktemp -t docker-iidfile.XXXXXXXXXX) iid=buildx-shell
DOCKER_BUILDKIT=1 docker build --iidfile $iidfile --target demo-env . (set -x ; ${BUILDX_CMD} build --output "type=docker,name=$iid" --target shell .)
trap clean EXIT trap clean EXIT
SSH= SSH=
if [ -n "$MOUNT_SSH_AUTH_SOCK" ]; then if [ -n "$MOUNT_SSH_AUTH_SOCK" ]; then
SSH="-v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK -e SSH_AUTH_SOCK" SSH="-v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK -e SSH_AUTH_SOCK"
fi fi
docker run $SSH -it --privileged --rm -e TMUX_ENTRYPOINT=$TMUX $(cat $iidfile) docker run $SSH -it --privileged --rm -e TMUX_ENTRYPOINT=$TMUX $iid

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
. $(dirname $0)/util
set -eu -o pipefail
: ${BUILDX_NOCACHE=}
: ${TEST_COVERAGE=}
importCacheFlags=""
if [ -n "$cacheRefFrom" ]; then
if [ "$cacheType" = "local" ]; then
for ref in $cacheRefFrom; do
importCacheFlags="$importCacheFlags--cache-from=type=local,src=$ref "
done
fi
fi
iid="buildx-tests"
iidfile=$(mktemp -t docker-iidfile.XXXXXXXXXX)
coverageVol=""
coverageFlags=""
if [ "$TEST_COVERAGE" = "1" ]; then
covdir="$(pwd)/coverage"
mkdir -p "$covdir"
coverageVol="-v $covdir:/coverage"
coverageFlags="-coverprofile=/coverage/coverage.txt -covermode=atomic"
fi
buildxCmd build $importCacheFlags \
--target "integration-tests" \
--output "type=docker,name=$iid" \
$currentcontext
cacheVolume="buildx-cache"
if ! docker inspect "$cacheVolume" > /dev/null 2>&1; then
cacheVolume=$(docker create --name=buildx-cache -v /root/.cache -v /go/pkg/mod alpine)
fi
docker run --rm -v /tmp $coverageVol --volumes-from=$cacheVolume --privileged $iid go test $coverageFlags ${TESTFLAGS:--v} ${TESTPKGS:-./...}
if [ -n "$BUILDX_NOCACHE" ]; then
docker rm -v $cacheVolume
fi
rm "$iidfile"
docker rmi $iid

161
hack/test-driver Executable file
View File

@@ -0,0 +1,161 @@
#!/usr/bin/env bash
set -eu -o pipefail
: ${BUILDX_CMD=docker buildx}
: ${BUILDKIT_IMAGE=moby/buildkit:buildx-stable-1}
: ${BUILDKIT_CFG=}
: ${DRIVER=docker-container}
: ${DRIVER_OPT=}
: ${MULTI_NODE=0}
: ${PLATFORMS=linux/amd64,linux/arm64}
function buildxCmd {
(set -x ; $BUILDX_CMD "$@")
}
function clean {
rm -rf "$context"
if [ "$builderName" != "default" ]; then
buildxCmd rm "$builderName"
fi
}
context=$(mktemp -d -t buildx-output.XXXXXXXXXX)
dockerfile=${context}/Dockerfile
bakedef=${context}/docker-bake.hcl
trap clean EXIT
builderName=buildx-test-$(openssl rand -hex 16)
buildPlatformFlag=
if [ "$DRIVER" = "docker" ]; then
builderName=default
else
buildPlatformFlag=--platform="${PLATFORMS}"
fi
driverOpt=image=${BUILDKIT_IMAGE}
if [ -n "$DRIVER_OPT" ]; then
driverOpt=$driverOpt,$DRIVER_OPT
fi
# create builder except for docker driver
if [ "$DRIVER" != "docker" ]; then
if [ "${MULTI_NODE}" = "1" ]; then
firstNode=1
for platform in ${PLATFORMS//,/ }; do
createFlags=""
if [ -f "$BUILDKIT_CFG" ]; then
createFlags="$createFlags --config=${BUILDKIT_CFG}"
fi
if [ "$firstNode" = "0" ]; then
createFlags="$createFlags --append"
fi
buildxCmd create ${createFlags} \
--name="${builderName}" \
--node="${builderName}-${platform/\//-}" \
--driver="${DRIVER}" \
--driver-opt="${driverOpt}" \
--platform="${platform}"
firstNode=0
done
else
createFlags=""
if [ -f "$BUILDKIT_CFG" ]; then
createFlags="$createFlags --config=${BUILDKIT_CFG}"
fi
buildxCmd create ${createFlags} \
--name="${builderName}" \
--driver="${DRIVER}" \
--driver-opt="${driverOpt}" \
--platform="${PLATFORMS}"
fi
fi
function buildOutput {
local name=$1
if [ "$DRIVER" != "docker" ]; then
if [ "${MULTI_NODE}" = "1" ]; then
echo "type=cacheonly"
else
echo "type=oci,dest=${context}/${name}.tar"
fi
else
echo "type=docker,name=${name}"
fi
}
# multi-platform not supported by docker driver
buildPlatformFlag=
bakePlatformFlag=
if [ "$DRIVER" != "docker" ]; then
buildPlatformFlag=--platform="${PLATFORMS}"
bakePlatformFlag=--set="*.platform=${PLATFORMS}"
fi
# inspect and bootstrap
buildxCmd inspect --bootstrap --builder="${builderName}"
# create dockerfile
cat > "${dockerfile}" <<EOL
FROM busybox as build
ARG TARGETPLATFORM
ARG BUILDPLATFORM
RUN echo "I am running on \$BUILDPLATFORM, building for \$TARGETPLATFORM" > /log
FROM busybox AS log
COPY --from=build /log /log
RUN cat /log
RUN uname -a
FROM busybox AS hello
RUN echo hello > /hello
FROM scratch
COPY --from=log /log /log
COPY --from=hello /hello /hello
EOL
# build
buildxCmd build ${buildPlatformFlag} \
--output="$(buildOutput buildx-test-build)" \
--builder="${builderName}" \
--metadata-file="${context}/metadata-build.json" \
"${context}"
cat "${context}/metadata-build.json"
# create bake def
cat > "${bakedef}" <<EOL
group "default" {
targets = ["release"]
}
group "all" {
targets = ["log", "hello"]
}
target "release" {
output = ["$(buildOutput buildx-test-bake-release)"]
}
target "log" {
output = ["$(buildOutput buildx-test-bake-log)"]
}
target "hello" {
output = ["$(buildOutput buildx-test-bake-hello)"]
}
EOL
# bake default target
buildxCmd bake ${bakePlatformFlag} \
--file="${bakedef}" \
--builder="${builderName}" \
--set "*.context=${context}" \
--metadata-file="${context}/metadata-bake-def.json"
cat "${context}/metadata-bake-def.json"
# bake all target
buildxCmd bake ${bakePlatformFlag} \
--file="${bakedef}" \
--builder="${builderName}" \
--set "*.context=${context}" \
--metadata-file="${context}/metadata-bake-all.json" \
all
cat "${context}/metadata-bake-all.json"

View File

@@ -1,16 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
. $(dirname $0)/util set -eu -o pipefail
set -eu
: ${BUILDX_CMD=docker buildx}
: ${FORMATS=md}
output=$(mktemp -d -t buildx-output.XXXXXXXXXX) output=$(mktemp -d -t buildx-output.XXXXXXXXXX)
(set -x ; DOCS_FORMATS=$FORMATS ${BUILDX_CMD} bake --set "*.output=$output" update-docs)
buildxCmd build \
--target "update" \
--output "type=local,dest=$output" \
--file "./hack/dockerfiles/docs.Dockerfile" \
.
rm -rf ./docs/reference/* rm -rf ./docs/reference/*
cp -R "$output"/out/* ./docs/ cp -R "$output"/out/* ./docs/
rm -rf $output rm -rf $output

View File

@@ -1,16 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
. $(dirname $0)/util set -eu -o pipefail
set -eu
: ${BUILDX_CMD=docker buildx}
output=$(mktemp -d -t buildx-output.XXXXXXXXXX) output=$(mktemp -d -t buildx-output.XXXXXXXXXX)
(set -x ; ${BUILDX_CMD} bake --set "*.output=$output" update-vendor)
buildxCmd build \
--target "update" \
--output "type=local,dest=$output" \
--file "./hack/dockerfiles/vendor.Dockerfile" \
.
rm -rf ./vendor rm -rf ./vendor
cp -R "$output"/out/* . cp -R "$output"/out/* .
rm -rf $output rm -rf $output

View File

@@ -1,66 +0,0 @@
#!/usr/bin/env sh
: ${CI=}
: ${PREFER_BUILDCTL=}
: ${PREFER_LEGACY=}
: ${CLI_PLATFORM=}
: ${GITHUB_ACTIONS=}
: ${CACHEDIR_FROM=}
: ${CACHEDIR_TO=}
if [ "$PREFER_BUILDCTL" = "1" ]; then
echo >&2 "WARNING: PREFER_BUILDCTL is no longer supported. Ignoring."
fi
if [ "$PREFER_LEGACY" = "1" ]; then
echo >&2 "WARNING: PREFER_LEGACY is no longer supported. Ignoring."
fi
progressFlag=""
if [ "$CI" = "true" ]; then
progressFlag="--progress=plain"
fi
buildxCmd() {
if docker buildx version >/dev/null 2>&1; then
set -x
docker buildx "$@" $progressFlag
elif buildx version >/dev/null 2>&1; then
set -x
buildx "$@" $progressFlag
elif docker version >/dev/null 2>&1; then
set -x
DOCKER_BUILDKIT=1 docker "$@" $progressFlag
else
echo >&2 "ERROR: Please enable DOCKER_BUILDKIT or install standalone buildx"
exit 1
fi
}
if [ -z "$CLI_PLATFORM" ]; then
if [ "$(uname -s)" = "Darwin" ]; then
arch="$(uname -m)"
if [ "$arch" = "x86_64" ]; then
arch="amd64"
fi
CLI_PLATFORM="darwin/$arch"
elif uname -s | grep MINGW > /dev/null 2>&1 ; then
CLI_PLATFORM="windows/amd64"
fi
fi
cacheType=""
cacheRefFrom=""
cacheRefTo=""
currentref=""
if [ "$GITHUB_ACTIONS" = "true" ]; then
currentref="git://github.com/$GITHUB_REPOSITORY#$GITHUB_REF"
cacheType="local"
cacheRefFrom="$CACHEDIR_FROM"
cacheRefTo="$CACHEDIR_TO"
fi
currentcontext="."
if [ -n "$currentref" ]; then
currentcontext="--build-arg BUILDKIT_CONTEXT_KEEP_GIT_DIR=1 $currentref"
fi

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