Compare commits

..

583 Commits
v0.3.1 ... v0.8

Author SHA1 Message Date
Tõnis Tiigi
6224def4dd Merge pull request #1042 from tonistiigi/update-buildkit-220403-v0.8
[v0.8] vendor: update buildkit to 10e6f94b
2022-04-03 15:35:43 -07:00
Tõnis Tiigi
5abee699a6 Merge pull request #1022 from crazy-max/v0.8-bake-fix-visited-group
[v0.8] bake: fix skipped group when already visited by another one
2022-04-03 15:15:00 -07:00
Tonis Tiigi
54e497dfba vendor: update buildkit to 10e6f94b
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2022-04-03 15:06:21 -07:00
Tõnis Tiigi
dfbd226285 Merge pull request #1034 from crazy-max/v0.8-update-compose-go
[v0.8] update github.com/compose-spec/compose-go to v1.2.1
2022-04-01 15:48:13 -07:00
CrazyMax
a78c2957a2 compose: add test for port mapping
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
(cherry picked from commit 243b428a58)
2022-03-30 00:38:22 +02:00
CrazyMax
e80890ec89 update github.com/compose-spec/compose-go to v1.2.1
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
(cherry picked from commit 785dc17f13)
2022-03-30 00:38:22 +02:00
CrazyMax
10fe8f380c bake: fix skipped group when already visited by another one
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
(cherry picked from commit 0b8dde1071)
2022-03-23 11:18:17 +01:00
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
CrazyMax
260d07a9a1 Merge pull request #704 from crazy-max/use-compose-config-file
Set `ConfigFile` to parse compose files with bake
2021-07-30 18:35:00 +02:00
CrazyMax
9aa8f09f14 Set ConfigFile to parse compose files with bake
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-07-30 17:56:40 +02:00
CrazyMax
0363b676bc Merge pull request #697 from docker/dependabot/go_modules/github.com/containerd/containerd-1.5.4
build(deps): bump github.com/containerd/containerd from 1.5.2 to 1.5.4
2021-07-28 14:09:33 +02:00
dependabot[bot]
a10045e8cb build(deps): bump github.com/containerd/containerd from 1.5.2 to 1.5.4
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.5.2 to 1.5.4.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.5.2...v1.5.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-26 21:37:28 +00:00
CrazyMax
0afcca221d Merge pull request #694 from crazy-max/codecov
Bump to codecov/codecov-action v2
2021-07-26 10:22:00 +02:00
CrazyMax
5daf176722 Merge pull request #693 from crazy-max/buildkit-progress
Duplicated progress env var
2021-07-25 22:18:29 +02:00
CrazyMax
3d1ab82dc6 Duplicated progress env var
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-07-25 12:40:13 +02:00
CrazyMax
872430d2d3 Bump to codecov/codecov-action v2
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-07-24 23:24:12 +02:00
CrazyMax
7d312eaa0a Merge pull request #686 from morlay/nil-client-fix
fix: should ignore nil client
2021-07-23 20:12:32 +02:00
CrazyMax
a6bc4ed21e Merge pull request #679 from akvadrako/patch-1
Fix link to generally useful functions
2021-07-23 08:19:53 +02:00
Morlay
3768ab268b fix: should ignore nil client
Signed-off-by: Morlay <morlay.null@gmail.com>
2021-07-21 15:47:55 +08:00
Devin Bayer
4c2daeb852 Fix link to generally useful functions
Signed-off-by: Devin Bayer <dev@doubly.so>
2021-07-20 18:09:53 +02:00
Tõnis Tiigi
d9ee3b134c Merge pull request #676 from tonistiigi/vol-delete
don't error on deleting old build containers without state volume
2021-07-16 12:37:37 -07:00
Tonis Tiigi
0b6ba1cd32 don't error on deleting old build containers without state volume
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-07-16 12:00:27 -07:00
Tõnis Tiigi
65a6955db8 Merge pull request #672 from crazy-max/keep-buildkit-state
Keep BuildKit state in a volume
2021-07-13 10:21:51 -07:00
CrazyMax
258d12b2e7 Keep BuildKit state in a volume
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-07-13 18:09:35 +02:00
Tõnis Tiigi
6e3a319a9d Merge pull request #671 from tonistiigi/remote-context
bake: allow BAKE_CMD_CONTEXT builtin var
2021-07-13 08:50:27 -07:00
Tonis Tiigi
1bb425a882 bake: allow BAKE_CMD_CONTEXT builtin var
Allows accessing the main context for bake command from bake
file that has been imported remotely.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-07-12 21:01:41 -07:00
Tõnis Tiigi
5f6ad50df4 Merge pull request #635 from tonistiigi/otel
OpenTelemetry support
2021-07-12 14:27:32 -07:00
Tonis Tiigi
9d88450118 enable opentelemetry support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-07-12 13:42:52 -07:00
Tonis Tiigi
334c93fbbe vendor: update buildkit to opentelemetry support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-07-12 13:42:45 -07:00
Tõnis Tiigi
6ba080d337 Merge pull request #669 from crazy-max/compose-spec
Use compose-spec parser
2021-07-12 09:46:13 -07:00
CrazyMax
ba443811e4 Use compose-spec parser
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-07-12 11:21:04 +02:00
CrazyMax
67bd6f4dc8 Merge pull request #659 from crazy-max/release-out
Ignore release-out folder
2021-07-02 11:12:46 +02:00
CrazyMax
9f50eccbd7 Ignore release-out folder
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-07-02 07:18:18 +02:00
Tõnis Tiigi
12db50748b Merge pull request #645 from tonistiigi/new-bake-parser
New bake parser
2021-07-01 17:09:45 -07:00
CrazyMax
9b4937f062 Merge pull request #656 from crazy-max/buildx-image
Create buildx image
2021-06-30 20:22:38 +02:00
CrazyMax
3d48359e95 Create buildx image
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-06-30 16:01:28 +02:00
CrazyMax
70002ebbc7 Merge pull request #657 from crazy-max/moby-ioutils
Use `ioutils.AtomicWriteFile` from moby
2021-06-30 09:20:38 +02:00
CrazyMax
ef95f8135b Use ioutils.AtomicWriteFile from moby
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-06-30 08:34:45 +02:00
CrazyMax
9215fc56a3 Merge pull request #605 from crazy-max/bake-iidfile
Add `metadata-file` flag
2021-06-30 08:25:33 +02:00
CrazyMax
1253020b3d Add metadata-file flag
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-06-30 07:41:26 +02:00
Tõnis Tiigi
621c55066c Merge pull request #654 from tonistiigi/enable-win-arm64
Dockerfile: enable windows/arm64
2021-06-29 10:52:29 -07:00
Tonis Tiigi
77632ac15f Dockerfile: enable windows/arm64
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-06-29 08:55:05 -07:00
Tõnis Tiigi
db6aa34252 Merge pull request #653 from tonistiigi/xx-v2
Dockerfile: update xx
2021-06-29 08:53:25 -07:00
CrazyMax
7ecfd3d298 Merge pull request #652 from tonistiigi/enable-riscv64
enable linux/riscv64 builds
2021-06-29 09:52:44 +02:00
Tonis Tiigi
9a8c287629 Dockerfile: update xx
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-06-28 18:30:57 -07:00
Tonis Tiigi
591099a4b8 enable linux/riscv64 builds
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-06-28 18:16:53 -07:00
Tõnis Tiigi
31309b9205 Merge pull request #582 from rumpl/feat-fail-fast
Fail fast on multi platform build with load
2021-06-28 09:29:12 -07:00
CrazyMax
8c0cefcd89 Merge pull request #649 from admackin/patch-3
note that buildx not in all distros
2021-06-28 03:45:27 +02:00
Andy MacKinlay
a07f5cdf42 note that buildx not in all distros
Signed-off-by: Andy MacKinlay <admackin@gmail.com>
2021-06-28 09:41:19 +10:00
Djordje Lukic
a1d899d400 Fail fast on multi platform build with load
Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
2021-06-27 16:18:08 +02:00
CrazyMax
886e1a378c Merge pull request #639 from eitsupi/fix-link
Minor fix to links in the document
2021-06-25 14:51:31 +02:00
SHIMA Tatsuya
47b7ba4e79 change the default branch name of the linked repo
Signed-off-by: SHIMA Tatsuya <ts1s1andn@gmail.com>
2021-06-25 19:17:40 +09:00
Tonis Tiigi
79433cef7a bake: fix target merge between compose and hcl
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-06-24 22:40:32 -07:00
Tonis Tiigi
c5eb8f58b4 bake: new hclparser package
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-06-24 22:40:25 -07:00
Tõnis Tiigi
03b7128b60 Merge pull request #575 from tonistiigi/user-func-vars
bake: allow user functions in variables and vice-versa
2021-06-23 11:08:56 -07:00
Tõnis Tiigi
15b358bec6 Merge pull request #618 from MichalAugustyn/handle-resources
feat: add resources handling to kubernetes driver
2021-05-29 22:25:40 -07:00
Michal Augustyn
a53e392afb feat: add resources handling to kubernetes driver
Signed-off-by: Michal Augustyn <michal.augustyn@mail.com>
2021-05-29 13:45:52 +02:00
Akihiro Suda
4fec647b9d Merge pull request #613 from tonistiigi/docs-update
readme: update installation instructions
2021-05-19 15:50:28 +09:00
Tonis Tiigi
d7b28fb4d3 docs: update bake notes
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-05-17 22:53:56 -07:00
Tonis Tiigi
9bc9291fc9 readme: update installation instructions
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-05-17 22:33:08 -07:00
Tonis Tiigi
df7a318ec0 bake: allow user functions in variables and vice-versa
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-05-05 22:08:31 -07:00
Tõnis Tiigi
908a856079 Merge pull request #590 from AkihiroSuda/split-flagparser
build: split buildflags package
2021-04-22 11:50:57 -07:00
Akihiro Suda
8d64b6484f Merge pull request #592 from tonistiigi/descriptor-merge
imagetools: fix merging JSON descriptor with old one
2021-04-15 21:23:56 +09:00
Akihiro Suda
399df854ea build: split buildflags package
Planned to be imported by nerdctl in future.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2021-04-12 14:36:56 +09:00
Tonis Tiigi
328441cdc6 imagetools: fix merging JSON descriptor with old one
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-04-09 11:02:16 -07:00
Tõnis Tiigi
5ca0cbff8e Merge pull request #535 from tonistiigi/github-actions-cache
allow exporting to github cache backend
2021-04-09 10:30:46 -07:00
Tõnis Tiigi
ab09846df7 Merge pull request #583 from thaJeztah/docs_fixes
docs: address some review comments from the docs repository
2021-04-07 08:07:49 -07:00
Sebastiaan van Stijn
cd3a9ad38d docs: address some review comments from the docs repository
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-04-07 16:04:11 +02:00
Tõnis Tiigi
adc5f35237 Merge pull request #581 from alexcb/use-default-ssh-when-missing-v2
include default ssh socket when given an ssh-based git url
2021-04-01 19:32:18 -07:00
Alex Couture-Beil
0b984e429b Update buildkit
- updated buildkit to current code in master via:

  go mod edit -require github.com/moby/buildkit@master && go mod tidy && ./hack/update-vendor

Signed-off-by: Alex Couture-Beil <alex@earthly.dev>
2021-04-01 16:38:16 -07:00
Alex Couture-Beil
eec843a325 include default ssh socket when given an ssh-based git url
Signed-off-by: Alex Couture-Beil <alex@earthly.dev>
2021-04-01 11:25:18 -07:00
Tonis Tiigi
83868a48b7 temp local copy of userfunc
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-26 17:06:38 -07:00
Tõnis Tiigi
98d337af21 Merge pull request #541 from tonistiigi/json-vars-attrs
bake: allow attributes in global scope
2021-03-26 17:06:19 -07:00
Tõnis Tiigi
b2c7dc00cc Merge pull request #570 from thaJeztah/fix_trailing_whitespace
remove trailing whitespace in command descriptions
2021-03-26 16:47:29 -07:00
Sebastiaan van Stijn
44ddc5a02b remove trailing whitespace in command descriptions
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-25 23:19:15 +01:00
Tonis Tiigi
f036bba48c bake: add test for json vars and attributes
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-25 11:00:17 -07:00
Tonis Tiigi
0fe2ce7fac bake: allow attributes in global scope
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-25 11:00:17 -07:00
Tõnis Tiigi
0147b92230 Merge pull request #506 from thaJeztah/split_docs
Split docs to separate files
2021-03-24 22:14:43 -07:00
Tonis Tiigi
4047bccf6c docs: add external docs links support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-25 00:44:29 +01:00
Tonis Tiigi
363c0fdf4b hack: add docs generation/validation
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-25 00:44:26 +01:00
Tonis Tiigi
c46407b2d3 docs: reference docs updates
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-25 00:44:17 +01:00
Tonis Tiigi
ca0f5dabea docs: add md generation
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-25 00:38:04 +01:00
Sebastiaan van Stijn
17d4106e1b docs: fix markdown formatting and highlighting
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-25 00:38:02 +01:00
Sebastiaan van Stijn
442d38080e docs: use "examples" section, and rephrase headings
Put the flag descriptions/examples under an "examples"
section (used at docs.docker.com), and rephrase the
headings to be more consistent with other pages in the
docker documentation.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-25 00:38:01 +01:00
Sebastiaan van Stijn
87ec3af5bb docs: add stubs for all commands
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-25 00:37:59 +01:00
Sebastiaan van Stijn
1a8af33ff6 docs: standardize format for usage
Use the usage output of `--help` for each subcommand, to make
sure all flags/options are included on the page, and to make
it easier to keep docs in sync.

Note that the usage output is only used when reading these
docs on GitHub; docs.docker.com only consumes the "description"
and "example" sections (when present), and generates flag information
and usage output from source

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-25 00:37:57 +01:00
Sebastiaan van Stijn
ff749d8863 docs: split reference docs to separate files
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-25 00:37:53 +01:00
Tõnis Tiigi
2d86ddd37f Merge pull request #569 from thaJeztah/dont_discard_manifest_errors
imagetools inspect: don't discard errors from PrintManifestList
2021-03-24 09:23:43 -07:00
Sebastiaan van Stijn
e1bbb9d8de imagetools inspect: don't discard errors from PrintManifestList
Looks like this function may return an error, which we currently discard.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-03-24 15:54:32 +01:00
Tonis Tiigi
d7964be29c gha cache caps detection
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-23 08:58:50 -07:00
Tonis Tiigi
3fef64f584 allow exporting to github cache backend
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-23 08:58:50 -07:00
Akihiro Suda
319b6503a5 Merge pull request #565 from tonistiigi/buildkit-update-0322
vendor: update buildkit to 8effd45b
2021-03-23 18:32:30 +09:00
Tonis Tiigi
d40a6082fa vendor: update buildkit to 8effd45b
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-22 15:54:07 -07:00
Akihiro Suda
28809b82a2 Merge pull request #539 from tonistiigi/vars-cross-refs
bake: allow variables to reference each other
2021-03-19 18:34:29 +09:00
Tõnis Tiigi
c9f02c32d4 Merge pull request #558 from ulyssessouza/add-quiet-mode-to-progress-printer
Add quiet mode to progress printer
2021-03-09 13:07:48 -08:00
Ulysses Souza
55d5b80dfe Add quiet mode to progress printer
Signed-off-by: Ulysses Souza <ulyssessouza@gmail.com>
2021-03-09 17:12:46 -03:00
Tonis Tiigi
33f25acb08 bake: allow variables to reference each other
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-07 21:27:30 -08:00
Tonis Tiigi
0e9066f6ed bake: fix hcl tests layout
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-03-01 12:08:09 -08:00
Akihiro Suda
7d2e30096b Merge pull request #538 from tonistiigi/vars-multi-file 2021-02-27 18:11:36 +09:00
Tonis Tiigi
0e9d6460db bake: allow variables across files
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-25 16:30:27 -08:00
Tõnis Tiigi
927163bf13 Merge pull request #481 from fauust/patch-1
Update README.md
2021-02-25 16:05:16 -08:00
Tibor Vass
8ac1cf6e45 Merge pull request #545 from tonistiigi/update-go116
update to go1.16
2021-02-23 08:41:34 -08:00
Tonis Tiigi
dba79ba223 update lint to go1.16/golangci
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-16 23:43:06 -08:00
Tonis Tiigi
905be6431b Dockerfile: update to go1.16
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-16 23:42:59 -08:00
Tõnis Tiigi
ad95d6ba04 Merge pull request #532 from tonistiigi/vars-in-func
bake: allow variables in user functions
2021-02-10 10:06:13 -08:00
Tõnis Tiigi
b77690a373 Merge pull request #540 from tonistiigi/maintainers-update
add akihiro and crazy-max to maintainers
2021-02-10 09:52:50 -08:00
Akihiro Suda
84a734dc87 Merge pull request #531 from tonistiigi/net-none 2021-02-11 02:34:46 +09:00
Akihiro Suda
5079b64ab5 Merge pull request #536 from tonistiigi/hcl-update 2021-02-11 02:32:09 +09:00
Tonis Tiigi
6a343488d2 add akihiro and crazy-max to maintainers
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-10 09:18:05 -08:00
Tonis Tiigi
98c3ef60e6 vendor: update hcl
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-08 10:18:14 -08:00
faust
73fa351b1c Update README.md
typo

Signed-off-by: Faustin Lammler <faustin@fala.red>
2021-02-08 12:26:18 +01:00
Tibor Vass
c88f7fc307 Merge pull request #529 from tonistiigi/darwin-arm-ci
ci: enable building darwin/arm64 in CI
2021-02-05 08:54:06 -08:00
Tõnis Tiigi
55b8712268 Merge pull request #530 from HollowMan6/patch-1
Fix typos in README.md
2021-02-05 00:51:02 -08:00
Tonis Tiigi
7878f0c514 bake: allow variables in user functions
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-04 23:27:09 -08:00
Tonis Tiigi
0f09e2ecfe don't require entitlement for network none
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-04 22:45:20 -08:00
Hollow Man
bea3acd4b6 Fix typos
defintion -> definition
avaialble -> available
registed -> registered

Signed-off-by: Hollow Man <hollowman@hollowman.ml>
2021-02-05 14:26:26 +08:00
Tonis Tiigi
fb9004d6b2 ci: enable building darwin/arm64 in CI
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-04 19:00:41 -08:00
Tibor Vass
42b7e7bc56 Merge pull request #526 from tonistiigi/darwin-arm64
Dockerfile: add darwin/arm64 support
2021-02-04 17:07:07 -08:00
Tibor Vass
4b2ddd5b6e Merge pull request #527 from tonistiigi/avoid-cp
avoid cp in install target
2021-02-04 17:04:23 -08:00
Tonis Tiigi
b3006221f1 avoid cp in install target
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-04 13:29:16 -08:00
Tonis Tiigi
e57108e7c9 Dockerfile: add darwin/arm64 support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2021-02-04 12:23:23 -08:00
Tõnis Tiigi
6b3dc6687b Merge pull request #517 from tiborvass/execabs
Use golang.org/x/sys/execabs
2021-01-28 15:47:19 -08:00
Tibor Vass
92f6f9f973 Use golang.org/x/sys/execabs
Signed-off-by: Tibor Vass <tibor@docker.com>
2021-01-27 21:09:53 +00:00
Tõnis Tiigi
a56a4c00dd Merge pull request #503 from felipecrs/patch-1
Add setproduct function to the bake HCL
2021-01-19 16:54:35 -08:00
Tõnis Tiigi
ee4a115d4c Merge pull request #505 from thaJeztah/bump_cobra
vendor: github.com/spf13/cobra v1.1.1
2021-01-19 16:53:11 -08:00
Sebastiaan van Stijn
976a58c918 vendor: github.com/spf13/cobra v1.1.1
v1.1.1:

- Fix: yaml.v2 2.3.0 contained a unintended breaking change. This release reverts
  to yaml.v2 v2.2.8 which has recent critical CVE fixes, but does not have the
  breaking changes.
- Fix: correct internal formatting for go-md2man v2 (which caused man page
  generation to be broken).

v1.1.0:

- Extend Go completions and revamp zsh comp
- Add completion for help command
- Complete subcommands when TraverseChildren is set
- Fix stderr printing functions
- fix: fish output redirection

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-01-13 11:18:25 +01:00
Tõnis Tiigi
db82aa1b77 Merge pull request #504 from crazy-max/default-progress
Allow to set default progress through env var
2021-01-11 09:33:33 -08:00
CrazyMax
d05504c50f Allow to set default progress through env var
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2021-01-11 01:41:44 +01:00
Felipe Santos
f1f464e364 Add setproduct function to the bake HCL
As explained in the following link, it's a very useful function.

https://www.terraform.io/docs/configuration/functions/setproduct.html#finding-combinations-for-for_each
Signed-off-by: Felipe Santos <felipecassiors@gmail.com>
2021-01-10 06:11:14 +00:00
Tõnis Tiigi
57b875a955 Merge pull request #491 from crazy-max/hcl2-funcs
Extend hcl2 support with more functions
2021-01-04 10:44:23 -08:00
Tõnis Tiigi
ea5d32ddff Merge pull request #488 from crazy-max/secrets-env
Allow secrets with env
2021-01-04 10:40:09 -08:00
Tõnis Tiigi
da8c8ccaf5 Merge pull request #497 from morlay/k8s-driver-configuration-bind
feat: store kube config file to make buildx builder switchable
2021-01-04 10:39:14 -08:00
Tõnis Tiigi
dcbe4b3e1a Merge pull request #477 from morlay/master
should list real pod nodes when all drivers are kubernetes
2021-01-04 10:37:55 -08:00
Wang
68cebffe13 feat: store kube config file to make buildx builder switchable
Signed-off-by: Wang <morlay.null@gmail.com>
2020-12-30 17:28:14 +08:00
CrazyMax
96e7f3224a Extend hcl2 support with more functions
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-23 12:33:55 +01:00
CrazyMax
f6d83c97bb Allow secrets with env
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-19 04:18:51 +01:00
Tõnis Tiigi
74f76cf4e9 Merge pull request #480 from AkihiroSuda/bkimage-rootless
set DefaultRootlessImage to "moby/buildkit:buildx-stable-1-rootless"
2020-12-16 11:59:56 -08:00
Akihiro Suda
8b8725d1fd set DefaultRootlessImage to "moby/buildkit:buildx-stable-1-rootless"
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2020-12-16 17:35:38 +09:00
Tõnis Tiigi
20494f799d Merge pull request #470 from tonistiigi/dockerfile-1.2
update Dockerfile to v1.2
2020-12-15 10:35:55 -08:00
Wang Jinglei
dd13e16bc7 should list real pod nodes when all drivers are kubernetes
Signed-off-by: Wang <morlay.null@gmail.com>
2020-12-15 17:09:03 +08:00
Tõnis Tiigi
11057da373 Merge pull request #475 from tiborvass/fix_create_platform_regression
driver: do not insert "platform" as driver-opt
2020-12-14 23:37:22 -08:00
Tibor Vass
381dc8fb43 driver: do not insert "platform" as driver-opt
Addresses https://github.com/docker/setup-buildx-action/issues/45

Simple repro:
```
$ buildx create --platform linux/amd64 --use
$ buildx build - <<EOF
from scratch
EOF
```

Since https://github.com/docker/buildx/pull/370 a `platform` driver-opt was automatically inserted with the value specified by `--platform` flag on regardless of the type of driver, even though it was only used in the kubernetes driver. However, because the docker-container driver is pedantic about the options being passed, it errored out.

Another side-effect I suspect is that with the kubernetes driver it was now possible to specify the platforms in two different ways: `--driver-opt platform=...` and `--platform`.

This patch reverts completely the `platform` driver-opt and instead ensures the platforms information is passed onto the kubernetes driver via variables.

Signed-off-by: Tibor Vass <tibor@docker.com>
2020-12-15 07:09:46 +00:00
Tõnis Tiigi
780fad46f2 Merge pull request #471 from crazy-max/remove-travis
Remove travis support and unused buildmode
2020-12-09 20:40:15 -08:00
CrazyMax
2ca5ffa06a Remove travis support and unused buildmode
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-09 07:51:32 +01:00
Tonis Tiigi
f349ba8750 update Dockerfile to v1.2
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-08 15:44:08 -08:00
Tõnis Tiigi
33e3ca524e Merge pull request #442 from tonistiigi/moby-push
build: add push support to docker driver
2020-12-08 15:01:37 -08:00
Tõnis Tiigi
ea1a71dc07 Merge pull request #467 from cpuguy83/deterministic_output
Get multi-platform buildkit frontend opt from args
2020-12-08 15:00:57 -08:00
Tõnis Tiigi
ae820293a2 Merge pull request #468 from crazy-max/ghactions-release
GitHub Actions release
2020-12-08 13:57:38 -08:00
Tonis Tiigi
f68f42cb11 build: add push support to docker driver
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-08 13:54:41 -08:00
Brian Goff
7f58ad45fa Get multi-platform buildkit frontend opt from args
This allows builders to opt into determnistic output regardless of
multi-platform output or not.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-12-08 13:18:56 -08:00
Tõnis Tiigi
aa7c17989b Merge pull request #469 from tonistiigi/buildkit-update-1207
vendor: update buildkit to v0.8
2020-12-08 13:10:07 -08:00
Tonis Tiigi
6b6afc4077 build: add logger for auth
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 23:43:28 -08:00
Tonis Tiigi
69a1419ab1 vendor: update buildkit to v0.8
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 23:16:06 -08:00
Tõnis Tiigi
080e9981c7 Merge pull request #398 from tonistiigi/remote-bake
bake: remote inputs support
2020-12-07 23:07:24 -08:00
CrazyMax
8cc00ab486 GitHub Actions release
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-08 07:20:41 +01:00
Tonis Tiigi
40fad4bbb5 progress: make sure all channels have written before returning
Possible write on closed channel on cancellation before.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 22:02:51 -08:00
Tonis Tiigi
232af9aa0d move moby check to driver interface
Driver caching masked the method detection

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 22:02:51 -08:00
Tonis Tiigi
5bf2ff98c9 bake: support filenames without suffix
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 22:02:51 -08:00
Tonis Tiigi
570e733a51 bake: support inline dockerfile
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 22:02:51 -08:00
Tonis Tiigi
cffcd57edb bake: support for remote files
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 22:02:51 -08:00
Tonis Tiigi
1496ac9b55 util: simplify progress syncronization
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 22:02:51 -08:00
Tonis Tiigi
290e25917c build: allow dockerfile from URL
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 22:02:51 -08:00
Tõnis Tiigi
0360668cc1 Merge pull request #428 from zanven42/master
fixes #427: Handle empty strings in elements enabling conditional logic
2020-12-07 21:59:43 -08:00
Tõnis Tiigi
343a4753c7 Merge pull request #460 from morlay/master
fix: buildx in k8s pod with kube client config in cluster
2020-12-07 21:59:23 -08:00
Wang
d827f42d38 fix: buildx in k8s pod with kube client config in cluster
Signed-off-by: Wang <morlay.null@gmail.com>
2020-12-08 13:17:23 +08:00
Tonis Tiigi
5843e67a90 fix indentations in example
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-12-07 21:02:48 -08:00
Tõnis Tiigi
517df133e3 Merge pull request #403 from jygastaud/master
fixes #401 : Replace error generated by `quiet` option by a warning.
2020-12-07 20:57:09 -08:00
Tõnis Tiigi
621114fbe1 Merge pull request #465 from crazy-max/update-vendor
Update vendor and binaries script
2020-12-07 20:55:32 -08:00
Tõnis Tiigi
2066051d3a Merge pull request #466 from crazy-max/ghactions-cross
GitHub Actions cross
2020-12-07 20:53:09 -08:00
CrazyMax
d94cbd870c GitHub Actions cross
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-07 17:29:35 +01:00
CrazyMax
48f15dcf3d Update vendor and binaries script
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-07 17:14:45 +01:00
Tõnis Tiigi
35a60b8e04 Merge pull request #441 from tonistiigi/buildkit-pull-creds2
refactor driver auth for easier passing
2020-12-05 00:02:38 -08:00
CrazyMax
4b3df09155 Merge pull request #463 from crazy-max/ghactions-test
GitHub Actions for test
2020-12-05 08:13:41 +01:00
CrazyMax
b1215c2ce2 GitHub Actions for test
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-12-05 04:18:28 +01:00
Tõnis Tiigi
99ac03f9f3 Merge pull request #451 from docker/ghactions-validate
Refactor validate for GitHub Actions
2020-12-04 15:19:37 -08:00
Tõnis Tiigi
a0aa45a4a7 Merge pull request #462 from zencargo/userns
Disable user namespace remapping in docker-container driver
2020-12-03 20:03:32 -08:00
Tõnis Tiigi
aab3a92890 Merge pull request #444 from tonistiigi/lowercase-dockerfile
build: handle lowercase Dockerfile name as a fallback
2020-12-03 17:54:47 -08:00
Andrew Haines
37020dc8da Disable user namespace remapping in docker-container driver
Signed-off-by: Andrew Haines <andrew.haines@zencargo.com>
2020-12-03 10:50:00 +00:00
CrazyMax
d66d3a2d09 Refactor validate for GitHub Actions
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
2020-11-30 08:14:28 +01:00
Tonis Tiigi
f057195a4f build: handle lowercase Dockerfile name as a fallback
This was supported by the legacy builder: moby#10858

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-11-18 22:11:35 -08:00
Tonis Tiigi
378bf70d4b refactor driver auth for easier passing
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-11-15 20:49:58 -08:00
Tõnis Tiigi
1ccf0bd7d8 Merge pull request #433 from tonistiigi/buildkit-pull-creds
docker-container: ensure credentials are passed when pulling buildkit
2020-11-15 20:13:27 -08:00
Tõnis Tiigi
ddbfddce88 Merge pull request #425 from tonistiigi/default-builder
allow builder flag to switch to default instance
2020-11-12 11:07:37 -08:00
Tonis Tiigi
ea19cf9d8d inspect: make sure to show boot error from driver
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-11-04 14:39:54 -08:00
Tonis Tiigi
3b69482a2f docker-container: ensure credentials are passed when pulling buildkit image
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-11-04 13:04:04 -08:00
Tõnis Tiigi
778fbb4669 Merge pull request #390 from tonistiigi/fix-warn
build: avoid warn on empty config value
2020-11-02 19:36:20 -08:00
Tõnis Tiigi
13533e359a Merge pull request #432 from thaJeztah/bump_console_v1.0.1
vendor: containerd/console v1.0.1, and remove golang.org/x/sys replace
2020-11-02 19:35:34 -08:00
Sebastiaan van Stijn
3c2d0aa667 vendor: containerd/console v1.0.1, and remove golang.org/x/sys replace
this removes the replace rule that was added in 3c94621142,
to fix compile failures for macOS.

containerd/console v1.0.1 fixes those issues, which allows us to remove the
replace rule again.

full diff: https://github.com/containerd/console/compare/v1.0.0...v1.0.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-10-31 14:18:57 +01:00
Sebastiaan van Stijn
5551de4b8a go.mod: reformat file
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-10-31 13:50:45 +01:00
Sebastiaan van Stijn
fa51b90094 vendor: fix docker/docker vendoring (update to 9f28837c1d93
commit c41b006be1 updated the version of
docker/docker in go.mod, but possibly overlooked that there was still a
replace rule present. As a result the version was not actually updated.

This patch removes the replace rule, updating docker/docker to 9f28837c1d93

full diff: 4634ce647c...9f28837c1d

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-10-31 13:50:34 +01:00
Anthony Poschen
bfd1ea3877 Added doco for conditonal tags in bake file.
Signed-off-by: Anthony Poschen <zanven42@gmail.com>
2020-10-26 16:37:28 +11:00
Anthony Poschen
abfb2c064d Add support for empty strings in target elements and compact func.
Signed-off-by: Anthony Poschen <zanven42@gmail.com>
2020-10-26 15:56:18 +11:00
Tonis Tiigi
4f7517115c allow builder flag to switch to default instance
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-10-21 23:19:28 -07:00
Tonis Tiigi
1621b9bad0 build: avoid warn on empty config value
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-10-16 23:05:37 -07:00
Tibor Vass
d2bf42f8b4 Merge pull request #397 from tonistiigi/update-buildkit-20200919
vendor: update buildkit to 2943a0838
2020-10-14 01:03:56 -07:00
Tõnis Tiigi
d1a46faf84 Merge pull request #404 from thaJeztah/remove_annotations
Remove "version" annotations from du and prune commands
2020-09-29 14:35:05 -07:00
Sebastiaan van Stijn
39f1d99dcc Remove "version" annotations from du and prune commands
These annotations were picked up by the YAML docs generator, and shows up as
"minimum API version". I couldn't find a reference to these annotations in the
PR that added them, so thought it would be ok to remove

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-09-29 23:13:53 +02:00
Jean-Yves Gastaud
1f04ec9575 fixes #401
Replace error generated by `quiet` option by a warning.

Signed-off-by: Jean-Yves Gastaud <jygastaud@gmail.com>
2020-09-28 11:24:14 +02:00
Tõnis Tiigi
ac2e081528 Merge pull request #391 from tonistiigi/hcl-errors
bake: format hcl errors with source definition
2020-09-19 23:19:41 -07:00
Tonis Tiigi
95ac9ebb8a bake: format hcl errors with source definition
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-09-19 22:50:47 -07:00
Tonis Tiigi
c41b006be1 vendor: update buildkit to 2943a0838
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-09-19 22:49:12 -07:00
Sebastiaan van Stijn
92fb995505 Merge pull request #389 from tonistiigi/buildkit-update-20200913
vendor: update buildkit with typed errors support
2020-09-17 12:40:34 +02:00
Tonis Tiigi
3c94621142 vendor: downgrade sys unix
containerd and moby packages do not build in darwin anymore

6fcdbc0bbc

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-09-14 20:50:09 -07:00
Tonis Tiigi
2d720a1e0b vendor: update buildkit with typed errors support
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-09-13 21:28:20 -07:00
Tõnis Tiigi
0269388aa7 Merge pull request #385 from errordeveloper/fix-308
Allow users to have separate store paths
2020-09-10 10:24:05 -07:00
Ilya Dmitrichenko
4b2aab09b5 Allow users to have separate store paths
- decouple store path from `$DOCKER_CONFIG`
- improve containerised build setup
- introduce new `$BUILDX_CONFIG` environment variable

Signed-off-by: Ilya Dmitrichenko <errordeveloper@gmail.com>
2020-09-10 17:14:41 +01:00
Tõnis Tiigi
1c7434a8f0 Merge pull request #372 from thaJeztah/arch_detect
hack/util: take other arches into account on Darwin
2020-09-02 08:30:22 -07:00
Sebastiaan van Stijn
20f8f67928 hack/util: take other arches into account on Darwin
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-31 11:40:10 +02:00
Tõnis Tiigi
159535261d Merge pull request #370 from morlay/kubernetes-driver-enhancement
feat: enhance kubernetes driver
2020-08-30 19:35:13 -07:00
Tõnis Tiigi
a840e891fe Merge pull request #371 from tonistiigi/update-travis-image
travis: update base image
2020-08-30 19:33:34 -07:00
Wang Jinglei
a7c704c39d feat: enhance kubernetes driver
Signed-off-by: Wang Jinglei <morlay.null@gmail.com>
2020-08-31 09:20:49 +08:00
Tonis Tiigi
e1c0eb2187 travis: update base image
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-08-30 17:26:20 -07:00
Sebastiaan van Stijn
aa8ab9fcca hack/util: fix mixed tab/spaces for indentation
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-30 13:00:37 +02:00
Sebastiaan van Stijn
a746959fc1 hack: fix SC2069 : to redirect stdout+stderr, 2>&1 must be last
See https://github.com/koalaman/shellcheck/wiki/SC2069

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-30 12:57:19 +02:00
Tõnis Tiigi
ee34eb2180 Merge pull request #368 from morlay/master
feat: use k8s cluster config when buildx used in k8s cluster
2020-08-29 12:15:17 -07:00
Wang Jinglei
844b901005 feat: use k8s cluster config when buildx used in k8s cluster
Signed-off-by: Wang Jinglei <morlay.null@gmail.com>
2020-08-28 17:51:18 +08:00
Tõnis Tiigi
83ebc13a37 Merge pull request #297 from AkihiroSuda/doc-linuxkit-binfmt
README.md: add usage of tonistiigi/binfmt
2020-08-26 16:49:01 -07:00
Akihiro Suda
82c8f2d8f0 README.md: add usage of tonistiigi/binfmt
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2020-08-26 13:52:26 +09:00
Tõnis Tiigi
a11cc8840e Merge pull request #360 from kotaroooo0/fix-error-message-for-push-option
build: fix error message for --push option
2020-08-25 15:59:14 -07:00
Tõnis Tiigi
35ee6ce62d Merge pull request #363 from thaJeztah/bump_deps
Update dependencies
2020-08-25 11:37:26 -07:00
Sebastiaan van Stijn
37861cb99f vendor: hashicorp/hcl v2.6.0
full diff: https://github.com/hashicorp/hcl/compare/v2.4.0...v2.6.0

v2.6.0
-------------------------

Enhancements:

- hcldec: Add a new Spec, ValidateSpec, which allows custom validation of values at decode-time.

Bugs Fixed:

- hclsyntax: Fix panic with combination of sequences and null arguments
- hclsyntax: Fix handling of unknown values and sequences

v2.5.1
-------------------------

- hclwrite: handle legacy dot access of numeric indexes. (#369)
- hclwrite: Fix panic for dotted full splat (foo.*)

v2.5.0
-------------------------

- hclwrite: Generate multi-line objects and maps.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-24 11:04:40 +02:00
Sebastiaan van Stijn
a178d05023 vendor: github.com/gofrs/flock v0.7.3
full diff: https://github.com/gofrs/flock/compare/v0.7.0...v0.7.3

v0.7.3
-------------------------

- Fix issues in the license file, update year.

v0.7.2
-------------------------

- Ensure we release file handle if we failed to take an exclusive lock

v0.7.1
-------------------------

- Fix linting issues and add goreportcard badge

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-24 11:02:16 +02:00
Sebastiaan van Stijn
ee9ccfe2e3 vendor: buildkit v0.7.2
v0.7.2
----------------

Fixes:

- solver: gracefully handle cache loading errors
- remotecache: only visit each item once when walking results
- cache: avoid possible nil dereference on error handling
- contenthash: allow security.capability in cache checksum
- contenthash: treat unix sockets as regular files
- push: fix race condition on pushing the same layers in parallel
- inline cache: fix handling of duplicate blobs in same image
- gateway: fix metadata getting lost on subsolve in external frontend
- filesync: avoid ignoring close error
- runc: update runc binary to v1.0.0-rc91
- buildctl-daemonless: allow max retries on socket connect for buildctl
- buildctl-daemonless: fix shell args expansion
- buildctl-daemonless: show log on startup timeout

v0.7.1
----------------

Fixes:

- git: use --force flag on fetch
- tar exporter: handle symlinks properly
- resolver: disable http2 for pushing

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-24 10:30:00 +02:00
Sebastiaan van Stijn
c6c4b4a871 vendor: sirupsen/logrus v1.6.0
1.6.0
--------------------------

Fixes:
  * end of line cleanup
  * revert the entry concurrency bug fix whic leads to deadlock under some circumstances
  * update dependency on go-windows-terminal-sequences to fix a crash with go 1.14

Features:
  * add an option to the `TextFormatter` to completely disable fields quoting

1.5.0
--------------------------

Code quality:
  * add golangci linter run on travis

Fixes:
  * add mutex for hooks concurrent access on `Entry` data
  * caller function field for go1.14
  * fix build issue for gopherjs target

Feature:
  * add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
  * add a `DisableHTMLEscape` option in the `JSONFormatter`
  * add `ForceQuote` and `PadLevelText` options in the `TextFormatter`

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-24 10:26:07 +02:00
Sebastiaan van Stijn
273c6a75a2 vendor: update Cobra v1.0.0
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-08-24 10:20:59 +02:00
Kotaro Adachi
1384bf02f9 error message for --push option
Signed-off-by: Kotaro Adachi <k33asby@gmail.com>
2020-08-23 00:03:13 +09:00
Tõnis Tiigi
fb7b670b76 Merge pull request #338 from tonistiigi/load-no-warn
build: avoid warning if default load disabled
2020-08-21 19:36:07 -07:00
Tõnis Tiigi
9ac5b075cf Merge pull request #323 from saulshanabrook/patch-1
Increase ls and inspect timeouts
2020-08-21 12:44:33 -07:00
Tonis Tiigi
0124b6b9c9 build: avoid warning if default load disabled
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-08-21 12:43:49 -07:00
Tõnis Tiigi
691a1479fc Merge pull request #351 from tonistiigi/multi-iidfile
build: remove warning for multi-platform iidfile
2020-08-21 12:29:47 -07:00
Tonis Tiigi
c9d69b082b build: remove warning for multi-platform iidfile
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-08-17 22:34:45 -07:00
Tõnis Tiigi
e24e04be57 Merge pull request #332 from tonistiigi/bootstrap-logs
docker-container: show logs on bootstrap error
2020-07-29 19:33:08 -07:00
Tõnis Tiigi
38f9241316 Merge pull request #337 from tonistiigi/cacheonly-exporter
build: support cacheonly exporter
2020-07-29 19:32:54 -07:00
Tonis Tiigi
3862ff269b build: add opt-out from default load behavior
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-07-27 22:38:06 -07:00
Tonis Tiigi
9e5321eab8 build: support cacheonly exporter
cacheonly is supported by moby so add support for buildx
as well so same flags can be used

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-07-27 22:33:44 -07:00
Tibor Vass
f2cf7cf281 Merge pull request #333 from tonistiigi/dockerd-log
demo-env: correct dockerd logging
2020-07-27 17:44:06 +02:00
Tonis Tiigi
8961d3573e hack: correct dockerd logging
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-07-26 00:13:02 -07:00
Tonis Tiigi
26570d05c1 docker-container: increase bootstrap timeout
Previous value was only 2 sec

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-07-26 00:11:33 -07:00
Tonis Tiigi
8627f668f2 docker-container: show logs on bootstrap error
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-07-26 00:11:27 -07:00
Tõnis Tiigi
d8ca46066d Merge pull request #321 from errordeveloper/fix-320
bake: ensure `--builder` is wired from root options
2020-07-19 15:58:33 -07:00
Saul Shanabrook
c00c5a89e5 Increase inspect timeout from 5 to 20 seconds
Signed-off-by: Saul Shanabrook <s.shanabrook@gmail.com>
2020-07-16 11:56:40 -04:00
Saul Shanabrook
14b7936c3b Increase ls timeout from 7 to 20 seconds
Signed-off-by: Saul Shanabrook <s.shanabrook@gmail.com>
2020-07-16 11:56:40 -04:00
Ilya Dmitrichenko
40b41ac6e4 bake: ensure --builder is wired from root options
Signed-off-by: Ilya Dmitrichenko <errordeveloper@gmail.com>
2020-07-08 12:03:15 +01:00
Tõnis Tiigi
fd6de6b6ae Merge pull request #281 from tonistiigi/load-fix
build: improve error checking on load
2020-07-07 09:06:34 -07:00
Tõnis Tiigi
f3111bcbef Merge pull request #312 from donhui/master
README.md: update the content which not display in markdown
2020-06-20 17:41:31 -07:00
Donghui Wang
e6be472831 update the content which not display in markdown
Signed-off-by: Donghui Wang <977675308@qq.com>
2020-06-19 17:30:49 +08:00
Tibor Vass
e5217f26e2 Merge pull request #296 from tonistiigi/seed-fix
cmd: seed math rand
2020-05-18 12:03:57 -07:00
Tonis Tiigi
7f7acf7837 cmd: seed math rand
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-05-17 16:21:11 -07:00
Tonis Tiigi
baae4b2e71 build: improve error checking on load
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-05-08 17:46:28 -07:00
Tõnis Tiigi
42448c5f37 Merge pull request #280 from vanstee/hcl-json-support
Support parsing json config with hcl v2
2020-05-08 09:25:25 -07:00
Tõnis Tiigi
fc7875675c Merge pull request #277 from cpuguy83/moar_hcl_stdlib_funcs
Update go-cty to pull in more stdlib funcs.
2020-05-08 09:23:47 -07:00
Patrick Van Stee
355261e49e Parse bake config as hcl falling back to json
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-05-07 23:53:49 -04:00
Patrick Van Stee
44c840b31d Add test of parsing a json bake config
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-05-07 23:53:49 -04:00
Patrick Van Stee
1bc068a583 Fix json keys for groups and targets
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-05-07 23:53:49 -04:00
Patrick Van Stee
340686a383 Support parsing json config with hcl v2
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-05-07 23:53:49 -04:00
Brian Goff
1ad87c6ba6 Update go-cty to pull in more stdlib funcs.
I needed "split" specifically so I can do something like:

```hcl
variable PLATFORMS {
  default = "linux/amd64"
}

target foo {
  platforms = split(",", "${PLATFORMS}")
  # other stuff
}
```

Where the existing "csvdecode" does not work for this because it parses
the string into a list of objects instead of a list of strings.

I went ahead and just added all the available new functions.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-05-07 16:05:17 -07:00
Tõnis Tiigi
eadf5eddbc Merge pull request #270 from thaJeztah/prune_force_shorthand
Add -f shorthand flag for prune --force
2020-05-07 11:41:32 -07:00
Sebastiaan van Stijn
f4f58003fb Add -f shorthand flag for prune --force
The docker builder prune command has a shorthand `-f` flag for `--force`:

    docker builder prune --help

    Usage:	docker builder prune

    Remove build cache

    Options:
      -a, --all                  Remove all unused build cache, not just dangling ones
          --filter filter        Provide filter values (e.g. 'until=24h')
      -f, --force                Do not prompt for confirmation
          --keep-storage bytes   Amount of disk space to keep for cache

Given that `buildx` can be used as a drop-in replacement for the native build
commands, it should match the UI, and also have a shorthand flag.

This patch also updates the flag's description to be in line with the docker commandline

With this patch applied;

    buildx prune --help
    Remove build cache

    Usage:
      buildx prune [flags]

    Flags:
      -a, --all                  Remove all unused images, not just dangling ones
          --filter filter        Provide filter values (e.g. 'until=24h')
      -f, --force                Do not prompt for confirmation
      -h, --help                 help for prune
          --keep-storage bytes   Amount of disk space to keep for cache
          --verbose              Provide a more verbose output

    Global Flags:
          --builder string   Override the configured builder instance

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-05-02 21:29:23 +02:00
Tõnis Tiigi
bda4882a65 Merge pull request #268 from tiborvass/fix-tristate
Fix --pull and --no-cache behavior
2020-04-30 15:08:21 -07:00
Tibor Vass
77ddee9314 bake: fix pull and no-cache overrides
Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-30 14:05:21 -07:00
Tonis Tiigi
c9676c79d1 bake: fix hcl tags
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-04-30 13:41:49 -07:00
Tonis Tiigi
18095ee87b bake: reset no-cache and pull if not set
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-04-30 13:01:45 -07:00
Tibor Vass
c4d07f67e3 commands: check if flag is set instead of using flagutil.Tristate
Fixes --pull and --no-cache without argument

Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-30 12:25:41 -07:00
Tõnis Tiigi
205165bec5 Merge pull request #192 from vanstee/hcl2-with-interpolation
Upgrade to hcl2 to support variables and functions
2020-04-29 10:55:53 -07:00
Patrick Van Stee
870b38837b Allow for user defined functions
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-29 08:52:48 -04:00
Patrick Van Stee
10d4b7a878 Add example of interpolation to the README
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-29 08:52:20 -04:00
Patrick Van Stee
abed97cf33 Include test cases of different hcl files
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-29 08:51:26 -04:00
Patrick Van Stee
f10d8dab5e Define variables as blocks with defaults
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-29 08:51:26 -04:00
Patrick Van Stee
5185d534bc Include go-cty stdlib functions in HCL file scope
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-29 08:51:26 -04:00
Patrick Van Stee
a520de447e Provide current env as variables in eval context
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-29 08:51:26 -04:00
Patrick Van Stee
4121ae50b5 Modify parsing functions and config structs to accept hcl changes
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-29 08:51:26 -04:00
Patrick Van Stee
87c4bf1df9 Upgrade hcl to v2
Signed-off-by: Patrick Van Stee <patrick@vanstee.me>
2020-04-28 22:07:52 -04:00
Tibor Vass
09339bf500 Merge pull request #263 from tonistiigi/platforms-print
separate manual and automatically detected platforms
2020-04-28 17:46:09 -07:00
Tõnis Tiigi
af9edb6ba4 Merge pull request #246 from cpuguy83/override_instance
Add option to build/bake to override instance
2020-04-28 17:38:38 -07:00
Brian Goff
b2ec1d331c Add builder as a global flag.
This allows all subcommands to use this flag.
Additionally reads the default value for the flag from the
`BUILDX_BUILDER` env var.

Precedence is:

CLI ARG > flag > env var > config file

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-04-27 14:37:17 -07:00
Brian Goff
213d3af3b0 Add option to build/bake to override instance
This helps prevent race conditions with concurrent build invocations.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-04-27 11:12:31 -07:00
Tonis Tiigi
4804824c78 separate manual and automatically detected platforms
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-04-24 20:08:43 -07:00
Tõnis Tiigi
d89e3f3014 Merge pull request #249 from tonistiigi/prune
add prune and du commands
2020-04-23 12:17:49 -07:00
Tõnis Tiigi
9ab9b852c2 Merge pull request #165 from tiborvass/pertarget-nocache
bake: allow overriding no-cache and pull per target via --set
2020-04-23 12:17:30 -07:00
Tibor Vass
2a257a8252 bake: allow overriding no-cache and pull per target via --set
Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-23 18:11:22 +00:00
Tõnis Tiigi
0e1f0e3c73 Merge pull request #164 from tiborvass/multitarget-set
bake: allow pattern matching for target names in --set
2020-04-23 09:23:12 -07:00
Tibor Vass
078b65905a bake: add test cases for pattern matching
Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-23 05:54:11 +00:00
Tibor Vass
417f52e001 bake: add --load and --push shorthands for --set
Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-23 05:54:11 +00:00
Tibor Vass
2bca8fa677 bake: allow pattern matching for target names in --set
Although bake is for running multiple targets, --set required a single
target name for overriding a property. This change allows matching
multiple targets for overrides.

Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-23 05:54:11 +00:00
Tõnis Tiigi
721b63f3a0 Merge pull request #259 from tiborvass/fix-inherits-override
bake: fix override bug with inheritance
2020-04-22 22:24:12 -07:00
Tibor Vass
14e65ff3b4 bake: fix override+inheritance bug
Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-21 22:42:03 +00:00
Tibor Vass
3282dae09b bake: add tests for override+inheritance bug
Signed-off-by: Tibor Vass <tibor@docker.com>
2020-04-21 22:38:06 +00:00
Sebastiaan van Stijn
7b297eb895 Merge pull request #251 from philips/patch-1
README: add mkdir for .docker/cli-plugins
2020-04-14 18:06:04 +02:00
Brandon Philips
bae6b1cec8 README: add mkdir for .docker/cli-plugins
this dir doesn't exist by default so add a mkdir
2020-04-11 08:54:09 -07:00
Tibor Vass
f4ac640252 Merge pull request #250 from tonistiigi/buildkit-bump
vendor: update buildkit to v0.7.0
2020-04-10 13:43:09 -07:00
Tonis Tiigi
7c627da986 vendor: update buildkit to v0.7.0
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-04-09 22:37:06 -07:00
Tonis Tiigi
d52f5db6ba commands: add du command
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-04-06 19:32:21 -07:00
Tonis Tiigi
66672b4052 commands: add prune command
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2020-04-06 18:03:30 -07:00
Tõnis Tiigi
ed6be92de4 Merge pull request #245 from developer-guy/master
i added kubernetes driver information to drivers section
2020-03-30 15:07:50 -07:00
Batuhan Apaydın
2def02ea74 added kubernetes driver information 2020-03-30 18:34:22 +03:00
Tibor Vass
52b0ea328f Merge pull request #233 from silvin-lubecki/go-1.13
bump to Go 1.13
2020-03-04 12:19:16 -08:00
Silvin Lubecki
960107d00f Bump golang to 1.13 in Dockerfiles
Signed-off-by: Silvin Lubecki <silvin.lubecki@docker.com>
2020-03-04 18:37:46 +01:00
Silvin Lubecki
bbc902b4d6 Bump buildkit to master and fix versions incompatible with go mod 1.13
Bump github.com/gogo/googleapis to v1.3.2
Bump github.com/docker/cli to master

Signed-off-by: Silvin Lubecki <silvin.lubecki@docker.com>
2020-03-04 18:37:42 +01:00
Sebastiaan van Stijn
54549235da Merge pull request #234 from thaJeztah/remove_fossa_scan
Remove FOSSA checks from Jenkins CI
2020-03-04 13:38:47 +01:00
Sebastiaan van Stijn
231f983600 Revert "Add FOSSA checks to Jenkins CI"
This reverts commit 5f4d4a87f7.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2020-03-04 13:01:41 +01:00
Tõnis Tiigi
891d355679 Merge pull request #225 from cpuguy83/k8s_priority
Make k8s driver priority lower
2020-02-16 11:11:40 -08:00
Brian Goff
87fbc406f5 Make k8s driver priority lower
Otherwise it ends up being default and it's probably not the normal
case.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-02-12 16:26:53 -08:00
Tõnis Tiigi
08b5a52ccd Merge pull request #219 from ArturKlauser/patch-1
Explain binfmt_misc requirements
2020-01-13 09:38:16 -08:00
Artur Klauser
14a28d7fc3 Update README.md 2020-01-12 10:01:13 +01:00
Tõnis Tiigi
5a79b401b0 Merge pull request #221 from cpuguy83/build_args_env
build: only use env for args if set
2020-01-08 15:30:01 -08:00
Brian Goff
5e4444823c build: only use env for args if set
When following this pattern:

  buildx build --arg FOO

Where we want to pull `FOO` from env, currently we always set `FOO`
regardless if the `FOO` env var is even set.

This change makes it so that `FOO` would only be set if it has been set
in the env (even if it is set to empty).

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2020-01-06 12:02:06 -08:00
Artur Klauser
5ff7635447 Explain binfmt_misc requirements
Getting the QEMU interpreters to work transparently inside containers seems to be a bit of a sticking point, with major distributions still on kernels and/or binfmt-support package versions that don't support the fix_binary flag yet. Give the reader a little more guidance what to look out for.
2020-01-04 00:00:40 +01:00
Tõnis Tiigi
709ef36b4f Merge pull request #207 from ulyssessouza/use-file-interface
Bump containerd/console to use console.File instead of os.File
2019-12-11 17:03:00 -08:00
ulyssessouza
7f0b59dc37 Remove replace for docker/docker and restore prefix on docker/cli
Signed-off-by: ulyssessouza <ulyssessouza@gmail.com>
2019-12-12 01:27:56 +01:00
ulyssessouza
9e8c532e61 Bump docker/cli to 06f34ba50786ec67761745c818e87baecc2ba139
Signed-off-by: ulyssessouza <ulyssessouza@gmail.com>
2019-12-11 14:55:53 +01:00
ulyssessouza
f2be09f4e4 Bump microsoft/hcsshim to v0.8.7
Signed-off-by: ulyssessouza <ulyssessouza@gmail.com>
2019-12-11 14:35:32 +01:00
ulyssessouza
3ff9abca3a Bump moby/buildkit
Signed-off-by: ulyssessouza <ulyssessouza@gmail.com>
2019-12-11 14:13:56 +01:00
ulyssessouza
3d630c6f7f Replace usage of *os.File by console.File interface on printer
Signed-off-by: ulyssessouza <ulyssessouza@gmail.com>
2019-12-10 23:37:42 +01:00
ulyssessouza
9f4f945d4f Bump docker/docker and containerd/console
Signed-off-by: ulyssessouza <ulyssessouza@gmail.com>
2019-12-10 23:37:11 +01:00
Tõnis Tiigi
a0490a8720 Merge pull request #204 from tiborvass/remove-fossa
Revert "ADDED .fossa file for fossa scans"
2019-12-10 10:13:04 -08:00
Tibor Vass
aba962c12c Revert "ADDED .fossa file for fossa scans"
This reverts commit 6e1fd0eab6.
2019-12-10 09:11:39 -08:00
Tõnis Tiigi
aa21e3c731 Merge pull request #200 from jingxiaolu/inspect_image
docker-container: check local image store if pulling image failed
2019-12-08 18:00:55 -08:00
l00397676
5b9d88b3ad docker-container: check local image store if pulling image failed
When booting `docker-container` driver, it will pull and run image
`moby/buildkit:buildx-stable-1`.
If current node cannot connect to dockerhub, driver `docker-container`
will always booting failure.
But user may already load the image manually or pull it from a priviate
registry.
Buildx should check local docker image store after pull failed.

Fixes: #199 issuecomment-561996661

Signed-off-by: Lu Jingxiao <lujingxiao@huawei.com>
2019-12-09 09:29:56 +08:00
Tibor Vass
8bce430f4d Merge pull request #167 from AkihiroSuda/kube
new driver: kubernetes
2019-11-21 09:46:00 -08:00
Akihiro Suda
c6f8de90aa kubernetes: show Kubernetes Pods as buildx "Nodes" in docker buildx inspect
Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2019-11-21 10:30:39 +09:00
Akihiro Suda
6b65b0c982 new driver: kubernetes
Tested with `kind` and GKE.

Note: "nodes" shown in `docker buildx ls` are unrelated to Kubernetes "nodes".
Probably buildx should come up with an alternative term.

Usage:

  $ kind create cluster
  $ export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"

  $ docker buildx create --driver kubernetes --driver-opt replicas=3 --use
  $ docker buildx build -t foo --load .

`--load` loads the image into the local Docker.

Driver opts:

  - `image=IMAGE` - Sets the container image to be used for running buildkit.
  - `namespace=NS` - Sets the Kubernetes namespace. Defaults to the current namespace.
  - `replicas=N` - Sets the number of `Pod` replicas. Defaults to 1.
  - `rootless=(true|false)` - Run the container as a non-root user without `securityContext.privileged`. 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"

Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
2019-11-21 10:30:39 +09:00
Tõnis Tiigi
f5c2673878 Merge pull request #184 from cpuguy83/bake_args_from_env
Support reading from env on bake --set <t>.args
2019-11-20 10:32:12 -08:00
Tõnis Tiigi
8e92bfc8f0 Merge pull request #188 from shykes/patch-1
Clarify documentation structure
2019-11-07 10:50:04 -08:00
Solomon Hykes
d7adb9ef6e Clarify documentation structure
Move a paragraph in README to clarify where it fits in the structure.

- Before the move, the paragraph seems to apply to the `--output=local` section when in fact it applies to the entire `--output` section. This is especially confusing for the sentence "if just the path is specified as a value, `buildx` will use the local exporter with this path as the destination".

- After the move, it is clear that the paragraph applies to `--output`
2019-11-06 16:42:12 -08:00
Brian Goff
6634f1e75c Support reading from env on bake --set <t>.args
This works just like the `build` command where if you have `--build-arg
FOO`, it will read the variable from env and only set a value if the
variable is defined.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2019-10-30 15:31:38 -07:00
Tõnis Tiigi
6aba19193a Merge pull request #182 from tonistiigi/raw-newline
imagetools: avoid printing newline on raw mode
2019-10-28 16:37:53 -07:00
Tonis Tiigi
eb1aabe9e3 imagetools: avoid printing newline on raw mode
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2019-10-28 16:14:35 -07:00
Tõnis Tiigi
714f181d81 Merge pull request #170 from sirlatrom/169-docker-container-driver-envs
Support environment variables in docker-container driver
2019-10-28 15:05:58 -07:00
Sune Keller
fd44accc79 Support environment variables in docker-container driver
Fixes #169

Signed-off-by: Sune Keller <absukl@almbrand.dk>
2019-10-28 21:56:14 +01:00
Tõnis Tiigi
43edd6b77e Merge pull request #162 from daixiang0/patch-1
Update README.md
2019-10-25 21:58:27 -07:00
Xiang Dai
427c19d65c Update README.md
Comment that with docker 19.03-, can not use buildx as docker plugin.
2019-10-26 11:07:27 +08:00
4936 changed files with 955050 additions and 273129 deletions

View File

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

View File

@@ -1,14 +0,0 @@
# Generated by FOSSA CLI (https://github.com/fossas/fossa-cli)
# Visit https://fossa.com to learn more
version: 2
cli:
server: https://app.fossa.io
fetcher: custom
project: git@github.com:docker/buildx
analyze:
modules:
- name: github.com/docker/buildx/cmd/buildx
type: go
target: github.com/docker/buildx/cmd/buildx
path: cmd/buildx

112
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,112 @@
name: build
on:
workflow_dispatch:
push:
branches:
- 'master'
- 'v[0-9]*'
tags:
- 'v*'
pull_request:
branches:
- 'master'
- 'v[0-9]*'
env:
REPO_SLUG: "docker/buildx-bin"
RELEASE_OUT: "./release-out"
jobs:
build:
runs-on: ubuntu-latest
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
-
name: Test
run: |
make test
-
name: Send to Codecov
uses: codecov/codecov-action@v2
with:
file: ./coverage/coverage.txt
-
name: Build binaries
run: |
make release
-
name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: buildx
path: ${{ env.RELEASE_OUT }}/*
if-no-files-found: error
-
name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: |
${{ env.REPO_SLUG }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
bake-target: meta-helper
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push image
uses: docker/bake-action@v1
with:
files: |
./docker-bake.hcl
${{ steps.meta.outputs.bake-file }}
targets: image-cross
push: ${{ github.event_name != 'pull_request' }}
-
name: GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
draft: true
files: ${{ env.RELEASE_OUT }}/*
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 }}

25
.github/workflows/godev.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
# Workflow used to make a request to proxy.golang.org to refresh cache on https://pkg.go.dev/github.com/docker/buildx
# when a released of buildx is produced
name: godev
on:
push:
tags:
- 'v*'
jobs:
update:
runs-on: ubuntu-latest
steps:
-
name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.13
-
name: Call pkg.go.dev
run: |
go get github.com/${GITHUB_REPOSITORY}@${GITHUB_REF#refs/tags/}
env:
GO111MODULE: on
GOPROXY: https://proxy.golang.org

48
.github/workflows/validate.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: validate
on:
workflow_dispatch:
push:
branches:
- 'master'
- 'v[0-9]*'
tags:
- 'v*'
pull_request:
branches:
- 'master'
- 'v[0-9]*'
jobs:
validate:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- lint
- validate-vendor
- validate-docs
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Run
run: |
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

4
.gitignore vendored
View File

@@ -1,2 +1,4 @@
bin
cross-out
coverage
cross-out
release-out

30
.golangci.yml Normal file
View File

@@ -0,0 +1,30 @@
run:
timeout: 10m
skip-files:
- ".*\\.pb\\.go$"
modules-download-mode: vendor
build-tags:
linters:
enable:
- gofmt
- govet
- deadcode
- goimports
- ineffassign
- misspell
- unused
- varcheck
- golint
- staticcheck
- typecheck
- structcheck
disable-all: true
issues:
exclude-rules:
- linters:
- golint
text: "stutters"

View File

@@ -1,6 +1,13 @@
# 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> <tiborvass@users.noreply.github.com>
Tõnis Tiigi <tonistiigi@gmail.com>
Ulysses Souza <ulyssessouza@gmail.com>
Wang Jinglei <morlay.null@gmail.com>

View File

@@ -1,35 +0,0 @@
dist: trusty
sudo: required
install:
- docker run --name buildkit --rm -d --privileged -p 1234:1234 $REPO_SLUG_ORIGIN --addr tcp://0.0.0.0:1234
- sudo docker cp buildkit:/usr/bin/buildctl /usr/bin/
- export BUILDKIT_HOST=tcp://0.0.0.0:1234
env:
global:
- PLATFORMS="linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x,linux/ppc64le"
- CROSS_PLATFORMS="${PLATFORMS},darwin/amd64,windows/amd64"
- PREFER_BUILDCTL="1"
script:
- make binaries validate-all && TARGETPLATFORM="${CROSS_PLATFORMS}" ./hack/cross
deploy:
- provider: script
script: PLATFORMS="${CROSS_PLATFORMS}" ./hack/release $TRAVIS_TAG release-out
on:
repo: docker/buildx
tags: true
condition: $TRAVIS_TAG =~ ^v[0-9]
- provider: releases
api_key:
secure: "VKVL+tyS3BfqjM4VMGHoHJbcKY4mqq4AGrclVEvBnt0gm1LkGeKxSheCZgF1EC4oSV8rCy6dkoRWL0PLkl895MIl20Z4v53o1NOQ4Fn0A+eptnrld8jYUkL5PcD+kdEqv2GkBn7vO6E/fwYY/wH9FYlE+fXUa0c/YQGqNGS+XVDtgkftqBV+F2EzaIwk+D+QClFBRmKvIbXrUQASi1K6K2eT3gvzR4zh679TSdI2nbnTKtE06xG1PBFVmb1Ux3/Jz4yHFvf2d3M1mOyqIBsozKoyxisiFQxnm3FjhPrdlZJ9oy/nsQM3ahQKJ3DF8hiLI1LxcxRa6wo//t3uu2eJSYl/c5nu0T7gVw4sChQNy52fUhEGoDTDwYoAxsLSDXcpj1jevRsKvxt/dh2e2De1a9HYj5oM+z2O+pcyiY98cKDbhe2miUqUdiYMBy24xUunB46zVcJF3pIqCYtw5ts8ES6Ixn3u+4OGV/hMDrVdiG2bOZtNVkdbKMEkOEBGa3parPJ69jh6og639kdAD3DFxyZn3YKYuJlcNShn3tj6iPokBYhlLwwf8vuEV7gK7G0rDS9yxuF03jgkwpBBF2wy+u1AbJv241T7v2ZB8H8VlYyHA0E5pnoWbw+lIOTy4IAc8gIesMvDuFFi4r1okhiAt/24U0p4aAohjh1nPuU3spY="
file: release-out/**/*
skip_cleanup: true
file_glob: true
on:
repo: docker/buildx
tags: true
condition: $TRAVIS_TAG =~ ^v[0-9]

13
.yamllint.yml Normal file
View File

@@ -0,0 +1,13 @@
ignore: |
/vendor
extends: default
yaml-files:
- '*.yaml'
- '*.yml'
rules:
truthy: disable
line-length: disable
document-start: disable

40
AUTHORS
View File

@@ -1,7 +1,45 @@
# 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>
Brandon Philips <brandon@ifup.org>
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>
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,15 +1,17 @@
# syntax=docker/dockerfile:1.1-experimental
# syntax=docker/dockerfile:1.3
ARG DOCKERD_VERSION=19.03-rc
ARG CLI_VERSION=19.03
ARG GO_VERSION=1.17
ARG DOCKERD_VERSION=20.10.8
FROM docker:$DOCKERD_VERSION AS dockerd-release
# xgo is a helper for golang cross-compilation
FROM --platform=$BUILDPLATFORM tonistiigi/xx:golang@sha256:6f7d999551dd471b58f70716754290495690efa8421e0a1fcf18eb11d0c0a537 AS xgo
# xx is a helper for cross-compilation
FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.0.0 AS xx
FROM --platform=$BUILDPLATFORM golang:1.12-alpine AS gobase
COPY --from=xgo / /
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS golatest
FROM golatest AS gobase
COPY --from=xx / /
RUN apk add --no-cache file git
ENV GOFLAGS=-mod=vendor
WORKDIR /src
@@ -22,25 +24,24 @@ RUN --mount=target=. \
FROM gobase AS buildx-build
ENV CGO_ENABLED=0
ARG LDFLAGS="-w -s"
ARG TARGETPLATFORM
RUN --mount=target=. --mount=target=/root/.cache,type=cache \
--mount=target=/go/pkg/mod,type=cache \
--mount=source=/tmp/.ldflags,target=/tmp/.ldflags,from=buildx-version \
set -x; go build -ldflags "$(cat /tmp/.ldflags)" -o /usr/bin/buildx ./cmd/buildx && \
file /usr/bin/buildx && file /usr/bin/buildx | egrep "statically linked|Mach-O|Windows"
RUN --mount=type=bind,target=. \
--mount=type=cache,target=/root/.cache \
--mount=type=cache,target=/go/pkg/mod \
--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
FROM buildx-build AS integration-tests
COPY . .
FROM buildx-build AS test
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
# RUN apk add -U git bash coreutils gcc musl-dev
# 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 test-coverage
COPY --from=test /tmp/coverage.txt /coverage.txt
FROM scratch AS binaries-unix
COPY --from=buildx-build /usr/bin/buildx /
@@ -53,28 +54,29 @@ COPY --from=buildx-build /usr/bin/buildx /buildx.exe
FROM binaries-$TARGETOS AS binaries
# Release
FROM --platform=$BUILDPLATFORM alpine AS releaser
WORKDIR /work
ARG TARGETPLATFORM
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//')"
FROM scratch AS release
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 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/tmux.conf /root/.tmux.conf
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
COPY ./hack/demo-env/examples .
COPY --from=binaries / /usr/local/bin/
VOLUME /var/lib/docker
ENTRYPOINT ["entrypoint.sh"]
FROM binaries
FROM binaries

29
Jenkinsfile vendored
View File

@@ -1,29 +0,0 @@
@Library('jps')
_
pipeline {
agent {
node {
label 'ubuntu-1804-overlay2'
}
}
options {
disableConcurrentBuilds()
}
stages {
stage("FOSSA Analyze") {
steps {
withCredentials([string(credentialsId: 'fossa-api-key', variable: 'FOSSA_API_KEY')]) {
withGithubStatus('FOSSA.scan') {
labelledShell returnStatus: false, returnStdout: true, label: "make fossa-analyze",
script:'make -f Makefile.fossa BRANCH_NAME=${BRANCH_NAME} fossa-analyze'
labelledShell returnStatus: false, returnStdout: true, label: "make fossa-test",
script: 'make -f Makefile.fossa BRANCH_NAME=${BRANCH_NAME} fossa-test'
}
}
}
}
}
}

View File

@@ -150,6 +150,8 @@ made through a pull request.
[Org.Maintainers]
people = [
"akihirosuda",
"crazy-max",
"tiborvass",
"tonistiigi",
]
@@ -176,6 +178,16 @@ made through a pull request.
# All other sections should refer to people by their canonical key
# in the people section.
[people.akihirosuda]
Name = "Akihiro Suda"
Email = "akihiro.suda.cz@hco.ntt.co.jp"
GitHub = "AkihiroSuda"
[people.crazy-max]
Name = "Kevin Alvarez"
Email = "contact@crazymax.dev"
GitHub = "crazy-max"
[people.thajeztah]
Name = "Sebastiaan van Stijn"
Email = "github@gone.nl"

View File

@@ -1,31 +1,62 @@
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:
./hack/shell
binaries:
./hack/binaries
$(BUILDX_CMD) bake binaries
binaries-cross:
EXPORT_LOCAL=cross-out ./hack/cross
$(BUILDX_CMD) bake binaries-cross
install: binaries
mkdir -p ~/.docker/cli-plugins
cp 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:
./hack/lint
$(BUILDX_CMD) bake lint
test:
./hack/test
$(BUILDX_CMD) bake test
validate-vendor:
./hack/validate-vendor
validate-all: lint test validate-vendor
$(BUILDX_CMD) bake validate-vendor
validate-docs:
$(BUILDX_CMD) bake validate-docs
validate-authors:
$(BUILDX_CMD) bake validate-authors
test-driver:
./hack/test-driver
vendor:
./hack/update-vendor
generate-authors:
./hack/generate-authors
docs:
./hack/update-docs
.PHONY: vendor lint shell binaries install binaries-cross validate-all generate-authors
authors:
$(BUILDX_CMD) bake update-authors
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

View File

@@ -1,18 +0,0 @@
REPO_PATH?=docker/buildx
BUILD_ANALYZER?=docker/fossa-analyzer
FOSSA_OPTS?=--option all-tags:true --option allow-unresolved:true --no-ansi
fossa-analyze:
docker run -i --rm -e FOSSA_API_KEY=$(FOSSA_API_KEY) \
-v $(CURDIR)/$*:/go/src/github.com/$(REPO_PATH) \
-w /go/src/github.com/$(REPO_PATH) \
-e GO111MODULE=on \
$(BUILD_ANALYZER) analyze $(FOSSA_OPTS) --branch $(BRANCH_NAME)
# This command is used to run the fossa test command
fossa-test:
docker run -i --rm -e FOSSA_API_KEY=$(FOSSA_API_KEY) \
-v $(CURDIR)/$*:/go/src/github.com/$(REPO_PATH) \
-w /go/src/github.com/$(REPO_PATH) \
-e GO111MODULE=on \
$(BUILD_ANALYZER) test --debug

849
README.md
View File

@@ -1,138 +1,307 @@
# buildx
### Docker CLI plugin for extended build capabilities with BuildKit
_buildx is Tech Preview_
[![GitHub release](https://img.shields.io/github/release/docker/buildx.svg?style=flat-square)](https://github.com/docker/buildx/releases/latest)
[![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)
[![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)
[![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)
### TL;DR
`buildx` is a Docker CLI plugin for extended build capabilities with
[BuildKit](https://github.com/moby/buildkit).
Key features:
- Familiar UI from `docker build`
- Full BuildKit capabilities with container driver
- Multiple builder instance support
- Multi-node builds for cross-platform images
- Compose build support
- WIP: High-level build constructs (`bake`)
- TODO: In-container driver support
- High-level build constructs (`bake`)
- In-container driver support (both Docker and Kubernetes)
# Table of Contents
- [Installing](#installing)
- [Windows and macOS](#windows-and-macos)
- [Linux packages](#linux-packages)
- [Manual download](#manual-download)
- [Dockerfile](#dockerfile)
- [Set buildx as the default builder](#set-buildx-as-the-default-builder)
- [Building](#building)
+ [with Docker 18.09+](#with-docker-1809)
+ [with buildx or Docker 19.03](#with-buildx-or-docker-1903)
- [Getting started](#getting-started)
* [Building with buildx](#building-with-buildx)
* [Working with builder instances](#working-with-builder-instances)
* [Building multi-platform images](#building-multi-platform-images)
* [High-level build options](#high-level-build-options)
- [Documentation](#documentation)
+ [`buildx build [OPTIONS] PATH | URL | -`](#buildx-build-options-path--url---)
+ [`buildx create [OPTIONS] [CONTEXT|ENDPOINT]`](#buildx-create-options-contextendpoint)
+ [`buildx use NAME`](#buildx-use-name)
+ [`buildx inspect [NAME]`](#buildx-inspect-name)
+ [`buildx ls`](#buildx-ls)
+ [`buildx stop [NAME]`](#buildx-stop-name)
+ [`buildx rm [NAME]`](#buildx-rm-name)
+ [`buildx bake [OPTIONS] [TARGET...]`](#buildx-bake-options-target)
+ [`buildx imagetools create [OPTIONS] [SOURCE] [SOURCE...]`](#buildx-imagetools-create-options-source-source)
+ [`buildx imagetools inspect NAME`](#buildx-imagetools-inspect-name)
- [Setting buildx as default builder in Docker 19.03+](#setting-buildx-as-default-builder-in-docker-1903)
- [Building with buildx](#building-with-buildx)
- [Working with builder instances](#working-with-builder-instances)
- [Building multi-platform images](#building-multi-platform-images)
- [High-level build options](#high-level-build-options)
- [Guides](docs/guides)
- [CI/CD](docs/guides/cicd.md)
- [CNI networking](docs/guides/cni-networking.md)
- [Registry mirror](docs/guides/registry-mirror.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 prune`](docs/reference/buildx_prune.md)
- [`buildx rm`](docs/reference/buildx_rm.md)
- [`buildx stop`](docs/reference/buildx_stop.md)
- [`buildx uninstall`](docs/reference/buildx_uninstall.md)
- [`buildx use`](docs/reference/buildx_use.md)
- [`buildx version`](docs/reference/buildx_version.md)
- [Contributing](#contributing)
# Installing
Using `buildx` as a docker CLI plugin requires using Docker 19.03. 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 CE
## Windows and macOS
`buildx` comes bundled with Docker CE starting with 19.03, but requires experimental mode to be enabled on the Docker CLI.
To enable it, `"experimental": "enabled"` can be added to the CLI configuration file `~/.docker/config.json`. An alternative is to set the `DOCKER_CLI_EXPERIMENTAL=enabled` environment variable.
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:
```sh
chmod a+x ~/.docker/cli-plugins/docker-buildx
## Manual download
> **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).
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
FROM docker
COPY --from=docker/buildx-bin /buildx /usr/libexec/docker/cli-plugins/docker-buildx
RUN docker buildx version
```
After installing you can run `docker buildx` to see the new commands.
# 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
### with Docker 18.09+
```
$ git clone git://github.com/docker/buildx && cd buildx
$ make install
```
```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
```
$ export DOCKER_BUILDKIT=1
$ docker build --platform=local -o . git://github.com/docker/buildx
# Docker 19.03+
$ DOCKER_BUILDKIT=1 docker build --platform=local -o . "https://github.com/docker/buildx.git"
$ mkdir -p ~/.docker/cli-plugins
$ mv buildx ~/.docker/cli-plugins/docker-buildx
# Local
$ git clone https://github.com/docker/buildx.git && cd buildx
$ make install
```
# Getting started
## 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. `docker buildx build` is the command for starting a new build.
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 .
[+] 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 "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.
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`.
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](docs/reference/buildx_create.md#docker-driver) that uses
the BuildKit library bundled into the Docker daemon binary, a
[`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
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
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.
You can build multi-platform images using three different strategies that are
supported by Buildx and Dockerfiles:
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.
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
```
# assuming contexts node-amd64 and node-arm64 exist in "docker context ls"
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`;
```console
$ docker buildx create --use --name mybuild node-amd64
mybuild
$ docker buildx create --append --name mybuild node-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
ARG TARGETPLATFORM
ARG BUILDPLATFORM
@@ -141,537 +310,31 @@ FROM alpine
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
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.
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`.
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.
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.
# Documentation
### `buildx build [OPTIONS] PATH | URL | -`
The `buildx build` command starts a build using BuildKit. This command is similar to the UI of `docker build` command and takes the same flags and arguments.
Options:
| Flag | Description |
| --- | --- |
| --add-host [] | Add a custom host-to-IP mapping (host:ip)
| --allow [] | Allow extra privileged entitlement, e.g. network.host, security.insecure
| --build-arg [] | Set build-time variables
| --cache-from [] | External cache sources (eg. user/app:cache, type=local,src=path/to/dir)
| --cache-to [] | Cache export destinations (eg. user/app:cache, type=local,dest=path/to/dir)
| --file string | Name of the Dockerfile (Default is 'PATH/Dockerfile')
| --iidfile string | Write the image ID to the file
| --label [] | Set metadata for an image
| --load | Shorthand for --output=type=docker
| --network string | Set the networking mode for the RUN instructions during build (default "default")
| --no-cache | Do not use cache when building the image
| --output [] | Output destination (format: type=local,dest=path)
| --platform [] | Set target platform for build
| --progress string | Set type of progress output (auto, plain, tty). Use plain to show container output (default "auto")
| --pull | Always attempt to pull a newer version of the image
| --push | Shorthand for --output=type=registry
| --secret [] | Secret file to expose to the build: id=mysecret,src=/local/secret
| --ssh [] | SSH agent socket or keys to expose to the build (format: default|<id>[=<socket>|<key>[,<key>]])
| --tag [] | Name and optionally a tag in the 'name:tag' format
| --target string | Set the target build stage to build.
For documentation on most of these flags refer to `docker build` documentation in https://docs.docker.com/engine/reference/commandline/build/ . In here well document a subset of the new flags.
#### ` --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 `docker build` documentation for the full description of automatic platform argument variants https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope .
The formatting for the platform specifier is defined in https://github.com/containerd/containerd/blob/v1.2.6/platforms/platforms.go#L63 .
Examples:
```
docker buildx build --platform=linux/arm64 .
docker buildx build --platform=linux/amd64,linux/arm64,linux/arm/v7 .
docker buildx build --platform=darwin .
```
#### `-o, --output=[PATH,-,type=TYPE[,KEY=VALUE]`
Sets the export action for the build result. In `docker build` all builds finish by creating a container image and exporting it to `docker images`. `buildx` makes this step configurable allowing results to be exported directly to the client, oci image tarballs, registry etc.
Supported exported types are:
##### `local`
The `local` export type writes all result files to a directory on the client. The new files will be owned by the current user. On multi-platform builds, all results will be put in subdirectories by their platform.
Attribute key:
- `dest` - destination directory where files will be written
##### `tar`
The `tar` export type writes all result files as a single tarball on the client. On multi-platform builds all results will be put in subdirectories by their platform.
Attribute key:
- `dest` - destination path where tarball will be written. “-” writes to stdout.
##### `oci`
The `oci` export type writes the result image or manifest list as an OCI image layout tarball https://github.com/opencontainers/image-spec/blob/master/image-layout.md on the client.
Attribute key:
- `dest` - destination path where tarball will be written. “-” writes to stdout.
##### `docker`
The `docker` export type writes the single-platform result image as a Docker image specification tarball https://github.com/moby/moby/blob/master/image/spec/v1.2.md on the client. Tarballs created by this exporter are also OCI compatible.
Currently, multi-platform images cannot be exported with the `docker` export type. The most common usecase for multi-platform images is to directly push to a registry (see [`registry`](#registry)).
Attribute keys:
- `dest` - destination path where tarball will be written. If not specified the tar will be loaded automatically to the current docker instance.
- `context` - name for the docker context where to import the result
##### `image`
The `image` exporter writes the build result as an image or a manifest list. When using `docker` driver the image will appear in `docker images`. Optionally image can be automatically pushed to a registry by specifying attributes.
Attribute keys:
- `name` - name (references) for the new image.
- `push` - boolean to automatically push the image.
##### `registry`
The `registry` exporter is a shortcut for `type=image,push=true`.
Buildx with `docker` driver currently only supports local, tarball exporter and image exporter. `docker-container` driver supports all the exporters.
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` exporter and write to `stdout`.
Examples:
```
docker buildx build -o . .
docker buildx build -o outdir .
docker buildx build -o - - > out.tar
docker buildx build -o type=docker .
docker buildx build -o type=docker,dest=- . > myimage.tar
docker buildx build -t tonistiigi/foo -o type=registry
````
#### `--push`
Shorthand for [`--output=type=registry`](#registry). Will automatically push the build result to registry.
#### `--load`
Shorthand for [`--output=type=docker`](#docker). Will automatically load the single-platform build result to `docker images`.
#### `--cache-from=[NAME|type=TYPE[,KEY=VALUE]]`
Use an external cache source for a build. Supported types are `registry` and `local`. The `registry` source can import cache from a cache manifest or (special) image 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.
`docker` driver currently only supports importing build cache from the registry.
Examples:
```
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 .
```
#### `--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 build stage, “max” exports layers for all stages. Metadata is always exported for the whole build.
Examples:
```
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 .
```
#### `--allow=ENTITLEMENT`
Allow extra privileged entitlement. List of entitlements:
- `network.host` - Allows executions with host networking.
- `security.insecure` - Allows executions without sandbox. See [related Dockerfile extensions](https://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 with `--allow-insecure-entitlement` (see [`create --buildkitd-flags`](#--buildkitd-flags-flags))
Example:
```
$ docker buildx create --use --name insecure-builder --buildkitd-flags '--allow-insecure-entitlement security.insecure'
$ docker buildx build --allow security.insecure .
```
### `buildx create [OPTIONS] [CONTEXT|ENDPOINT]`
Create makes a new builder instance pointing to a docker context or endpoint, where context is the name of a context from `docker context ls` and endpoint is the address for docker socket (eg. `DOCKER_HOST` value).
By default, the current docker configuration is used for determining the context/endpoint value.
Builder instances are isolated environments where builds can be invoked. All docker contexts also get the default builder instance.
Options:
| Flag | Description |
| --- | --- |
| --append | Append a node to builder instead of changing it
| --buildkitd-flags string | Flags for buildkitd daemon
| --config string | BuildKit config file
| --driver string | Driver to use (eg. docker-container)
| --driver-opt stringArray | Options for the driver
| --leave | Remove a node from builder instead of changing it
| --name string | Builder instance name
| --node string | Create/modify node with given name
| --platform stringArray | Fixed platforms for current node
| --use | Set the current builder instance
#### `--append`
Changes the action of the command to appends a new node to an existing builder specified by `--name`. Buildx will choose an appropriate node for a build based on the platforms it supports.
Example:
```
$ docker buildx create mycontext1
eager_beaver
$ docker buildx create --name eager_beaver --append mycontext2
eager_beaver
```
#### `--buildkitd-flags FLAGS`
Adds flags when starting the buildkitd daemon. They take precedence over the configuration file specified by [`--config`](#--config-file). See `buildkitd --help` for the available flags.
Example:
```
--buildkitd-flags '--debug --debugaddr 0.0.0.0:6666'
```
#### `--config FILE`
Specifies the configuration file for the buildkitd daemon to use. The configuration can be overridden by [`--buildkitd-flags`](#--buildkitd-flags-flags). See an [example buildkitd configuration file](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md).
#### `--driver DRIVER`
Sets the builder driver to be used. There are two available drivers, each have their own specificities.
- `docker` - Uses the builder that is built into the docker daemon. With this driver, the [`--load`](#--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`](#--load)).
#### `--driver-opt OPTIONS`
Passes additional driver-specific options. Details for each driver:
- `docker` - No driver options
- `docker-container`
- `image` - Sets the container image to be used for running buildkit.
- `network` - Sets the network mode for running the buildkit container.
- Example:
```
--driver docker-container --driver-opt image=moby/buildkit:master,network=host
```
#### `--leave`
Changes the action of the command to removes a node from a builder. The builder needs to be specified with `--name` and node that is removed is set with `--node`.
Example:
```
docker buildx create --name mybuilder --node mybuilder0 --leave
```
#### `--name NAME`
Specifies the name of the builder to be created or modified. If none is specified, one will be automatically generated.
#### `--node NODE`
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 number suffix.
#### `--platform PLATFORMS`
Sets the platforms supported by the node. It expects a comma-separated list of platforms of the form OS/architecture/variant. The node 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 building for the same platform.
Example:
```
docker buildx create --platform linux/amd64
docker buildx create --platform linux/arm64,linux/arm/v8
```
#### `--use`
Automatically switches the current builder to the newly created one. Equivalent to running `docker buildx use $(docker buildx create ...)`.
### `buildx use NAME`
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 switch to the default builder of that context.
### `buildx inspect [NAME]`
Shows information about the current or specified builder.
Example:
```
Name: elated_tesla
Driver: docker-container
Nodes:
Name: elated_tesla0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64
Name: elated_tesla1
Endpoint: ssh://ubuntu@1.2.3.4
Status: running
Platforms: linux/arm64, linux/arm/v7, linux/arm/v6
```
#### `--bootstrap`
Ensures 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, it is thus not necessary. The same BuildKit container is used during the lifetime of the associated builder node (as displayed in `buildx ls`).
### `buildx ls`
Lists all builder instances and the nodes for each instance
Example:
```
docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
elated_tesla * docker-container
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
default docker
default default running linux/amd64
```
Each builder has one or more nodes associated with it. The current builders name is marked with a `*`.
### `buildx stop [NAME]`
Stops the specified or current builder. This will not prevent buildx build to restart the builder. The implementation of stop depends on the driver.
### `buildx rm [NAME]`
Removes the specified or current builder. It is a no-op attempting to remove the default builder.
### `buildx bake [OPTIONS] [TARGET...]`
Bake is a high-level build command.
Each specified target will run in parallel as part of the build.
Options:
| Flag | Description |
| --- | --- |
| -f, --file stringArray | Build definition file
| --no-cache | Do not use cache when building the image
| --print | Print the options without building
| --progress string | Set type of progress output (auto, plain, tty). Use plain to show container output (default "auto")
| --pull | Always attempt to pull a newer version of the image
| --set stringArray | Override target value (eg: target.key=value)
#### `-f, --file FILE`
Specifies the bake definition file. The file can be a Docker Compose, JSON or HCL file. If multiple files are specified they are all read and configurations are combined. By default, if no files are specified, the following are parsed:
docker-compose.yml
docker-compose.yaml
docker-bake.json
docker-bake.override.json
docker-bake.hcl
docker-bake.override.hcl
#### `--no-cache`
Same as `build --no-cache`. Do not use cache when building the image.
#### `--print`
Prints the resulting options of the targets desired to be built, in a JSON format, without starting a build.
```
$ docker buildx bake -f docker-bake.hcl --print db
{
"target": {
"db": {
"context": "./",
"dockerfile": "Dockerfile",
"tags": [
"docker.io/tiborvass/db"
]
}
}
}
```
#### `--progress`
Same as `build --progress`. Set type of progress output (auto, plain, tty). Use plain to show container output (default "auto").
#### `--pull`
Same as `build --pull`.
#### `--set target.key[.subkey]=value`
Override target configurations from command line.
Example:
```
docker buildx bake --set target.args.mybuildarg=value
docker buildx bake --set target.platform=linux/arm64
```
#### File definition
In addition to compose files, bake supports a JSON and an equivalent HCL file format for defining build groups and targets.
A target reflects a single docker build invocation with the same options that you would specify for `docker build`. A group is a grouping of targets.
Multiple files can include the same target and final build options will be determined by merging them together.
In the case of compose files, each service corresponds to a target.
A group can specify its list of targets with the `targets` option. A target can inherit build options by setting the `inherits` option to the list of targets or groups to inherit from.
Note: Design of bake command is work in progress, the user experience may change based on feedback.
Example HCL defintion:
```
group "default" {
targets = ["db", "webapp-dev"]
}
target "webapp-dev" {
dockerfile = "Dockerfile.webapp"
tags = ["docker.io/username/webapp"]
}
target "webapp-release" {
inherits = ["webapp-dev"]
platforms = ["linux/amd64", "linux/arm64"]
}
target "db" {
dockerfile = "Dockerfile.db"
tags = ["docker.io/username/db"]
}
```
### `buildx imagetools create [OPTIONS] [SOURCE] [SOURCE...]`
Imagetools contains commands for working with manifest lists in the registry. These commands are useful for inspecting multi-platform build results.
Create creates a new manifest list based on source manifests. The source manifests can 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 specified create performs a carbon copy.
Options:
| Flag | Description |
| --- | --- |
| --append | Append to existing manifest
| --dry-run | Show final image instead of pushing
| -f, --file stringArray | Read source descriptor from file
| -t, --tag stringArray | Set reference for new image
#### `--append`
Append appends the new sources to an existing manifest list in the destination.
#### `--dry-run`
Do not push the image, just show it.
#### `-f, --file FILE`
Reads source from files. A source can be a manifest digest, manifest reference or a JSON of OCI descriptor object.
#### `-t, --tag IMAGE`
Name of the image to be created.
Examples:
```
docker buildx imagetools create --dry-run alpine@sha256:5c40b3c27b9f13c873fefb2139765c56ce97fd50230f1f2d5c91e55dec171907 sha256:c4ba6347b0e4258ce6a6de2401619316f982b7bcc529f73d2a410d0097730204
docker buildx imagetools create -t tonistiigi/myapp -f image1 -f image2
```
### `buildx imagetools inspect NAME`
Show details of image in the registry.
Example:
```
$ docker buildx imagetools inspect alpine
Name: docker.io/library/alpine:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:28ef97b8686a0b5399129e9b763d5b7e5ff03576aa5580d6f4182a49c5fe1913
Manifests:
Name: docker.io/library/alpine:latest@sha256:5c40b3c27b9f13c873fefb2139765c56ce97fd50230f1f2d5c91e55dec171907
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64
Name: docker.io/library/alpine:latest@sha256:c4ba6347b0e4258ce6a6de2401619316f982b7bcc529f73d2a410d0097730204
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm/v6
...
```
#### `--raw`
Raw prints the original JSON bytes instead of the formatted output.
# 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`.
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 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).
The `bake` command supports building images from compose files, similar to
[`docker-compose build`](https://docs.docker.com/compose/reference/build/),
but allowing all the services to be built concurrently as part of a single
request.
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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,22 +6,21 @@ import (
"reflect"
"strings"
"github.com/docker/cli/cli/compose/loader"
composetypes "github.com/docker/cli/cli/compose/types"
"github.com/compose-spec/compose-go/loader"
compose "github.com/compose-spec/compose-go/types"
"github.com/pkg/errors"
)
func parseCompose(dt []byte) (*composetypes.Config, error) {
parsed, err := loader.ParseYAML([]byte(dt))
if err != nil {
return nil, err
}
return loader.Load(composetypes.ConfigDetails{
ConfigFiles: []composetypes.ConfigFile{
func parseCompose(dt []byte) (*compose.Project, error) {
return loader.Load(compose.ConfigDetails{
ConfigFiles: []compose.ConfigFile{
{
Config: parsed,
Content: dt,
},
},
Environment: envMap(os.Environ()),
}, func(options *loader.Options) {
options.SkipNormalization = true
})
}
@@ -44,23 +43,27 @@ func ParseCompose(dt []byte) (*Config, error) {
}
var c Config
var zeroBuildConfig composetypes.BuildConfig
var zeroBuildConfig compose.BuildConfig
if len(cfg.Services) > 0 {
c.Group = map[string]Group{}
c.Target = map[string]Target{}
c.Groups = []*Group{}
c.Targets = []*Target{}
var g Group
g := &Group{Name: "default"}
for _, s := range cfg.Services {
if reflect.DeepEqual(s.Build, zeroBuildConfig) {
if s.Build == nil || reflect.DeepEqual(s.Build, zeroBuildConfig) {
// if not make sure they're setting an image or it's invalid d-c.yml
if s.Image == "" {
return nil, fmt.Errorf("compose file invalid: service %s has neither an image nor a build context specified. At least one must be provided.", s.Name)
return nil, fmt.Errorf("compose file invalid: service %s has neither an image nor a build context specified. At least one must be provided", s.Name)
}
continue
}
if err = validateTargetNameCompose(s.Name); err != nil {
return nil, errors.Wrapf(err, "invalid service name %q", s.Name)
}
var contextPathP *string
if s.Build.Context != "" {
contextPath := s.Build.Context
@@ -72,38 +75,137 @@ func ParseCompose(dt []byte) (*Config, error) {
dockerfilePathP = &dockerfilePath
}
g.Targets = append(g.Targets, s.Name)
t := Target{
t := &Target{
Name: s.Name,
Context: contextPathP,
Dockerfile: dockerfilePathP,
Labels: s.Build.Labels,
Args: toMap(s.Build.Args),
CacheFrom: s.Build.CacheFrom,
// TODO: add platforms
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]
return val, ok
})),
CacheFrom: s.Build.CacheFrom,
NetworkMode: &s.Build.Network,
}
if err = t.composeExtTarget(s.Build.Extensions); err != nil {
return nil, err
}
if s.Build.Target != "" {
target := s.Build.Target
t.Target = &target
}
if s.Image != "" {
if len(t.Tags) == 0 && s.Image != "" {
t.Tags = []string{s.Image}
}
c.Target[s.Name] = t
c.Targets = append(c.Targets, t)
}
c.Group["default"] = g
c.Groups = append(c.Groups, g)
}
return &c, nil
}
func toMap(in composetypes.MappingWithEquals) map[string]string {
m := map[string]string{}
func flatten(in compose.MappingWithEquals) compose.Mapping {
if len(in) == 0 {
return nil
}
out := compose.Mapping{}
for k, v := range in {
if v != nil {
m[k] = *v
} else {
m[k] = os.Getenv(k)
if v == nil {
continue
}
out[k] = *v
}
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 m
return nil
}

View File

@@ -1,6 +1,7 @@
package bake
import (
"os"
"sort"
"testing"
@@ -9,8 +10,6 @@ import (
func TestParseCompose(t *testing.T) {
var dt = []byte(`
version: "3"
services:
db:
build: ./db
@@ -20,6 +19,8 @@ services:
build:
context: ./dir
dockerfile: Dockerfile-alternate
network:
none
args:
buildno: 123
`)
@@ -27,23 +28,28 @@ services:
c, err := ParseCompose(dt)
require.NoError(t, err)
require.Equal(t, 1, len(c.Group))
sort.Strings(c.Group["default"].Targets)
require.Equal(t, []string{"db", "webapp"}, c.Group["default"].Targets)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, c.Groups[0].Name, "default")
sort.Strings(c.Groups[0].Targets)
require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
require.Equal(t, 2, len(c.Target))
require.Equal(t, "./db", *c.Target["db"].Context)
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, "db", c.Targets[0].Name)
require.Equal(t, "./db", *c.Targets[0].Context)
require.Equal(t, "./dir", *c.Target["webapp"].Context)
require.Equal(t, "Dockerfile-alternate", *c.Target["webapp"].Dockerfile)
require.Equal(t, 1, len(c.Target["webapp"].Args))
require.Equal(t, "123", c.Target["webapp"].Args["buildno"])
require.Equal(t, "webapp", c.Targets[1].Name)
require.Equal(t, "./dir", *c.Targets[1].Context)
require.Equal(t, "Dockerfile-alternate", *c.Targets[1].Dockerfile)
require.Equal(t, 1, len(c.Targets[1].Args))
require.Equal(t, "123", c.Targets[1].Args["buildno"])
require.Equal(t, "none", *c.Targets[1].NetworkMode)
}
func TestNoBuildOutOfTreeService(t *testing.T) {
var dt = []byte(`
version: "3.7"
services:
external:
image: "verycooldb:1337"
@@ -52,13 +58,11 @@ services:
`)
c, err := ParseCompose(dt)
require.NoError(t, err)
require.Equal(t, 1, len(c.Group))
require.Equal(t, 1, len(c.Groups))
}
func TestParseComposeTarget(t *testing.T) {
var dt = []byte(`
version: "3.7"
services:
db:
build:
@@ -73,14 +77,18 @@ services:
c, err := ParseCompose(dt)
require.NoError(t, err)
require.Equal(t, "db", *c.Target["db"].Target)
require.Equal(t, "webapp", *c.Target["webapp"].Target)
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, "db", c.Targets[0].Name)
require.Equal(t, "db", *c.Targets[0].Target)
require.Equal(t, "webapp", c.Targets[1].Name)
require.Equal(t, "webapp", *c.Targets[1].Target)
}
func TestComposeBuildWithoutContext(t *testing.T) {
var dt = []byte(`
version: "3.7"
services:
db:
build:
@@ -93,14 +101,47 @@ services:
c, err := ParseCompose(dt)
require.NoError(t, err)
require.Equal(t, "db", *c.Target["db"].Target)
require.Equal(t, "webapp", *c.Target["webapp"].Target)
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].Name, "db")
require.Equal(t, "db", *c.Targets[0].Target)
require.Equal(t, c.Targets[1].Name, "webapp")
require.Equal(t, "webapp", *c.Targets[1].Target)
}
func TestBuildArgEnvCompose(t *testing.T) {
var dt = []byte(`
version: "3.8"
services:
example:
image: example
build:
context: .
dockerfile: Dockerfile
args:
FOO:
BAR: $ZZZ_BAR
BRB: FOO
`)
os.Setenv("FOO", "bar")
defer os.Unsetenv("FOO")
os.Setenv("BAR", "foo")
defer os.Unsetenv("BAR")
os.Setenv("ZZZ_BAR", "zzz_foo")
defer os.Unsetenv("ZZZ_BAR")
c, err := ParseCompose(dt)
require.NoError(t, err)
require.Equal(t, c.Targets[0].Args["FOO"], "bar")
require.Equal(t, c.Targets[0].Args["BAR"], "zzz_foo")
require.Equal(t, c.Targets[0].Args["BRB"], "FOO")
}
func TestBogusCompose(t *testing.T) {
var dt = []byte(`
version: "3.7"
services:
db:
labels:
@@ -113,5 +154,240 @@ services:
_, err := ParseCompose(dt)
require.Error(t, err)
require.Contains(t, err.Error(), "has neither an image nor a build context specified. At least one must be provided")
require.Contains(t, err.Error(), "has neither an image nor a build context specified: invalid compose project")
}
func TestAdvancedNetwork(t *testing.T) {
var dt = []byte(`
services:
db:
networks:
- example.com
build:
context: ./db
target: db
networks:
example.com:
name: example.com
driver: bridge
ipam:
config:
- subnet: 10.5.0.0/24
ip_range: 10.5.0.0/24
gateway: 10.5.0.254
`)
_, err := ParseCompose(dt)
require.NoError(t, err)
}
func TestDependsOnList(t *testing.T) {
var dt = []byte(`
version: "3.8"
services:
example-container:
image: example/fails:latest
build:
context: .
dockerfile: Dockerfile
depends_on:
other-container:
condition: service_healthy
networks:
default:
aliases:
- integration-tests
other-container:
image: example/other:latest
healthcheck:
test: ["CMD", "echo", "success"]
retries: 5
interval: 5s
timeout: 10s
start_period: 5s
networks:
default:
name: test-net
`)
_, err := ParseCompose(dt)
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 TestPorts(t *testing.T) {
var dt = []byte(`
services:
foo:
build:
context: .
ports:
- 3306:3306
bar:
build:
context: .
ports:
- mode: ingress
target: 3306
published: "3306"
protocol: tcp
`)
_, err := ParseCompose(dt)
require.NoError(t, err)
}
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

@@ -1,11 +1,78 @@
package bake
import "github.com/hashicorp/hcl"
import (
"strings"
func ParseHCL(dt []byte) (*Config, error) {
var c Config
if err := hcl.Unmarshal(dt, &c); err != nil {
return nil, err
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/solver/pb"
)
func ParseHCLFile(dt []byte, fn string) (*hcl.File, bool, error) {
var err error
if strings.HasSuffix(fn, ".json") {
f, diags := hclparse.NewParser().ParseJSON(dt, fn)
if diags.HasErrors() {
err = diags
}
return f, true, err
}
if strings.HasSuffix(fn, ".hcl") {
f, diags := hclparse.NewParser().ParseHCL(dt, fn)
if diags.HasErrors() {
err = diags
}
return f, true, err
}
f, diags := hclparse.NewParser().ParseHCL(dt, fn+".hcl")
if diags.HasErrors() {
f, diags2 := hclparse.NewParser().ParseJSON(dt, fn+".json")
if !diags2.HasErrors() {
return f, true, nil
}
return nil, false, diags
}
return f, true, nil
}
func formatHCLError(err error, files []File) error {
if err == nil {
return nil
}
diags, ok := err.(hcl.Diagnostics)
if !ok {
return err
}
for _, d := range diags {
if d.Severity != hcl.DiagError {
continue
}
if d.Subject != nil {
var dt []byte
for _, f := range files {
if d.Subject.Filename == f.Name {
dt = f.Data
break
}
}
src := errdefs.Source{
Info: &pb.SourceInfo{
Filename: d.Subject.Filename,
Data: dt,
},
Ranges: []*pb.Range{toErrRange(d.Subject)},
}
err = errdefs.WithSource(err, src)
break
}
}
return err
}
func toErrRange(in *hcl.Range) *pb.Range {
return &pb.Range{
Start: pb.Position{Line: int32(in.Start.Line), Character: int32(in.Start.Column)},
End: pb.Position{Line: int32(in.End.Line), Character: int32(in.End.Column)},
}
return &c, nil
}

View File

@@ -1,57 +1,622 @@
package bake
import (
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestParseHCL(t *testing.T) {
var dt = []byte(`
group "default" {
targets = ["db", "webapp"]
}
target "db" {
context = "./db"
tags = ["docker.io/tonistiigi/db"]
}
target "webapp" {
context = "./dir"
dockerfile = "Dockerfile-alternate"
args = {
buildno = "123"
func TestHCLBasic(t *testing.T) {
t.Parallel()
dt := []byte(`
group "default" {
targets = ["db", "webapp"]
}
}
target "cross" {
platforms = [
"linux/amd64",
"linux/arm64"
]
}
target "webapp-plus" {
inherits = ["webapp", "cross"]
args = {
IAMCROSS = "true"
target "db" {
context = "./db"
tags = ["docker.io/tonistiigi/db"]
}
}
`)
c, err := ParseHCL(dt)
target "webapp" {
context = "./dir"
dockerfile = "Dockerfile-alternate"
args = {
buildno = "123"
}
}
target "cross" {
platforms = [
"linux/amd64",
"linux/arm64"
]
}
target "webapp-plus" {
inherits = ["webapp", "cross"]
args = {
IAMCROSS = "true"
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
require.Equal(t, 4, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "db")
require.Equal(t, "./db", *c.Targets[0].Context)
require.Equal(t, c.Targets[1].Name, "webapp")
require.Equal(t, 1, len(c.Targets[1].Args))
require.Equal(t, "123", c.Targets[1].Args["buildno"])
require.Equal(t, c.Targets[2].Name, "cross")
require.Equal(t, 2, len(c.Targets[2].Platforms))
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[2].Platforms)
require.Equal(t, c.Targets[3].Name, "webapp-plus")
require.Equal(t, 1, len(c.Targets[3].Args))
require.Equal(t, map[string]string{"IAMCROSS": "true"}, c.Targets[3].Args)
}
func TestHCLBasicInJSON(t *testing.T) {
dt := []byte(`
{
"group": {
"default": {
"targets": ["db", "webapp"]
}
},
"target": {
"db": {
"context": "./db",
"tags": ["docker.io/tonistiigi/db"]
},
"webapp": {
"context": "./dir",
"dockerfile": "Dockerfile-alternate",
"args": {
"buildno": "123"
}
},
"cross": {
"platforms": [
"linux/amd64",
"linux/arm64"
]
},
"webapp-plus": {
"inherits": ["webapp", "cross"],
"args": {
"IAMCROSS": "true"
}
}
}
}
`)
c, err := ParseFile(dt, "docker-bake.json")
require.NoError(t, err)
require.Equal(t, 1, len(c.Group))
require.Equal(t, []string{"db", "webapp"}, c.Group["default"].Targets)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"db", "webapp"}, c.Groups[0].Targets)
require.Equal(t, 4, len(c.Target))
require.Equal(t, "./db", *c.Target["db"].Context)
require.Equal(t, 4, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "db")
require.Equal(t, "./db", *c.Targets[0].Context)
require.Equal(t, 1, len(c.Target["webapp"].Args))
require.Equal(t, "123", c.Target["webapp"].Args["buildno"])
require.Equal(t, c.Targets[1].Name, "webapp")
require.Equal(t, 1, len(c.Targets[1].Args))
require.Equal(t, "123", c.Targets[1].Args["buildno"])
require.Equal(t, 2, len(c.Target["cross"].Platforms))
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Target["cross"].Platforms)
require.Equal(t, c.Targets[2].Name, "cross")
require.Equal(t, 2, len(c.Targets[2].Platforms))
require.Equal(t, []string{"linux/amd64", "linux/arm64"}, c.Targets[2].Platforms)
require.Equal(t, c.Targets[3].Name, "webapp-plus")
require.Equal(t, 1, len(c.Targets[3].Args))
require.Equal(t, map[string]string{"IAMCROSS": "true"}, c.Targets[3].Args)
}
func TestHCLWithFunctions(t *testing.T) {
dt := []byte(`
group "default" {
targets = ["webapp"]
}
target "webapp" {
args = {
buildno = "${add(123, 1)}"
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "webapp")
require.Equal(t, "124", c.Targets[0].Args["buildno"])
}
func TestHCLWithUserDefinedFunctions(t *testing.T) {
dt := []byte(`
function "increment" {
params = [number]
result = number + 1
}
group "default" {
targets = ["webapp"]
}
target "webapp" {
args = {
buildno = "${increment(123)}"
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "webapp")
require.Equal(t, "124", c.Targets[0].Args["buildno"])
}
func TestHCLWithVariables(t *testing.T) {
dt := []byte(`
variable "BUILD_NUMBER" {
default = "123"
}
group "default" {
targets = ["webapp"]
}
target "webapp" {
args = {
buildno = "${BUILD_NUMBER}"
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "webapp")
require.Equal(t, "123", c.Targets[0].Args["buildno"])
os.Setenv("BUILD_NUMBER", "456")
c, err = ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Groups))
require.Equal(t, "default", c.Groups[0].Name)
require.Equal(t, []string{"webapp"}, c.Groups[0].Targets)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "webapp")
require.Equal(t, "456", c.Targets[0].Args["buildno"])
}
func TestHCLWithVariablesInFunctions(t *testing.T) {
dt := []byte(`
variable "REPO" {
default = "user/repo"
}
function "tag" {
params = [tag]
result = ["${REPO}:${tag}"]
}
target "webapp" {
tags = tag("v1")
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "webapp")
require.Equal(t, []string{"user/repo:v1"}, c.Targets[0].Tags)
os.Setenv("REPO", "docker/buildx")
c, err = ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "webapp")
require.Equal(t, []string{"docker/buildx:v1"}, c.Targets[0].Tags)
}
func TestHCLMultiFileSharedVariables(t *testing.T) {
dt := []byte(`
variable "FOO" {
default = "abc"
}
target "app" {
args = {
v1 = "pre-${FOO}"
}
}
`)
dt2 := []byte(`
target "app" {
args = {
v2 = "${FOO}-post"
}
}
`)
c, err := ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
{Data: dt2, Name: "c2.hcl"},
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre-abc", c.Targets[0].Args["v1"])
require.Equal(t, "abc-post", c.Targets[0].Args["v2"])
os.Setenv("FOO", "def")
c, err = ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
{Data: dt2, Name: "c2.hcl"},
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre-def", c.Targets[0].Args["v1"])
require.Equal(t, "def-post", c.Targets[0].Args["v2"])
}
func TestHCLVarsWithVars(t *testing.T) {
os.Unsetenv("FOO")
dt := []byte(`
variable "FOO" {
default = upper("${BASE}def")
}
variable "BAR" {
default = "-${FOO}-"
}
target "app" {
args = {
v1 = "pre-${BAR}"
}
}
`)
dt2 := []byte(`
variable "BASE" {
default = "abc"
}
target "app" {
args = {
v2 = "${FOO}-post"
}
}
`)
c, err := ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
{Data: dt2, Name: "c2.hcl"},
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre--ABCDEF-", c.Targets[0].Args["v1"])
require.Equal(t, "ABCDEF-post", c.Targets[0].Args["v2"])
os.Setenv("BASE", "new")
c, err = ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
{Data: dt2, Name: "c2.hcl"},
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre--NEWDEF-", c.Targets[0].Args["v1"])
require.Equal(t, "NEWDEF-post", c.Targets[0].Args["v2"])
}
func TestHCLTypedVariables(t *testing.T) {
os.Unsetenv("FOO")
dt := []byte(`
variable "FOO" {
default = 3
}
variable "IS_FOO" {
default = true
}
target "app" {
args = {
v1 = FOO > 5 ? "higher" : "lower"
v2 = IS_FOO ? "yes" : "no"
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "lower", c.Targets[0].Args["v1"])
require.Equal(t, "yes", c.Targets[0].Args["v2"])
os.Setenv("FOO", "5.1")
os.Setenv("IS_FOO", "0")
c, err = ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "higher", c.Targets[0].Args["v1"])
require.Equal(t, "no", c.Targets[0].Args["v2"])
os.Setenv("FOO", "NaN")
_, err = ParseFile(dt, "docker-bake.hcl")
require.Error(t, err)
require.Contains(t, err.Error(), "failed to parse FOO as number")
os.Setenv("FOO", "0")
os.Setenv("IS_FOO", "maybe")
_, err = ParseFile(dt, "docker-bake.hcl")
require.Error(t, err)
require.Contains(t, err.Error(), "failed to parse IS_FOO as bool")
}
func TestHCLVariableCycle(t *testing.T) {
dt := []byte(`
variable "FOO" {
default = BAR
}
variable "FOO2" {
default = FOO
}
variable "BAR" {
default = FOO
}
target "app" {}
`)
_, err := ParseFile(dt, "docker-bake.hcl")
require.Error(t, err)
require.Contains(t, err.Error(), "variable cycle not allowed")
}
func TestHCLAttrs(t *testing.T) {
dt := []byte(`
FOO="abc"
BAR="attr-${FOO}def"
target "app" {
args = {
"v1": BAR
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "attr-abcdef", c.Targets[0].Args["v1"])
// env does not apply if no variable
os.Setenv("FOO", "bar")
c, err = ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "attr-abcdef", c.Targets[0].Args["v1"])
// attr-multifile
}
func TestHCLAttrsCustomType(t *testing.T) {
dt := []byte(`
platforms=["linux/arm64", "linux/amd64"]
target "app" {
platforms = platforms
args = {
"v1": platforms[0]
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, []string{"linux/arm64", "linux/amd64"}, c.Targets[0].Platforms)
require.Equal(t, "linux/arm64", c.Targets[0].Args["v1"])
}
func TestHCLMultiFileAttrs(t *testing.T) {
os.Unsetenv("FOO")
dt := []byte(`
variable "FOO" {
default = "abc"
}
target "app" {
args = {
v1 = "pre-${FOO}"
}
}
`)
dt2 := []byte(`
FOO="def"
`)
c, err := ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
{Data: dt2, Name: "c2.hcl"},
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre-def", c.Targets[0].Args["v1"])
os.Setenv("FOO", "ghi")
c, err = ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
{Data: dt2, Name: "c2.hcl"},
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre-ghi", c.Targets[0].Args["v1"])
}
func TestJSONAttributes(t *testing.T) {
dt := []byte(`{"FOO": "abc", "variable": {"BAR": {"default": "def"}}, "target": { "app": { "args": {"v1": "pre-${FOO}-${BAR}"}} } }`)
c, err := ParseFile(dt, "docker-bake.json")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre-abc-def", c.Targets[0].Args["v1"])
}
func TestJSONFunctions(t *testing.T) {
dt := []byte(`{
"FOO": "abc",
"function": {
"myfunc": {
"params": ["inp"],
"result": "<${upper(inp)}-${FOO}>"
}
},
"target": {
"app": {
"args": {
"v1": "pre-${myfunc(\"foo\")}"
}
}
}}`)
c, err := ParseFile(dt, "docker-bake.json")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "pre-<FOO-abc>", c.Targets[0].Args["v1"])
}
func TestHCLFunctionInAttr(t *testing.T) {
dt := []byte(`
function "brace" {
params = [inp]
result = "[${inp}]"
}
function "myupper" {
params = [val]
result = "${upper(val)} <> ${brace(v2)}"
}
v1=myupper("foo")
v2=lower("BAZ")
target "app" {
args = {
"v1": v1
}
}
`)
c, err := ParseFile(dt, "docker-bake.hcl")
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "FOO <> [baz]", c.Targets[0].Args["v1"])
}
func TestHCLCombineCompose(t *testing.T) {
dt := []byte(`
target "app" {
context = "dir"
args = {
v1 = "foo"
}
}
`)
dt2 := []byte(`
version: "3"
services:
app:
build:
dockerfile: Dockerfile-alternate
args:
v2: "bar"
`)
c, err := ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
{Data: dt2, Name: "c2.yml"},
}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "foo", c.Targets[0].Args["v1"])
require.Equal(t, "bar", c.Targets[0].Args["v2"])
require.Equal(t, "dir", *c.Targets[0].Context)
require.Equal(t, "Dockerfile-alternate", *c.Targets[0].Dockerfile)
}
func TestHCLBuiltinVars(t *testing.T) {
dt := []byte(`
target "app" {
context = BAKE_CMD_CONTEXT
dockerfile = "test"
}
`)
c, err := ParseFiles([]File{
{Data: dt, Name: "c1.hcl"},
}, map[string]string{
"BAKE_CMD_CONTEXT": "foo",
})
require.NoError(t, err)
require.Equal(t, 1, len(c.Targets))
require.Equal(t, c.Targets[0].Name, "app")
require.Equal(t, "foo", *c.Targets[0].Context)
require.Equal(t, "test", *c.Targets[0].Dockerfile)
}

153
bake/hclparser/expr.go Normal file
View File

@@ -0,0 +1,153 @@
package hclparser
import (
"reflect"
"unsafe"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/pkg/errors"
)
func funcCalls(exp hcl.Expression) ([]string, hcl.Diagnostics) {
node, ok := exp.(hclsyntax.Node)
if !ok {
fns, err := jsonFuncCallsRecursive(exp)
if err != nil {
return nil, hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid expression",
Detail: err.Error(),
Subject: exp.Range().Ptr(),
Context: exp.Range().Ptr(),
},
}
}
return fns, nil
}
var funcnames []string
hcldiags := hclsyntax.VisitAll(node, func(n hclsyntax.Node) hcl.Diagnostics {
if fe, ok := n.(*hclsyntax.FunctionCallExpr); ok {
funcnames = append(funcnames, fe.Name)
}
return nil
})
if hcldiags.HasErrors() {
return nil, hcldiags
}
return funcnames, nil
}
func jsonFuncCallsRecursive(exp hcl.Expression) ([]string, error) {
je, ok := exp.(jsonExp)
if !ok {
return nil, errors.Errorf("invalid expression type %T", exp)
}
m := map[string]struct{}{}
for _, e := range elementExpressions(je, exp) {
if err := appendJSONFuncCalls(e, m); err != nil {
return nil, err
}
}
arr := make([]string, 0, len(m))
for n := range m {
arr = append(arr, n)
}
return arr, nil
}
func appendJSONFuncCalls(exp hcl.Expression, m map[string]struct{}) error {
v := reflect.ValueOf(exp)
if v.Kind() != reflect.Ptr || v.IsNil() {
return errors.Errorf("invalid json expression kind %T %v", exp, v.Kind())
}
src := v.Elem().FieldByName("src")
if src.IsZero() {
return errors.Errorf("%v has no property src", v.Elem().Type())
}
if src.Kind() != reflect.Interface {
return errors.Errorf("%v src is not interface: %v", src.Type(), src.Kind())
}
src = src.Elem()
if src.IsNil() {
return nil
}
if src.Kind() == reflect.Ptr {
src = src.Elem()
}
if src.Kind() != reflect.Struct {
return errors.Errorf("%v is not struct: %v", src.Type(), src.Kind())
}
// hcl/v2/json/ast#stringVal
val := src.FieldByName("Value")
if val.IsZero() {
return nil
}
rng := src.FieldByName("SrcRange")
if val.IsZero() {
return nil
}
var stringVal struct {
Value string
SrcRange hcl.Range
}
if !val.Type().AssignableTo(reflect.ValueOf(stringVal.Value).Type()) {
return nil
}
if !rng.Type().AssignableTo(reflect.ValueOf(stringVal.SrcRange).Type()) {
return nil
}
// reflect.Set does not work for unexported fields
stringVal.Value = *(*string)(unsafe.Pointer(val.UnsafeAddr()))
stringVal.SrcRange = *(*hcl.Range)(unsafe.Pointer(rng.UnsafeAddr()))
expr, diags := hclsyntax.ParseExpression([]byte(stringVal.Value), stringVal.SrcRange.Filename, stringVal.SrcRange.Start)
if diags.HasErrors() {
return nil
}
fns, err := funcCalls(expr)
if err != nil {
return err
}
for _, fn := range fns {
m[fn] = struct{}{}
}
return nil
}
type jsonExp interface {
ExprList() []hcl.Expression
ExprMap() []hcl.KeyValuePair
}
func elementExpressions(je jsonExp, exp hcl.Expression) []hcl.Expression {
list := je.ExprList()
if len(list) != 0 {
exp := make([]hcl.Expression, 0, len(list))
for _, e := range list {
if je, ok := e.(jsonExp); ok {
exp = append(exp, elementExpressions(je, e)...)
}
}
return exp
}
kvlist := je.ExprMap()
if len(kvlist) != 0 {
exp := make([]hcl.Expression, 0, len(kvlist)*2)
for _, p := range kvlist {
exp = append(exp, p.Key)
if je, ok := p.Value.(jsonExp); ok {
exp = append(exp, elementExpressions(je, p.Value)...)
}
}
return exp
}
return []hcl.Expression{exp}
}

516
bake/hclparser/hclparser.go Normal file
View File

@@ -0,0 +1,516 @@
package hclparser
import (
"fmt"
"math"
"math/big"
"reflect"
"strconv"
"strings"
"github.com/docker/buildx/util/userfunc"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/pkg/errors"
"github.com/zclconf/go-cty/cty"
)
type Opt struct {
LookupVar func(string) (string, bool)
Vars map[string]string
ValidateLabel func(string) error
}
type variable struct {
Name string `json:"-" hcl:"name,label"`
Default *hcl.Attribute `json:"default,omitempty" hcl:"default,optional"`
Body hcl.Body `json:"-" hcl:",body"`
}
type functionDef struct {
Name string `json:"-" hcl:"name,label"`
Params *hcl.Attribute `json:"params,omitempty" hcl:"params"`
Variadic *hcl.Attribute `json:"variadic_param,omitempty" hcl:"variadic_params"`
Result *hcl.Attribute `json:"result,omitempty" hcl:"result"`
}
type inputs struct {
Variables []*variable `hcl:"variable,block"`
Functions []*functionDef `hcl:"function,block"`
Remain hcl.Body `json:"-" hcl:",remain"`
}
type parser struct {
opt Opt
vars map[string]*variable
attrs map[string]*hcl.Attribute
funcs map[string]*functionDef
ectx *hcl.EvalContext
progress map[string]struct{}
progressF map[string]struct{}
doneF map[string]struct{}
}
func (p *parser) loadDeps(exp hcl.Expression, exclude map[string]struct{}) hcl.Diagnostics {
fns, hcldiags := funcCalls(exp)
if hcldiags.HasErrors() {
return hcldiags
}
for _, fn := range fns {
if err := p.resolveFunction(fn); err != nil {
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid expression",
Detail: err.Error(),
Subject: exp.Range().Ptr(),
Context: exp.Range().Ptr(),
},
}
}
}
for _, v := range exp.Variables() {
if _, ok := exclude[v.RootName()]; ok {
continue
}
if err := p.resolveValue(v.RootName()); err != nil {
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid expression",
Detail: err.Error(),
Subject: v.SourceRange().Ptr(),
Context: v.SourceRange().Ptr(),
},
}
}
}
return nil
}
func (p *parser) resolveFunction(name string) error {
if _, ok := p.doneF[name]; ok {
return nil
}
f, ok := p.funcs[name]
if !ok {
if _, ok := p.ectx.Functions[name]; ok {
return nil
}
return errors.Errorf("undefined function %s", name)
}
if _, ok := p.progressF[name]; ok {
return errors.Errorf("function cycle not allowed for %s", name)
}
p.progressF[name] = struct{}{}
paramExprs, paramsDiags := hcl.ExprList(f.Params.Expr)
if paramsDiags.HasErrors() {
return paramsDiags
}
var diags hcl.Diagnostics
params := map[string]struct{}{}
for _, paramExpr := range paramExprs {
param := hcl.ExprAsKeyword(paramExpr)
if param == "" {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid param element",
Detail: "Each parameter name must be an identifier.",
Subject: paramExpr.Range().Ptr(),
})
}
params[param] = struct{}{}
}
var variadic hcl.Expression
if f.Variadic != nil {
variadic = f.Variadic.Expr
param := hcl.ExprAsKeyword(variadic)
if param == "" {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid param element",
Detail: "Each parameter name must be an identifier.",
Subject: f.Variadic.Range.Ptr(),
})
}
params[param] = struct{}{}
}
if diags.HasErrors() {
return diags
}
if diags := p.loadDeps(f.Result.Expr, params); diags.HasErrors() {
return diags
}
v, diags := userfunc.NewFunction(f.Params.Expr, variadic, f.Result.Expr, func() *hcl.EvalContext {
return p.ectx
})
if diags.HasErrors() {
return diags
}
p.doneF[name] = struct{}{}
p.ectx.Functions[name] = v
return nil
}
func (p *parser) resolveValue(name string) (err error) {
if _, ok := p.ectx.Variables[name]; ok {
return nil
}
if _, ok := p.progress[name]; ok {
return errors.Errorf("variable cycle not allowed for %s", name)
}
p.progress[name] = struct{}{}
var v *cty.Value
defer func() {
if v != nil {
p.ectx.Variables[name] = *v
}
}()
def, ok := p.attrs[name]
if _, builtin := p.opt.Vars[name]; !ok && !builtin {
vr, ok := p.vars[name]
if !ok {
return errors.Errorf("undefined variable %q", name)
}
def = vr.Default
}
if def == nil {
val, ok := p.opt.Vars[name]
if !ok {
val, _ = p.opt.LookupVar(name)
}
vv := cty.StringVal(val)
v = &vv
return
}
if diags := p.loadDeps(def.Expr, nil); diags.HasErrors() {
return diags
}
vv, diags := def.Expr.Value(p.ectx)
if diags.HasErrors() {
return diags
}
_, isVar := p.vars[name]
if envv, ok := p.opt.LookupVar(name); ok && isVar {
if vv.Type().Equals(cty.Bool) {
b, err := strconv.ParseBool(envv)
if err != nil {
return errors.Wrapf(err, "failed to parse %s as bool", name)
}
vv := cty.BoolVal(b)
v = &vv
return nil
} else if vv.Type().Equals(cty.String) {
vv := cty.StringVal(envv)
v = &vv
return nil
} else if vv.Type().Equals(cty.Number) {
n, err := strconv.ParseFloat(envv, 64)
if err == nil && (math.IsNaN(n) || math.IsInf(n, 0)) {
err = errors.Errorf("invalid number value")
}
if err != nil {
return errors.Wrapf(err, "failed to parse %s as number", name)
}
vv := cty.NumberVal(big.NewFloat(n))
v = &vv
return nil
} else {
// TODO: support lists with csv values
return errors.Errorf("unsupported type %s for variable %s", v.Type(), name)
}
}
v = &vv
return nil
}
func Parse(b hcl.Body, opt Opt, val interface{}) hcl.Diagnostics {
reserved := map[string]struct{}{}
schema, _ := gohcl.ImpliedBodySchema(val)
for _, bs := range schema.Blocks {
reserved[bs.Type] = struct{}{}
}
for k := range opt.Vars {
reserved[k] = struct{}{}
}
var defs inputs
if err := gohcl.DecodeBody(b, nil, &defs); err != nil {
return err
}
if opt.LookupVar == nil {
opt.LookupVar = func(string) (string, bool) {
return "", false
}
}
if opt.ValidateLabel == nil {
opt.ValidateLabel = func(string) error {
return nil
}
}
p := &parser{
opt: opt,
vars: map[string]*variable{},
attrs: map[string]*hcl.Attribute{},
funcs: map[string]*functionDef{},
progress: map[string]struct{}{},
progressF: map[string]struct{}{},
doneF: map[string]struct{}{},
ectx: &hcl.EvalContext{
Variables: map[string]cty.Value{},
Functions: stdlibFunctions,
},
}
for _, v := range defs.Variables {
// TODO: validate name
if _, ok := reserved[v.Name]; ok {
continue
}
p.vars[v.Name] = v
}
for _, v := range defs.Functions {
// TODO: validate name
if _, ok := reserved[v.Name]; ok {
continue
}
p.funcs[v.Name] = v
}
attrs, diags := b.JustAttributes()
if diags.HasErrors() {
for _, d := range diags {
if d.Detail != "Blocks are not allowed here." {
return diags
}
}
}
for _, v := range attrs {
if _, ok := reserved[v.Name]; ok {
continue
}
p.attrs[v.Name] = v
}
delete(p.attrs, "function")
for k := range p.opt.Vars {
_ = p.resolveValue(k)
}
for k := range p.attrs {
if err := p.resolveValue(k); err != nil {
if diags, ok := err.(hcl.Diagnostics); ok {
return diags
}
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid attribute",
Detail: err.Error(),
Subject: &p.attrs[k].Range,
Context: &p.attrs[k].Range,
},
}
}
}
for k := range p.vars {
if err := p.resolveValue(k); err != nil {
if diags, ok := err.(hcl.Diagnostics); ok {
return diags
}
r := p.vars[k].Body.MissingItemRange()
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid value",
Detail: err.Error(),
Subject: &r,
Context: &r,
},
}
}
}
for k := range p.funcs {
if err := p.resolveFunction(k); err != nil {
if diags, ok := err.(hcl.Diagnostics); ok {
return diags
}
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid function",
Detail: err.Error(),
Subject: &p.funcs[k].Params.Range,
Context: &p.funcs[k].Params.Range,
},
}
}
}
content, _, diags := b.PartialContent(schema)
if diags.HasErrors() {
return diags
}
for _, a := range content.Attributes {
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid attribute",
Detail: "global attributes currently not supported",
Subject: &a.Range,
Context: &a.Range,
},
}
}
m := map[string]map[string][]*hcl.Block{}
for _, b := range content.Blocks {
if len(b.Labels) == 0 || len(b.Labels) > 1 {
return hcl.Diagnostics{
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid block",
Detail: fmt.Sprintf("invalid block label: %v", b.Labels),
Subject: &b.LabelRanges[0],
Context: &b.LabelRanges[0],
},
}
}
bm, ok := m[b.Type]
if !ok {
bm = map[string][]*hcl.Block{}
m[b.Type] = bm
}
lbl := b.Labels[0]
bm[lbl] = append(bm[lbl], b)
}
vt := reflect.ValueOf(val).Elem().Type()
numFields := vt.NumField()
type value struct {
reflect.Value
idx int
}
type field struct {
idx int
typ reflect.Type
values map[string]value
}
types := map[string]field{}
for i := 0; i < numFields; i++ {
tags := strings.Split(vt.Field(i).Tag.Get("hcl"), ",")
types[tags[0]] = field{
idx: i,
typ: vt.Field(i).Type,
values: make(map[string]value),
}
}
diags = hcl.Diagnostics{}
for _, b := range content.Blocks {
v := reflect.ValueOf(val)
t, ok := types[b.Type]
if !ok {
continue
}
vv := reflect.New(t.typ.Elem().Elem())
diag := gohcl.DecodeBody(b.Body, p.ectx, vv.Interface())
if diag.HasErrors() {
diags = append(diags, diag...)
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])
oldValue, exists := t.values[b.Labels[0]]
if !exists && lblIndex != -1 {
if v.Elem().Field(t.idx).Type().Kind() == reflect.Slice {
for i := 0; i < v.Elem().Field(t.idx).Len(); i++ {
if b.Labels[0] == v.Elem().Field(t.idx).Index(i).Elem().Field(lblIndex).String() {
exists = true
oldValue = value{Value: v.Elem().Field(t.idx).Index(i), idx: i}
break
}
}
}
}
if exists {
if m := oldValue.Value.MethodByName("Merge"); m.IsValid() {
m.Call([]reflect.Value{vv})
} else {
v.Elem().Field(t.idx).Index(oldValue.idx).Set(vv)
}
} else {
slice := v.Elem().Field(t.idx)
if slice.IsNil() {
slice = reflect.New(t.typ).Elem()
}
t.values[b.Labels[0]] = value{Value: vv, idx: slice.Len()}
v.Elem().Field(t.idx).Set(reflect.Append(slice, vv))
}
}
if diags.HasErrors() {
return diags
}
return nil
}
func setLabel(v reflect.Value, lbl string) int {
// cache field index?
numFields := v.Elem().Type().NumField()
for i := 0; i < numFields; i++ {
for _, t := range strings.Split(v.Elem().Type().Field(i).Tag.Get("hcl"), ",") {
if t == "label" {
v.Elem().Field(i).Set(reflect.ValueOf(lbl))
return i
}
}
}
return -1
}

111
bake/hclparser/stdlib.go Normal file
View File

@@ -0,0 +1,111 @@
package hclparser
import (
"github.com/hashicorp/go-cty-funcs/cidr"
"github.com/hashicorp/go-cty-funcs/crypto"
"github.com/hashicorp/go-cty-funcs/encoding"
"github.com/hashicorp/go-cty-funcs/uuid"
"github.com/hashicorp/hcl/v2/ext/tryfunc"
"github.com/hashicorp/hcl/v2/ext/typeexpr"
"github.com/zclconf/go-cty/cty/function"
"github.com/zclconf/go-cty/cty/function/stdlib"
)
var stdlibFunctions = map[string]function.Function{
"absolute": stdlib.AbsoluteFunc,
"add": stdlib.AddFunc,
"and": stdlib.AndFunc,
"base64decode": encoding.Base64DecodeFunc,
"base64encode": encoding.Base64EncodeFunc,
"bcrypt": crypto.BcryptFunc,
"byteslen": stdlib.BytesLenFunc,
"bytesslice": stdlib.BytesSliceFunc,
"can": tryfunc.CanFunc,
"ceil": stdlib.CeilFunc,
"chomp": stdlib.ChompFunc,
"chunklist": stdlib.ChunklistFunc,
"cidrhost": cidr.HostFunc,
"cidrnetmask": cidr.NetmaskFunc,
"cidrsubnet": cidr.SubnetFunc,
"cidrsubnets": cidr.SubnetsFunc,
"csvdecode": stdlib.CSVDecodeFunc,
"coalesce": stdlib.CoalesceFunc,
"coalescelist": stdlib.CoalesceListFunc,
"compact": stdlib.CompactFunc,
"concat": stdlib.ConcatFunc,
"contains": stdlib.ContainsFunc,
"convert": typeexpr.ConvertFunc,
"distinct": stdlib.DistinctFunc,
"divide": stdlib.DivideFunc,
"element": stdlib.ElementFunc,
"equal": stdlib.EqualFunc,
"flatten": stdlib.FlattenFunc,
"floor": stdlib.FloorFunc,
"formatdate": stdlib.FormatDateFunc,
"format": stdlib.FormatFunc,
"formatlist": stdlib.FormatListFunc,
"greaterthan": stdlib.GreaterThanFunc,
"greaterthanorequalto": stdlib.GreaterThanOrEqualToFunc,
"hasindex": stdlib.HasIndexFunc,
"indent": stdlib.IndentFunc,
"index": stdlib.IndexFunc,
"int": stdlib.IntFunc,
"jsondecode": stdlib.JSONDecodeFunc,
"jsonencode": stdlib.JSONEncodeFunc,
"keys": stdlib.KeysFunc,
"join": stdlib.JoinFunc,
"length": stdlib.LengthFunc,
"lessthan": stdlib.LessThanFunc,
"lessthanorequalto": stdlib.LessThanOrEqualToFunc,
"log": stdlib.LogFunc,
"lookup": stdlib.LookupFunc,
"lower": stdlib.LowerFunc,
"max": stdlib.MaxFunc,
"md5": crypto.Md5Func,
"merge": stdlib.MergeFunc,
"min": stdlib.MinFunc,
"modulo": stdlib.ModuloFunc,
"multiply": stdlib.MultiplyFunc,
"negate": stdlib.NegateFunc,
"notequal": stdlib.NotEqualFunc,
"not": stdlib.NotFunc,
"or": stdlib.OrFunc,
"parseint": stdlib.ParseIntFunc,
"pow": stdlib.PowFunc,
"range": stdlib.RangeFunc,
"regexall": stdlib.RegexAllFunc,
"regex": stdlib.RegexFunc,
"regex_replace": stdlib.RegexReplaceFunc,
"reverse": stdlib.ReverseFunc,
"reverselist": stdlib.ReverseListFunc,
"rsadecrypt": crypto.RsaDecryptFunc,
"sethaselement": stdlib.SetHasElementFunc,
"setintersection": stdlib.SetIntersectionFunc,
"setproduct": stdlib.SetProductFunc,
"setsubtract": stdlib.SetSubtractFunc,
"setsymmetricdifference": stdlib.SetSymmetricDifferenceFunc,
"setunion": stdlib.SetUnionFunc,
"sha1": crypto.Sha1Func,
"sha256": crypto.Sha256Func,
"sha512": crypto.Sha512Func,
"signum": stdlib.SignumFunc,
"slice": stdlib.SliceFunc,
"sort": stdlib.SortFunc,
"split": stdlib.SplitFunc,
"strlen": stdlib.StrlenFunc,
"substr": stdlib.SubstrFunc,
"subtract": stdlib.SubtractFunc,
"timeadd": stdlib.TimeAddFunc,
"title": stdlib.TitleFunc,
"trim": stdlib.TrimFunc,
"trimprefix": stdlib.TrimPrefixFunc,
"trimspace": stdlib.TrimSpaceFunc,
"trimsuffix": stdlib.TrimSuffixFunc,
"try": tryfunc.TryFunc,
"upper": stdlib.UpperFunc,
"urlencode": encoding.URLEncodeFunc,
"uuidv4": uuid.V4Func,
"uuidv5": uuid.V5Func,
"values": stdlib.ValuesFunc,
"zipmap": stdlib.ZipmapFunc,
}

237
bake/remote.go Normal file
View File

@@ -0,0 +1,237 @@
package bake
import (
"archive/tar"
"bytes"
"context"
"strings"
"github.com/docker/buildx/build"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/pkg/errors"
)
type Input struct {
State *llb.State
URL string
}
func ReadRemoteFiles(ctx context.Context, dis []build.DriverInfo, url string, names []string, pw progress.Writer) ([]File, *Input, error) {
var filename string
st, ok := detectGitContext(url)
if !ok {
st, filename, ok = detectHTTPContext(url)
if !ok {
return nil, nil, errors.Errorf("not url context")
}
}
inp := &Input{State: st, URL: url}
var files []File
var di *build.DriverInfo
for _, d := range dis {
if d.Err == nil {
di = &d
continue
}
}
if di == nil {
return nil, nil, nil
}
c, err := driver.Boot(ctx, ctx, di.Driver, pw)
if err != nil {
return nil, nil, err
}
ch, done := progress.NewChannel(pw)
defer func() { <-done }()
_, err = c.Build(ctx, client.SolveOpt{}, "buildx", func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
def, err := st.Marshal(ctx)
if err != nil {
return nil, err
}
res, err := c.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}
ref, err := res.SingleRef()
if err != nil {
return nil, err
}
if filename != "" {
files, err = filesFromURLRef(ctx, c, ref, inp, filename, names)
} else {
files, err = filesFromRef(ctx, ref, names)
}
return nil, err
}, ch)
if err != nil {
return nil, nil, err
}
return files, inp, nil
}
func IsRemoteURL(url string) bool {
if _, _, ok := detectHTTPContext(url); ok {
return true
}
if _, ok := detectGitContext(url); ok {
return true
}
return false
}
func detectHTTPContext(url string) (*llb.State, string, bool) {
if httpPrefix.MatchString(url) {
httpContext := llb.HTTP(url, llb.Filename("context"), llb.WithCustomName("[internal] load remote build context"))
return &httpContext, "context", true
}
return nil, "", false
}
func detectGitContext(ref string) (*llb.State, bool) {
found := false
if httpPrefix.MatchString(ref) && gitURLPathWithFragmentSuffix.MatchString(ref) {
found = true
}
for _, prefix := range []string{"git://", "github.com/", "git@"} {
if strings.HasPrefix(ref, prefix) {
found = true
break
}
}
if !found {
return nil, false
}
parts := strings.SplitN(ref, "#", 2)
branch := ""
if len(parts) > 1 {
branch = parts[1]
}
gitOpts := []llb.GitOption{llb.WithCustomName("[internal] load git source " + ref)}
st := llb.Git(parts[0], branch, gitOpts...)
return &st, true
}
func isArchive(header []byte) bool {
for _, m := range [][]byte{
{0x42, 0x5A, 0x68}, // bzip2
{0x1F, 0x8B, 0x08}, // gzip
{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00}, // xz
} {
if len(header) < len(m) {
continue
}
if bytes.Equal(m, header[:len(m)]) {
return true
}
}
r := tar.NewReader(bytes.NewBuffer(header))
_, err := r.Next()
return err == nil
}
func filesFromURLRef(ctx context.Context, c gwclient.Client, ref gwclient.Reference, inp *Input, filename string, names []string) ([]File, error) {
stat, err := ref.StatFile(ctx, gwclient.StatRequest{Path: filename})
if err != nil {
return nil, err
}
dt, err := ref.ReadFile(ctx, gwclient.ReadRequest{
Filename: filename,
Range: &gwclient.FileRange{
Length: 1024,
},
})
if err != nil {
return nil, err
}
if isArchive(dt) {
bc := llb.Scratch().File(llb.Copy(inp.State, filename, "/", &llb.CopyInfo{
AttemptUnpack: true,
}))
inp.State = &bc
inp.URL = ""
def, err := bc.Marshal(ctx)
if err != nil {
return nil, err
}
res, err := c.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}
ref, err := res.SingleRef()
if err != nil {
return nil, err
}
return filesFromRef(ctx, ref, names)
}
inp.State = nil
name := inp.URL
inp.URL = ""
if len(dt) > stat.Size() {
if stat.Size() > 1024*512 {
return nil, errors.Errorf("non-archive definition URL bigger than maximum allowed size")
}
dt, err = ref.ReadFile(ctx, gwclient.ReadRequest{
Filename: filename,
})
if err != nil {
return nil, err
}
}
return []File{{Name: name, Data: dt}}, nil
}
func filesFromRef(ctx context.Context, ref gwclient.Reference, names []string) ([]File, error) {
// TODO: auto-remove parent dir in needed
var files []File
isDefault := false
if len(names) == 0 {
isDefault = true
names = defaultFilenames()
}
for _, name := range names {
_, err := ref.StatFile(ctx, gwclient.StatRequest{Path: name})
if err != nil {
if isDefault {
continue
}
return nil, err
}
dt, err := ref.ReadFile(ctx, gwclient.ReadRequest{Filename: name})
if err != nil {
return nil, err
}
files = append(files, File{Name: name, Data: dt})
}
return files, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,60 +0,0 @@
package build
import (
"encoding/csv"
"strings"
"github.com/moby/buildkit/client"
"github.com/pkg/errors"
)
func ParseCacheEntry(in []string) ([]client.CacheOptionsEntry, error) {
imports := make([]client.CacheOptionsEntry, 0, len(in))
for _, in := range in {
csvReader := csv.NewReader(strings.NewReader(in))
fields, err := csvReader.Read()
if err != nil {
return nil, err
}
if isRefOnlyFormat(fields) {
for _, field := range fields {
imports = append(imports, client.CacheOptionsEntry{
Type: "registry",
Attrs: map[string]string{"ref": field},
})
}
continue
}
im := client.CacheOptionsEntry{
Attrs: map[string]string{},
}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
return nil, errors.Errorf("invalid value %s", field)
}
key := strings.ToLower(parts[0])
value := parts[1]
switch key {
case "type":
im.Type = value
default:
im.Attrs[key] = value
}
}
if im.Type == "" {
return nil, errors.Errorf("type required form> %q", in)
}
imports = append(imports, im)
}
return imports, nil
}
func isRefOnlyFormat(in []string) bool {
for _, v := range in {
if strings.Contains(v, "=") {
return false
}
}
return true
}

View File

@@ -1,60 +0,0 @@
package build
import (
"encoding/csv"
"strings"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/secrets/secretsprovider"
"github.com/pkg/errors"
)
func ParseSecretSpecs(sl []string) (session.Attachable, error) {
fs := make([]secretsprovider.FileSource, 0, len(sl))
for _, v := range sl {
s, err := parseSecret(v)
if err != nil {
return nil, err
}
fs = append(fs, *s)
}
store, err := secretsprovider.NewFileStore(fs)
if err != nil {
return nil, err
}
return secretsprovider.NewSecretProvider(store), nil
}
func parseSecret(value string) (*secretsprovider.FileSource, error) {
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return nil, errors.Wrap(err, "failed to parse csv secret")
}
fs := secretsprovider.FileSource{}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
key := strings.ToLower(parts[0])
if len(parts) != 2 {
return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field)
}
value := parts[1]
switch key {
case "type":
if value != "file" {
return nil, errors.Errorf("unsupported secret type %q", value)
}
case "id":
fs.ID = value
case "source", "src":
fs.FilePath = value
default:
return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field)
}
}
return &fs, nil
}

View File

@@ -1,31 +0,0 @@
package build
import (
"strings"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/sshforward/sshprovider"
)
func ParseSSHSpecs(sl []string) (session.Attachable, error) {
configs := make([]sshprovider.AgentConfig, 0, len(sl))
for _, v := range sl {
c, err := parseSSH(v)
if err != nil {
return nil, err
}
configs = append(configs, *c)
}
return sshprovider.NewSSHAgentProvider(configs)
}
func parseSSH(value string) (*sshprovider.AgentConfig, error) {
parts := strings.SplitN(value, "=", 2)
cfg := sshprovider.AgentConfig{
ID: parts[0],
}
if len(parts) > 1 {
cfg.Paths = strings.Split(parts[1], ",")
}
return &cfg, nil
}

71
build/url.go Normal file
View File

@@ -0,0 +1,71 @@
package build
import (
"context"
"io/ioutil"
"path/filepath"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
gwclient "github.com/moby/buildkit/frontend/gateway/client"
"github.com/pkg/errors"
)
func createTempDockerfileFromURL(ctx context.Context, d driver.Driver, url string, pw progress.Writer) (string, error) {
c, err := driver.Boot(ctx, ctx, d, pw)
if err != nil {
return "", err
}
var out string
ch, done := progress.NewChannel(pw)
defer func() { <-done }()
_, err = c.Build(ctx, client.SolveOpt{}, "buildx", func(ctx context.Context, c gwclient.Client) (*gwclient.Result, error) {
def, err := llb.HTTP(url, llb.Filename("Dockerfile"), llb.WithCustomNamef("[internal] load %s", url)).Marshal(ctx)
if err != nil {
return nil, err
}
res, err := c.Solve(ctx, gwclient.SolveRequest{
Definition: def.ToPB(),
})
if err != nil {
return nil, err
}
ref, err := res.SingleRef()
if err != nil {
return nil, err
}
stat, err := ref.StatFile(ctx, gwclient.StatRequest{
Path: "Dockerfile",
})
if err != nil {
return nil, err
}
if stat.Size() > 512*1024 {
return nil, errors.Errorf("Dockerfile %s bigger than allowed max size", url)
}
dt, err := ref.ReadFile(ctx, gwclient.ReadRequest{
Filename: "Dockerfile",
})
if err != nil {
return nil, err
}
dir, err := ioutil.TempDir("", "buildx")
if err != nil {
return nil, err
}
if err := ioutil.WriteFile(filepath.Join(dir, "Dockerfile"), dt, 0600); err != nil {
return nil, err
}
out = dir
return nil, nil
}, ch)
if err != nil {
return "", err
}
return out, nil
}

View File

@@ -7,6 +7,7 @@ import (
"os"
"strings"
"github.com/docker/cli/opts"
"github.com/pkg/errors"
)
@@ -53,3 +54,15 @@ func toBuildkitExtraHosts(inp []string) (string, error) {
}
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

@@ -4,45 +4,100 @@ import (
"fmt"
"os"
"github.com/containerd/containerd/pkg/seed"
"github.com/docker/buildx/commands"
"github.com/docker/buildx/version"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli-plugins/plugin"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/debug"
cliflags "github.com/docker/cli/cli/flags"
"github.com/spf13/cobra"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/util/stack"
"github.com/moby/buildkit/util/tracing/detect"
"go.opentelemetry.io/otel"
_ "github.com/moby/buildkit/util/tracing/detect/delegated"
_ "github.com/moby/buildkit/util/tracing/env"
// FIXME: "k8s.io/client-go/plugin/pkg/client/auth/azure" is excluded because of compilation error
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
_ "github.com/docker/buildx/driver/docker"
_ "github.com/docker/buildx/driver/docker-container"
_ "github.com/docker/buildx/driver/kubernetes"
)
var experimental string
func init() {
seed.WithTimeAndRand()
stack.SetVersionInfo(version.Version, version.Revision)
detect.ServiceName = "buildx"
// do not log tracing errors to stdio
otel.SetErrorHandler(skipErrors{})
}
func main() {
if os.Getenv("DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND") == "" {
if len(os.Args) < 2 || os.Args[1] != manager.MetadataSubcommandName {
dockerCli, err := command.NewDockerCli()
if err != nil {
fmt.Fprintln(os.Stderr, err)
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)
if plugin.RunningStandalone() {
dockerCli, err := command.NewDockerCli()
if err != nil {
fmt.Fprintln(os.Stderr, err)
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)
}
plugin.Run(func(dockerCli command.Cli) *cobra.Command {
return commands.NewRootCmd("buildx", true, dockerCli)
},
manager.Metadata{
SchemaVersion: "0.1.0",
Vendor: "Docker Inc.",
Version: version.Version,
Experimental: experimental != "",
})
dockerCli, err := command.NewDockerCli()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
p := commands.NewRootCmd("buildx", true, dockerCli)
meta := manager.Metadata{
SchemaVersion: "0.1.0",
Vendor: "Docker Inc.",
Version: version.Version,
Experimental: experimental != "",
}
if err := plugin.RunPlugin(dockerCli, p, meta); err != nil {
if sterr, ok := err.(cli.StatusError); ok {
if sterr.Status != "" {
fmt.Fprintln(dockerCli.Err(), sterr.Status)
}
// StatusError should only be used for errors, and all errors should
// have a non-zero exit status, so never exit with 0
if sterr.StatusCode == 0 {
os.Exit(1)
}
os.Exit(sterr.StatusCode)
}
for _, s := range errdefs.Sources(err) {
s.Print(dockerCli.Err())
}
if debug.IsEnabled() {
fmt.Fprintf(dockerCli.Err(), "error: %+v", stack.Formatter(err))
} else {
fmt.Fprintf(dockerCli.Err(), "error: %v\n", err)
}
os.Exit(1)
}
}
type skipErrors struct{}
func (skipErrors) Handle(err error) {}

1
codecov.yml Normal file
View File

@@ -0,0 +1 @@
comment: false

View File

@@ -1,11 +1,17 @@
package commands
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/containerd/containerd/platforms"
"github.com/docker/buildx/bake"
"github.com/docker/buildx/build"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/buildx/util/tracing"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
@@ -14,36 +20,124 @@ import (
type bakeOptions struct {
files []string
printOnly bool
overrides []string
printOnly bool
commonOptions
}
func runBake(dockerCli command.Cli, targets []string, in bakeOptions) error {
func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error) {
ctx := appcontext.Context()
if len(in.files) == 0 {
files, err := defaultFiles()
if err != nil {
return err
ctx, end, err := tracing.TraceCurrentCommand(ctx, "bake")
if err != nil {
return err
}
defer func() {
end(err)
}()
var url string
cmdContext := "cwd://"
if len(targets) > 0 {
if bake.IsRemoteURL(targets[0]) {
url = targets[0]
targets = targets[1:]
if len(targets) > 0 {
if bake.IsRemoteURL(targets[0]) {
cmdContext = targets[0]
targets = targets[1:]
}
}
}
if len(files) == 0 {
return errors.Errorf("no docker-compose.yml or docker-bake.hcl found, specify build file with -f/--file")
}
in.files = files
}
if len(targets) == 0 {
targets = []string{"default"}
}
m, err := bake.ReadTargets(ctx, in.files, targets, in.overrides)
overrides := in.overrides
if in.exportPush {
if in.exportLoad {
return errors.Errorf("push and load may not be set together at the moment")
}
overrides = append(overrides, "*.push=true")
} else if in.exportLoad {
overrides = append(overrides, "*.output=type=docker")
}
if in.noCache != nil {
overrides = append(overrides, fmt.Sprintf("*.no-cache=%t", *in.noCache))
}
if in.pull != nil {
overrides = append(overrides, fmt.Sprintf("*.pull=%t", *in.pull))
}
contextPathHash, _ := os.Getwd()
ctx2, cancel := context.WithCancel(context.TODO())
defer cancel()
printer := progress.NewPrinter(ctx2, os.Stderr, os.Stderr, in.progress)
defer func() {
if printer != nil {
err1 := printer.Wait()
if err == nil {
err = err1
}
}
}()
dis, err := getInstanceOrDefault(ctx, dockerCli, in.builder, contextPathHash)
if err != nil {
return err
}
var files []bake.File
var inp *bake.Input
if url != "" {
files, inp, err = bake.ReadRemoteFiles(ctx, dis, url, in.files, printer)
} else {
files, err = bake.ReadLocalFiles(in.files)
}
if err != nil {
return err
}
tgts, grps, err := bake.ReadTargets(ctx, files, targets, overrides, map[string]string{
// 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 {
return err
}
// this function can update target context string from the input so call before printOnly check
bo, err := bake.TargetsToBuildOpt(tgts, inp)
if err != nil {
return err
}
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 {
return err
}
err = printer.Wait()
printer = nil
if err != nil {
return err
}
@@ -51,37 +145,25 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) error {
return nil
}
bo, err := bake.TargetsToBuildOpt(m, in.noCache, in.pull)
resp, err := build.Build(ctx, dis, bo, dockerAPI(dockerCli), confutil.ConfigDir(dockerCli), printer)
if err != nil {
return err
return wrapBuildError(err, true)
}
return buildTargets(ctx, dockerCli, bo, in.progress)
}
func defaultFiles() ([]string, error) {
fns := []string{
"docker-compose.yml", // support app
"docker-compose.yaml", // support app
"docker-bake.json",
"docker-bake.override.json",
"docker-bake.hcl",
"docker-bake.override.hcl",
}
out := make([]string, 0, len(fns))
for _, f := range fns {
if _, err := os.Stat(f); err != nil {
if os.IsNotExist(errors.Cause(err)) {
continue
}
return nil, err
if len(in.metadataFile) > 0 {
dt := make(map[string]interface{})
for t, r := range resp {
dt[t] = decodeExporterResponse(r.ExporterResponse)
}
if err := writeMetadataFile(in.metadataFile, dt); err != nil {
return err
}
out = append(out, f)
}
return out, nil
return err
}
func bakeCmd(dockerCli command.Cli) *cobra.Command {
func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
var options bakeOptions
cmd := &cobra.Command{
@@ -89,6 +171,14 @@ func bakeCmd(dockerCli command.Cli) *cobra.Command {
Aliases: []string{"f"},
Short: "Build from a file",
RunE: func(cmd *cobra.Command, args []string) error {
// reset to nil to avoid override is unset
if !cmd.Flags().Lookup("no-cache").Changed {
options.noCache = nil
}
if !cmd.Flags().Lookup("pull").Changed {
options.pull = nil
}
options.commonOptions.builder = rootOpts.builder
return runBake(dockerCli, args, options)
},
}
@@ -96,10 +186,12 @@ func bakeCmd(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
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.StringArrayVar(&options.overrides, "set", nil, "Override target value (eg: target.key=value)")
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")`)
commonFlags(&options.commonOptions, flags)
commonBuildFlags(&options.commonOptions, flags)
return cmd
}

View File

@@ -1,99 +1,139 @@
package commands
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/docker/buildx/build"
"github.com/docker/buildx/util/buildflags"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/buildx/util/tracing"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli"
"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/go-units"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/session/auth/authprovider"
"github.com/moby/buildkit/solver/errdefs"
"github.com/moby/buildkit/util/appcontext"
"github.com/moby/buildkit/util/grpcerrors"
"github.com/morikuni/aec"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"google.golang.org/grpc/codes"
)
const defaultTargetName = "default"
type buildOptions struct {
commonOptions
contextPath string
dockerfileName string
tags []string
labels []string
buildArgs []string
cacheFrom []string
cacheTo []string
target string
platforms []string
secrets []string
ssh []string
outputs []string
imageIDFile string
extraHosts []string
networkMode string
exportPush bool
exportLoad bool
// unimplemented
squash bool
quiet bool
allow []string
// hidden
// untrusted bool
// ulimits *opts.UlimitOpt
// memory opts.MemBytes
// memorySwap opts.MemSwapBytes
// shmSize opts.MemBytes
// cpuShares int64
// cpuPeriod int64
// cpuQuota int64
// cpuSetCpus string
// cpuSetMems string
// cgroupParent string
// isolation string
// compress bool
// securityOpt []string
allow []string
buildArgs []string
cacheFrom []string
cacheTo []string
cgroupParent string
contexts []string
extraHosts []string
imageIDFile string
labels []string
networkMode string
noCacheFilter []string
outputs []string
platforms []string
quiet bool
secrets []string
shmSize dockeropts.MemBytes
ssh []string
tags []string
target string
ulimits *dockeropts.UlimitOpt
commonOptions
}
type commonOptions struct {
noCache bool
progress string
pull bool
builder string
metadataFile string
noCache *bool
progress string
pull *bool
// golangci-lint#826
// nolint:structcheck
exportPush bool
// nolint:structcheck
exportLoad bool
}
func runBuild(dockerCli command.Cli, in buildOptions) error {
if in.squash {
return errors.Errorf("squash currently not implemented")
func runBuild(dockerCli command.Cli, in buildOptions) (err error) {
ctx := appcontext.Context()
ctx, end, err := tracing.TraceCurrentCommand(ctx, "build")
if err != nil {
return err
}
if in.quiet {
return errors.Errorf("quiet currently not implemented")
defer func() {
end(err)
}()
noCache := false
if in.noCache != nil {
noCache = *in.noCache
}
pull := false
if in.pull != nil {
pull = *in.pull
}
ctx := appcontext.Context()
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{
Inputs: build.Inputs{
ContextPath: in.contextPath,
DockerfilePath: in.dockerfileName,
InStream: os.Stdin,
NamedContexts: contexts,
},
Tags: in.tags,
Labels: listToMap(in.labels, false),
BuildArgs: listToMap(in.buildArgs, true),
Pull: in.pull,
NoCache: in.noCache,
Target: in.target,
ImageIDFile: in.imageIDFile,
ExtraHosts: in.extraHosts,
NetworkMode: in.networkMode,
BuildArgs: listToMap(in.buildArgs, true),
ExtraHosts: in.extraHosts,
ImageIDFile: in.imageIDFile,
Labels: listToMap(in.labels, false),
NetworkMode: in.networkMode,
NoCache: noCache,
NoCacheFilter: in.noCacheFilter,
Pull: pull,
ShmSize: in.shmSize,
Tags: in.tags,
Target: in.target,
Ulimits: in.ulimits,
}
platforms, err := platformutil.Parse(in.platforms)
@@ -104,19 +144,23 @@ func runBuild(dockerCli command.Cli, in buildOptions) error {
opts.Session = append(opts.Session, authprovider.NewDockerAuthProvider(os.Stderr))
secrets, err := build.ParseSecretSpecs(in.secrets)
secrets, err := buildflags.ParseSecretSpecs(in.secrets)
if err != nil {
return err
}
opts.Session = append(opts.Session, secrets)
ssh, err := build.ParseSSHSpecs(in.ssh)
sshSpecs := in.ssh
if len(sshSpecs) == 0 && buildflags.IsGitSSH(in.contextPath) {
sshSpecs = []string{"default"}
}
ssh, err := buildflags.ParseSSHSpecs(sshSpecs)
if err != nil {
return err
}
opts.Session = append(opts.Session, ssh)
outputs, err := build.ParseOutputs(in.outputs)
outputs, err := buildflags.ParseOutputs(in.outputs)
if err != nil {
return err
}
@@ -157,43 +201,122 @@ func runBuild(dockerCli command.Cli, in buildOptions) error {
opts.Exports = outputs
cacheImports, err := build.ParseCacheEntry(in.cacheFrom)
cacheImports, err := buildflags.ParseCacheEntry(in.cacheFrom)
if err != nil {
return err
}
opts.CacheFrom = cacheImports
cacheExports, err := build.ParseCacheEntry(in.cacheTo)
cacheExports, err := buildflags.ParseCacheEntry(in.cacheTo)
if err != nil {
return err
}
opts.CacheTo = cacheExports
allow, err := build.ParseEntitlements(in.allow)
allow, err := buildflags.ParseEntitlements(in.allow)
if err != nil {
return err
}
opts.Allow = allow
return buildTargets(ctx, dockerCli, map[string]build.Options{"default": opts}, in.progress)
}
// key string used for kubernetes "sticky" mode
contextPathHash, err := filepath.Abs(in.contextPath)
if err != nil {
contextPathHash = in.contextPath
}
func buildTargets(ctx context.Context, dockerCli command.Cli, opts map[string]build.Options, progressMode string) error {
dis, err := getDefaultDrivers(ctx, dockerCli)
imageID, err := buildTargets(ctx, dockerCli, map[string]build.Options{defaultTargetName: opts}, in.progress, contextPathHash, in.builder, in.metadataFile)
err = wrapBuildError(err, false)
if err != nil {
return err
}
ctx2, cancel := context.WithCancel(context.TODO())
defer cancel()
pw := progress.NewPrinter(ctx2, os.Stderr, progressMode)
_, err = build.Build(ctx, dis, opts, dockerAPI(dockerCli), dockerCli.ConfigFile(), pw)
return err
if in.quiet {
fmt.Println(imageID)
}
return nil
}
func buildCmd(dockerCli command.Cli) *cobra.Command {
var options buildOptions
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())
defer cancel()
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()
if err == nil {
err = err1
}
if err != nil {
return "", err
}
if len(metadataFile) > 0 && resp != nil {
if err := writeMetadataFile(metadataFile, decodeExporterResponse(resp[defaultTargetName].ExporterResponse)); err != nil {
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 {
options := newBuildOptions()
cmd := &cobra.Command{
Use: "build [OPTIONS] PATH | URL | -",
@@ -202,94 +325,139 @@ func buildCmd(dockerCli command.Cli) *cobra.Command {
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
options.contextPath = args[0]
options.builder = rootOpts.builder
cmd.Flags().VisitAll(checkWarnedFlags)
return runBuild(dockerCli, options)
},
}
var platformsDefault []string
if v := os.Getenv("DOCKER_DEFAULT_PLATFORM"); v != "" {
platformsDefault = []string{v}
}
flags := cmd.Flags()
flags.BoolVar(&options.exportPush, "push", false, "Shorthand for --output=type=registry")
flags.BoolVar(&options.exportLoad, "load", false, "Shorthand for --output=type=docker")
flags.StringSliceVar(&options.extraHosts, "add-host", []string{}, `Add a custom host-to-IP mapping (format: "host:ip")`)
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.StringArrayVar(&options.buildArgs, "build-arg", []string{}, "Set build-time variables")
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.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.cacheFrom, "cache-from", []string{}, "External cache sources (eg. user/app:cache, type=local,src=path/to/dir)")
flags.StringArrayVar(&options.cacheTo, "cache-to", []string{}, "Cache export destinations (eg. user/app:cache, type=local,dest=path/to/dir)")
flags.BoolVar(&options.exportLoad, "load", false, `Shorthand for "--output=type=docker"`)
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.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.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.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file")
flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer")
flags.MarkHidden("quiet")
flags.MarkHidden("squash")
flags.StringArrayVar(&options.secrets, "secret", []string{}, `Secret to expose to the build (format: "id=mysecret[,src=/local/secret]")`)
flags.Var(&options.shmSize, "shm-size", `Size of "/dev/shm"`)
flags.StringArrayVar(&options.ssh, "ssh", []string{}, `SSH agent socket or keys to expose to the build (format: "default|<id>[=<socket>|<key>[,<key>]]")`)
flags.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
var ignore string
var ignoreSlice []string
var ignoreBool bool
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.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.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.MarkHidden("rm")
flags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers")
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)")
commonFlags(&options.commonOptions, flags)
commonBuildFlags(&options.commonOptions, flags)
return cmd
}
func commonFlags(options *commonOptions, flags *pflag.FlagSet) {
flags.BoolVar(&options.noCache, "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.BoolVar(&options.pull, "pull", false, "Always attempt to pull a newer version of the image")
func commonBuildFlags(options *commonOptions, flags *pflag.FlagSet) {
options.noCache = flags.Bool("no-cache", false, "Do not use cache when building the image")
flags.StringVar(&options.progress, "progress", "auto", `Set type of progress output ("auto", "plain", "tty"). Use plain to show container output`)
options.pull = flags.Bool("pull", false, "Always attempt to pull all referenced images")
flags.StringVar(&options.metadataFile, "metadata-file", "", "Write build result metadata to 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 {
@@ -298,7 +466,10 @@ func listToMap(values []string, defaultEnv bool) map[string]string {
kv := strings.SplitN(value, "=", 2)
if len(kv) == 1 {
if defaultEnv {
result[kv[0]] = os.Getenv(kv[0])
v, ok := os.LookupEnv(kv[0])
if ok {
result[kv[0]] = v
}
} else {
result[kv[0]] = ""
}
@@ -308,3 +479,80 @@ func listToMap(values []string, defaultEnv bool) map[string]string {
}
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,13 +1,19 @@
package commands
import (
"bytes"
"context"
"encoding/csv"
"fmt"
"net/url"
"os"
"strings"
"time"
"github.com/docker/buildx/driver"
"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/command"
"github.com/google/shlex"
@@ -28,6 +34,7 @@ type createOptions struct {
flags string
configFile string
driverOpts []string
bootstrap bool
// upgrade bool // perform upgrade of the driver
}
@@ -69,7 +76,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
return errors.Errorf("failed to find driver %q", in.driver)
}
txn, release, err := getStore(dockerCli)
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return err
}
@@ -137,11 +144,24 @@ 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>`")
}
ep, err = getCurrentEndpoint(dockerCli)
ep, err = storeutil.GetCurrentEndpoint(dockerCli)
if err != nil {
return err
}
}
if in.driver == "kubernetes" {
// naming endpoint to make --append works
ep = (&url.URL{
Scheme: in.driver,
Path: "/" + in.name,
RawQuery: (&url.Values{
"deployment": {in.nodeName},
"kubeconfig": {os.Getenv("KUBECONFIG")},
}).Encode(),
}).String()
}
m, err := csvToMap(in.driverOpts)
if err != nil {
return err
@@ -156,7 +176,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
}
if in.use && ep != "" {
current, err := getCurrentEndpoint(dockerCli)
current, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil {
return err
}
@@ -165,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)
return nil
}
@@ -172,9 +207,12 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
func createCmd(dockerCli command.Cli) *cobra.Command {
var options createOptions
var drivers []string
for s := range driver.GetFactories() {
drivers = append(drivers, s)
var drivers bytes.Buffer
for _, d := range driver.GetFactories() {
if len(drivers.String()) > 0 {
drivers.WriteString(", ")
}
drivers.WriteString(fmt.Sprintf(`"%s"`, d.Name()))
}
cmd := &cobra.Command{
@@ -189,18 +227,20 @@ func createCmd(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
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.flags, "buildkitd-flags", "", "Flags for buildkitd daemon")
flags.StringVar(&options.configFile, "config", "", "BuildKit config file")
flags.StringArrayVar(&options.platform, "platform", []string{}, "Fixed platforms for current node")
flags.StringArrayVar(&options.driverOpts, "driver-opt", []string{}, "Options for the driver")
flags.BoolVar(&options.bootstrap, "bootstrap", false, "Boot builder after creation")
flags.BoolVar(&options.actionAppend, "append", false, "Append a node to builder instead of changing it")
flags.BoolVar(&options.actionLeave, "leave", false, "Remove a node from builder instead of changing it")
flags.BoolVar(&options.use, "use", false, "Set the current builder instance")
_ = flags
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
return cmd
}

202
commands/diskusage.go Normal file
View File

@@ -0,0 +1,202 @@
package commands
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"time"
"github.com/docker/buildx/build"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/docker/go-units"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/appcontext"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
type duOptions struct {
builder string
filter opts.FilterOpt
verbose bool
}
func runDiskUsage(dockerCli command.Cli, opts duOptions) error {
ctx := appcontext.Context()
pi, err := toBuildkitPruneInfo(opts.filter.Value())
if err != nil {
return err
}
dis, err := getInstanceOrDefault(ctx, dockerCli, opts.builder, "")
if err != nil {
return err
}
for _, di := range dis {
if di.Err != nil {
return err
}
}
out := make([][]*client.UsageInfo, len(dis))
eg, ctx := errgroup.WithContext(ctx)
for i, di := range dis {
func(i int, di build.DriverInfo) {
eg.Go(func() error {
if di.Driver != nil {
c, err := di.Driver.Client(ctx)
if err != nil {
return err
}
du, err := c.DiskUsage(ctx, client.WithFilter(pi.Filter))
if err != nil {
return err
}
out[i] = du
return nil
}
return nil
})
}(i, di)
}
if err := eg.Wait(); err != nil {
return err
}
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0)
first := true
for _, du := range out {
if du == nil {
continue
}
if opts.verbose {
printVerbose(tw, du)
} else {
if first {
printTableHeader(tw)
first = false
}
for _, di := range du {
printTableRow(tw, di)
}
tw.Flush()
}
}
if opts.filter.Value().Len() == 0 {
printSummary(tw, out)
}
tw.Flush()
return nil
}
func duCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
options := duOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
Use: "du",
Short: "Disk usage",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.builder
return runDiskUsage(dockerCli, options)
},
}
flags := cmd.Flags()
flags.Var(&options.filter, "filter", "Provide filter values")
flags.BoolVar(&options.verbose, "verbose", false, "Provide a more verbose output")
return cmd
}
func printKV(w io.Writer, k string, v interface{}) {
fmt.Fprintf(w, "%s:\t%v\n", k, v)
}
func printVerbose(tw *tabwriter.Writer, du []*client.UsageInfo) {
for _, di := range du {
printKV(tw, "ID", di.ID)
if len(di.Parents) != 0 {
printKV(tw, "Parent", strings.Join(di.Parents, ","))
}
printKV(tw, "Created at", di.CreatedAt)
printKV(tw, "Mutable", di.Mutable)
printKV(tw, "Reclaimable", !di.InUse)
printKV(tw, "Shared", di.Shared)
printKV(tw, "Size", units.HumanSize(float64(di.Size)))
if di.Description != "" {
printKV(tw, "Description", di.Description)
}
printKV(tw, "Usage count", di.UsageCount)
if di.LastUsedAt != nil {
printKV(tw, "Last used", units.HumanDuration(time.Since(*di.LastUsedAt))+" ago")
}
if di.RecordType != "" {
printKV(tw, "Type", di.RecordType)
}
fmt.Fprintf(tw, "\n")
}
tw.Flush()
}
func printTableHeader(tw *tabwriter.Writer) {
fmt.Fprintln(tw, "ID\tRECLAIMABLE\tSIZE\tLAST ACCESSED")
}
func printTableRow(tw *tabwriter.Writer, di *client.UsageInfo) {
id := di.ID
if di.Mutable {
id += "*"
}
size := units.HumanSize(float64(di.Size))
if di.Shared {
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) {
total := int64(0)
reclaimable := int64(0)
shared := int64(0)
for _, du := range dus {
for _, di := range du {
if di.Size > 0 {
total += di.Size
if !di.InUse {
reclaimable += di.Size
}
}
if di.Shared {
shared += di.Size
}
}
}
if shared > 0 {
fmt.Fprintf(tw, "Shared:\t%s\n", units.HumanSize(float64(shared)))
fmt.Fprintf(tw, "Private:\t%s\n", units.HumanSize(float64(total-shared)))
}
fmt.Fprintf(tw, "Reclaimable:\t%s\n", units.HumanSize(float64(reclaimable)))
fmt.Fprintf(tw, "Total:\t%s\n", units.HumanSize(float64(total)))
tw.Flush()
}

View File

@@ -6,6 +6,8 @@ import (
"io/ioutil"
"strings"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli/cli/command"
"github.com/docker/distribution/reference"
@@ -18,6 +20,7 @@ import (
)
type createOptions struct {
builder string
files []string
tags []string
dryrun bool
@@ -101,9 +104,32 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
ctx := appcontext.Context()
r := imagetools.New(imagetools.Opt{
Auth: dockerCli.ConfigFile(),
})
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 {
return err
}
r := imagetools.New(imageopt)
if sourceRefs {
eg, ctx2 := errgroup.WithContext(ctx)
@@ -118,7 +144,15 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
return err
}
srcs[i].Ref = nil
srcs[i].Desc = desc
if srcs[i].Desc.Digest == "" {
srcs[i].Desc = desc
} else {
var err error
srcs[i].Desc, err = mergeDesc(desc, srcs[i].Desc)
if err != nil {
return err
}
}
return nil
})
}(i)
@@ -144,9 +178,7 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error {
}
// new resolver cause need new auth
r = imagetools.New(imagetools.Opt{
Auth: dockerCli.ConfigFile(),
})
r = imagetools.New(imageopt)
for _, t := range tags {
if err := r.Push(ctx, t, desc, dt); err != nil {
@@ -168,7 +200,7 @@ func parseSources(in []string) ([]*src, error) {
for i, in := range in {
s, err := parseSource(in)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse source %q, valid sources are digests, refereces and descriptors", in)
return nil, errors.Wrapf(err, "failed to parse source %q, valid sources are digests, references and descriptors", in)
}
out[i] = s
}
@@ -216,25 +248,39 @@ func parseSource(in string) (*src, error) {
return &s, nil
}
func createCmd(dockerCli command.Cli) *cobra.Command {
func createCmd(dockerCli command.Cli, opts RootOptions) *cobra.Command {
var options createOptions
cmd := &cobra.Command{
Use: "create [OPTIONS] [SOURCE] [SOURCE...]",
Short: "Create a new image based on source images",
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = opts.Builder
return runCreate(dockerCli, options, args)
},
}
flags := cmd.Flags()
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.BoolVar(&options.dryrun, "dry-run", false, "Show final image instead of pushing")
flags.BoolVar(&options.actionAppend, "append", false, "Append to existing manifest")
_ = flags
return cmd
}
func mergeDesc(d1, d2 ocispec.Descriptor) (ocispec.Descriptor, error) {
if d2.Size != 0 && d1.Size != d2.Size {
return ocispec.Descriptor{}, errors.Errorf("invalid size mismatch for %s, %d != %d", d1.Digest, d2.Size, d1.Size)
}
if d2.MediaType != "" {
d1.MediaType = d2.MediaType
}
if len(d2.Annotations) != 0 {
d1.Annotations = d2.Annotations // no merge so support removes
}
if d2.Platform != nil {
d1.Platform = d2.Platform // missing items filled in later from image config
}
return d1, nil
}

View File

@@ -1,68 +1,82 @@
package commands
import (
"fmt"
"os"
"github.com/containerd/containerd/images"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/cli-docs-tool/annotation"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type inspectOptions struct {
raw bool
builder string
format string
raw bool
}
func runInspect(dockerCli command.Cli, in inspectOptions, name string) error {
ctx := appcontext.Context()
r := imagetools.New(imagetools.Opt{
Auth: dockerCli.ConfigFile(),
})
if in.format != "" && in.raw {
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 {
return err
}
if in.raw {
fmt.Printf("%s\n", dt)
return nil
p, err := imagetools.NewPrinter(ctx, imageopt, name, in.format)
if err != nil {
return err
}
switch desc.MediaType {
// case images.MediaTypeDockerSchema2Manifest, specs.MediaTypeImageManifest:
// TODO: handle distribution manifest and schema1
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
imagetools.PrintManifestList(dt, desc, name, os.Stdout)
default:
fmt.Printf("%s\n", dt)
}
return nil
return p.Print(in.raw, dockerCli.Out())
}
func inspectCmd(dockerCli command.Cli) *cobra.Command {
func inspectCmd(dockerCli command.Cli, rootOpts RootOptions) *cobra.Command {
var options inspectOptions
cmd := &cobra.Command{
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),
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.Builder
return runInspect(dockerCli, options, args[0])
},
}
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
}

View File

@@ -5,15 +5,19 @@ import (
"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{
Use: "imagetools",
Short: "Commands to work on images in registry",
}
cmd.AddCommand(
inspectCmd(dockerCli),
createCmd(dockerCli),
createCmd(dockerCli, opts),
inspectCmd(dockerCli, opts),
)
return cmd

View File

@@ -8,40 +8,24 @@ import (
"text/tabwriter"
"time"
"github.com/docker/buildx/build"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
type inspectOptions struct {
bootstrap bool
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, args []string) error {
func runInspect(dockerCli command.Cli, in inspectOptions) error {
ctx := appcontext.Context()
txn, release, err := getStore(dockerCli)
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return err
}
@@ -49,13 +33,13 @@ func runInspect(dockerCli command.Cli, in inspectOptions, args []string) error {
var ng *store.NodeGroup
if len(args) > 0 {
ng, err = getNodeGroup(txn, dockerCli, args[0])
if in.builder != "" {
ng, err = storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil {
return err
}
} else {
ng, err = getCurrentInstance(txn, dockerCli)
ng, err = storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil {
return err
}
@@ -73,17 +57,19 @@ func runInspect(dockerCli command.Cli, in inspectOptions, args []string) error {
ngi := &nginfo{ng: ng}
timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
timeoutCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
err = loadNodeGroupData(timeoutCtx, dockerCli, ngi)
var bootNgi *nginfo
if in.bootstrap {
var ok bool
ok, err = boot(ctx, ngi)
if err != nil {
return err
}
bootNgi = ngi
if ok {
ngi = &nginfo{ng: ng}
err = loadNodeGroupData(ctx, dockerCli, ngi)
@@ -112,12 +98,14 @@ func runInspect(dockerCli command.Cli, in inspectOptions, args []string) error {
fmt.Fprintf(w, "Error:\t%s\n", err.Error())
} else if err := ngi.drivers[i].err; err != nil {
fmt.Fprintf(w, "Error:\t%s\n", err.Error())
} else if bootNgi != nil && len(bootNgi.drivers) > i && bootNgi.drivers[i].err != nil {
fmt.Fprintf(w, "Error:\t%s\n", bootNgi.drivers[i].err.Error())
} else {
fmt.Fprintf(w, "Status:\t%s\n", ngi.drivers[i].info.Status)
if len(n.Flags) > 0 {
fmt.Fprintf(w, "Flags:\t%s\n", strings.Join(n.Flags, " "))
}
fmt.Fprintf(w, "Platforms:\t%s\n", strings.Join(platformutil.Format(platformutil.Dedupe(append(n.Platforms, ngi.drivers[i].platforms...))), ", "))
fmt.Fprintf(w, "Platforms:\t%s\n", strings.Join(platformutil.FormatInGroups(n.Platforms, ngi.drivers[i].platforms), ", "))
}
}
}
@@ -127,7 +115,7 @@ func runInspect(dockerCli command.Cli, in inspectOptions, args []string) error {
return nil
}
func inspectCmd(dockerCli command.Cli) *cobra.Command {
func inspectCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
var options inspectOptions
cmd := &cobra.Command{
@@ -135,52 +123,16 @@ func inspectCmd(dockerCli command.Cli) *cobra.Command {
Short: "Inspect current builder instance",
Args: cli.RequiresMaxArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runInspect(dockerCli, options, args)
options.builder = rootOpts.builder
if len(args) > 0 {
options.builder = args[0]
}
return runInspect(dockerCli, options)
},
}
flags := cmd.Flags()
flags.BoolVar(&options.bootstrap, "bootstrap", false, "Ensure builder has booted before inspecting")
_ = flags
return cmd
}
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
}
pw := progress.NewPrinter(context.TODO(), os.Stderr, "auto")
mw := progress.NewMultiWriter(pw)
eg, _ := errgroup.WithContext(ctx)
for _, idx := range toBoot {
func(idx int) {
eg.Go(func() error {
pw := mw.WithPrefix(ngi.ng.Nodes[idx].Name, len(toBoot) > 1)
_, err := driver.Boot(ctx, ngi.drivers[idx].di.Driver, pw)
if err != nil {
ngi.drivers[idx].err = err
}
close(pw.Status())
<-pw.Done()
return nil
})
}(idx)
}
return true, eg.Wait()
}

View File

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

View File

@@ -10,6 +10,8 @@ import (
"time"
"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/cli/cli"
"github.com/docker/cli/cli/command"
@@ -24,13 +26,13 @@ type lsOptions struct {
func runLs(dockerCli command.Cli, in lsOptions) error {
ctx := appcontext.Context()
txn, release, err := getStore(dockerCli)
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return err
}
defer release()
ctx, cancel := context.WithTimeout(ctx, 7*time.Second)
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
ll, err := txn.List()
@@ -79,7 +81,7 @@ func runLs(dockerCli command.Cli, in lsOptions) error {
}
currentName := "default"
current, err := getCurrentInstance(txn, dockerCli)
current, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil {
return err
}
@@ -126,11 +128,10 @@ func printngi(w io.Writer, ngi *nginfo) {
if d.info != nil {
status = d.info.Status.String()
}
p := append(n.Platforms, d.platforms...)
if err != "" {
fmt.Fprintf(w, " %s\t%s\t%s\n", n.Name, n.Endpoint, err)
} else {
fmt.Fprintf(w, " %s\t%s\t%s\t%s\n", n.Name, n.Endpoint, status, strings.Join(platformutil.Format(p), ", "))
fmt.Fprintf(w, " %s\t%s\t%s\t%s\n", n.Name, n.Endpoint, status, strings.Join(platformutil.FormatInGroups(n.Platforms, d.platforms), ", "))
}
}
}
@@ -148,5 +149,8 @@ func lsCmd(dockerCli command.Cli) *cobra.Command {
},
}
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
return cmd
}

197
commands/prune.go Normal file
View File

@@ -0,0 +1,197 @@
package commands
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"time"
"github.com/docker/buildx/build"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/opts"
"github.com/docker/docker/api/types/filters"
"github.com/docker/go-units"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
type pruneOptions struct {
builder string
all bool
filter opts.FilterOpt
keepStorage opts.MemBytes
force bool
verbose bool
}
const (
normalWarning = `WARNING! This will remove all dangling build cache. Are you sure you want to continue?`
allCacheWarning = `WARNING! This will remove all build cache. Are you sure you want to continue?`
)
func runPrune(dockerCli command.Cli, opts pruneOptions) error {
ctx := appcontext.Context()
pruneFilters := opts.filter.Value()
pruneFilters = command.PruneFilters(dockerCli, pruneFilters)
pi, err := toBuildkitPruneInfo(pruneFilters)
if err != nil {
return err
}
warning := normalWarning
if opts.all {
warning = allCacheWarning
}
if !opts.force && !command.PromptForConfirmation(dockerCli.In(), dockerCli.Out(), warning) {
return nil
}
dis, err := getInstanceOrDefault(ctx, dockerCli, opts.builder, "")
if err != nil {
return err
}
for _, di := range dis {
if di.Err != nil {
return err
}
}
ch := make(chan client.UsageInfo)
printed := make(chan struct{})
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0)
first := true
total := int64(0)
go func() {
defer close(printed)
for du := range ch {
total += du.Size
if opts.verbose {
printVerbose(tw, []*client.UsageInfo{&du})
} else {
if first {
printTableHeader(tw)
first = false
}
printTableRow(tw, &du)
tw.Flush()
}
}
}()
eg, ctx := errgroup.WithContext(ctx)
for _, di := range dis {
func(di build.DriverInfo) {
eg.Go(func() error {
if di.Driver != nil {
c, err := di.Driver.Client(ctx)
if err != nil {
return err
}
popts := []client.PruneOption{
client.WithKeepOpt(pi.KeepDuration, opts.keepStorage.Value()),
client.WithFilter(pi.Filter),
}
if opts.all {
popts = append(popts, client.PruneAll)
}
return c.Prune(ctx, ch, popts...)
}
return nil
})
}(di)
}
if err := eg.Wait(); err != nil {
return err
}
close(ch)
<-printed
tw = tabwriter.NewWriter(os.Stdout, 1, 8, 1, '\t', 0)
fmt.Fprintf(tw, "Total:\t%s\n", units.HumanSize(float64(total)))
tw.Flush()
return nil
}
func pruneCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
options := pruneOptions{filter: opts.NewFilterOpt()}
cmd := &cobra.Command{
Use: "prune",
Short: "Remove build cache",
Args: cli.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
options.builder = rootOpts.builder
return runPrune(dockerCli, options)
},
}
flags := cmd.Flags()
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.keepStorage, "keep-storage", "Amount of disk space to keep for cache")
flags.BoolVar(&options.verbose, "verbose", false, "Provide a more verbose output")
flags.BoolVarP(&options.force, "force", "f", false, "Do not prompt for confirmation")
return cmd
}
func toBuildkitPruneInfo(f filters.Args) (*client.PruneInfo, error) {
var until time.Duration
untilValues := f.Get("until") // canonical
unusedForValues := f.Get("unused-for") // deprecated synonym for "until" filter
if len(untilValues) > 0 && len(unusedForValues) > 0 {
return nil, errors.Errorf("conflicting filters %q and %q", "until", "unused-for")
}
filterKey := "until"
if len(unusedForValues) > 0 {
filterKey = "unused-for"
}
untilValues = append(untilValues, unusedForValues...)
switch len(untilValues) {
case 0:
// nothing to do
case 1:
var err error
until, err = time.ParseDuration(untilValues[0])
if err != nil {
return nil, errors.Wrapf(err, "%q filter expects a duration (e.g., '24h')", filterKey)
}
default:
return nil, errors.Errorf("filters expect only one value")
}
bkFilter := make([]string, 0, f.Len())
for _, field := range f.Keys() {
values := f.Get(field)
switch len(values) {
case 0:
bkFilter = append(bkFilter, field)
case 1:
if field == "id" {
bkFilter = append(bkFilter, field+"~="+values[0])
} else {
bkFilter = append(bkFilter, field+"=="+values[0])
}
default:
return nil, errors.Errorf("filters expect only one value")
}
}
return &client.PruneInfo{
KeepDuration: until,
Filter: []string{strings.Join(bkFilter, ",")},
}, nil
}

View File

@@ -2,44 +2,65 @@ package commands
import (
"context"
"time"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
type rmOptions struct {
builder string
keepState bool
keepDaemon bool
allInactive bool
force bool
}
func runRm(dockerCli command.Cli, in rmOptions, args []string) error {
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 {
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 {
return err
}
defer release()
if len(args) > 0 {
ng, err := getNodeGroup(txn, dockerCli, args[0])
if in.allInactive {
return rmAllInactive(ctx, txn, dockerCli, in)
}
if in.builder != "" {
ng, err := storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil {
return err
}
err1 := stop(ctx, dockerCli, ng, true)
err1 := rm(ctx, dockerCli, in, ng)
if err := txn.Remove(ng.Name); err != nil {
return err
}
return err1
}
ng, err := getCurrentInstance(txn, dockerCli)
ng, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil {
return err
}
if ng != nil {
err1 := stop(ctx, dockerCli, ng, true)
err1 := rm(ctx, dockerCli, in, ng)
if err := txn.Remove(ng.Name); err != nil {
return err
}
@@ -49,7 +70,7 @@ func runRm(dockerCli command.Cli, in rmOptions, args []string) error {
return nil
}
func rmCmd(dockerCli command.Cli) *cobra.Command {
func rmCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
var options rmOptions
cmd := &cobra.Command{
@@ -57,28 +78,43 @@ func rmCmd(dockerCli command.Cli) *cobra.Command {
Short: "Remove a builder instance",
Args: cli.RequiresMaxArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runRm(dockerCli, options, args)
options.builder = rootOpts.builder
if len(args) > 0 {
if options.allInactive {
return errors.New("cannot specify builder name when --all-inactive is set")
}
options.builder = args[0]
}
return runRm(dockerCli, options)
},
}
flags := cmd.Flags()
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
}
func stop(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, rm bool) error {
dis, err := driversForNodeGroup(ctx, dockerCli, ng)
func rm(ctx context.Context, dockerCli command.Cli, in rmOptions, ng *store.NodeGroup) error {
dis, err := driversForNodeGroup(ctx, dockerCli, ng, "")
if err != nil {
return err
}
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 {
return err
}
if rm {
if err := di.Driver.Rm(ctx, true); err != nil {
return err
}
}
}
if err := di.Driver.Rm(ctx, true, !in.keepState, !in.keepDaemon); err != nil {
return err
}
if di.Err != nil {
err = di.Err
@@ -87,25 +123,41 @@ func stop(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, rm bo
return err
}
func stopCurrent(ctx context.Context, dockerCli command.Cli, rm bool) error {
dis, err := getDefaultDrivers(ctx, dockerCli)
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
}
for _, di := range dis {
if di.Driver != nil {
if err := di.Driver.Stop(ctx, true); err != nil {
return err
}
if rm {
if err := di.Driver.Rm(ctx, true); err != nil {
return err
}
}
}
if di.Err != nil {
err = di.Err
}
builders := make([]*nginfo, len(ll))
for i, ng := range ll {
builders[i] = &nginfo{ng: ng}
}
return err
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

@@ -1,16 +1,26 @@
package commands
import (
"os"
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/command"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func NewRootCmd(name string, isPlugin bool, dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Short: "Build with BuildKit",
Short: "Docker Buildx",
Long: `Extended build capabilities with BuildKit`,
Use: name,
Annotations: map[string]string{
annotation.CodeDelimiter: `"`,
},
}
if isPlugin {
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
@@ -18,23 +28,56 @@ 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)
return cmd
}
type rootOptions struct {
builder string
}
func addCommands(cmd *cobra.Command, dockerCli command.Cli) {
opts := &rootOptions{}
rootFlags(opts, cmd.PersistentFlags())
cmd.AddCommand(
buildCmd(dockerCli),
bakeCmd(dockerCli),
buildCmd(dockerCli, opts),
bakeCmd(dockerCli, opts),
createCmd(dockerCli),
rmCmd(dockerCli),
rmCmd(dockerCli, opts),
lsCmd(dockerCli),
useCmd(dockerCli),
inspectCmd(dockerCli),
stopCmd(dockerCli),
useCmd(dockerCli, opts),
inspectCmd(dockerCli, opts),
stopCmd(dockerCli, opts),
installCmd(dockerCli),
uninstallCmd(dockerCli),
versionCmd(dockerCli),
imagetoolscmd.RootCmd(dockerCli),
pruneCmd(dockerCli, opts),
duCmd(dockerCli, opts),
imagetoolscmd.RootCmd(dockerCli, imagetoolscmd.RootOptions{Builder: opts.builder}),
)
}
func rootFlags(options *rootOptions, flags *pflag.FlagSet) {
flags.StringVar(&options.builder, "builder", os.Getenv("BUILDX_BUILDER"), "Override the configured builder instance")
}

View File

@@ -1,6 +1,10 @@
package commands
import (
"context"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/moby/buildkit/util/appcontext"
@@ -8,40 +12,41 @@ import (
)
type stopOptions struct {
builder string
}
func runStop(dockerCli command.Cli, in stopOptions, args []string) error {
func runStop(dockerCli command.Cli, in stopOptions) error {
ctx := appcontext.Context()
txn, release, err := getStore(dockerCli)
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return err
}
defer release()
if len(args) > 0 {
ng, err := getNodeGroup(txn, dockerCli, args[0])
if in.builder != "" {
ng, err := storeutil.GetNodeGroup(txn, dockerCli, in.builder)
if err != nil {
return err
}
if err := stop(ctx, dockerCli, ng, false); err != nil {
if err := stop(ctx, dockerCli, ng); err != nil {
return err
}
return nil
}
ng, err := getCurrentInstance(txn, dockerCli)
ng, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil {
return err
}
if ng != nil {
return stop(ctx, dockerCli, ng, false)
return stop(ctx, dockerCli, ng)
}
return stopCurrent(ctx, dockerCli, false)
return stopCurrent(ctx, dockerCli)
}
func stopCmd(dockerCli command.Cli) *cobra.Command {
func stopCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
var options stopOptions
cmd := &cobra.Command{
@@ -49,15 +54,49 @@ func stopCmd(dockerCli command.Cli) *cobra.Command {
Short: "Stop builder instance",
Args: cli.RequiresMaxArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runStop(dockerCli, options, args)
options.builder = rootOpts.builder
if len(args) > 0 {
options.builder = args[0]
}
return runStop(dockerCli, options)
},
}
flags := cmd.Flags()
// flags.StringArrayVarP(&options.outputs, "output", "o", []string{}, "Output destination (format: type=local,dest=path)")
_ = flags
return cmd
}
func stop(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup) error {
dis, err := driversForNodeGroup(ctx, dockerCli, ng, "")
if err != nil {
return err
}
for _, di := range dis {
if di.Driver != nil {
if err := di.Driver.Stop(ctx, true); err != nil {
return err
}
}
if di.Err != nil {
err = di.Err
}
}
return err
}
func stopCurrent(ctx context.Context, dockerCli command.Cli) error {
dis, err := getDefaultDrivers(ctx, dockerCli, false, "")
if err != nil {
return err
}
for _, di := range dis {
if di.Driver != nil {
if err := di.Driver.Stop(ctx, true); err != nil {
return err
}
}
if di.Err != nil {
err = di.Err
}
}
return err
}

View File

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

View File

@@ -3,6 +3,7 @@ package commands
import (
"os"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/pkg/errors"
@@ -12,22 +13,23 @@ import (
type useOptions struct {
isGlobal bool
isDefault bool
builder string
}
func runUse(dockerCli command.Cli, in useOptions, name string) error {
txn, release, err := getStore(dockerCli)
func runUse(dockerCli command.Cli, in useOptions) error {
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return err
}
defer release()
if _, err := txn.NodeGroupByName(name); err != nil {
if _, err := txn.NodeGroupByName(in.builder); err != nil {
if os.IsNotExist(errors.Cause(err)) {
if name == "default" && name != dockerCli.CurrentContext() {
if in.builder == "default" && in.builder != dockerCli.CurrentContext() {
return errors.Errorf("run `docker context use default` to switch to default context")
}
if name == "default" || name == dockerCli.CurrentContext() {
ep, err := getCurrentEndpoint(dockerCli)
if in.builder == "default" || in.builder == dockerCli.CurrentContext() {
ep, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil {
return err
}
@@ -41,44 +43,45 @@ func runUse(dockerCli command.Cli, in useOptions, name string) error {
return err
}
for _, l := range list {
if l.Name == name {
return errors.Errorf("run `docker context use %s` to switch to context %s", name, name)
if l.Name == in.builder {
return errors.Errorf("run `docker context use %s` to switch to context %s", in.builder, in.builder)
}
}
}
return errors.Wrapf(err, "failed to find instance %q", name)
return errors.Wrapf(err, "failed to find instance %q", in.builder)
}
ep, err := getCurrentEndpoint(dockerCli)
ep, err := storeutil.GetCurrentEndpoint(dockerCli)
if err != nil {
return err
}
if err := txn.SetCurrent(ep, name, in.isGlobal, in.isDefault); err != nil {
if err := txn.SetCurrent(ep, in.builder, in.isGlobal, in.isDefault); err != nil {
return err
}
return nil
}
func useCmd(dockerCli command.Cli) *cobra.Command {
func useCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
var options useOptions
cmd := &cobra.Command{
Use: "use [OPTIONS] NAME",
Short: "Set the current builder instance",
Args: cli.ExactArgs(1),
Args: cli.RequiresMaxArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return runUse(dockerCli, options, args[0])
options.builder = rootOpts.builder
if len(args) > 0 {
options.builder = args[0]
}
return runUse(dockerCli, options)
},
}
flags := cmd.Flags()
flags.BoolVar(&options.isGlobal, "global", false, "Builder persists context changes")
flags.BoolVar(&options.isDefault, "default", false, "Set builder as default for current context")
_ = flags
return cmd
}

View File

@@ -2,69 +2,32 @@ package commands
import (
"context"
"net/url"
"os"
"path/filepath"
"strings"
"github.com/docker/buildx/build"
"github.com/docker/buildx/driver"
ctxkube "github.com/docker/buildx/driver/kubernetes/context"
"github.com/docker/buildx/store"
"github.com/docker/buildx/store/storeutil"
"github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context/docker"
ctxstore "github.com/docker/cli/cli/context/store"
dopts "github.com/docker/cli/opts"
dockerclient "github.com/docker/docker/client"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sync/errgroup"
"k8s.io/client-go/tools/clientcmd"
)
// getStore returns current builder instance store
func getStore(dockerCli command.Cli) (*store.Txn, func(), error) {
dir := filepath.Dir(dockerCli.ConfigFile().Filename)
s, err := store.New(dir)
if err != nil {
return nil, nil, err
}
return s.Txn()
}
// 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
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 ep == "default" {
return de, nil
@@ -78,62 +41,8 @@ func validateEndpoint(dockerCli command.Cli, ep string) (string, error) {
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
func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup) ([]build.DriverInfo, error) {
func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.NodeGroup, contextPathHash string) ([]build.DriverInfo, error) {
eg, _ := errgroup.WithContext(ctx)
dis := make([]build.DriverInfo, len(ng.Nodes))
@@ -155,13 +64,18 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
}
ng.Driver = f.Name()
}
imageopt, err := storeutil.GetImageConfig(dockerCli, ng)
if err != nil {
return nil, err
}
for i, n := range ng.Nodes {
func(i int, n store.Node) {
eg.Go(func() error {
di := build.DriverInfo{
Name: n.Name,
Platform: n.Platforms,
Name: n.Name,
Platform: n.Platforms,
ProxyConfig: storeutil.GetProxyConfig(dockerCli),
}
defer func() {
dis[i] = di
@@ -174,12 +88,43 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
// TODO: replace the following line with dockerclient.WithAPIVersionNegotiation option in clientForEndpoint
dockerapi.NegotiateAPIVersion(ctx)
d, err := driver.GetDriver(ctx, "buildx_buildkit_"+n.Name, f, dockerapi, n.Flags, n.ConfigFile, n.DriverOpts)
contextStore := dockerCli.ContextStore()
var kcc driver.KubeClientConfig
kcc, err = configFromContext(n.Endpoint, contextStore)
if err != nil {
// err is returned if n.Endpoint is non-context name like "unix:///var/run/docker.sock".
// try again with name="default".
// FIXME: n should retain real context name.
kcc, err = configFromContext("default", contextStore)
if err != nil {
logrus.Error(err)
}
}
tryToUseKubeConfigInCluster := false
if kcc == nil {
tryToUseKubeConfigInCluster = true
} else {
if _, err := kcc.ClientConfig(); err != nil {
tryToUseKubeConfigInCluster = true
}
}
if tryToUseKubeConfigInCluster {
kccInCluster := driver.KubeClientConfigInCluster{}
if _, err := kccInCluster.ClientConfig(); err == nil {
logrus.Debug("using kube config in cluster")
kcc = kccInCluster
}
}
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 {
di.Err = err
return nil
}
di.Driver = d
di.ImageOpt = imageopt
return nil
})
}(i, n)
@@ -192,6 +137,22 @@ func driversForNodeGroup(ctx context.Context, dockerCli command.Cli, ng *store.N
return dis, nil
}
func configFromContext(endpointName string, s ctxstore.Reader) (clientcmd.ClientConfig, error) {
if strings.HasPrefix(endpointName, "kubernetes://") {
u, _ := url.Parse(endpointName)
if kubeconfig := u.Query().Get("kubeconfig"); kubeconfig != "" {
_ = os.Setenv(clientcmd.RecommendedConfigPathEnvVar, kubeconfig)
}
rules := clientcmd.NewDefaultClientConfigLoadingRules()
apiConfig, err := rules.Load()
if err != nil {
return nil, err
}
return clientcmd.NewDefaultClientConfig(*apiConfig, &clientcmd.ConfigOverrides{}), nil
}
return ctxkube.ConfigFromContext(endpointName, s)
}
// clientForEndpoint returns a docker client for an endpoint
func clientForEndpoint(dockerCli command.Cli, name string) (dockerclient.APIClient, error) {
list, err := dockerCli.ContextStore().List()
@@ -234,31 +195,80 @@ func clientForEndpoint(dockerCli command.Cli, name string) (dockerclient.APIClie
return dockerclient.NewClientWithOpts(clientOpts...)
}
// getDefaultDrivers returns drivers based on current cli config
func getDefaultDrivers(ctx context.Context, dockerCli command.Cli) ([]build.DriverInfo, error) {
txn, release, err := getStore(dockerCli)
func getInstanceOrDefault(ctx context.Context, dockerCli command.Cli, instance, contextPathHash string) ([]build.DriverInfo, error) {
var defaultOnly bool
if instance == "default" && instance != dockerCli.CurrentContext() {
return nil, errors.Errorf("use `docker --context=default buildx` to switch to default context")
}
if instance == "default" || instance == dockerCli.CurrentContext() {
instance = ""
defaultOnly = true
}
list, err := dockerCli.ContextStore().List()
if err != nil {
return nil, err
}
for _, l := range list {
if l.Name == instance {
return nil, errors.Errorf("use `docker --context=%s buildx` to switch to context %s", instance, instance)
}
}
if instance != "" {
return getInstanceByName(ctx, dockerCli, instance, contextPathHash)
}
return getDefaultDrivers(ctx, dockerCli, defaultOnly, contextPathHash)
}
func getInstanceByName(ctx context.Context, dockerCli command.Cli, instance, contextPathHash string) ([]build.DriverInfo, error) {
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return nil, err
}
defer release()
ng, err := getCurrentInstance(txn, dockerCli)
ng, err := txn.NodeGroupByName(instance)
if err != nil {
return nil, err
}
return driversForNodeGroup(ctx, dockerCli, ng, contextPathHash)
}
// getDefaultDrivers returns drivers based on current cli config
func getDefaultDrivers(ctx context.Context, dockerCli command.Cli, defaultOnly bool, contextPathHash string) ([]build.DriverInfo, error) {
txn, release, err := storeutil.GetStore(dockerCli)
if err != nil {
return nil, err
}
defer release()
if !defaultOnly {
ng, err := storeutil.GetCurrentInstance(txn, dockerCli)
if err != nil {
return nil, err
}
if ng != nil {
return driversForNodeGroup(ctx, dockerCli, ng, contextPathHash)
}
}
imageopt, err := storeutil.GetImageConfig(dockerCli, nil)
if err != nil {
return nil, err
}
if ng != nil {
return driversForNodeGroup(ctx, dockerCli, ng)
}
d, err := driver.GetDriver(ctx, "buildx_buildkit_default", nil, dockerCli.Client(), nil, "", nil)
d, err := driver.GetDriver(ctx, "buildx_buildkit_default", nil, dockerCli.Client(), imageopt.Auth, nil, nil, nil, nil, nil, contextPathHash)
if err != nil {
return nil, err
}
return []build.DriverInfo{
{
Name: "default",
Driver: d,
Name: "default",
Driver: d,
ImageOpt: imageopt,
ProxyConfig: storeutil.GetProxyConfig(dockerCli),
},
}, nil
}
@@ -282,9 +292,7 @@ func loadInfoData(ctx context.Context, d *dinfo) error {
return errors.Wrap(err, "listing workers")
}
for _, w := range workers {
for _, p := range w.Platforms {
d.platforms = append(d.platforms, p)
}
d.platforms = append(d.platforms, w.Platforms...)
}
d.platforms = platformutil.Dedupe(d.platforms)
}
@@ -294,7 +302,7 @@ func loadInfoData(ctx context.Context, d *dinfo) error {
func loadNodeGroupData(ctx context.Context, dockerCli command.Cli, ngi *nginfo) error {
eg, _ := errgroup.WithContext(ctx)
dis, err := driversForNodeGroup(ctx, dockerCli, ngi.ng)
dis, err := driversForNodeGroup(ctx, dockerCli, ngi.ng, "")
if err != nil {
return err
}
@@ -312,7 +320,47 @@ func loadNodeGroupData(ctx context.Context, dockerCli command.Cli, ngi *nginfo)
}(&ngi.drivers[i])
}
return eg.Wait()
if eg.Wait(); err != nil {
return err
}
kubernetesDriverCount := 0
for _, di := range ngi.drivers {
if di.info != nil && len(di.info.DynamicNodes) > 0 {
kubernetesDriverCount++
}
}
isAllKubernetesDrivers := len(ngi.drivers) == kubernetesDriverCount
if isAllKubernetesDrivers {
var drivers []dinfo
var dynamicNodes []store.Node
for _, di := range ngi.drivers {
// dynamic nodes are used in Kubernetes driver.
// Kubernetes pods are dynamically mapped to BuildKit Nodes.
if di.info != nil && len(di.info.DynamicNodes) > 0 {
for i := 0; i < len(di.info.DynamicNodes); i++ {
// all []dinfo share *build.DriverInfo and *driver.Info
diClone := di
if pl := di.info.DynamicNodes[i].Platforms; len(pl) > 0 {
diClone.platforms = pl
}
drivers = append(drivers, di)
}
dynamicNodes = append(dynamicNodes, di.info.DynamicNodes...)
}
}
// not append (remove the static nodes in the store)
ngi.ng.Nodes = dynamicNodes
ngi.drivers = drivers
ngi.ng.Dynamic = true
}
return nil
}
func dockerAPI(dockerCli command.Cli) *api {
@@ -329,3 +377,67 @@ func (a *api) DockerAPI(name string) (dockerclient.APIClient, error) {
}
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 (
"fmt"
"github.com/docker/buildx/util/cobrautil"
"github.com/docker/buildx/version"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
@@ -17,11 +18,15 @@ func runVersion(dockerCli command.Cli) error {
func versionCmd(dockerCli command.Cli) *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Show buildx version information ",
Short: "Show buildx version information",
Args: cli.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
return runVersion(dockerCli)
},
}
// hide builder persistent flag for this command
cobrautil.HideInheritedFlags(cmd, "builder")
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"]
}

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)

43
docs/reference/buildx.md Normal file
View File

@@ -0,0 +1,43 @@
# buildx
```
docker buildx [OPTIONS] COMMAND
```
<!---MARKER_GEN_START-->
Extended build capabilities with BuildKit
### Subcommands
| Name | Description |
| --- | --- |
| [`bake`](buildx_bake.md) | Build from a file |
| [`build`](buildx_build.md) | Start a build |
| [`create`](buildx_create.md) | Create a new builder instance |
| [`du`](buildx_du.md) | Disk usage |
| [`imagetools`](buildx_imagetools.md) | Commands to work on images in registry |
| [`inspect`](buildx_inspect.md) | Inspect current builder instance |
| [`install`](buildx_install.md) | Install buildx as a 'docker builder' alias |
| [`ls`](buildx_ls.md) | List builder instances |
| [`prune`](buildx_prune.md) | Remove build cache |
| [`rm`](buildx_rm.md) | Remove a builder instance |
| [`stop`](buildx_stop.md) | Stop builder instance |
| [`uninstall`](buildx_uninstall.md) | Uninstall the 'docker builder' alias |
| [`use`](buildx_use.md) | Set the current builder instance |
| [`version`](buildx_version.md) | Show buildx version information |
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
<!---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

@@ -0,0 +1,986 @@
# buildx bake
```
docker buildx bake [OPTIONS] [TARGET...]
```
<!---MARKER_GEN_START-->
Build from a file
### Aliases
`bake`, `f`
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Build definition file |
| `--load` | | | Shorthand for `--set=*.output=type=docker` |
| `--metadata-file` | `string` | | Write build result metadata to the file |
| [`--no-cache`](#no-cache) | | | Do not use cache when building the image |
| [`--print`](#print) | | | Print the options without building |
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
| [`--pull`](#pull) | | | Always attempt to pull all referenced images |
| `--push` | | | Shorthand for `--set=*.output=type=registry` |
| [`--set`](#set) | `stringArray` | | Override target value (e.g., `targetpattern.key=value`) |
<!---MARKER_GEN_END-->
## Description
Bake is a high-level build command. Each specified target will run in parallel
as part of the build.
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.
## 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)
By default, `buildx bake` looks for build definition files in the current
directory, the following are parsed:
- `docker-compose.yml`
- `docker-compose.yaml`
- `docker-bake.json`
- `docker-bake.override.json`
- `docker-bake.hcl`
- `docker-bake.override.hcl`
Use the `-f` / `--file` option to specify the build definition file to use. The
file can be a Docker Compose, JSON or HCL file. If multiple files are specified
they are all read and configurations are combined.
The following example uses a Docker Compose file named `docker-compose.dev.yaml`
as build definition file, and builds all targets in the file:
```console
$ docker buildx bake -f docker-compose.dev.yaml
[+] Building 66.3s (30/30) FINISHED
=> [frontend internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 36B 0.0s
=> [backend internal] load build definition from Dockerfile 0.2s
=> => transferring dockerfile: 3.73kB 0.0s
=> [database internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 5.77kB 0.0s
...
```
Pass the names of the targets to build, to build only specific target(s). The
following example builds the `backend` and `database` targets that are defined
in the `docker-compose.dev.yaml` file, skipping the build for the `frontend`
target:
```console
$ docker buildx bake -f docker-compose.dev.yaml backend database
[+] Building 2.4s (13/13) FINISHED
=> [backend internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 81B 0.0s
=> [database internal] load build definition from Dockerfile 0.2s
=> => transferring dockerfile: 36B 0.0s
=> [backend internal] load .dockerignore 0.3s
...
```
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)
Same as `build --no-cache`. Do not use cache when building the image.
### <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, without starting a build.
```console
$ docker buildx bake -f docker-bake.hcl --print db
{
"group": {
"default": {
"targets": [
"db"
]
}
},
"target": {
"db": {
"context": "./",
"dockerfile": "Dockerfile",
"tags": [
"docker.io/tiborvass/db"
]
}
}
}
```
### <a name="progress"></a> Set type of progress output (--progress)
Same as [`build --progress`](buildx_build.md#progress). 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 bake --progress=plain
#2 [backend internal] load build definition from Dockerfile.test
#2 sha256:de70cb0bb6ed8044f7b9b1b53b67f624e2ccfb93d96bb48b70c1fba562489618
#2 ...
#1 [database internal] load build definition from Dockerfile.test
#1 sha256:453cb50abd941762900a1212657a35fc4aad107f5d180b0ee9d93d6b74481bce
#1 transferring dockerfile: 36B done
#1 DONE 0.1s
...
```
### <a name="pull"></a> Always attempt to pull a newer version of the image (--pull)
Same as `build --pull`.
### <a name="set"></a> Override target configurations from command line (--set)
```
--set targetpattern.key[.subkey]=value
```
Override target configurations from command line. The pattern matching syntax
is defined in https://golang.org/pkg/path/#Match.
```console
$ docker buildx bake --set target.args.mybuildarg=value
$ docker buildx bake --set target.platform=linux/arm64
$ docker buildx bake --set foo*.args.mybuildarg=value # overrides build arg for all targets starting with 'foo'
$ docker buildx bake --set *.platform=linux/arm64 # overrides platform for all targets
$ docker buildx bake --set foo*.no-cache # bypass caching only for targets starting with 'foo'
```
Complete list of overridable fields:
`args`, `cache-from`, `cache-to`, `context`, `dockerfile`, `labels`, `no-cache`,
`output`, `platform`, `pull`, `secrets`, `ssh`, `tags`, `target`
### File definition
In addition to compose files, bake supports a JSON and an equivalent HCL file
format for defining build groups and targets.
A target reflects a single docker build invocation with the same options that
you would specify for `docker build`. A group is a grouping of targets.
Multiple files can include the same target and final build options will be
determined by merging them together.
In the case of compose files, each service corresponds to a target.
A group can specify its list of targets with the `targets` option. A target can
inherit build options by setting the `inherits` option to the list of targets or
groups to inherit from.
Note: Design of bake command is work in progress, the user experience may change
based on feedback.
HCL definition example:
```hcl
group "default" {
targets = ["db", "webapp-dev"]
}
target "webapp-dev" {
dockerfile = "Dockerfile.webapp"
tags = ["docker.io/username/webapp"]
}
target "webapp-release" {
inherits = ["webapp-dev"]
platforms = ["linux/amd64", "linux/arm64"]
}
target "db" {
dockerfile = "Dockerfile.db"
tags = ["docker.io/username/db"]
}
```
Complete list of valid target fields:
`args`, `cache-from`, `cache-to`, `context`, `contexts`, `dockerfile`, `inherits`, `labels`,
`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
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
to define variables with values provided by the current environment, or a
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.
#### Using interpolation to tag an image with the git sha
Bake supports variable blocks which are assigned to matching environment
variables or default values.
```hcl
# docker-bake.hcl
variable "TAG" {
default = "latest"
}
group "default" {
targets = ["webapp"]
}
target "webapp" {
tags = ["docker.io/username/webapp:${TAG}"]
}
```
alternatively, in json format:
```json
{
"variable": {
"TAG": {
"default": "latest"
}
}
"group": {
"default": {
"targets": ["webapp"]
}
},
"target": {
"webapp": {
"tags": ["docker.io/username/webapp:${TAG}"]
}
}
}
```
```console
$ docker buildx bake --print webapp
{
"group": {
"default": {
"targets": [
"webapp"
]
}
},
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"tags": [
"docker.io/username/webapp:latest"
]
}
}
}
```
```console
$ 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" {
default = "latest"
}
group "default" {
targets = ["webapp"]
}
target "webapp" {
args = {
buildno = "${add(123, 1)}"
}
}
```
```console
$ docker buildx bake --print webapp
{
"group": {
"default": {
"targets": [
"webapp"
]
}
},
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"buildno": "124"
}
}
}
}
```
#### Defining an `increment` function
It also supports [user defined functions](https://github.com/hashicorp/hcl/tree/main/ext/userfunc).
The following example defines a simple an `increment` function.
```hcl
# docker-bake.hcl
function "increment" {
params = [number]
result = number + 1
}
group "default" {
targets = ["webapp"]
}
target "webapp" {
args = {
buildno = "${increment(123)}"
}
}
```
```console
$ docker buildx bake --print webapp
{
"group": {
"default": {
"targets": [
"webapp"
]
}
},
"target": {
"webapp": {
"context": ".",
"dockerfile": "Dockerfile",
"args": {
"buildno": "124"
}
}
}
}
```
#### Only adding tags if a variable is not empty using an `notequal`
Here we are using the conditional `notequal` function which is just for
symmetry with the `equal` one.
```hcl
# docker-bake.hcl
variable "TAG" {default="" }
group "default" {
targets = [
"webapp",
]
}
target "webapp" {
context="."
dockerfile="Dockerfile"
tags = [
"my-image:latest",
notequal("",TAG) ? "my-image:${TAG}": "",
]
}
```
```console
$ docker buildx bake --print webapp
{
"group": {
"default": {
"targets": [
"webapp"
]
}
},
"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

@@ -0,0 +1,512 @@
# buildx build
```
docker buildx build [OPTIONS] PATH | URL | -
```
<!---MARKER_GEN_START-->
Start a build
### Aliases
`build`, `b`
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--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`](#allow) | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
| [`--build-arg`](#build-arg) | `stringArray` | | Set build-time variables |
| [`--build-context`](#build-context) | `stringArray` | | Additional build contexts (e.g., name=path) |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--cache-from`](#cache-from) | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
| [`--cache-to`](#cache-to) | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
| [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent) | `string` | | Optional parent cgroup for the container |
| [`-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`) |
| `--iidfile` | `string` | | Write the image ID to the file |
| `--label` | `stringArray` | | Set metadata for an image |
| [`--load`](#load) | | | Shorthand for `--output=type=docker` |
| [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to the file |
| `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
| `--no-cache` | | | Do not use cache when building the image |
| `--no-cache-filter` | `stringArray` | | Do not cache specified stages |
| [`-o`](#output), [`--output`](#output) | `stringArray` | | Output destination (format: `type=local,dest=path`) |
| [`--platform`](#platform) | `stringArray` | | Set target platform for build |
| [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `plain`, `tty`). Use plain to show container output |
| `--pull` | | | Always attempt to pull all referenced images |
| [`--push`](#push) | | | Shorthand for `--output=type=registry` |
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
| [`--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-->
## Description
The `buildx build` command starts a build using BuildKit. This command is similar
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`
documentation](https://docs.docker.com/engine/reference/commandline/build/). In
here we'll document a subset of the new flags.
## Examples
### <a name="allow"></a> Allow extra privileged entitlement (--allow)
```
--allow=ENTITLEMENT
```
Allow extra privileged entitlement. List of entitlements:
- `network.host` - Allows executions with host networking.
- `security.insecure` - Allows executions without sandbox. See
[related Dockerfile extensions](https://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
with `--allow-insecure-entitlement` (see [`create --buildkitd-flags`](buildx_create.md#buildkitd-flags))
**Examples**
```console
$ docker buildx create --use --name insecure-builder --buildkitd-flags '--allow-insecure-entitlement security.insecure'
$ docker buildx build --allow security.insecure .
```
### <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).
There are also useful built-in build args like:
* `BUILDKIT_CONTEXT_KEEP_GIT_DIR=<bool>` trigger git context to keep the `.git` directory
* `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
* `BUILDKIT_MULTI_PLATFORM=<bool>` opt into determnistic output regardless of multi-platform output or not
```console
$ docker buildx build --build-arg BUILDKIT_MULTI_PLATFORM=1 .
```
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).
### <a name="build-context"></a> Additional build contexts (--build-context)
```
--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)
```
-o, --output=[PATH,-,type=TYPE[,KEY=VALUE]
```
Sets the export action for the build result. In `docker build` all builds finish
by creating a container image and exporting it to `docker images`. `buildx` makes
this step configurable allowing results to be exported directly to the client,
oci image tarballs, registry etc.
Buildx with `docker` driver currently only supports local, tarball exporter and
image exporter. `docker-container` driver supports all the exporters.
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`
exporter and write to `stdout`.
```console
$ docker buildx build -o . .
$ docker buildx build -o outdir .
$ docker buildx build -o - - > out.tar
$ docker buildx build -o type=docker .
$ docker buildx build -o type=docker,dest=- . > myimage.tar
$ docker buildx build -t tonistiigi/foo -o type=registry
```
Supported exported types are:
#### `local`
The `local` export type writes all result files to a directory on the client. The
new files will be owned by the current user. On multi-platform builds, all results
will be put in subdirectories by their platform.
Attribute key:
- `dest` - destination directory where files will be written
#### `tar`
The `tar` export type writes all result files as a single tarball on the client.
On multi-platform builds all results will be put in subdirectories by their platform.
Attribute key:
- `dest` - destination path where tarball will be written. “-” writes to stdout.
#### `oci`
The `oci` export type writes the result image or manifest list as an [OCI image
layout](https://github.com/opencontainers/image-spec/blob/v1.0.1/image-layout.md)
tarball on the client.
Attribute key:
- `dest` - destination path where tarball will be written. “-” writes to stdout.
#### `docker`
The `docker` export type writes the single-platform result image as a [Docker image
specification](https://github.com/docker/docker/blob/v20.10.2/image/spec/v1.2.md)
tarball on the client. Tarballs created by this exporter are also OCI compatible.
Currently, multi-platform images cannot be exported with the `docker` export type.
The most common usecase for multi-platform images is to directly push to a registry
(see [`registry`](#registry)).
Attribute keys:
- `dest` - destination path where tarball will be written. If not specified the
tar will be loaded automatically to the current docker instance.
- `context` - name for the docker context where to import the result
#### `image`
The `image` exporter writes the build result as an image or a manifest list. When
using `docker` driver the image will appear in `docker images`. Optionally, image
can be automatically pushed to a registry by specifying attributes.
Attribute keys:
- `name` - name (references) for the new image.
- `push` - boolean to automatically push the image.
#### `registry`
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)
Shorthand for [`--output=type=registry`](#registry). Will automatically push the
build result to registry.
### <a name="secret"></a> Secret to expose to the build (--secret)
```
--secret=[type=TYPE[,KEY=VALUE]
```
Exposes secret to the build. The secret can be used by the build using
[`RUN --mount=type=secret` mount](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#run---mounttypesecret).
If `type` is unset it will be detected. Supported types are:
#### `file`
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
$ docker buildx build --secret id=aws,src=$HOME/.aws/credentials .
```
#### `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
```
```console
$ SECRET_TOKEN=token docker buildx build --secret id=SECRET_TOKEN .
```
### <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)
```
--ssh=default|<id>[=<socket>|<key>[,<key>]]
```
This can be useful when some commands in your Dockerfile need specific SSH
authentication (e.g., cloning a private repository).
`--ssh` exposes SSH agent socket or keys to the build and can be used with the
[`RUN --mount=type=ssh` mount](https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/syntax.md#run---mounttypessh).
Example to access Gitlab using an SSH agent socket:
```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
$ eval $(ssh-agent)
$ 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

@@ -0,0 +1,197 @@
# buildx create
```
docker buildx create [OPTIONS] [CONTEXT|ENDPOINT]
```
<!---MARKER_GEN_START-->
Create a new builder instance
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--append`](#append) | | | Append a node to builder instead of changing it |
| `--bootstrap` | | | Boot builder after creation |
| [`--buildkitd-flags`](#buildkitd-flags) | `string` | | Flags for buildkitd daemon |
| [`--config`](#config) | `string` | | BuildKit config file |
| [`--driver`](#driver) | `string` | | Driver to use (available: `docker`, `docker-container`, `kubernetes`) |
| [`--driver-opt`](#driver-opt) | `stringArray` | | Options for the driver |
| [`--leave`](#leave) | | | Remove a node from builder instead of changing it |
| [`--name`](#name) | `string` | | Builder instance name |
| [`--node`](#node) | `string` | | Create/modify node with given name |
| [`--platform`](#platform) | `stringArray` | | Fixed platforms for current node |
| [`--use`](#use) | | | Set the current builder instance |
<!---MARKER_GEN_END-->
## Description
Create makes a new builder instance pointing to a docker context or endpoint,
where context is the name of a context from `docker context ls` and endpoint is
the address for docker socket (eg. `DOCKER_HOST` value).
By default, the current Docker configuration is used for determining the
context/endpoint value.
Builder instances are isolated environments where builds can be invoked. All
Docker contexts also get the default builder instance.
## Examples
### <a name="append"></a> Append a new node to an existing builder (--append)
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
for a build based on the platforms it supports.
```console
$ docker buildx create mycontext1
eager_beaver
$ docker buildx create --name eager_beaver --append mycontext2
eager_beaver
```
### <a name="buildkitd-flags"></a> Specify options for the buildkitd daemon (--buildkitd-flags)
```
--buildkitd-flags FLAGS
```
Adds flags when starting the buildkitd daemon. They take precedence over the
configuration file specified by [`--config`](#config). See `buildkitd --help`
for the available flags.
```
--buildkitd-flags '--debug --debugaddr 0.0.0.0:6666'
```
### <a name="config"></a> Specify a configuration file for the buildkitd daemon (--config)
```
--config FILE
```
Specifies the configuration file for the buildkitd daemon to use. The configuration
can be overridden by [`--buildkitd-flags`](#buildkitd-flags).
See an [example buildkitd configuration file](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md).
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)
```
--driver DRIVER
```
Sets the builder driver to be used. There are two available drivers, each have
their own specificities.
#### `docker` driver
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)
```
--driver-opt OPTIONS
```
Passes additional driver-specific options. Details for each driver:
- `docker` - No driver options
- `docker-container`
- `image=IMAGE` - Sets the container image to be used for running buildkit.
- `network=NETMODE` - Sets the network mode for running the buildkit container.
- `cgroup-parent=CGROUP` - Sets the cgroup parent of the buildkit container if docker is using the "cgroupfs" driver. Defaults to `/docker/buildx`.
- `kubernetes`
- `image=IMAGE` - Sets the container image to be used for running buildkit.
- `namespace=NS` - Sets the Kubernetes namespace. Defaults to the current namespace.
- `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.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.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`
- `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"
- `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)
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
is set with `--node`.
```console
$ docker buildx create --name mybuilder --node mybuilder0 --leave
```
### <a name="name"></a> Specify the name of the builder (--name)
```
--name NAME
```
The `--name` flag specifies the name of the builder to be created or modified.
If none is specified, one will be automatically generated.
### <a name="node"></a> Specify the name of the node (--node)
```
--node NODE
```
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
number suffix.
### <a name="platform"></a> Set the platforms supported by the node (--platform)
```
--platform PLATFORMS
```
The `--platform` flag sets the platforms supported by the node. It expects a
comma-separated list of platforms of the form OS/architecture/variant. The node
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
building for the same platform.
```console
$ docker buildx create --platform linux/amd64
$ docker buildx create --platform linux/arm64,linux/arm/v8
```
### <a name="use"></a> Automatically switch to the newly created builder (--use)
The `--use` flag automatically switches the current builder to the newly created
one. Equivalent to running `docker buildx use $(docker buildx create ...)`.

View File

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

View File

@@ -0,0 +1,36 @@
# buildx imagetools
```
docker buildx imagetools [OPTIONS] COMMAND
```
<!---MARKER_GEN_START-->
Commands to work on images in registry
### Subcommands
| Name | Description |
| --- | --- |
| [`create`](buildx_imagetools_create.md) | Create a new image based on source images |
| [`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-->
## Description
Imagetools contains commands for working with manifest lists in the registry.
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

@@ -0,0 +1,77 @@
# buildx imagetools create
```
docker buildx imagetools create [OPTIONS] [SOURCE] [SOURCE...]
```
<!---MARKER_GEN_START-->
Create a new image based on source images
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--append`](#append) | | | Append to existing manifest |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--dry-run`](#dry-run) | | | Show final image instead of pushing |
| [`-f`](#file), [`--file`](#file) | `stringArray` | | Read source descriptor from file |
| [`-t`](#tag), [`--tag`](#tag) | `stringArray` | | Set reference for new image |
<!---MARKER_GEN_END-->
## Description
Create a new manifest list based on source manifests. The source manifests can
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
specified, create performs a carbon copy.
## Examples
### <a name="append"></a> Append new sources to an existing manifest list (--append)
Use the `--append` flag to append the new sources to an existing manifest list
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)
Use the `--dry-run` flag to not push the image, just show it.
### <a name="file"></a> Read source descriptor from a file (-f, --file)
```
-f FILE or --file FILE
```
Reads source from files. A source can be a manifest digest, manifest reference,
or a JSON of OCI descriptor object.
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.
```console
$ 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
```
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) .
### <a name="tag"></a> Set reference for new image (-t, --tag)
```
-t IMAGE or --tag IMAGE
```
Use the `-t` or `--tag` flag to set the name of the image to be created.
```console
$ docker buildx imagetools create --dry-run alpine@sha256:5c40b3c27b9f13c873fefb2139765c56ce97fd50230f1f2d5c91e55dec171907 sha256:c4ba6347b0e4258ce6a6de2401619316f982b7bcc529f73d2a410d0097730204
$ docker buildx imagetools create -t tonistiigi/myapp -f image1 -f image2
```

View File

@@ -0,0 +1,632 @@
# buildx imagetools inspect
```
docker buildx imagetools inspect [OPTIONS] NAME
```
<!---MARKER_GEN_START-->
Show details of an image in the registry
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`--format`](#format) | `string` | `{{.Manifest}}` | Format the output using the given Go template |
| [`--raw`](#raw) | | | Show original, unformatted JSON manifest |
<!---MARKER_GEN_END-->
## Description
Show details of an image in the registry.
```console
$ docker buildx imagetools inspect alpine
Name: docker.io/library/alpine:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300
Manifests:
Name: docker.io/library/alpine:latest@sha256:e7d88de73db3d3fd9b2d63aa7f447a10fd0220b7cbf39803c803f2af9ba256b3
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64
Name: docker.io/library/alpine:latest@sha256:e047bc2af17934d38c5a7fa9f46d443f1de3a7675546402592ef805cfa929f9d
MediaType: application/vnd.docker.distribution.manifest.v2+json
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)
Use the `--raw` option to print the unformatted JSON manifest bytes.
> `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

@@ -0,0 +1,62 @@
# buildx inspect
```
docker buildx inspect [NAME]
```
<!---MARKER_GEN_START-->
Inspect current builder instance
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--bootstrap`](#bootstrap) | | | Ensure builder has booted before inspecting |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
<!---MARKER_GEN_END-->
## Description
Shows information about the current or specified builder.
## 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
By default, `inspect` shows information about the current builder. Specify the
name of the builder to inspect to get information about that builder.
The following example shows information about a builder instance named
`elated_tesla`:
```console
$ docker buildx inspect elated_tesla
Name: elated_tesla
Driver: docker-container
Nodes:
Name: elated_tesla0
Endpoint: unix:///var/run/docker.sock
Status: running
Platforms: linux/amd64
Name: elated_tesla1
Endpoint: ssh://ubuntu@1.2.3.4
Status: running
Platforms: linux/arm64, linux/arm/v7, linux/arm/v6
```

View File

@@ -0,0 +1,11 @@
# buildx install
```
docker buildx install
```
<!---MARKER_GEN_START-->
Install buildx as a 'docker builder' alias
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,30 @@
# buildx ls
```
docker buildx ls
```
<!---MARKER_GEN_START-->
List builder instances
<!---MARKER_GEN_END-->
## Description
Lists all builder instances and the nodes for each instance
```console
$ docker buildx ls
NAME/NODE DRIVER/ENDPOINT STATUS PLATFORMS
elated_tesla * docker-container
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
default docker
default default running linux/amd64
```
Each builder has one or more nodes associated with it. The current builder's
name is marked with a `*` in `NAME/NODE` and explicit node to build against for
the target platform marked with a `*` in the `PLATFORMS` column.

View File

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

View File

@@ -0,0 +1,59 @@
# buildx rm
```
docker buildx rm [NAME]
```
<!---MARKER_GEN_START-->
Remove a builder instance
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--all-inactive`](#all-inactive) | | | Remove all inactive builders |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| [`-f`](#force), [`--force`](#force) | | | Do not prompt for confirmation |
| [`--keep-daemon`](#keep-daemon) | | | Keep the buildkitd daemon running |
| [`--keep-state`](#keep-state) | | | Keep BuildKit state |
<!---MARKER_GEN_END-->
## Description
Removes the specified or current builder. It is a no-op attempting to remove the
default builder.
## 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)
Keep BuildKit state, so it can be reused by a new builder with the same name.
Currently, only supported by the [`docker-container` driver](buildx_create.md#driver).

View File

@@ -0,0 +1,28 @@
# buildx stop
```
docker buildx stop [NAME]
```
<!---MARKER_GEN_START-->
Stop builder instance
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
<!---MARKER_GEN_END-->
## Description
Stops the specified or current builder. This will not prevent buildx build to
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

@@ -0,0 +1,11 @@
# buildx uninstall
```
docker buildx uninstall
```
<!---MARKER_GEN_START-->
Uninstall the 'docker builder' alias
<!---MARKER_GEN_END-->

View File

@@ -0,0 +1,31 @@
# buildx use
```
docker buildx use [OPTIONS] NAME
```
<!---MARKER_GEN_START-->
Set the current builder instance
### Options
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| [`--builder`](#builder) | `string` | | Override the configured builder instance |
| `--default` | | | Set builder as default for current context |
| `--global` | | | Builder persists context changes |
<!---MARKER_GEN_END-->
## Description
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
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

@@ -0,0 +1,20 @@
# buildx version
```
docker buildx version
```
<!---MARKER_GEN_START-->
Show buildx version information
<!---MARKER_GEN_END-->
## Description
View version information
```console
$ docker buildx version
github.com/docker/buildx v0.5.1-docker 11057da37336192bfc57d81e02359ba7ba848e4a
```

View File

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

View File

@@ -1,34 +1,55 @@
package docker
import (
"archive/tar"
"bytes"
"context"
"io"
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"time"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/driver/bkimage"
"github.com/docker/buildx/util/confutil"
"github.com/docker/buildx/util/imagetools"
"github.com/docker/buildx/util/progress"
"github.com/docker/docker/api/types"
dockertypes "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
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/moby/buildkit/client"
"github.com/moby/buildkit/util/tracing/detect"
"github.com/pkg/errors"
)
var defaultBuildkitImage = "moby/buildkit:buildx-stable-1" // TODO: make this verified
const (
volumeStateSuffix = "_state"
)
type Driver struct {
driver.InitConfig
factory driver.Factory
netMode string
image string
factory driver.Factory
userNSRemap bool // true if dockerd is running with userns-remap mode
netMode string
image string
cgroupParent string
env []string
}
func (d *Driver) IsMobyDriver() bool {
return false
}
func (d *Driver) Config() driver.InitConfig {
return d.InitConfig
}
func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
@@ -44,7 +65,7 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
if err := d.start(ctx, sub); err != nil {
return err
}
if err := d.wait(ctx); err != nil {
if err := d.wait(ctx, sub); err != nil {
return err
}
return nil
@@ -53,23 +74,37 @@ func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
}
func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
imageName := defaultBuildkitImage
imageName := bkimage.DefaultImage
if d.image != "" {
imageName = d.image
}
if err := l.Wrap("pulling image "+imageName, func() error {
rc, err := d.DockerAPI.ImageCreate(ctx, imageName, types.ImageCreateOptions{})
ra, err := imagetools.RegistryAuthForRef(imageName, d.Auth)
if err != nil {
return err
}
rc, err := d.DockerAPI.ImageCreate(ctx, imageName, types.ImageCreateOptions{
RegistryAuth: ra,
})
if err != nil {
return err
}
_, err = io.Copy(ioutil.Discard, rc)
return err
}); err != nil {
return err
// image pulling failed, check if it exists in local image store.
// if not, return pulling error. otherwise log it.
_, _, errInspect := d.DockerAPI.ImageInspectWithRaw(ctx, imageName)
if errInspect != nil {
return err
}
l.Wrap("pulling failed, using local image "+imageName, func() error { return nil })
}
cfg := &container.Config{
Image: imageName,
Env: d.env,
}
if d.InitConfig.BuildkitFlags != nil {
cfg.Cmd = d.InitConfig.BuildkitFlags
@@ -78,27 +113,39 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
if err := l.Wrap("creating container "+d.Name, func() error {
hc := &container.HostConfig{
Privileged: true,
Mounts: []mount.Mount{
{
Type: mount.TypeVolume,
Source: d.Name + volumeStateSuffix,
Target: confutil.DefaultBuildKitStateDir,
},
},
}
if d.userNSRemap {
hc.UsernsMode = "host"
}
if d.netMode != "" {
hc.NetworkMode = container.NetworkMode(d.netMode)
}
_, err := d.DockerAPI.ContainerCreate(ctx, cfg, hc, &network.NetworkingConfig{}, d.Name)
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)
if err != nil {
return err
}
if f := d.InitConfig.ConfigFile; f != "" {
buf, err := readFileToTar(f)
if err != nil {
return err
}
if err := d.DockerAPI.CopyToContainer(ctx, d.Name, "/", buf, dockertypes.CopyToContainerOptions{}); err != nil {
return err
}
if err := d.copyToContainer(ctx, d.InitConfig.Files); err != nil {
return err
}
if err := d.start(ctx, l); err != nil {
return err
}
if err := d.wait(ctx); err != nil {
if err := d.wait(ctx, l); err != nil {
return err
}
return nil
@@ -108,17 +155,28 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
return nil
}
func (d *Driver) wait(ctx context.Context) error {
try := 0
func (d *Driver) wait(ctx context.Context, l progress.SubLogger) error {
try := 1
for {
if err := d.run(ctx, []string{"buildctl", "debug", "workers"}); err != nil {
if try > 10 {
bufStdout := &bytes.Buffer{}
bufStderr := &bytes.Buffer{}
if err := d.run(ctx, []string{"buildctl", "debug", "workers"}, bufStdout, bufStderr); err != nil {
if try > 15 {
if err != nil {
d.copyLogs(context.TODO(), l)
if bufStdout.Len() != 0 {
l.Log(1, bufStdout.Bytes())
}
if bufStderr.Len() != 0 {
l.Log(2, bufStderr.Bytes())
}
}
return err
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(time.Duration(100+try*20) * time.Millisecond):
case <-time.After(time.Duration(try*120) * time.Millisecond):
try++
continue
}
@@ -127,6 +185,39 @@ func (d *Driver) wait(ctx context.Context) error {
}
}
func (d *Driver) copyLogs(ctx context.Context, l progress.SubLogger) error {
rc, err := d.DockerAPI.ContainerLogs(ctx, d.Name, types.ContainerLogsOptions{
ShowStdout: true, ShowStderr: true,
})
if err != nil {
return err
}
stdout := &logWriter{logger: l, stream: 1}
stderr := &logWriter{logger: l, stream: 2}
if _, err := stdcopy.StdCopy(stdout, stderr, rc); err != nil {
return err
}
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) {
execConfig := types.ExecConfig{
Cmd: cmd,
@@ -151,12 +242,12 @@ func (d *Driver) exec(ctx context.Context, cmd []string) (string, net.Conn, erro
return execID, resp.Conn, nil
}
func (d *Driver) run(ctx context.Context, cmd []string) error {
func (d *Driver) run(ctx context.Context, cmd []string, stdout, stderr io.Writer) (err error) {
id, conn, err := d.exec(ctx, cmd)
if err != nil {
return err
}
if _, err := io.Copy(ioutil.Discard, conn); err != nil {
if _, err := stdcopy.StdCopy(stdout, stderr, conn); err != nil {
return err
}
conn.Close()
@@ -175,7 +266,7 @@ func (d *Driver) start(ctx context.Context, l progress.SubLogger) error {
}
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
container, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
ctn, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
if err != nil {
if dockerclient.IsErrNotFound(err) {
return &driver.Info{
@@ -185,7 +276,7 @@ func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
return nil, err
}
if container.State.Running {
if ctn.State.Running {
return &driver.Info{
Status: driver.Running,
}, nil
@@ -207,16 +298,32 @@ func (d *Driver) Stop(ctx context.Context, force bool) error {
return nil
}
func (d *Driver) Rm(ctx context.Context, force bool) error {
func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
info, err := d.Info(ctx)
if err != nil {
return err
}
if info.Status != driver.Inactive {
return d.DockerAPI.ContainerRemove(ctx, d.Name, dockertypes.ContainerRemoveOptions{
RemoveVolumes: true,
Force: true,
})
container, err := d.DockerAPI.ContainerInspect(ctx, d.Name)
if err != nil {
return err
}
if rmDaemon {
if err := d.DockerAPI.ContainerRemove(ctx, d.Name, dockertypes.ContainerRemoveOptions{
RemoveVolumes: true,
Force: force,
}); err != nil {
return err
}
for _, v := range container.Mounts {
if v.Name != d.Name+volumeStateSuffix {
continue
}
if rmVolume {
return d.DockerAPI.VolumeRemove(ctx, d.Name+volumeStateSuffix, false)
}
}
}
}
return nil
}
@@ -229,9 +336,16 @@ func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
conn = demuxConn(conn)
return client.New(ctx, "", client.WithDialer(func(string, time.Duration) (net.Conn, error) {
exp, err := detect.Exporter()
if err != nil {
return nil, err
}
td, _ := exp.(client.TracerDelegate)
return client.New(ctx, "", client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return conn, nil
}))
}), client.WithTracerDelegate(td))
}
func (d *Driver) Factory() driver.Factory {
@@ -251,7 +365,7 @@ func (d *Driver) Features() map[driver.Feature]bool {
func demuxConn(c net.Conn) net.Conn {
pr, pw := io.Pipe()
// TODO: rewrite parser with Reader() to avoid goroutine switch
go stdcopy.StdCopy(pw, os.Stdout, c)
go stdcopy.StdCopy(pw, os.Stderr, c)
return &demux{
Conn: c,
Reader: pr,
@@ -267,25 +381,36 @@ func (d *demux) Read(dt []byte) (int, error) {
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 {
logger progress.SubLogger
stream int
}
func (l *logWriter) Write(dt []byte) (int, error) {
l.logger.Log(l.stream, dt)
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

@@ -2,8 +2,11 @@ package docker
import (
"context"
"fmt"
"strings"
"github.com/docker/buildx/driver"
dockertypes "github.com/docker/docker/api/types"
dockerclient "github.com/docker/docker/client"
"github.com/pkg/errors"
)
@@ -38,15 +41,37 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
return nil, errors.Errorf("%s driver requires docker API access", f.Name())
}
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 {
switch k {
case "network":
switch {
case k == "network":
d.netMode = v
if v == "host" {
d.InitConfig.BuildkitFlags = append(d.InitConfig.BuildkitFlags, "--allow-insecure-entitlement=network.host")
}
case "image":
case k == "image":
d.image = v
case k == "cgroup-parent":
d.cgroupParent = v
case strings.HasPrefix(k, "env."):
envName := strings.TrimPrefix(k, "env.")
if envName == "" {
return nil, errors.Errorf("invalid env option %q, expecting env.FOO=bar", k)
}
d.env = append(d.env, fmt.Sprintf("%s=%s", envName, v))
default:
return nil, errors.Errorf("invalid driver option %s for docker-container driver", k)
}

View File

@@ -3,7 +3,6 @@ package docker
import (
"context"
"net"
"time"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/util/progress"
@@ -34,13 +33,15 @@ func (d *Driver) Stop(ctx context.Context, force bool) error {
return nil
}
func (d *Driver) Rm(ctx context.Context, force bool) error {
func (d *Driver) Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error {
return nil
}
func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
return client.New(ctx, "", client.WithDialer(func(string, time.Duration) (net.Conn, error) {
return client.New(ctx, "", client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
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)
}))
}
@@ -48,9 +49,8 @@ func (d *Driver) Features() map[driver.Feature]bool {
return map[driver.Feature]bool{
driver.OCIExporter: false,
driver.DockerExporter: false,
driver.CacheExport: false,
driver.MultiPlatform: false,
driver.CacheExport: false,
driver.MultiPlatform: false,
}
}
@@ -58,4 +58,10 @@ func (d *Driver) Factory() driver.Factory {
return d.factory
}
func (d *Driver) IsDefaultMobyDriver() {}
func (d *Driver) IsMobyDriver() bool {
return true
}
func (d *Driver) Config() driver.InitConfig {
return d.InitConfig
}

View File

@@ -44,7 +44,7 @@ func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver
if cfg.DockerAPI == nil {
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")
}

View File

@@ -3,7 +3,9 @@ package driver
import (
"context"
"github.com/docker/buildx/store"
"github.com/docker/buildx/util/progress"
clitypes "github.com/docker/cli/cli/config/types"
"github.com/moby/buildkit/client"
"github.com/pkg/errors"
)
@@ -39,6 +41,12 @@ func (s Status) String() string {
type Info struct {
Status Status
// DynamicNodes must be empty if the actual nodes are statically listed in the store
DynamicNodes []store.Node
}
type Auth interface {
GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error)
}
type Driver interface {
@@ -46,12 +54,14 @@ type Driver interface {
Bootstrap(context.Context, progress.Logger) error
Info(context.Context) (*Info, error)
Stop(ctx context.Context, force bool) error
Rm(ctx context.Context, force bool) error
Rm(ctx context.Context, force, rmVolume, rmDaemon bool) error
Client(ctx context.Context) (*client.Client, error)
Features() map[Feature]bool
IsMobyDriver() bool
Config() InitConfig
}
func Boot(ctx context.Context, d Driver, pw progress.Writer) (*client.Client, error) {
func Boot(ctx, clientContext context.Context, d Driver, pw progress.Writer) (*client.Client, error) {
try := 0
for {
info, err := d.Info(ctx)
@@ -63,16 +73,12 @@ func Boot(ctx context.Context, d Driver, pw progress.Writer) (*client.Client, er
if try > 2 {
return nil, errors.Errorf("failed to bootstrap %T driver in attempts", d)
}
if err := d.Bootstrap(ctx, func(s *client.SolveStatus) {
if pw != nil {
pw.Status() <- s
}
}); err != nil {
if err := d.Bootstrap(ctx, pw.Write); err != nil {
return nil, err
}
}
c, err := d.Client(context.TODO())
c, err := d.Client(clientContext)
if err != nil {
if errors.Cause(err) == ErrNotRunning && try <= 2 {
continue

View File

@@ -0,0 +1,6 @@
package context
const (
// KubernetesEndpoint is the kubernetes endpoint name in a stored context
KubernetesEndpoint = "kubernetes"
)

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

@@ -0,0 +1,155 @@
package context
import (
"os"
"path/filepath"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/context"
"github.com/docker/cli/cli/context/store"
"github.com/docker/docker/pkg/homedir"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
// EndpointMeta is a typed wrapper around a context-store generic endpoint describing
// a Kubernetes endpoint, without TLS data
type EndpointMeta struct {
context.EndpointMetaBase
DefaultNamespace string `json:",omitempty"`
AuthProvider *clientcmdapi.AuthProviderConfig `json:",omitempty"`
Exec *clientcmdapi.ExecConfig `json:",omitempty"`
UsernamePassword *UsernamePassword `json:"usernamePassword,omitempty"`
}
// UsernamePassword contains username/password auth info
type UsernamePassword struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
var _ command.EndpointDefaultResolver = &EndpointMeta{}
// Endpoint is a typed wrapper around a context-store generic endpoint describing
// a Kubernetes endpoint, with TLS data
type Endpoint struct {
EndpointMeta
TLSData *context.TLSData
}
func init() {
command.RegisterDefaultStoreEndpoints(
store.EndpointTypeGetter(KubernetesEndpoint, func() interface{} { return &EndpointMeta{} }),
)
}
// WithTLSData loads TLS materials for the endpoint
func (c *EndpointMeta) WithTLSData(s store.Reader, contextName string) (Endpoint, error) {
tlsData, err := context.LoadTLSData(s, contextName, KubernetesEndpoint)
if err != nil {
return Endpoint{}, err
}
return Endpoint{
EndpointMeta: *c,
TLSData: tlsData,
}, nil
}
// KubernetesConfig creates the kubernetes client config from the endpoint
func (c *Endpoint) KubernetesConfig() clientcmd.ClientConfig {
cfg := clientcmdapi.NewConfig()
cluster := clientcmdapi.NewCluster()
cluster.Server = c.Host
cluster.InsecureSkipTLSVerify = c.SkipTLSVerify
authInfo := clientcmdapi.NewAuthInfo()
if c.TLSData != nil {
cluster.CertificateAuthorityData = c.TLSData.CA
authInfo.ClientCertificateData = c.TLSData.Cert
authInfo.ClientKeyData = c.TLSData.Key
}
if c.UsernamePassword != nil {
authInfo.Username = c.UsernamePassword.Username
authInfo.Password = c.UsernamePassword.Password
}
authInfo.AuthProvider = c.AuthProvider
authInfo.Exec = c.Exec
cfg.Clusters["cluster"] = cluster
cfg.AuthInfos["authInfo"] = authInfo
ctx := clientcmdapi.NewContext()
ctx.AuthInfo = "authInfo"
ctx.Cluster = "cluster"
ctx.Namespace = c.DefaultNamespace
cfg.Contexts["context"] = ctx
cfg.CurrentContext = "context"
return clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{})
}
// ResolveDefault returns endpoint metadata for the default Kubernetes
// endpoint, which is derived from the env-based kubeconfig.
func (c *EndpointMeta) ResolveDefault() (interface{}, *store.EndpointTLSData, error) {
kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" {
kubeconfig = filepath.Join(homedir.Get(), ".kube/config")
}
kubeEP, err := FromKubeConfig(kubeconfig, "", "")
if err != nil {
// We deliberately quash the error here, returning nil
// for the first argument is sufficient to indicate we weren't able to
// provide a default
return nil, nil, nil
}
var tls *store.EndpointTLSData
if kubeEP.TLSData != nil {
tls = kubeEP.TLSData.ToStoreTLSData()
}
return kubeEP.EndpointMeta, tls, nil
}
// EndpointFromContext extracts kubernetes endpoint info from current context
func EndpointFromContext(metadata store.Metadata) *EndpointMeta {
ep, ok := metadata.Endpoints[KubernetesEndpoint]
if !ok {
return nil
}
typed, ok := ep.(EndpointMeta)
if !ok {
return nil
}
return &typed
}
// ConfigFromContext resolves a kubernetes client config for the specified context.
// If kubeconfigOverride is specified, use this config file instead of the context defaults.ConfigFromContext
// if command.ContextDockerHost is specified as the context name, fallsback to the default user's kubeconfig file
func ConfigFromContext(name string, s store.Reader) (clientcmd.ClientConfig, error) {
ctxMeta, err := s.GetMetadata(name)
if err != nil {
return nil, err
}
epMeta := EndpointFromContext(ctxMeta)
if epMeta != nil {
ep, err := epMeta.WithTLSData(s, name)
if err != nil {
return nil, err
}
return ep.KubernetesConfig(), nil
}
// context has no kubernetes endpoint
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 (
"io/ioutil"
@@ -39,6 +39,13 @@ func FromKubeConfig(kubeconfig, kubeContext, namespaceOverride string) (Endpoint
Key: key,
}
}
var usernamePassword *UsernamePassword
if clientcfg.Username != "" || clientcfg.Password != "" {
usernamePassword = &UsernamePassword{
Username: clientcfg.Username,
Password: clientcfg.Password,
}
}
return Endpoint{
EndpointMeta: EndpointMeta{
EndpointMetaBase: context.EndpointMetaBase{
@@ -48,6 +55,7 @@ func FromKubeConfig(kubeconfig, kubeContext, namespaceOverride string) (Endpoint
DefaultNamespace: ns,
AuthProvider: clientcfg.AuthProvider,
Exec: clientcfg.ExecProvider,
UsernamePassword: usernamePassword,
},
TLSData: tlsData,
}, nil

233
driver/kubernetes/driver.go Normal file
View File

@@ -0,0 +1,233 @@
package kubernetes
import (
"context"
"fmt"
"net"
"strings"
"time"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/driver/kubernetes/execconn"
"github.com/docker/buildx/driver/kubernetes/manifest"
"github.com/docker/buildx/driver/kubernetes/podchooser"
"github.com/docker/buildx/store"
"github.com/docker/buildx/util/platformutil"
"github.com/docker/buildx/util/progress"
"github.com/moby/buildkit/client"
"github.com/moby/buildkit/util/tracing/detect"
"github.com/pkg/errors"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
clientappsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
clientcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)
const (
DriverName = "kubernetes"
)
const (
// valid values for driver-opt loadbalance
LoadbalanceRandom = "random"
LoadbalanceSticky = "sticky"
)
type Driver struct {
driver.InitConfig
factory driver.Factory
minReplicas int
deployment *appsv1.Deployment
configMaps []*corev1.ConfigMap
clientset *kubernetes.Clientset
deploymentClient clientappsv1.DeploymentInterface
podClient clientcorev1.PodInterface
configMapClient clientcorev1.ConfigMapInterface
podChooser podchooser.PodChooser
}
func (d *Driver) IsMobyDriver() bool {
return false
}
func (d *Driver) Config() driver.InitConfig {
return d.InitConfig
}
func (d *Driver) Bootstrap(ctx context.Context, l progress.Logger) error {
return progress.Wrap("[internal] booting buildkit", l, func(sub progress.SubLogger) error {
_, err := d.deploymentClient.Get(ctx, d.deployment.Name, metav1.GetOptions{})
if err != nil {
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{})
if err != nil {
return errors.Wrapf(err, "error while calling deploymentClient.Create for %q", d.deployment.Name)
}
}
return sub.Wrap(
fmt.Sprintf("waiting for %d pods to be ready", d.minReplicas),
func() error {
if err := d.wait(ctx); err != nil {
return err
}
return nil
})
})
}
func (d *Driver) wait(ctx context.Context) error {
// TODO: use watch API
var (
err error
depl *appsv1.Deployment
)
for try := 0; try < 100; try++ {
depl, err = d.deploymentClient.Get(ctx, d.deployment.Name, metav1.GetOptions{})
if err == nil {
if depl.Status.ReadyReplicas >= int32(d.minReplicas) {
return nil
}
err = errors.Errorf("expected %d replicas to be ready, got %d",
d.minReplicas, depl.Status.ReadyReplicas)
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(time.Duration(100+try*20) * time.Millisecond):
}
}
return err
}
func (d *Driver) Info(ctx context.Context) (*driver.Info, error) {
depl, err := d.deploymentClient.Get(ctx, d.deployment.Name, metav1.GetOptions{})
if err != nil {
// TODO: return err if err != ErrNotFound
return &driver.Info{
Status: driver.Inactive,
}, nil
}
if depl.Status.ReadyReplicas <= 0 {
return &driver.Info{
Status: driver.Stopped,
}, nil
}
pods, err := podchooser.ListRunningPods(ctx, d.podClient, depl)
if err != nil {
return nil, err
}
var dynNodes []store.Node
for _, p := range pods {
node := store.Node{
Name: p.Name,
// Other fields are unset (TODO: detect real platforms)
}
if p.Annotations != nil {
if p, ok := p.Annotations[manifest.AnnotationPlatform]; ok {
ps, err := platformutil.Parse(strings.Split(p, ","))
if err == nil {
node.Platforms = ps
}
}
}
dynNodes = append(dynNodes, node)
}
return &driver.Info{
Status: driver.Running,
DynamicNodes: dynNodes,
}, nil
}
func (d *Driver) Stop(ctx context.Context, force bool) error {
// future version may scale the replicas to zero here
return nil
}
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 !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
}
func (d *Driver) Client(ctx context.Context) (*client.Client, error) {
restClient := d.clientset.CoreV1().RESTClient()
restClientConfig, err := d.KubeClientConfig.ClientConfig()
if err != nil {
return nil, err
}
pod, err := d.podChooser.ChoosePod(ctx)
if err != nil {
return nil, err
}
if len(pod.Spec.Containers) == 0 {
return nil, errors.Errorf("pod %s does not have any container", pod.Name)
}
containerName := pod.Spec.Containers[0].Name
cmd := []string{"buildctl", "dial-stdio"}
conn, err := execconn.ExecConn(restClient, restClientConfig,
pod.Namespace, pod.Name, containerName, cmd)
if err != nil {
return nil, err
}
exp, err := detect.Exporter()
if err != nil {
return nil, err
}
td, _ := exp.(client.TracerDelegate)
return client.New(ctx, "", client.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return conn, nil
}), client.WithTracerDelegate(td))
}
func (d *Driver) Factory() driver.Factory {
return d.factory
}
func (d *Driver) Features() map[driver.Feature]bool {
return map[driver.Feature]bool{
driver.OCIExporter: true,
driver.DockerExporter: d.DockerAPI != nil,
driver.CacheExport: true,
driver.MultiPlatform: true, // Untested (needs multiple Driver instances)
}
}

View File

@@ -0,0 +1,135 @@
package execconn
import (
"io"
"net"
"os"
"sync"
"time"
"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/remotecommand"
)
func ExecConn(restClient rest.Interface, restConfig *rest.Config, namespace, pod, container string, cmd []string) (net.Conn, error) {
req := restClient.
Post().
Namespace(namespace).
Resource("pods").
Name(pod).
SubResource("exec").
VersionedParams(&corev1.PodExecOptions{
Container: container,
Command: cmd,
Stdin: true,
Stdout: true,
Stderr: true,
TTY: false,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(restConfig, "POST", req.URL())
if err != nil {
return nil, err
}
stdinR, stdinW := io.Pipe()
stdoutR, stdoutW := io.Pipe()
kc := &kubeConn{
stdin: stdinW,
stdout: stdoutR,
localAddr: dummyAddr{network: "dummy", s: "dummy-0"},
remoteAddr: dummyAddr{network: "dummy", s: "dummy-1"},
}
go func() {
serr := exec.Stream(remotecommand.StreamOptions{
Stdin: stdinR,
Stdout: stdoutW,
Stderr: os.Stderr,
Tty: false,
})
if serr != nil {
logrus.Error(serr)
}
}()
return kc, nil
}
type kubeConn struct {
stdin io.WriteCloser
stdout io.ReadCloser
stdioClosedMu sync.Mutex // for stdinClosed and stdoutClosed
stdinClosed bool
stdoutClosed bool
localAddr net.Addr
remoteAddr net.Addr
}
func (c *kubeConn) Write(p []byte) (int, error) {
return c.stdin.Write(p)
}
func (c *kubeConn) Read(p []byte) (int, error) {
return c.stdout.Read(p)
}
func (c *kubeConn) CloseWrite() error {
err := c.stdin.Close()
c.stdioClosedMu.Lock()
c.stdinClosed = true
c.stdioClosedMu.Unlock()
return err
}
func (c *kubeConn) CloseRead() error {
err := c.stdout.Close()
c.stdioClosedMu.Lock()
c.stdoutClosed = true
c.stdioClosedMu.Unlock()
return err
}
func (c *kubeConn) Close() error {
var err error
c.stdioClosedMu.Lock()
stdinClosed := c.stdinClosed
c.stdioClosedMu.Unlock()
if !stdinClosed {
err = c.CloseWrite()
}
c.stdioClosedMu.Lock()
stdoutClosed := c.stdoutClosed
c.stdioClosedMu.Unlock()
if !stdoutClosed {
err = c.CloseRead()
}
return err
}
func (c *kubeConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *kubeConn) RemoteAddr() net.Addr {
return c.remoteAddr
}
func (c *kubeConn) SetDeadline(t time.Time) error {
return nil
}
func (c *kubeConn) SetReadDeadline(t time.Time) error {
return nil
}
func (c *kubeConn) SetWriteDeadline(t time.Time) error {
return nil
}
type dummyAddr struct {
network string
s string
}
func (d dummyAddr) Network() string {
return d.network
}
func (d dummyAddr) String() string {
return d.s
}

View File

@@ -0,0 +1,184 @@
package kubernetes
import (
"context"
"strconv"
"strings"
"github.com/docker/buildx/driver"
"github.com/docker/buildx/driver/bkimage"
"github.com/docker/buildx/driver/kubernetes/manifest"
"github.com/docker/buildx/driver/kubernetes/podchooser"
dockerclient "github.com/docker/docker/client"
"github.com/pkg/errors"
"k8s.io/client-go/kubernetes"
)
const prioritySupported = 40
const priorityUnsupported = 80
func init() {
driver.Register(&factory{})
}
type factory struct {
}
func (*factory) Name() string {
return DriverName
}
func (*factory) Usage() string {
return DriverName
}
func (*factory) Priority(ctx context.Context, api dockerclient.APIClient) int {
if api == nil {
return priorityUnsupported
}
return prioritySupported
}
func (f *factory) New(ctx context.Context, cfg driver.InitConfig) (driver.Driver, error) {
if cfg.KubeClientConfig == nil {
return nil, errors.Errorf("%s driver requires kubernetes API access", DriverName)
}
deploymentName, err := buildxNameToDeploymentName(cfg.Name)
if err != nil {
return nil, err
}
namespace, _, err := cfg.KubeClientConfig.Namespace()
if err != nil {
return nil, errors.Wrap(err, "cannot determine Kubernetes namespace, specify manually")
}
restClientConfig, err := cfg.KubeClientConfig.ClientConfig()
if err != nil {
return nil, err
}
clientset, err := kubernetes.NewForConfig(restClientConfig)
if err != nil {
return nil, err
}
d := &Driver{
factory: f,
InitConfig: cfg,
clientset: clientset,
}
deploymentOpt := &manifest.DeploymentOpt{
Name: deploymentName,
Image: bkimage.DefaultImage,
Replicas: 1,
BuildkitFlags: cfg.BuildkitFlags,
Rootless: false,
Platforms: cfg.Platforms,
ConfigFiles: cfg.Files,
}
deploymentOpt.Qemu.Image = bkimage.QemuImage
loadbalance := LoadbalanceSticky
for k, v := range cfg.DriverOpts {
switch k {
case "image":
if v != "" {
deploymentOpt.Image = v
}
case "namespace":
namespace = v
case "replicas":
deploymentOpt.Replicas, err = strconv.Atoi(v)
if err != nil {
return nil, err
}
case "requests.cpu":
deploymentOpt.RequestsCPU = v
case "requests.memory":
deploymentOpt.RequestsMemory = v
case "limits.cpu":
deploymentOpt.LimitsCPU = v
case "limits.memory":
deploymentOpt.LimitsMemory = v
case "rootless":
deploymentOpt.Rootless, err = strconv.ParseBool(v)
if err != nil {
return nil, err
}
deploymentOpt.Image = bkimage.DefaultRootlessImage
case "nodeselector":
kvs := strings.Split(strings.Trim(v, `"`), ",")
s := map[string]string{}
for i := range kvs {
kv := strings.Split(kvs[i], "=")
if len(kv) == 2 {
s[kv[0]] = kv[1]
}
}
deploymentOpt.NodeSelector = s
case "loadbalance":
switch v {
case LoadbalanceSticky:
case LoadbalanceRandom:
default:
return nil, errors.Errorf("invalid loadbalance %q", v)
}
loadbalance = v
case "qemu.install":
deploymentOpt.Qemu.Install, err = strconv.ParseBool(v)
if err != nil {
return nil, err
}
case "qemu.image":
if v != "" {
deploymentOpt.Qemu.Image = v
}
default:
return nil, errors.Errorf("invalid driver option %s for driver %s", k, DriverName)
}
}
d.deployment, d.configMaps, err = manifest.NewDeployment(deploymentOpt)
if err != nil {
return nil, err
}
d.minReplicas = deploymentOpt.Replicas
d.deploymentClient = clientset.AppsV1().Deployments(namespace)
d.podClient = clientset.CoreV1().Pods(namespace)
d.configMapClient = clientset.CoreV1().ConfigMaps(namespace)
switch loadbalance {
case LoadbalanceSticky:
d.podChooser = &podchooser.StickyPodChooser{
Key: cfg.ContextPathHash,
PodClient: d.podClient,
Deployment: d.deployment,
}
case LoadbalanceRandom:
d.podChooser = &podchooser.RandomPodChooser{
PodClient: d.podClient,
Deployment: d.deployment,
}
}
return d, nil
}
func (f *factory) AllowsInstances() bool {
return true
}
// buildxNameToDeploymentName converts buildx name to Kubernetes Deployment name.
//
// eg. "buildx_buildkit_loving_mendeleev0" -> "loving-mendeleev0"
func buildxNameToDeploymentName(bx string) (string, error) {
// TODO: commands.util.go should not pass "buildx_buildkit_" prefix to drivers
if !strings.HasPrefix(bx, "buildx_buildkit_") {
return "", errors.Errorf("expected a string with \"buildx_buildkit_\", got %q", bx)
}
s := strings.TrimPrefix(bx, "buildx_buildkit_")
s = strings.ReplaceAll(s, "_", "-")
return s, nil
}

View File

@@ -0,0 +1,241 @@
package manifest
import (
"fmt"
"path"
"strings"
"github.com/docker/buildx/util/platformutil"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type DeploymentOpt struct {
Namespace string
Name string
Image string
Replicas int
// 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
NodeSelector map[string]string
RequestsCPU string
RequestsMemory string
LimitsCPU string
LimitsMemory string
Platforms []v1.Platform
}
const (
containerName = "buildkitd"
AnnotationPlatform = "buildx.docker.com/platform"
)
func NewDeployment(opt *DeploymentOpt) (d *appsv1.Deployment, c []*corev1.ConfigMap, err error) {
labels := map[string]string{
"app": opt.Name,
}
annotations := map[string]string{}
replicas := int32(opt.Replicas)
privileged := true
args := opt.BuildkitFlags
if len(opt.Platforms) > 0 {
annotations[AnnotationPlatform] = strings.Join(platformutil.Format(opt.Platforms), ",")
}
d = &appsv1.Deployment{
TypeMeta: metav1.TypeMeta{
APIVersion: appsv1.SchemeGroupVersion.String(),
Kind: "Deployment",
},
ObjectMeta: metav1.ObjectMeta{
Namespace: opt.Namespace,
Name: opt.Name,
Labels: labels,
Annotations: annotations,
},
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Annotations: annotations,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: containerName,
Image: opt.Image,
Args: args,
SecurityContext: &corev1.SecurityContext{
Privileged: &privileged,
},
ReadinessProbe: &corev1.Probe{
Handler: corev1.Handler{
Exec: &corev1.ExecAction{
Command: []string{"buildctl", "debug", "workers"},
},
},
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{},
Limits: corev1.ResourceList{},
},
},
},
},
},
},
}
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 err := toRootless(d); err != nil {
return nil, nil, err
}
}
if len(opt.NodeSelector) > 0 {
d.Spec.Template.Spec.NodeSelector = opt.NodeSelector
}
if opt.RequestsCPU != "" {
reqCPU, err := resource.ParseQuantity(opt.RequestsCPU)
if err != nil {
return nil, nil, err
}
d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceCPU] = reqCPU
}
if opt.RequestsMemory != "" {
reqMemory, err := resource.ParseQuantity(opt.RequestsMemory)
if err != nil {
return nil, nil, err
}
d.Spec.Template.Spec.Containers[0].Resources.Requests[corev1.ResourceMemory] = reqMemory
}
if opt.LimitsCPU != "" {
limCPU, err := resource.ParseQuantity(opt.LimitsCPU)
if err != nil {
return nil, nil, err
}
d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceCPU] = limCPU
}
if opt.LimitsMemory != "" {
limMemory, err := resource.ParseQuantity(opt.LimitsMemory)
if err != nil {
return nil, nil, err
}
d.Spec.Template.Spec.Containers[0].Resources.Limits[corev1.ResourceMemory] = limMemory
}
return
}
func toRootless(d *appsv1.Deployment) error {
d.Spec.Template.Spec.Containers[0].Args = append(
d.Spec.Template.Spec.Containers[0].Args,
"--oci-worker-no-process-sandbox",
)
d.Spec.Template.Spec.Containers[0].SecurityContext = nil
if d.Spec.Template.ObjectMeta.Annotations == nil {
d.Spec.Template.ObjectMeta.Annotations = make(map[string]string, 2)
}
d.Spec.Template.ObjectMeta.Annotations["container.apparmor.security.beta.kubernetes.io/"+containerName] = "unconfined"
d.Spec.Template.ObjectMeta.Annotations["container.seccomp.security.alpha.kubernetes.io/"+containerName] = "unconfined"
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

@@ -0,0 +1,97 @@
package podchooser
import (
"context"
"math/rand"
"sort"
"time"
"github.com/serialx/hashring"
"github.com/sirupsen/logrus"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
clientcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
)
type PodChooser interface {
ChoosePod(ctx context.Context) (*corev1.Pod, error)
}
type RandomPodChooser struct {
RandSource rand.Source
PodClient clientcorev1.PodInterface
Deployment *appsv1.Deployment
}
func (pc *RandomPodChooser) ChoosePod(ctx context.Context) (*corev1.Pod, error) {
pods, err := ListRunningPods(ctx, pc.PodClient, pc.Deployment)
if err != nil {
return nil, err
}
randSource := pc.RandSource
if randSource == nil {
randSource = rand.NewSource(time.Now().Unix())
}
rnd := rand.New(randSource)
n := rnd.Int() % len(pods)
logrus.Debugf("RandomPodChooser.ChoosePod(): len(pods)=%d, n=%d", len(pods), n)
return pods[n], nil
}
type StickyPodChooser struct {
Key string
PodClient clientcorev1.PodInterface
Deployment *appsv1.Deployment
}
func (pc *StickyPodChooser) ChoosePod(ctx context.Context) (*corev1.Pod, error) {
pods, err := ListRunningPods(ctx, pc.PodClient, pc.Deployment)
if err != nil {
return nil, err
}
var podNames []string
podMap := make(map[string]*corev1.Pod, len(pods))
for _, pod := range pods {
podNames = append(podNames, pod.Name)
podMap[pod.Name] = pod
}
ring := hashring.New(podNames)
chosen, ok := ring.GetNode(pc.Key)
if !ok {
// NOTREACHED
logrus.Errorf("no pod found for key %q", pc.Key)
rpc := &RandomPodChooser{
PodClient: pc.PodClient,
Deployment: pc.Deployment,
}
return rpc.ChoosePod(ctx)
}
return podMap[chosen], nil
}
func ListRunningPods(ctx context.Context, client clientcorev1.PodInterface, depl *appsv1.Deployment) ([]*corev1.Pod, error) {
selector, err := metav1.LabelSelectorAsSelector(depl.Spec.Selector)
if err != nil {
return nil, err
}
listOpts := metav1.ListOptions{
LabelSelector: selector.String(),
}
podList, err := client.List(ctx, listOpts)
if err != nil {
return nil, err
}
var runningPods []*corev1.Pod
for i := range podList.Items {
pod := &podList.Items[i]
if pod.Status.Phase == corev1.PodRunning {
logrus.Debugf("pod runnning: %q", pod.Name)
runningPods = append(runningPods, pod)
}
}
sort.Slice(runningPods, func(i, j int) bool {
return runningPods[i].Name < runningPods[j].Name
})
return runningPods, nil
}

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