Browse code

vendor: update buildkit to f5a55a95

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
(cherry picked from commit c60e53a274b348e9d922447df0a5344eb8e204ee)

Tonis Tiigi authored on 2019/08/07 08:08:45
Showing 63 changed files
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"io/ioutil"
8 8
 	nethttp "net/http"
9 9
 	"runtime"
10
+	"strings"
10 11
 	"time"
11 12
 
12 13
 	"github.com/containerd/containerd/content"
... ...
@@ -43,6 +44,7 @@ import (
43 43
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
44 44
 	"github.com/pkg/errors"
45 45
 	"github.com/sirupsen/logrus"
46
+	bolt "go.etcd.io/bbolt"
46 47
 )
47 48
 
48 49
 const labelCreatedAt = "buildkit/createdat"
... ...
@@ -257,6 +259,47 @@ func (w *Worker) GetRemote(ctx context.Context, ref cache.ImmutableRef, createIf
257 257
 	}, nil
258 258
 }
259 259
 
260
+// PruneCacheMounts removes the current cache snapshots for specified IDs
261
+func (w *Worker) PruneCacheMounts(ctx context.Context, ids []string) error {
262
+	mu := ops.CacheMountsLocker()
263
+	mu.Lock()
264
+	defer mu.Unlock()
265
+
266
+	for _, id := range ids {
267
+		id = "cache-dir:" + id
268
+		sis, err := w.MetadataStore.Search(id)
269
+		if err != nil {
270
+			return err
271
+		}
272
+		for _, si := range sis {
273
+			for _, k := range si.Indexes() {
274
+				if k == id || strings.HasPrefix(k, id+":") {
275
+					if siCached := w.CacheManager.Metadata(si.ID()); siCached != nil {
276
+						si = siCached
277
+					}
278
+					if err := cache.CachePolicyDefault(si); err != nil {
279
+						return err
280
+					}
281
+					si.Queue(func(b *bolt.Bucket) error {
282
+						return si.SetValue(b, k, nil)
283
+					})
284
+					if err := si.Commit(); err != nil {
285
+						return err
286
+					}
287
+					// if ref is unused try to clean it up right away by releasing it
288
+					if mref, err := w.CacheManager.GetMutable(ctx, si.ID()); err == nil {
289
+						go mref.Release(context.TODO())
290
+					}
291
+					break
292
+				}
293
+			}
294
+		}
295
+	}
296
+
297
+	ops.ClearActiveCacheMounts()
298
+	return nil
299
+}
300
+
260 301
 // FromRemote converts a remote snapshot reference to a local one
261 302
 func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error) {
262 303
 	rootfs, err := getLayers(ctx, remote.Descriptors)
... ...
@@ -27,7 +27,7 @@ github.com/imdario/mergo                            7c29201646fa3de8506f70121347
27 27
 golang.org/x/sync                                   e225da77a7e68af35c70ccbf71af2b83e6acac3c
28 28
 
29 29
 # buildkit
30
-github.com/moby/buildkit                            1f89ec125f84c097bdf3a063be622c4238dba5f8
30
+github.com/moby/buildkit                            f5a55a9516d1c6e2ade9bec22b83259caeed3a84 
31 31
 github.com/tonistiigi/fsutil                        3bbb99cdbd76619ab717299830c60f6f2a533a6b
32 32
 github.com/grpc-ecosystem/grpc-opentracing          8e809c8a86450a29b90dcc9efbf062d0fe6d9746
33 33
 github.com/opentracing/opentracing-go               1361b9cd60be79c4c3a7fa9841b3c132e40066a7
... ...
@@ -1,27 +1,25 @@
1 1
 [![asciicinema example](https://asciinema.org/a/gPEIEo1NzmDTUu2bEPsUboqmU.png)](https://asciinema.org/a/gPEIEo1NzmDTUu2bEPsUboqmU)
2 2
 
3
-
4 3
 ## BuildKit
5 4
 
6 5
 [![GoDoc](https://godoc.org/github.com/moby/buildkit?status.svg)](https://godoc.org/github.com/moby/buildkit/client/llb)
7 6
 [![Build Status](https://travis-ci.org/moby/buildkit.svg?branch=master)](https://travis-ci.org/moby/buildkit)
8 7
 [![Go Report Card](https://goreportcard.com/badge/github.com/moby/buildkit)](https://goreportcard.com/report/github.com/moby/buildkit)
9 8
 
10
-
11 9
 BuildKit is a toolkit for converting source code to build artifacts in an efficient, expressive and repeatable manner.
12 10
 
13 11
 Key features:
14
-- Automatic garbage collection
15
-- Extendable frontend formats
16
-- Concurrent dependency resolution
17
-- Efficient instruction caching
18
-- Build cache import/export
19
-- Nested build job invocations
20
-- Distributable workers
21
-- Multiple output formats
22
-- Pluggable architecture
23
-- Execution without root privileges
24 12
 
13
+-   Automatic garbage collection
14
+-   Extendable frontend formats
15
+-   Concurrent dependency resolution
16
+-   Efficient instruction caching
17
+-   Build cache import/export
18
+-   Nested build job invocations
19
+-   Distributable workers
20
+-   Multiple output formats
21
+-   Pluggable architecture
22
+-   Execution without root privileges
25 23
 
26 24
 Read the proposal from https://github.com/moby/moby/issues/32925
27 25
 
... ...
@@ -33,20 +31,21 @@ Introductory blog post https://blog.mobyproject.org/introducing-buildkit-17e056c
33 33
 
34 34
 BuildKit is used by the following projects:
35 35
 
36
-- [Moby & Docker](https://github.com/moby/moby/pull/37151)
37
-- [img](https://github.com/genuinetools/img)
38
-- [OpenFaaS Cloud](https://github.com/openfaas/openfaas-cloud)
39
-- [container build interface](https://github.com/containerbuilding/cbi)
40
-- [Knative Build Templates](https://github.com/knative/build-templates)
41
-- [vab](https://github.com/stellarproject/vab)
42
-- [Rio](https://github.com/rancher/rio) (on roadmap)
36
+-   [Moby & Docker](https://github.com/moby/moby/pull/37151)
37
+-   [img](https://github.com/genuinetools/img)
38
+-   [OpenFaaS Cloud](https://github.com/openfaas/openfaas-cloud)
39
+-   [container build interface](https://github.com/containerbuilding/cbi)
40
+-   [Knative Build Templates](https://github.com/knative/build-templates)
41
+-   [the Sanic build tool](https://github.com/distributed-containers-inc/sanic)
42
+-   [vab](https://github.com/stellarproject/vab)
43
+-   [Rio](https://github.com/rancher/rio) (on roadmap)
43 44
 
44 45
 ### Quick start
45 46
 
46 47
 Dependencies:
47
-- [runc](https://github.com/opencontainers/runc)
48
-- [containerd](https://github.com/containerd/containerd) (if you want to use containerd worker)
49 48
 
49
+-   [runc](https://github.com/opencontainers/runc)
50
+-   [containerd](https://github.com/containerd/containerd) (if you want to use containerd worker)
50 51
 
51 52
 The following command installs `buildkitd` and `buildctl` to `/usr/local/bin`:
52 53
 
... ...
@@ -58,14 +57,13 @@ You can also use `make binaries-all` to prepare `buildkitd.containerd_only` and
58 58
 
59 59
 #### Starting the buildkitd daemon:
60 60
 
61
-```
61
+```bash
62 62
 buildkitd --debug --root /var/lib/buildkit
63 63
 ```
64 64
 
65 65
 The buildkitd daemon supports two worker backends: OCI (runc) and containerd.
66 66
 
67
-By default, the OCI (runc) worker is used.
68
-You can set `--oci-worker=false --containerd-worker=true` to use the containerd worker.
67
+By default, the OCI (runc) worker is used. You can set `--oci-worker=false --containerd-worker=true` to use the containerd worker.
69 68
 
70 69
 We are open to adding more backends.
71 70
 
... ...
@@ -73,44 +71,46 @@ We are open to adding more backends.
73 73
 
74 74
 BuildKit builds are based on a binary intermediate format called LLB that is used for defining the dependency graph for processes running part of your build. tl;dr: LLB is to Dockerfile what LLVM IR is to C.
75 75
 
76
-- Marshaled as Protobuf messages
77
-- Concurrently executable
78
-- Efficiently cacheable
79
-- Vendor-neutral (i.e. non-Dockerfile languages can be easily implemented)
76
+-   Marshaled as Protobuf messages
77
+-   Concurrently executable
78
+-   Efficiently cacheable
79
+-   Vendor-neutral (i.e. non-Dockerfile languages can be easily implemented)
80 80
 
81 81
 See [`solver/pb/ops.proto`](./solver/pb/ops.proto) for the format definition.
82 82
 
83 83
 Currently, following high-level languages has been implemented for LLB:
84 84
 
85
-- Dockerfile (See [Exploring Dockerfiles](#exploring-dockerfiles))
86
-- [Buildpacks](https://github.com/tonistiigi/buildkit-pack)
87
-- (open a PR to add your own language)
85
+-   Dockerfile (See [Exploring Dockerfiles](#exploring-dockerfiles))
86
+-   [Buildpacks](https://github.com/tonistiigi/buildkit-pack)
87
+-   (open a PR to add your own language)
88 88
 
89 89
 For understanding the basics of LLB, `examples/buildkit*` directory contains scripts that define how to build different configurations of BuildKit itself and its dependencies using the `client` package. Running one of these scripts generates a protobuf definition of a build graph. Note that the script itself does not execute any steps of the build.
90 90
 
91 91
 You can use `buildctl debug dump-llb` to see what data is in this definition. Add `--dot` to generate dot layout.
92 92
 
93 93
 ```bash
94
-go run examples/buildkit0/buildkit.go | buildctl debug dump-llb | jq .
94
+go run examples/buildkit0/buildkit.go \
95
+    | buildctl debug dump-llb \
96
+    | jq .
95 97
 ```
96 98
 
97
-To start building use `buildctl build` command. The example script accepts `--with-containerd` flag to choose if containerd binaries and support should be included in the end result as well. 
99
+To start building use `buildctl build` command. The example script accepts `--with-containerd` flag to choose if containerd binaries and support should be included in the end result as well.
98 100
 
99 101
 ```bash
100
-go run examples/buildkit0/buildkit.go | buildctl build
102
+go run examples/buildkit0/buildkit.go \
103
+    | buildctl build
101 104
 ```
102 105
 
103 106
 `buildctl build` will show interactive progress bar by default while the build job is running. If the path to the trace file is specified, the trace file generated will contain all information about the timing of the individual steps and logs.
104 107
 
105 108
 Different versions of the example scripts show different ways of describing the build definition for this project to show the capabilities of the library. New versions have been added when new features have become available.
106 109
 
107
-- `./examples/buildkit0` - uses only exec operations, defines a full stage per component.
108
-- `./examples/buildkit1` - cloning git repositories has been separated for extra concurrency.
109
-- `./examples/buildkit2` - uses git sources directly instead of running `git clone`, allowing better performance and much safer caching.
110
-- `./examples/buildkit3` - allows using local source files for separate components eg. `./buildkit3 --runc=local | buildctl build --local runc-src=some/local/path`  
111
-- `./examples/dockerfile2llb` - can be used to convert a Dockerfile to LLB for debugging purposes
112
-- `./examples/gobuild` - shows how to use nested invocation to generate LLB for Go package internal dependencies
113
-
110
+-   `./examples/buildkit0` - uses only exec operations, defines a full stage per component.
111
+-   `./examples/buildkit1` - cloning git repositories has been separated for extra concurrency.
112
+-   `./examples/buildkit2` - uses git sources directly instead of running `git clone`, allowing better performance and much safer caching.
113
+-   `./examples/buildkit3` - allows using local source files for separate components eg. `./buildkit3 --runc=local | buildctl build --local runc-src=some/local/path`
114
+-   `./examples/dockerfile2llb` - can be used to convert a Dockerfile to LLB for debugging purposes
115
+-   `./examples/gobuild` - shows how to use nested invocation to generate LLB for Go package internal dependencies
114 116
 
115 117
 #### Exploring Dockerfiles
116 118
 
... ...
@@ -120,9 +120,18 @@ During development, Dockerfile frontend (dockerfile.v0) is also part of the Buil
120 120
 
121 121
 ##### Building a Dockerfile with `buildctl`
122 122
 
123
-```
124
-buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=.
125
-buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. --opt target=foo --opt build-arg:foo=bar
123
+```bash
124
+buildctl build \
125
+    --frontend=dockerfile.v0 \
126
+    --local context=. \
127
+    --local dockerfile=.
128
+# or
129
+buildctl build \
130
+    --frontend=dockerfile.v0 \
131
+    --local context=. \
132
+    --local dockerfile=. \
133
+    --opt target=foo \
134
+    --opt build-arg:foo=bar
126 135
 ```
127 136
 
128 137
 `--local` exposes local source files from client to the builder. `context` and `dockerfile` are the names Dockerfile frontend looks for build context and Dockerfile location.
... ...
@@ -131,8 +140,9 @@ buildctl build --frontend=dockerfile.v0 --local context=. --local dockerfile=. -
131 131
 
132 132
 For people familiar with `docker build` command, there is an example wrapper utility in `./examples/build-using-dockerfile` that allows building Dockerfiles with BuildKit using a syntax similar to `docker build`.
133 133
 
134
-```
135
-go build ./examples/build-using-dockerfile && sudo install build-using-dockerfile /usr/local/bin
134
+```bash
135
+go build ./examples/build-using-dockerfile \
136
+    && sudo install build-using-dockerfile /usr/local/bin
136 137
 
137 138
 build-using-dockerfile -t myimage .
138 139
 build-using-dockerfile -t mybuildkit -f ./hack/dockerfiles/test.Dockerfile .
... ...
@@ -145,10 +155,18 @@ docker inspect myimage
145 145
 
146 146
 External versions of the Dockerfile frontend are pushed to https://hub.docker.com/r/docker/dockerfile-upstream and https://hub.docker.com/r/docker/dockerfile and can be used with the gateway frontend. The source for the external frontend is currently located in `./frontend/dockerfile/cmd/dockerfile-frontend` but will move out of this repository in the future ([#163](https://github.com/moby/buildkit/issues/163)). For automatic build from master branch of this repository `docker/dockerfile-upsteam:master` or `docker/dockerfile-upstream:master-experimental` image can be used.
147 147
 
148
+```bash
149
+buildctl build \
150
+    --frontend gateway.v0 \
151
+    --opt source=docker/dockerfile \
152
+    --local context=. \
153
+    --local dockerfile=.
154
+buildctl build \
155
+    --frontend gateway.v0 \
156
+    --opt source=docker/dockerfile \
157
+    --opt context=git://github.com/moby/moby \
158
+    --opt build-arg:APT_MIRROR=cdn-fastly.deb.debian.org
148 159
 ```
149
-buildctl build --frontend gateway.v0 --opt source=docker/dockerfile --local context=. --local dockerfile=.
150
-buildctl build --frontend gateway.v0 --opt source=docker/dockerfile --opt context=git://github.com/moby/moby --opt build-arg:APT_MIRROR=cdn-fastly.deb.debian.org
151
-````
152 160
 
153 161
 ##### Building a Dockerfile with experimental features like `RUN --mount=type=(bind|cache|tmpfs|secret|ssh)`
154 162
 
... ...
@@ -162,29 +180,29 @@ By default, the build result and intermediate cache will only remain internally
162 162
 
163 163
 The containerd worker needs to be used
164 164
 
165
-```
165
+```bash
166 166
 buildctl build ... --output type=image,name=docker.io/username/image
167 167
 ctr --namespace=buildkit images ls
168 168
 ```
169 169
 
170 170
 ##### Push resulting image to registry
171 171
 
172
-```
172
+```bash
173 173
 buildctl build ... --output type=image,name=docker.io/username/image,push=true
174 174
 ```
175 175
 
176 176
 If credentials are required, `buildctl` will attempt to read Docker configuration file.
177 177
 
178
-
179 178
 ##### Exporting build result back to client
180 179
 
181 180
 The local client will copy the files directly to the client. This is useful if BuildKit is being used for building something else than container images.
182 181
 
183
-```
182
+```bash
184 183
 buildctl build ... --output type=local,dest=path/to/output-dir
185 184
 ```
186 185
 
187 186
 To export specific files use multi-stage builds with a scratch stage and copy the needed files into that stage with `COPY --from`.
187
+
188 188
 ```dockerfile
189 189
 ...
190 190
 FROM scratch as testresult
... ...
@@ -193,28 +211,27 @@ COPY --from=builder /usr/src/app/testresult.xml .
193 193
 ...
194 194
 ```
195 195
 
196
-```
196
+```bash
197 197
 buildctl build ... --opt target=testresult --output type=local,dest=path/to/output-dir
198 198
 ```
199 199
 
200 200
 Tar exporter is similar to local exporter but transfers the files through a tarball.
201 201
 
202
-```
202
+```bash
203 203
 buildctl build ... --output type=tar,dest=out.tar
204 204
 buildctl build ... --output type=tar > out.tar
205 205
 ```
206 206
 
207
-
208 207
 ##### Exporting built image to Docker
209 208
 
210
-```
209
+```bash
211 210
 # exported tarball is also compatible with OCI spec
212 211
 buildctl build ... --output type=docker,name=myimage | docker load
213 212
 ```
214 213
 
215 214
 ##### Exporting [OCI Image Format](https://github.com/opencontainers/image-spec) tarball to client
216 215
 
217
-```
216
+```bash
218 217
 buildctl build ... --output type=oci,dest=path/to/output.tar
219 218
 buildctl build ... --output type=oci > output.tar
220 219
 ```
... ...
@@ -223,14 +240,14 @@ buildctl build ... --output type=oci > output.tar
223 223
 
224 224
 #### To/From registry
225 225
 
226
-```
226
+```bash
227 227
 buildctl build ... --export-cache type=registry,ref=localhost:5000/myrepo:buildcache
228 228
 buildctl build ... --import-cache type=registry,ref=localhost:5000/myrepo:buildcache
229 229
 ```
230 230
 
231 231
 #### To/From local filesystem
232 232
 
233
-```
233
+```bash
234 234
 buildctl build ... --export-cache type=local,dest=path/to/output-dir
235 235
 buildctl build ... --import-cache type=local,src=path/to/input-dir
236 236
 ```
... ...
@@ -238,27 +255,29 @@ buildctl build ... --import-cache type=local,src=path/to/input-dir
238 238
 The directory layout conforms to OCI Image Spec v1.0.
239 239
 
240 240
 #### `--export-cache` options
241
-* `mode=min` (default): only export layers for the resulting image
242
-* `mode=max`: export all the layers of all intermediate steps
243
-* `ref=docker.io/user/image:tag`: reference for `registry` cache exporter
244
-* `dest=path/to/output-dir`: directory for `local` cache exporter
241
+
242
+-   `mode=min` (default): only export layers for the resulting image
243
+-   `mode=max`: export all the layers of all intermediate steps
244
+-   `ref=docker.io/user/image:tag`: reference for `registry` cache exporter
245
+-   `dest=path/to/output-dir`: directory for `local` cache exporter
245 246
 
246 247
 #### `--import-cache` options
247
-* `ref=docker.io/user/image:tag`: reference for `registry` cache importer
248
-* `src=path/to/input-dir`: directory for `local` cache importer
249
-* `digest=sha256:deadbeef`: digest of the manifest list to import for `local` cache importer. Defaults to the digest of "latest" tag in `index.json`
248
+
249
+-   `ref=docker.io/user/image:tag`: reference for `registry` cache importer
250
+-   `src=path/to/input-dir`: directory for `local` cache importer
251
+-   `digest=sha256:deadbeef`: digest of the manifest list to import for `local` cache importer. Defaults to the digest of "latest" tag in `index.json`
250 252
 
251 253
 ### Other
252 254
 
253 255
 #### View build cache
254 256
 
255
-```
257
+```bash
256 258
 buildctl du -v
257 259
 ```
258 260
 
259 261
 #### Show enabled workers
260 262
 
261
-```
263
+```bash
262 264
 buildctl debug workers -v
263 265
 ```
264 266
 
... ...
@@ -268,14 +287,14 @@ BuildKit can also be used by running the `buildkitd` daemon inside a Docker cont
268 268
 
269 269
 We provide `buildkitd` container images as [`moby/buildkit`](https://hub.docker.com/r/moby/buildkit/tags/):
270 270
 
271
-* `moby/buildkit:latest`: built from the latest regular [release](https://github.com/moby/buildkit/releases)
272
-* `moby/buildkit:rootless`: same as `latest` but runs as an unprivileged user, see [`docs/rootless.md`](docs/rootless.md)
273
-* `moby/buildkit:master`: built from the master branch
274
-* `moby/buildkit:master-rootless`: same as master but runs as an unprivileged user, see [`docs/rootless.md`](docs/rootless.md)
271
+-   `moby/buildkit:latest`: built from the latest regular [release](https://github.com/moby/buildkit/releases)
272
+-   `moby/buildkit:rootless`: same as `latest` but runs as an unprivileged user, see [`docs/rootless.md`](docs/rootless.md)
273
+-   `moby/buildkit:master`: built from the master branch
274
+-   `moby/buildkit:master-rootless`: same as master but runs as an unprivileged user, see [`docs/rootless.md`](docs/rootless.md)
275 275
 
276 276
 To run daemon in a container:
277 277
 
278
-```
278
+```bash
279 279
 docker run -d --privileged -p 1234:1234 moby/buildkit:latest --addr tcp://0.0.0.0:1234
280 280
 export BUILDKIT_HOST=tcp://0.0.0.0:1234
281 281
 buildctl build --help
... ...
@@ -283,26 +302,50 @@ buildctl build --help
283 283
 
284 284
 To run client and an ephemeral daemon in a single container ("daemonless mode"):
285 285
 
286
+```bash
287
+docker run \
288
+    -it \
289
+    --rm \
290
+    --privileged \
291
+    -v /path/to/dir:/tmp/work \
292
+    --entrypoint buildctl-daemonless.sh \
293
+    moby/buildkit:master \
294
+        build \
295
+        --frontend dockerfile.v0 \
296
+        --local context=/tmp/work \
297
+        --local dockerfile=/tmp/work
286 298
 ```
287
-docker run -it --rm --privileged -v /path/to/dir:/tmp/work --entrypoint buildctl-daemonless.sh moby/buildkit:master build --frontend dockerfile.v0 --local context=/tmp/work --local dockerfile=/tmp/work
288
-```
299
+
289 300
 or
290
-```
291
-docker run -it --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined -e BUILDKITD_FLAGS=--oci-worker-no-process-sandbox -v /path/to/dir:/tmp/work --entrypoint buildctl-daemonless.sh moby/buildkit:master-rootless build --frontend dockerfile.v0 --local context=/tmp/work --local dockerfile=/tmp/work
292
-```
293 301
 
294
-The images can be also built locally using `./hack/dockerfiles/test.Dockerfile` (or `./hack/dockerfiles/test.buildkit.Dockerfile` if you already have BuildKit).
295
-Run `make images` to build the images as `moby/buildkit:local` and `moby/buildkit:local-rootless`.
302
+```bash
303
+docker run \
304
+    -it \
305
+    --rm \
306
+    --security-opt seccomp=unconfined \
307
+    --security-opt apparmor=unconfined \
308
+    -e BUILDKITD_FLAGS=--oci-worker-no-process-sandbox \
309
+    -v /path/to/dir:/tmp/work \
310
+    --entrypoint buildctl-daemonless.sh \
311
+    moby/buildkit:master-rootless \
312
+        build \
313
+        --frontend \
314
+        dockerfile.v0 \
315
+        --local context=/tmp/work \
316
+        --local dockerfile=/tmp/work
317
+```
318
+
319
+The images can be also built locally using `./hack/dockerfiles/test.Dockerfile` (or `./hack/dockerfiles/test.buildkit.Dockerfile` if you already have BuildKit). Run `make images` to build the images as `moby/buildkit:local` and `moby/buildkit:local-rootless`.
296 320
 
297 321
 #### Connection helpers
298 322
 
299 323
 If you are running `moby/buildkit:master` or `moby/buildkit:master-rootless` as a Docker/Kubernetes container, you can use special `BUILDKIT_HOST` URL for connecting to the BuildKit daemon in the container:
300 324
 
301
-```
302
-export BUILDKIT_HOST=docker://<container>
325
+```bash
326
+export BUILDKIT_HOST=docker-container://<container>
303 327
 ```
304 328
 
305
-```
329
+```bash
306 330
 export BUILDKIT_HOST=kube-pod://<pod>
307 331
 ```
308 332
 
... ...
@@ -310,15 +353,13 @@ export BUILDKIT_HOST=kube-pod://<pod>
310 310
 
311 311
 BuildKit supports opentracing for buildkitd gRPC API and buildctl commands. To capture the trace to [Jaeger](https://github.com/jaegertracing/jaeger), set `JAEGER_TRACE` environment variable to the collection address.
312 312
 
313
-
314
-```
313
+```bash
315 314
 docker run -d -p6831:6831/udp -p16686:16686 jaegertracing/all-in-one:latest
316 315
 export JAEGER_TRACE=0.0.0.0:6831
317 316
 # restart buildkitd and buildctl so they know JAEGER_TRACE
318 317
 # any buildctl command should be traced to http://127.0.0.1:16686/
319 318
 ```
320 319
 
321
-
322 320
 ### Supported runc version
323 321
 
324 322
 During development, BuildKit is tested with the version of runc that is being used by the containerd repository. Please refer to [runc.md](https://github.com/containerd/containerd/blob/v1.2.1/RUNC.md) for more information.
... ...
@@ -329,5 +370,4 @@ Please refer to [`docs/rootless.md`](docs/rootless.md).
329 329
 
330 330
 ### Contributing
331 331
 
332
-Want to contribute to BuildKit? Awesome! You can find information about
333
-contributing to this project in the [CONTRIBUTING.md](/.github/CONTRIBUTING.md)
332
+Want to contribute to BuildKit? Awesome! You can find information about contributing to this project in the [CONTRIBUTING.md](/.github/CONTRIBUTING.md)
... ...
@@ -36,6 +36,7 @@ type Accessor interface {
36 36
 	New(ctx context.Context, s ImmutableRef, opts ...RefOption) (MutableRef, error)
37 37
 	GetMutable(ctx context.Context, id string) (MutableRef, error) // Rebase?
38 38
 	IdentityMapping() *idtools.IdentityMapping
39
+	Metadata(string) *metadata.StorageItem
39 40
 }
40 41
 
41 42
 type Controller interface {
... ...
@@ -124,6 +125,16 @@ func (cm *cacheManager) GetFromSnapshotter(ctx context.Context, id string, opts
124 124
 	return cm.get(ctx, id, true, opts...)
125 125
 }
126 126
 
127
+func (cm *cacheManager) Metadata(id string) *metadata.StorageItem {
128
+	cm.mu.Lock()
129
+	defer cm.mu.Unlock()
130
+	r, ok := cm.records[id]
131
+	if !ok {
132
+		return nil
133
+	}
134
+	return r.Metadata()
135
+}
136
+
127 137
 // get requires manager lock to be taken
128 138
 func (cm *cacheManager) get(ctx context.Context, id string, fromSnapshotter bool, opts ...RefOption) (*immutableRef, error) {
129 139
 	rec, err := cm.getRecord(ctx, id, fromSnapshotter, opts...)
... ...
@@ -157,14 +168,14 @@ func (cm *cacheManager) get(ctx context.Context, id string, fromSnapshotter bool
157 157
 func (cm *cacheManager) getRecord(ctx context.Context, id string, fromSnapshotter bool, opts ...RefOption) (cr *cacheRecord, retErr error) {
158 158
 	if rec, ok := cm.records[id]; ok {
159 159
 		if rec.isDead() {
160
-			return nil, errNotFound
160
+			return nil, errors.Wrapf(errNotFound, "failed to get dead record %s", id)
161 161
 		}
162 162
 		return rec, nil
163 163
 	}
164 164
 
165 165
 	md, ok := cm.md.Get(id)
166 166
 	if !ok && !fromSnapshotter {
167
-		return nil, errNotFound
167
+		return nil, errors.WithStack(errNotFound)
168 168
 	}
169 169
 	if mutableID := getEqualMutable(md); mutableID != "" {
170 170
 		mutable, err := cm.getRecord(ctx, mutableID, fromSnapshotter)
... ...
@@ -222,7 +233,7 @@ func (cm *cacheManager) getRecord(ctx context.Context, id string, fromSnapshotte
222 222
 		if err := rec.remove(ctx, true); err != nil {
223 223
 			return nil, err
224 224
 		}
225
-		return nil, errNotFound
225
+		return nil, errors.Wrapf(errNotFound, "failed to get deleted record %s", id)
226 226
 	}
227 227
 
228 228
 	if err := initializeMetadata(rec, opts...); err != nil {
... ...
@@ -330,14 +341,14 @@ func (cm *cacheManager) Prune(ctx context.Context, ch chan client.UsageInfo, opt
330 330
 func (cm *cacheManager) pruneOnce(ctx context.Context, ch chan client.UsageInfo, opt client.PruneInfo) error {
331 331
 	filter, err := filters.ParseAll(opt.Filter...)
332 332
 	if err != nil {
333
-		return err
333
+		return errors.Wrapf(err, "failed to parse prune filters %v", opt.Filter)
334 334
 	}
335 335
 
336 336
 	var check ExternalRefChecker
337 337
 	if f := cm.PruneRefChecker; f != nil && (!opt.All || len(opt.Filter) > 0) {
338 338
 		c, err := f()
339 339
 		if err != nil {
340
-			return err
340
+			return errors.WithStack(err)
341 341
 		}
342 342
 		check = c
343 343
 	}
... ...
@@ -549,7 +560,7 @@ func (cm *cacheManager) markShared(m map[string]*cacheUsageInfo) error {
549 549
 	}
550 550
 	c, err := cm.PruneRefChecker()
551 551
 	if err != nil {
552
-		return err
552
+		return errors.WithStack(err)
553 553
 	}
554 554
 
555 555
 	var markAllParentsShared func(string)
... ...
@@ -590,7 +601,7 @@ type cacheUsageInfo struct {
590 590
 func (cm *cacheManager) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) {
591 591
 	filter, err := filters.ParseAll(opt.Filter...)
592 592
 	if err != nil {
593
-		return nil, err
593
+		return nil, errors.Wrapf(err, "failed to parse diskusage filters %v", opt.Filter)
594 594
 	}
595 595
 
596 596
 	cm.mu.Lock()
... ...
@@ -55,7 +55,7 @@ func (s *Store) All() ([]*StorageItem, error) {
55 55
 			return nil
56 56
 		})
57 57
 	})
58
-	return out, err
58
+	return out, errors.WithStack(err)
59 59
 }
60 60
 
61 61
 func (s *Store) Probe(index string) (bool, error) {
... ...
@@ -77,7 +77,7 @@ func (s *Store) Probe(index string) (bool, error) {
77 77
 		}
78 78
 		return nil
79 79
 	})
80
-	return exists, err
80
+	return exists, errors.WithStack(err)
81 81
 }
82 82
 
83 83
 func (s *Store) Search(index string) ([]*StorageItem, error) {
... ...
@@ -114,7 +114,7 @@ func (s *Store) Search(index string) ([]*StorageItem, error) {
114 114
 		}
115 115
 		return nil
116 116
 	})
117
-	return out, err
117
+	return out, errors.WithStack(err)
118 118
 }
119 119
 
120 120
 func (s *Store) View(id string, fn func(b *bolt.Bucket) error) error {
... ...
@@ -132,7 +132,7 @@ func (s *Store) View(id string, fn func(b *bolt.Bucket) error) error {
132 132
 }
133 133
 
134 134
 func (s *Store) Clear(id string) error {
135
-	return s.db.Update(func(tx *bolt.Tx) error {
135
+	return errors.WithStack(s.db.Update(func(tx *bolt.Tx) error {
136 136
 		external := tx.Bucket([]byte(externalBucket))
137 137
 		if external != nil {
138 138
 			external.DeleteBucket([]byte(id))
... ...
@@ -160,21 +160,21 @@ func (s *Store) Clear(id string) error {
160 160
 			}
161 161
 		}
162 162
 		return main.DeleteBucket([]byte(id))
163
-	})
163
+	}))
164 164
 }
165 165
 
166 166
 func (s *Store) Update(id string, fn func(b *bolt.Bucket) error) error {
167
-	return s.db.Update(func(tx *bolt.Tx) error {
167
+	return errors.WithStack(s.db.Update(func(tx *bolt.Tx) error {
168 168
 		b, err := tx.CreateBucketIfNotExists([]byte(mainBucket))
169 169
 		if err != nil {
170
-			return err
170
+			return errors.WithStack(err)
171 171
 		}
172 172
 		b, err = b.CreateBucketIfNotExists([]byte(id))
173 173
 		if err != nil {
174
-			return err
174
+			return errors.WithStack(err)
175 175
 		}
176 176
 		return fn(b)
177
-	})
177
+	}))
178 178
 }
179 179
 
180 180
 func (s *Store) Get(id string) (*StorageItem, bool) {
... ...
@@ -200,7 +200,7 @@ func (s *Store) Get(id string) (*StorageItem, bool) {
200 200
 }
201 201
 
202 202
 func (s *Store) Close() error {
203
-	return s.db.Close()
203
+	return errors.WithStack(s.db.Close())
204 204
 }
205 205
 
206 206
 type StorageItem struct {
... ...
@@ -222,13 +222,13 @@ func newStorageItem(id string, b *bolt.Bucket, s *Store) (*StorageItem, error) {
222 222
 			var sv Value
223 223
 			if len(v) > 0 {
224 224
 				if err := json.Unmarshal(v, &sv); err != nil {
225
-					return err
225
+					return errors.WithStack(err)
226 226
 				}
227 227
 				si.values[string(k)] = &sv
228 228
 			}
229 229
 			return nil
230 230
 		}); err != nil {
231
-			return si, err
231
+			return si, errors.WithStack(err)
232 232
 		}
233 233
 	}
234 234
 	return si, nil
... ...
@@ -250,6 +250,10 @@ func (s *StorageItem) Update(fn func(b *bolt.Bucket) error) error {
250 250
 	return s.storage.Update(s.id, fn)
251 251
 }
252 252
 
253
+func (s *StorageItem) Metadata() *StorageItem {
254
+	return s
255
+}
256
+
253 257
 func (s *StorageItem) Keys() []string {
254 258
 	keys := make([]string, 0, len(s.values))
255 259
 	for k := range s.values {
... ...
@@ -283,23 +287,23 @@ func (s *StorageItem) GetExternal(k string) ([]byte, error) {
283 283
 		return nil
284 284
 	})
285 285
 	if err != nil {
286
-		return nil, err
286
+		return nil, errors.WithStack(err)
287 287
 	}
288 288
 	return dt, nil
289 289
 }
290 290
 
291 291
 func (s *StorageItem) SetExternal(k string, dt []byte) error {
292
-	return s.storage.db.Update(func(tx *bolt.Tx) error {
292
+	return errors.WithStack(s.storage.db.Update(func(tx *bolt.Tx) error {
293 293
 		b, err := tx.CreateBucketIfNotExists([]byte(externalBucket))
294 294
 		if err != nil {
295
-			return err
295
+			return errors.WithStack(err)
296 296
 		}
297 297
 		b, err = b.CreateBucketIfNotExists([]byte(s.id))
298 298
 		if err != nil {
299
-			return err
299
+			return errors.WithStack(err)
300 300
 		}
301 301
 		return b.Put([]byte(k), dt)
302
-	})
302
+	}))
303 303
 }
304 304
 
305 305
 func (s *StorageItem) Queue(fn func(b *bolt.Bucket) error) {
... ...
@@ -311,15 +315,15 @@ func (s *StorageItem) Queue(fn func(b *bolt.Bucket) error) {
311 311
 func (s *StorageItem) Commit() error {
312 312
 	s.mu.Lock()
313 313
 	defer s.mu.Unlock()
314
-	return s.Update(func(b *bolt.Bucket) error {
314
+	return errors.WithStack(s.Update(func(b *bolt.Bucket) error {
315 315
 		for _, fn := range s.queue {
316 316
 			if err := fn(b); err != nil {
317
-				return err
317
+				return errors.WithStack(err)
318 318
 			}
319 319
 		}
320 320
 		s.queue = s.queue[:0]
321 321
 		return nil
322
-	})
322
+	}))
323 323
 }
324 324
 
325 325
 func (s *StorageItem) Indexes() (out []string) {
... ...
@@ -333,6 +337,15 @@ func (s *StorageItem) Indexes() (out []string) {
333 333
 
334 334
 func (s *StorageItem) SetValue(b *bolt.Bucket, key string, v *Value) error {
335 335
 	if v == nil {
336
+		if old, ok := s.values[key]; ok {
337
+			if old.Index != "" {
338
+				b, err := b.Tx().CreateBucketIfNotExists([]byte(indexBucket))
339
+				if err != nil {
340
+					return errors.WithStack(err)
341
+				}
342
+				b.Delete([]byte(indexKey(old.Index, s.ID()))) // ignore error
343
+			}
344
+		}
336 345
 		if err := b.Put([]byte(key), nil); err != nil {
337 346
 			return err
338 347
 		}
... ...
@@ -341,18 +354,18 @@ func (s *StorageItem) SetValue(b *bolt.Bucket, key string, v *Value) error {
341 341
 	}
342 342
 	dt, err := json.Marshal(v)
343 343
 	if err != nil {
344
-		return err
344
+		return errors.WithStack(err)
345 345
 	}
346 346
 	if err := b.Put([]byte(key), dt); err != nil {
347
-		return err
347
+		return errors.WithStack(err)
348 348
 	}
349 349
 	if v.Index != "" {
350 350
 		b, err := b.Tx().CreateBucketIfNotExists([]byte(indexBucket))
351 351
 		if err != nil {
352
-			return err
352
+			return errors.WithStack(err)
353 353
 		}
354 354
 		if err := b.Put([]byte(indexKey(v.Index, s.ID())), []byte{}); err != nil {
355
-			return err
355
+			return errors.WithStack(err)
356 356
 		}
357 357
 	}
358 358
 	s.values[key] = v
... ...
@@ -367,14 +380,13 @@ type Value struct {
367 367
 func NewValue(v interface{}) (*Value, error) {
368 368
 	dt, err := json.Marshal(v)
369 369
 	if err != nil {
370
-		return nil, err
370
+		return nil, errors.WithStack(err)
371 371
 	}
372 372
 	return &Value{Value: json.RawMessage(dt)}, nil
373 373
 }
374 374
 
375 375
 func (v *Value) Unmarshal(target interface{}) error {
376
-	err := json.Unmarshal(v.Value, target)
377
-	return err
376
+	return errors.WithStack(json.Unmarshal(v.Value, target))
378 377
 }
379 378
 
380 379
 func indexKey(index, target string) string {
... ...
@@ -2,6 +2,7 @@ package cache
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"strings"
5 6
 	"sync"
6 7
 
7 8
 	"github.com/containerd/containerd/mount"
... ...
@@ -190,7 +191,7 @@ func (cr *cacheRecord) remove(ctx context.Context, removeSnapshot bool) error {
190 190
 	}
191 191
 	if removeSnapshot {
192 192
 		if err := cr.cm.Snapshotter.Remove(ctx, cr.ID()); err != nil {
193
-			return err
193
+			return errors.Wrapf(err, "failed to remove %s", cr.ID())
194 194
 		}
195 195
 	}
196 196
 	if err := cr.cm.md.Clear(cr.ID()); err != nil {
... ...
@@ -259,7 +260,7 @@ func (sr *immutableRef) release(ctx context.Context) error {
259 259
 	if len(sr.refs) == 0 {
260 260
 		if sr.viewMount != nil { // TODO: release viewMount earlier if possible
261 261
 			if err := sr.cm.Snapshotter.Remove(ctx, sr.view); err != nil {
262
-				return err
262
+				return errors.Wrapf(err, "failed to remove view %s", sr.view)
263 263
 			}
264 264
 			sr.view = ""
265 265
 			sr.viewMount = nil
... ...
@@ -429,6 +430,10 @@ func (m *readOnlyMounter) Mount() ([]mount.Mount, error) {
429 429
 		return nil, err
430 430
 	}
431 431
 	for i, m := range mounts {
432
+		if m.Type == "overlay" {
433
+			mounts[i].Options = readonlyOverlay(m.Options)
434
+			continue
435
+		}
432 436
 		opts := make([]string, 0, len(m.Options))
433 437
 		for _, opt := range m.Options {
434 438
 			if opt != "rw" {
... ...
@@ -440,3 +445,23 @@ func (m *readOnlyMounter) Mount() ([]mount.Mount, error) {
440 440
 	}
441 441
 	return mounts, nil
442 442
 }
443
+
444
+func readonlyOverlay(opt []string) []string {
445
+	out := make([]string, 0, len(opt))
446
+	upper := ""
447
+	for _, o := range opt {
448
+		if strings.HasPrefix(o, "upperdir=") {
449
+			upper = strings.TrimPrefix(o, "upperdir=")
450
+		} else if !strings.HasPrefix(o, "workdir=") {
451
+			out = append(out, o)
452
+		}
453
+	}
454
+	if upper != "" {
455
+		for i, o := range out {
456
+			if strings.HasPrefix(o, "lowerdir=") {
457
+				out[i] = "lowerdir=" + upper + ":" + strings.TrimPrefix(o, "lowerdir=")
458
+			}
459
+		}
460
+	}
461
+	return out
462
+}
... ...
@@ -100,7 +100,7 @@ func readBlob(ctx context.Context, provider content.Provider, desc ocispec.Descr
100 100
 			}
101 101
 		}
102 102
 	}
103
-	return dt, err
103
+	return dt, errors.WithStack(err)
104 104
 }
105 105
 
106 106
 func (ci *contentCacheImporter) importInlineCache(ctx context.Context, dt []byte, id string, w worker.Worker) (solver.CacheManager, error) {
... ...
@@ -120,7 +120,7 @@ func (ci *contentCacheImporter) importInlineCache(ctx context.Context, dt []byte
120 120
 				var m ocispec.Manifest
121 121
 
122 122
 				if err := json.Unmarshal(dt, &m); err != nil {
123
-					return err
123
+					return errors.WithStack(err)
124 124
 				}
125 125
 
126 126
 				if m.Config.Digest == "" || len(m.Layers) == 0 {
... ...
@@ -129,13 +129,13 @@ func (ci *contentCacheImporter) importInlineCache(ctx context.Context, dt []byte
129 129
 
130 130
 				p, err := content.ReadBlob(ctx, ci.provider, m.Config)
131 131
 				if err != nil {
132
-					return err
132
+					return errors.WithStack(err)
133 133
 				}
134 134
 
135 135
 				var img image
136 136
 
137 137
 				if err := json.Unmarshal(p, &img); err != nil {
138
-					return err
138
+					return errors.WithStack(err)
139 139
 				}
140 140
 
141 141
 				if len(img.Rootfs.DiffIDs) != len(m.Layers) {
... ...
@@ -149,7 +149,7 @@ func (ci *contentCacheImporter) importInlineCache(ctx context.Context, dt []byte
149 149
 
150 150
 				var config v1.CacheConfig
151 151
 				if err := json.Unmarshal(img.Cache, &config.Records); err != nil {
152
-					return err
152
+					return errors.WithStack(err)
153 153
 				}
154 154
 
155 155
 				createdDates, createdMsg, err := parseCreatedLayerInfo(img)
... ...
@@ -181,7 +181,7 @@ func (ci *contentCacheImporter) importInlineCache(ctx context.Context, dt []byte
181 181
 
182 182
 				dt, err = json.Marshal(config)
183 183
 				if err != nil {
184
-					return err
184
+					return errors.WithStack(err)
185 185
 				}
186 186
 
187 187
 				mu.Lock()
... ...
@@ -217,7 +217,7 @@ func (ci *contentCacheImporter) allDistributionManifests(ctx context.Context, dt
217 217
 	case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
218 218
 		var index ocispec.Index
219 219
 		if err := json.Unmarshal(dt, &index); err != nil {
220
-			return err
220
+			return errors.WithStack(err)
221 221
 		}
222 222
 
223 223
 		for _, d := range index.Manifests {
... ...
@@ -226,7 +226,7 @@ func (ci *contentCacheImporter) allDistributionManifests(ctx context.Context, dt
226 226
 			}
227 227
 			p, err := content.ReadBlob(ctx, ci.provider, d)
228 228
 			if err != nil {
229
-				return err
229
+				return errors.WithStack(err)
230 230
 			}
231 231
 			if err := ci.allDistributionManifests(ctx, p, m); err != nil {
232 232
 				return err
... ...
@@ -254,7 +254,7 @@ func (cs *cacheResultStorage) Load(ctx context.Context, res solver.CacheResult)
254 254
 
255 255
 	ref, err := cs.w.FromRemote(ctx, item.result)
256 256
 	if err != nil {
257
-		return nil, err
257
+		return nil, errors.Wrap(err, "failed to load result from remote")
258 258
 	}
259 259
 	return worker.NewWorkerRefResult(ref, cs.w), nil
260 260
 }
... ...
@@ -12,7 +12,7 @@ import (
12 12
 func Parse(configJSON []byte, provider DescriptorProvider, t solver.CacheExporterTarget) error {
13 13
 	var config CacheConfig
14 14
 	if err := json.Unmarshal(configJSON, &config); err != nil {
15
-		return err
15
+		return errors.WithStack(err)
16 16
 	}
17 17
 
18 18
 	return ParseConfig(config, provider, t)
... ...
@@ -67,8 +67,8 @@ func sortConfig(cc *CacheConfig) {
67 67
 		if ri.Digest != rj.Digest {
68 68
 			return ri.Digest < rj.Digest
69 69
 		}
70
-		if len(ri.Inputs) != len(ri.Inputs) {
71
-			return len(ri.Inputs) < len(ri.Inputs)
70
+		if len(ri.Inputs) != len(rj.Inputs) {
71
+			return len(ri.Inputs) < len(rj.Inputs)
72 72
 		}
73 73
 		for i, inputs := range ri.Inputs {
74 74
 			if len(ri.Inputs[i]) != len(rj.Inputs[i]) {
... ...
@@ -76,7 +76,7 @@ func sortConfig(cc *CacheConfig) {
76 76
 			}
77 77
 			for j := range inputs {
78 78
 				if ri.Inputs[i][j].Selector != rj.Inputs[i][j].Selector {
79
-					return ri.Inputs[i][j].Selector != rj.Inputs[i][j].Selector
79
+					return ri.Inputs[i][j].Selector < rj.Inputs[i][j].Selector
80 80
 				}
81 81
 				return cc.Records[ri.Inputs[i][j].LinkIndex].Digest < cc.Records[rj.Inputs[i][j].LinkIndex].Digest
82 82
 			}
... ...
@@ -61,23 +61,23 @@ func ReadFile(ctx context.Context, ref cache.ImmutableRef, req ReadRequest) ([]b
61 61
 	err := withMount(ctx, ref, func(root string) error {
62 62
 		fp, err := fs.RootPath(root, req.Filename)
63 63
 		if err != nil {
64
-			return err
64
+			return errors.WithStack(err)
65 65
 		}
66 66
 
67 67
 		if req.Range == nil {
68 68
 			dt, err = ioutil.ReadFile(fp)
69 69
 			if err != nil {
70
-				return err
70
+				return errors.WithStack(err)
71 71
 			}
72 72
 		} else {
73 73
 			f, err := os.Open(fp)
74 74
 			if err != nil {
75
-				return err
75
+				return errors.WithStack(err)
76 76
 			}
77 77
 			dt, err = ioutil.ReadAll(io.NewSectionReader(f, int64(req.Range.Offset), int64(req.Range.Length)))
78 78
 			f.Close()
79 79
 			if err != nil {
80
-				return err
80
+				return errors.WithStack(err)
81 81
 			}
82 82
 		}
83 83
 		return nil
... ...
@@ -101,7 +101,7 @@ func ReadDir(ctx context.Context, ref cache.ImmutableRef, req ReadDirRequest) ([
101 101
 	err := withMount(ctx, ref, func(root string) error {
102 102
 		fp, err := fs.RootPath(root, req.Path)
103 103
 		if err != nil {
104
-			return err
104
+			return errors.WithStack(err)
105 105
 		}
106 106
 		return fsutil.Walk(ctx, fp, &wo, func(path string, info os.FileInfo, err error) error {
107 107
 			if err != nil {
... ...
@@ -128,10 +128,10 @@ func StatFile(ctx context.Context, ref cache.ImmutableRef, path string) (*fstype
128 128
 	err := withMount(ctx, ref, func(root string) error {
129 129
 		fp, err := fs.RootPath(root, path)
130 130
 		if err != nil {
131
-			return err
131
+			return errors.WithStack(err)
132 132
 		}
133 133
 		if st, err = fsutil.Stat(fp); err != nil {
134
-			return err
134
+			return errors.WithStack(err)
135 135
 		}
136 136
 		return nil
137 137
 	})
... ...
@@ -427,11 +427,13 @@ func Security(s pb.SecurityMode) RunOption {
427 427
 }
428 428
 
429 429
 func Shlex(str string) RunOption {
430
-	return Shlexf(str)
430
+	return runOptionFunc(func(ei *ExecInfo) {
431
+		ei.State = shlexf(str, false)(ei.State)
432
+	})
431 433
 }
432 434
 func Shlexf(str string, v ...interface{}) RunOption {
433 435
 	return runOptionFunc(func(ei *ExecInfo) {
434
-		ei.State = shlexf(str, v...)(ei.State)
436
+		ei.State = shlexf(str, true, v...)(ei.State)
435 437
 	})
436 438
 }
437 439
 
... ...
@@ -442,7 +444,9 @@ func Args(a []string) RunOption {
442 442
 }
443 443
 
444 444
 func AddEnv(key, value string) RunOption {
445
-	return AddEnvf(key, value)
445
+	return runOptionFunc(func(ei *ExecInfo) {
446
+		ei.State = ei.State.AddEnv(key, value)
447
+	})
446 448
 }
447 449
 
448 450
 func AddEnvf(key, value string, v ...interface{}) RunOption {
... ...
@@ -458,7 +462,9 @@ func User(str string) RunOption {
458 458
 }
459 459
 
460 460
 func Dir(str string) RunOption {
461
-	return Dirf(str)
461
+	return runOptionFunc(func(ei *ExecInfo) {
462
+		ei.State = ei.State.Dir(str)
463
+	})
462 464
 }
463 465
 func Dirf(str string, v ...interface{}) RunOption {
464 466
 	return runOptionFunc(func(ei *ExecInfo) {
... ...
@@ -24,19 +24,24 @@ var (
24 24
 	keySecurity  = contextKeyT("llb.security")
25 25
 )
26 26
 
27
-func addEnvf(key, value string, v ...interface{}) StateOption {
27
+func addEnvf(key, value string, replace bool, v ...interface{}) StateOption {
28
+	if replace {
29
+		value = fmt.Sprintf(value, v...)
30
+	}
28 31
 	return func(s State) State {
29
-		return s.WithValue(keyEnv, getEnv(s).AddOrReplace(key, fmt.Sprintf(value, v...)))
32
+		return s.WithValue(keyEnv, getEnv(s).AddOrReplace(key, value))
30 33
 	}
31 34
 }
32 35
 
33 36
 func dir(str string) StateOption {
34
-	return dirf(str)
37
+	return dirf(str, false)
35 38
 }
36 39
 
37
-func dirf(str string, v ...interface{}) StateOption {
40
+func dirf(value string, replace bool, v ...interface{}) StateOption {
41
+	if replace {
42
+		value = fmt.Sprintf(value, v...)
43
+	}
38 44
 	return func(s State) State {
39
-		value := fmt.Sprintf(str, v...)
40 45
 		if !path.IsAbs(value) {
41 46
 			prev := getDir(s)
42 47
 			if prev == "" {
... ...
@@ -100,9 +105,12 @@ func args(args ...string) StateOption {
100 100
 	}
101 101
 }
102 102
 
103
-func shlexf(str string, v ...interface{}) StateOption {
103
+func shlexf(str string, replace bool, v ...interface{}) StateOption {
104
+	if replace {
105
+		str = fmt.Sprintf(str, v...)
106
+	}
104 107
 	return func(s State) State {
105
-		arg, err := shlex.Split(fmt.Sprintf(str, v...))
108
+		arg, err := shlex.Split(str)
106 109
 		if err != nil {
107 110
 			// TODO: handle error
108 111
 		}
... ...
@@ -240,18 +240,18 @@ func (s State) File(a *FileAction, opts ...ConstraintsOpt) State {
240 240
 }
241 241
 
242 242
 func (s State) AddEnv(key, value string) State {
243
-	return s.AddEnvf(key, value)
243
+	return addEnvf(key, value, false)(s)
244 244
 }
245 245
 
246 246
 func (s State) AddEnvf(key, value string, v ...interface{}) State {
247
-	return addEnvf(key, value, v...)(s)
247
+	return addEnvf(key, value, true, v...)(s)
248 248
 }
249 249
 
250 250
 func (s State) Dir(str string) State {
251
-	return s.Dirf(str)
251
+	return dirf(str, false)(s)
252 252
 }
253 253
 func (s State) Dirf(str string, v ...interface{}) State {
254
-	return dirf(str, v...)(s)
254
+	return dirf(str, true, v...)(s)
255 255
 }
256 256
 
257 257
 func (s State) GetEnv(key string) (string, bool) {
... ...
@@ -46,8 +46,8 @@ type SolveOpt struct {
46 46
 type ExportEntry struct {
47 47
 	Type      string
48 48
 	Attrs     map[string]string
49
-	Output    io.WriteCloser // for ExporterOCI and ExporterDocker
50
-	OutputDir string         // for ExporterLocal
49
+	Output    func(map[string]string) (io.WriteCloser, error) // for ExporterOCI and ExporterDocker
50
+	OutputDir string                                          // for ExporterLocal
51 51
 }
52 52
 
53 53
 type CacheOptionsEntry struct {
... ...
@@ -38,13 +38,13 @@ type Opt struct {
38 38
 }
39 39
 
40 40
 type Controller struct { // TODO: ControlService
41
+	buildCount       int64
41 42
 	opt              Opt
42 43
 	solver           *llbsolver.Solver
43 44
 	cache            solver.CacheManager
44 45
 	gatewayForwarder *controlgateway.GatewayForwarder
45 46
 	throttledGC      func()
46 47
 	gcmu             sync.Mutex
47
-	buildCount       int64
48 48
 }
49 49
 
50 50
 func NewController(opt Opt) (*Controller, error) {
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"os"
9 9
 	"path/filepath"
10 10
 
11
+	"github.com/docker/docker/pkg/idtools"
11 12
 	"github.com/moby/buildkit/executor"
12 13
 	"github.com/moby/buildkit/identity"
13 14
 )
... ...
@@ -17,10 +18,10 @@ const hostsContent = `
17 17
 ::1	localhost ip6-localhost ip6-loopback
18 18
 `
19 19
 
20
-func GetHostsFile(ctx context.Context, stateDir string, extraHosts []executor.HostIP) (string, func(), error) {
20
+func GetHostsFile(ctx context.Context, stateDir string, extraHosts []executor.HostIP, idmap *idtools.IdentityMapping) (string, func(), error) {
21 21
 	if len(extraHosts) == 0 {
22 22
 		_, err := g.Do(ctx, stateDir, func(ctx context.Context) (interface{}, error) {
23
-			_, _, err := makeHostsFile(stateDir, nil)
23
+			_, _, err := makeHostsFile(stateDir, nil, idmap)
24 24
 			return nil, err
25 25
 		})
26 26
 		if err != nil {
... ...
@@ -28,10 +29,10 @@ func GetHostsFile(ctx context.Context, stateDir string, extraHosts []executor.Ho
28 28
 		}
29 29
 		return filepath.Join(stateDir, "hosts"), func() {}, nil
30 30
 	}
31
-	return makeHostsFile(stateDir, extraHosts)
31
+	return makeHostsFile(stateDir, extraHosts, idmap)
32 32
 }
33 33
 
34
-func makeHostsFile(stateDir string, extraHosts []executor.HostIP) (string, func(), error) {
34
+func makeHostsFile(stateDir string, extraHosts []executor.HostIP, idmap *idtools.IdentityMapping) (string, func(), error) {
35 35
 	p := filepath.Join(stateDir, "hosts")
36 36
 	if len(extraHosts) != 0 {
37 37
 		p += "." + identity.NewID()
... ...
@@ -56,11 +57,19 @@ func makeHostsFile(stateDir string, extraHosts []executor.HostIP) (string, func(
56 56
 		}
57 57
 	}
58 58
 
59
-	if err := ioutil.WriteFile(p+".tmp", b.Bytes(), 0644); err != nil {
59
+	tmpPath := p + ".tmp"
60
+	if err := ioutil.WriteFile(tmpPath, b.Bytes(), 0644); err != nil {
60 61
 		return "", nil, err
61 62
 	}
62 63
 
63
-	if err := os.Rename(p+".tmp", p); err != nil {
64
+	if idmap != nil {
65
+		root := idmap.RootPair()
66
+		if err := os.Chown(tmpPath, root.UID, root.GID); err != nil {
67
+			return "", nil, err
68
+		}
69
+	}
70
+
71
+	if err := os.Rename(tmpPath, p); err != nil {
64 72
 		return "", nil, err
65 73
 	}
66 74
 	return p, func() {
... ...
@@ -6,7 +6,9 @@ import (
6 6
 	"os"
7 7
 	"path/filepath"
8 8
 
9
+	"github.com/docker/docker/pkg/idtools"
9 10
 	"github.com/docker/libnetwork/resolvconf"
11
+	"github.com/docker/libnetwork/types"
10 12
 	"github.com/moby/buildkit/util/flightcontrol"
11 13
 )
12 14
 
... ...
@@ -14,7 +16,13 @@ var g flightcontrol.Group
14 14
 var notFirstRun bool
15 15
 var lastNotEmpty bool
16 16
 
17
-func GetResolvConf(ctx context.Context, stateDir string) (string, error) {
17
+type DNSConfig struct {
18
+	Nameservers   []string
19
+	Options       []string
20
+	SearchDomains []string
21
+}
22
+
23
+func GetResolvConf(ctx context.Context, stateDir string, idmap *idtools.IdentityMapping, dns *DNSConfig) (string, error) {
18 24
 	p := filepath.Join(stateDir, "resolv.conf")
19 25
 	_, err := g.Do(ctx, stateDir, func(ctx context.Context) (interface{}, error) {
20 26
 		generate := !notFirstRun
... ...
@@ -60,16 +68,49 @@ func GetResolvConf(ctx context.Context, stateDir string) (string, error) {
60 60
 			dt = f.Content
61 61
 		}
62 62
 
63
-		f, err = resolvconf.FilterResolvDNS(dt, true)
64
-		if err != nil {
65
-			return "", err
63
+		if dns != nil {
64
+			var (
65
+				dnsNameservers   = resolvconf.GetNameservers(dt, types.IP)
66
+				dnsSearchDomains = resolvconf.GetSearchDomains(dt)
67
+				dnsOptions       = resolvconf.GetOptions(dt)
68
+			)
69
+			if len(dns.Nameservers) > 0 {
70
+				dnsNameservers = dns.Nameservers
71
+			}
72
+			if len(dns.SearchDomains) > 0 {
73
+				dnsSearchDomains = dns.SearchDomains
74
+			}
75
+			if len(dns.Options) > 0 {
76
+				dnsOptions = dns.Options
77
+			}
78
+
79
+			f, err = resolvconf.Build(p+".tmp", dnsNameservers, dnsSearchDomains, dnsOptions)
80
+			if err != nil {
81
+				return "", err
82
+			}
83
+		} else {
84
+			// Logic seems odd here: why are we filtering localhost IPs
85
+			// only if neither of the DNS configs were specified?
86
+			// Logic comes from https://github.com/docker/libnetwork/blob/164a77ee6d24fb2b1d61f8ad3403a51d8453899e/sandbox_dns_unix.go#L230-L269
87
+			f, err = resolvconf.FilterResolvDNS(f.Content, true)
88
+			if err != nil {
89
+				return "", err
90
+			}
66 91
 		}
67 92
 
68
-		if err := ioutil.WriteFile(p+".tmp", f.Content, 0644); err != nil {
93
+		tmpPath := p + ".tmp"
94
+		if err := ioutil.WriteFile(tmpPath, f.Content, 0644); err != nil {
69 95
 			return "", err
70 96
 		}
71 97
 
72
-		if err := os.Rename(p+".tmp", p); err != nil {
98
+		if idmap != nil {
99
+			root := idmap.RootPair()
100
+			if err := os.Chown(tmpPath, root.UID, root.GID); err != nil {
101
+				return "", err
102
+			}
103
+		}
104
+
105
+		if err := os.Rename(tmpPath, p); err != nil {
73 106
 			return "", err
74 107
 		}
75 108
 		return "", nil
76 109
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+package oci
1
+
2
+// ProcMode configures PID namespaces
3
+type ProcessMode int
4
+
5
+const (
6
+	// ProcessSandbox unshares pidns and mount procfs.
7
+	ProcessSandbox ProcessMode = iota
8
+	// NoProcessSandbox uses host pidns and bind-mount procfs.
9
+	// Note that NoProcessSandbox allows build containers to kill (and potentially ptrace) an arbitrary process in the BuildKit host namespace.
10
+	// NoProcessSandbox should be enabled only when the BuildKit is running in a container as an unprivileged user.
11
+	NoProcessSandbox
12
+)
... ...
@@ -27,18 +27,6 @@ import (
27 27
 
28 28
 // Ideally we don't have to import whole containerd just for the default spec
29 29
 
30
-// ProcMode configures PID namespaces
31
-type ProcessMode int
32
-
33
-const (
34
-	// ProcessSandbox unshares pidns and mount procfs.
35
-	ProcessSandbox ProcessMode = iota
36
-	// NoProcessSandbox uses host pidns and bind-mount procfs.
37
-	// Note that NoProcessSandbox allows build containers to kill (and potentially ptrace) an arbitrary process in the BuildKit host namespace.
38
-	// NoProcessSandbox should be enabled only when the BuildKit is running in a container as an unprivileged user.
39
-	NoProcessSandbox
40
-)
41
-
42 30
 // GenerateSpec generates spec using containerd functionality.
43 31
 // opts are ignored for s.Process, s.Hostname, and s.Mounts .
44 32
 func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mount, id, resolvConf, hostsFile string, namespace network.Namespace, processMode ProcessMode, idmap *idtools.IdentityMapping, opts ...oci.SpecOpts) (*specs.Spec, func(), error) {
... ...
@@ -113,11 +101,11 @@ func GenerateSpec(ctx context.Context, meta executor.Meta, mounts []executor.Mou
113 113
 	}
114 114
 
115 115
 	if meta.SecurityMode == pb.SecurityMode_INSECURE {
116
-		//make sysfs rw mount for insecure mode.
117
-		for _, m := range s.Mounts {
118
-			if m.Type == "sysfs" {
119
-				m.Options = []string{"nosuid", "noexec", "nodev", "rw"}
120
-			}
116
+		if err = oci.WithWriteableCgroupfs(ctx, nil, c, s); err != nil {
117
+			return nil, nil, err
118
+		}
119
+		if err = oci.WithWriteableSysfs(ctx, nil, c, s); err != nil {
120
+			return nil, nil, err
121 121
 		}
122 122
 	}
123 123
 
... ...
@@ -20,19 +20,11 @@ func GetUser(ctx context.Context, root, username string) (uint32, uint32, []uint
20 20
 		return uid, gid, nil, nil
21 21
 	}
22 22
 
23
-	passwdPath, err := user.GetPasswdPath()
24
-	if err != nil {
25
-		return 0, 0, nil, err
26
-	}
27
-	groupPath, err := user.GetGroupPath()
28
-	if err != nil {
29
-		return 0, 0, nil, err
30
-	}
31
-	passwdFile, err := openUserFile(root, passwdPath)
23
+	passwdFile, err := openUserFile(root, "/etc/passwd")
32 24
 	if err == nil {
33 25
 		defer passwdFile.Close()
34 26
 	}
35
-	groupFile, err := openUserFile(root, groupPath)
27
+	groupFile, err := openUserFile(root, "/etc/group")
36 28
 	if err == nil {
37 29
 		defer groupFile.Close()
38 30
 	}
... ...
@@ -43,6 +43,7 @@ type Opt struct {
43 43
 	IdentityMapping *idtools.IdentityMapping
44 44
 	// runc run --no-pivot (unrecommended)
45 45
 	NoPivot bool
46
+	DNS     *oci.DNSConfig
46 47
 }
47 48
 
48 49
 var defaultCommandCandidates = []string{"buildkit-runc", "runc"}
... ...
@@ -57,6 +58,7 @@ type runcExecutor struct {
57 57
 	processMode      oci.ProcessMode
58 58
 	idmap            *idtools.IdentityMapping
59 59
 	noPivot          bool
60
+	dns              *oci.DNSConfig
60 61
 }
61 62
 
62 63
 func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Executor, error) {
... ...
@@ -79,7 +81,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex
79 79
 
80 80
 	root := opt.Root
81 81
 
82
-	if err := os.MkdirAll(root, 0700); err != nil {
82
+	if err := os.MkdirAll(root, 0711); err != nil {
83 83
 		return nil, errors.Wrapf(err, "failed to create %s", root)
84 84
 	}
85 85
 
... ...
@@ -115,6 +117,7 @@ func New(opt Opt, networkProviders map[pb.NetMode]network.Provider) (executor.Ex
115 115
 		processMode:      opt.ProcessMode,
116 116
 		idmap:            opt.IdentityMapping,
117 117
 		noPivot:          opt.NoPivot,
118
+		dns:              opt.DNS,
118 119
 	}
119 120
 	return w, nil
120 121
 }
... ...
@@ -134,12 +137,12 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
134 134
 		logrus.Info("enabling HostNetworking")
135 135
 	}
136 136
 
137
-	resolvConf, err := oci.GetResolvConf(ctx, w.root)
137
+	resolvConf, err := oci.GetResolvConf(ctx, w.root, w.idmap, w.dns)
138 138
 	if err != nil {
139 139
 		return err
140 140
 	}
141 141
 
142
-	hostsFile, clean, err := oci.GetHostsFile(ctx, w.root, meta.ExtraHosts)
142
+	hostsFile, clean, err := oci.GetHostsFile(ctx, w.root, meta.ExtraHosts, w.idmap)
143 143
 	if err != nil {
144 144
 		return err
145 145
 	}
... ...
@@ -161,7 +164,7 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
161 161
 	id := identity.NewID()
162 162
 	bundle := filepath.Join(w.root, id)
163 163
 
164
-	if err := os.Mkdir(bundle, 0700); err != nil {
164
+	if err := os.Mkdir(bundle, 0711); err != nil {
165 165
 		return err
166 166
 	}
167 167
 	defer os.RemoveAll(bundle)
... ...
@@ -233,8 +236,10 @@ func (w *runcExecutor) Exec(ctx context.Context, meta executor.Meta, root cache.
233 233
 	if err != nil {
234 234
 		return errors.Wrapf(err, "working dir %s points to invalid target", newp)
235 235
 	}
236
-	if err := idtools.MkdirAllAndChown(newp, 0755, identity); err != nil {
237
-		return errors.Wrapf(err, "failed to create working directory %s", newp)
236
+	if _, err := os.Stat(newp); err != nil {
237
+		if err := idtools.MkdirAllAndChown(newp, 0755, identity); err != nil {
238
+			return errors.Wrapf(err, "failed to create working directory %s", newp)
239
+		}
238 240
 	}
239 241
 
240 242
 	if err := setOOMScoreAdj(spec); err != nil {
... ...
@@ -147,7 +147,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp exporter.Source)
147 147
 		fs = d.FS
148 148
 	}
149 149
 
150
-	w, err := filesync.CopyFileWriter(ctx, e.caller)
150
+	w, err := filesync.CopyFileWriter(ctx, nil, e.caller)
151 151
 	if err != nil {
152 152
 		return nil, err
153 153
 	}
... ...
@@ -34,6 +34,7 @@ const (
34 34
 	keyFilename                = "filename"
35 35
 	keyCacheFrom               = "cache-from"    // for registry only. deprecated in favor of keyCacheImports
36 36
 	keyCacheImports            = "cache-imports" // JSON representation of []CacheOptionsEntry
37
+	keyCacheNS                 = "build-arg:BUILDKIT_CACHE_MOUNT_NS"
37 38
 	defaultDockerfileName      = "Dockerfile"
38 39
 	dockerignoreFilename       = ".dockerignore"
39 40
 	buildArgPrefix             = "build-arg:"
... ...
@@ -50,8 +51,8 @@ const (
50 50
 	keyContextSubDir           = "contextsubdir"
51 51
 )
52 52
 
53
-var httpPrefix = regexp.MustCompile("^https?://")
54
-var gitUrlPathWithFragmentSuffix = regexp.MustCompile("\\.git(?:#.+)?$")
53
+var httpPrefix = regexp.MustCompile(`^https?://`)
54
+var gitUrlPathWithFragmentSuffix = regexp.MustCompile(`\.git(?:#.+)?$`)
55 55
 
56 56
 func Build(ctx context.Context, c client.Client) (*client.Result, error) {
57 57
 	opts := c.BuildOpts().Opts
... ...
@@ -322,6 +323,7 @@ func Build(ctx context.Context, c client.Client) (*client.Result, error) {
322 322
 					MetaResolver:      c,
323 323
 					BuildArgs:         filter(opts, buildArgPrefix),
324 324
 					Labels:            filter(opts, labelPrefix),
325
+					CacheIDNamespace:  opts[keyCacheNS],
325 326
 					SessionID:         c.BuildOpts().SessionID,
326 327
 					BuildContext:      buildContext,
327 328
 					Excludes:          excludes,
... ...
@@ -461,7 +461,7 @@ type dispatchOpt struct {
461 461
 func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
462 462
 	if ex, ok := cmd.Command.(instructions.SupportsSingleWordExpansion); ok {
463 463
 		err := ex.Expand(func(word string) (string, error) {
464
-			return opt.shlex.ProcessWordWithMap(word, toEnvMap(d.buildArgs, d.image.Config.Env))
464
+			return opt.shlex.ProcessWord(word, d.state.Env())
465 465
 		})
466 466
 		if err != nil {
467 467
 			return err
... ...
@@ -626,14 +626,7 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
626 626
 		args = withShell(d.image, args)
627 627
 	}
628 628
 	env := d.state.Env()
629
-	opt := []llb.RunOption{llb.Args(args)}
630
-	for _, arg := range d.buildArgs {
631
-		if arg.Value != nil {
632
-			env = append(env, fmt.Sprintf("%s=%s", arg.Key, arg.ValueString()))
633
-			opt = append(opt, llb.AddEnv(arg.Key, arg.ValueString()))
634
-		}
635
-	}
636
-	opt = append(opt, dfCmd(c))
629
+	opt := []llb.RunOption{llb.Args(args), dfCmd(c)}
637 630
 	if d.ignoreCache {
638 631
 		opt = append(opt, llb.IgnoreCache)
639 632
 	}
... ...
@@ -647,6 +640,11 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
647 647
 	}
648 648
 	opt = append(opt, runMounts...)
649 649
 
650
+	err = dispatchRunSecurity(d, c)
651
+	if err != nil {
652
+		return err
653
+	}
654
+
650 655
 	shlex := *dopt.shlex
651 656
 	shlex.RawQuotes = true
652 657
 	shlex.SkipUnsetEnv = true
... ...
@@ -656,7 +654,7 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
656 656
 		opt = append(opt, llb.AddExtraHost(h.Host, h.IP))
657 657
 	}
658 658
 	d.state = d.state.Run(opt...).Root()
659
-	return commitToHistory(&d.image, "RUN "+runCommandString(args, d.buildArgs), true, &d.state)
659
+	return commitToHistory(&d.image, "RUN "+runCommandString(args, d.buildArgs, shell.BuildEnvs(env)), true, &d.state)
660 660
 }
661 661
 
662 662
 func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bool, opt *dispatchOpt) error {
... ...
@@ -927,7 +925,7 @@ func dispatchHealthcheck(d *dispatchState, c *instructions.HealthCheckCommand) e
927 927
 func dispatchExpose(d *dispatchState, c *instructions.ExposeCommand, shlex *shell.Lex) error {
928 928
 	ports := []string{}
929 929
 	for _, p := range c.Ports {
930
-		ps, err := shlex.ProcessWordsWithMap(p, toEnvMap(d.buildArgs, d.image.Config.Env))
930
+		ps, err := shlex.ProcessWords(p, d.state.Env())
931 931
 		if err != nil {
932 932
 			return err
933 933
 		}
... ...
@@ -1000,6 +998,10 @@ func dispatchArg(d *dispatchState, c *instructions.ArgCommand, metaArgs []instru
1000 1000
 		}
1001 1001
 	}
1002 1002
 
1003
+	if buildArg.Value != nil {
1004
+		d.state = d.state.AddEnv(buildArg.Key, *buildArg.Value)
1005
+	}
1006
+
1003 1007
 	d.buildArgs = append(d.buildArgs, buildArg)
1004 1008
 	return commitToHistory(&d.image, commitStr, false, nil)
1005 1009
 }
... ...
@@ -1065,21 +1067,6 @@ func setKVValue(kvpo instructions.KeyValuePairOptional, values map[string]string
1065 1065
 	return kvpo
1066 1066
 }
1067 1067
 
1068
-func toEnvMap(args []instructions.KeyValuePairOptional, env []string) map[string]string {
1069
-	m := shell.BuildEnvs(env)
1070
-
1071
-	for _, arg := range args {
1072
-		// If key already exists, keep previous value.
1073
-		if _, ok := m[arg.Key]; ok {
1074
-			continue
1075
-		}
1076
-		if arg.Value != nil {
1077
-			m[arg.Key] = arg.ValueString()
1078
-		}
1079
-	}
1080
-	return m
1081
-}
1082
-
1083 1068
 func dfCmd(cmd interface{}) llb.ConstraintsOpt {
1084 1069
 	// TODO: add fmt.Stringer to instructions.Command to remove interface{}
1085 1070
 	var cmdStr string
... ...
@@ -1094,10 +1081,14 @@ func dfCmd(cmd interface{}) llb.ConstraintsOpt {
1094 1094
 	})
1095 1095
 }
1096 1096
 
1097
-func runCommandString(args []string, buildArgs []instructions.KeyValuePairOptional) string {
1097
+func runCommandString(args []string, buildArgs []instructions.KeyValuePairOptional, envMap map[string]string) string {
1098 1098
 	var tmpBuildEnv []string
1099 1099
 	for _, arg := range buildArgs {
1100
-		tmpBuildEnv = append(tmpBuildEnv, arg.Key+"="+arg.ValueString())
1100
+		v, ok := envMap[arg.Key]
1101
+		if !ok {
1102
+			v = arg.ValueString()
1103
+		}
1104
+		tmpBuildEnv = append(tmpBuildEnv, arg.Key+"="+v)
1101 1105
 	}
1102 1106
 	if len(tmpBuildEnv) > 0 {
1103 1107
 		tmpBuildEnv = append([]string{fmt.Sprintf("|%d", len(tmpBuildEnv))}, tmpBuildEnv...)
1104 1108
new file mode 100644
... ...
@@ -0,0 +1,11 @@
0
+// +build !dfrunsecurity
1
+
2
+package dockerfile2llb
3
+
4
+import (
5
+	"github.com/moby/buildkit/frontend/dockerfile/instructions"
6
+)
7
+
8
+func dispatchRunSecurity(d *dispatchState, c *instructions.RunCommand) error {
9
+	return nil
10
+}
... ...
@@ -124,6 +124,9 @@ func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*
124 124
 			if mount.CacheSharing == instructions.MountSharingLocked {
125 125
 				sharing = llb.CacheMountLocked
126 126
 			}
127
+			if mount.CacheID == "" {
128
+				mount.CacheID = path.Clean(mount.Target)
129
+			}
127 130
 			mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID, sharing))
128 131
 		}
129 132
 		target := mount.Target
... ...
@@ -144,7 +147,9 @@ func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*
144 144
 
145 145
 		out = append(out, llb.AddMount(target, st, mountOpts...))
146 146
 
147
-		d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{}
147
+		if mount.From == "" {
148
+			d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{}
149
+		}
148 150
 	}
149 151
 	return out, nil
150 152
 }
151 153
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+// +build dfrunsecurity
1
+
2
+package dockerfile2llb
3
+
4
+import (
5
+	"github.com/pkg/errors"
6
+
7
+	"github.com/moby/buildkit/frontend/dockerfile/instructions"
8
+	"github.com/moby/buildkit/solver/pb"
9
+)
10
+
11
+func dispatchRunSecurity(d *dispatchState, c *instructions.RunCommand) error {
12
+	security := instructions.GetSecurity(c)
13
+
14
+	for _, sec := range security {
15
+		switch sec {
16
+		case instructions.SecurityInsecure:
17
+			d.state = d.state.Security(pb.SecurityMode_INSECURE)
18
+		case instructions.SecuritySandbox:
19
+			d.state = d.state.Security(pb.SecurityMode_SANDBOX)
20
+		default:
21
+			return errors.Errorf("unsupported security mode %q", sec)
22
+		}
23
+	}
24
+
25
+	return nil
26
+}
... ...
@@ -142,6 +142,8 @@ func parseMount(value string) (*Mount, error) {
142 142
 				if m.Type == "secret" || m.Type == "ssh" {
143 143
 					m.Required = true
144 144
 					continue
145
+				} else {
146
+					return nil, errors.Errorf("unexpected key '%s' for mount type '%s'", key, m.Type)
145 147
 				}
146 148
 			}
147 149
 		}
... ...
@@ -176,6 +178,16 @@ func parseMount(value string) (*Mount, error) {
176 176
 			}
177 177
 			m.ReadOnly = !rw
178 178
 			roAuto = false
179
+		case "required":
180
+			if m.Type == "secret" || m.Type == "ssh" {
181
+				v, err := strconv.ParseBool(value)
182
+				if err != nil {
183
+					return nil, errors.Errorf("invalid value for %s: %s", key, value)
184
+				}
185
+				m.Required = v
186
+			} else {
187
+				return nil, errors.Errorf("unexpected key '%s' for mount type '%s'", key, m.Type)
188
+			}
179 189
 		case "id":
180 190
 			m.CacheID = value
181 191
 		case "sharing":
182 192
new file mode 100644
... ...
@@ -0,0 +1,83 @@
0
+// +build dfrunsecurity
1
+
2
+package instructions
3
+
4
+import (
5
+	"encoding/csv"
6
+	"strings"
7
+
8
+	"github.com/pkg/errors"
9
+)
10
+
11
+const (
12
+	SecurityInsecure = "insecure"
13
+	SecuritySandbox  = "sandbox"
14
+)
15
+
16
+var allowedSecurity = map[string]struct{}{
17
+	SecurityInsecure: {},
18
+	SecuritySandbox:  {},
19
+}
20
+
21
+func isValidSecurity(value string) bool {
22
+	_, ok := allowedSecurity[value]
23
+	return ok
24
+}
25
+
26
+type securityKeyT string
27
+
28
+var securityKey = securityKeyT("dockerfile/run/security")
29
+
30
+func init() {
31
+	parseRunPreHooks = append(parseRunPreHooks, runSecurityPreHook)
32
+	parseRunPostHooks = append(parseRunPostHooks, runSecurityPostHook)
33
+}
34
+
35
+func runSecurityPreHook(cmd *RunCommand, req parseRequest) error {
36
+	st := &securityState{}
37
+	st.flag = req.flags.AddStrings("security")
38
+	cmd.setExternalValue(securityKey, st)
39
+	return nil
40
+}
41
+
42
+func runSecurityPostHook(cmd *RunCommand, req parseRequest) error {
43
+	st := getSecurityState(cmd)
44
+	if st == nil {
45
+		return errors.Errorf("no security state")
46
+	}
47
+
48
+	for _, value := range st.flag.StringValues {
49
+		csvReader := csv.NewReader(strings.NewReader(value))
50
+		fields, err := csvReader.Read()
51
+		if err != nil {
52
+			return errors.Wrap(err, "failed to parse csv security")
53
+		}
54
+
55
+		for _, field := range fields {
56
+			if !isValidSecurity(field) {
57
+				return errors.Errorf("security %q is not valid", field)
58
+			}
59
+
60
+			st.security = append(st.security, field)
61
+		}
62
+	}
63
+
64
+	return nil
65
+}
66
+
67
+func getSecurityState(cmd *RunCommand) *securityState {
68
+	v := cmd.getExternalValue(securityKey)
69
+	if v == nil {
70
+		return nil
71
+	}
72
+	return v.(*securityState)
73
+}
74
+
75
+func GetSecurity(cmd *RunCommand) []string {
76
+	return getSecurityState(cmd).security
77
+}
78
+
79
+type securityState struct {
80
+	flag     *Flag
81
+	security []string
82
+}
... ...
@@ -417,10 +417,7 @@ func BuildEnvs(env []string) map[string]string {
417 417
 			k := e[:i]
418 418
 			v := e[i+1:]
419 419
 
420
-			// If key already exists, keep previous value.
421
-			if _, ok := envs[k]; ok {
422
-				continue
423
-			}
420
+			// overwrite value if key already exists
424 421
 			envs[k] = v
425 422
 		}
426 423
 	}
... ...
@@ -128,7 +128,7 @@ func (c *grpcClient) Run(ctx context.Context, f client.BuildFunc) (retError erro
128 128
 				}
129 129
 			}
130 130
 			if retError != nil {
131
-				st, _ := status.FromError(retError)
131
+				st, _ := status.FromError(errors.Cause(retError))
132 132
 				stp := st.Proto()
133 133
 				req.Error = &rpc.Status{
134 134
 					Code:    stp.Code,
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"context"
5 5
 
6 6
 	"github.com/moby/buildkit/session"
7
+	"github.com/pkg/errors"
7 8
 	"google.golang.org/grpc/codes"
8 9
 	"google.golang.org/grpc/status"
9 10
 )
... ...
@@ -16,10 +17,10 @@ func CredentialsFunc(ctx context.Context, c session.Caller) func(string) (string
16 16
 			Host: host,
17 17
 		})
18 18
 		if err != nil {
19
-			if st, ok := status.FromError(err); ok && st.Code() == codes.Unimplemented {
19
+			if st, ok := status.FromError(errors.Cause(err)); ok && st.Code() == codes.Unimplemented {
20 20
 				return "", "", nil
21 21
 			}
22
-			return "", "", err
22
+			return "", "", errors.WithStack(err)
23 23
 		}
24 24
 		return resp.Username, resp.Secret, nil
25 25
 	}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/moby/buildkit/session"
10 10
 	digest "github.com/opencontainers/go-digest"
11 11
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
12
+	"github.com/pkg/errors"
12 13
 	"google.golang.org/grpc/metadata"
13 14
 )
14 15
 
... ...
@@ -31,47 +32,53 @@ func (cs *callerContentStore) choose(ctx context.Context) context.Context {
31 31
 
32 32
 func (cs *callerContentStore) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) {
33 33
 	ctx = cs.choose(ctx)
34
-	return cs.store.Info(ctx, dgst)
34
+	info, err := cs.store.Info(ctx, dgst)
35
+	return info, errors.WithStack(err)
35 36
 }
36 37
 
37 38
 func (cs *callerContentStore) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) {
38 39
 	ctx = cs.choose(ctx)
39
-	return cs.store.Update(ctx, info, fieldpaths...)
40
+	info, err := cs.store.Update(ctx, info, fieldpaths...)
41
+	return info, errors.WithStack(err)
40 42
 }
41 43
 
42 44
 func (cs *callerContentStore) Walk(ctx context.Context, fn content.WalkFunc, fs ...string) error {
43 45
 	ctx = cs.choose(ctx)
44
-	return cs.store.Walk(ctx, fn, fs...)
46
+	return errors.WithStack(cs.store.Walk(ctx, fn, fs...))
45 47
 }
46 48
 
47 49
 func (cs *callerContentStore) Delete(ctx context.Context, dgst digest.Digest) error {
48 50
 	ctx = cs.choose(ctx)
49
-	return cs.store.Delete(ctx, dgst)
51
+	return errors.WithStack(cs.store.Delete(ctx, dgst))
50 52
 }
51 53
 
52 54
 func (cs *callerContentStore) ListStatuses(ctx context.Context, fs ...string) ([]content.Status, error) {
53 55
 	ctx = cs.choose(ctx)
54
-	return cs.store.ListStatuses(ctx, fs...)
56
+	resp, err := cs.store.ListStatuses(ctx, fs...)
57
+	return resp, errors.WithStack(err)
55 58
 }
56 59
 
57 60
 func (cs *callerContentStore) Status(ctx context.Context, ref string) (content.Status, error) {
58 61
 	ctx = cs.choose(ctx)
59
-	return cs.store.Status(ctx, ref)
62
+	st, err := cs.store.Status(ctx, ref)
63
+	return st, errors.WithStack(err)
60 64
 }
61 65
 
62 66
 func (cs *callerContentStore) Abort(ctx context.Context, ref string) error {
63 67
 	ctx = cs.choose(ctx)
64
-	return cs.store.Abort(ctx, ref)
68
+	return errors.WithStack(cs.store.Abort(ctx, ref))
65 69
 }
66 70
 
67 71
 func (cs *callerContentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
68 72
 	ctx = cs.choose(ctx)
69
-	return cs.store.Writer(ctx, opts...)
73
+	w, err := cs.store.Writer(ctx, opts...)
74
+	return w, errors.WithStack(err)
70 75
 }
71 76
 
72 77
 func (cs *callerContentStore) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
73 78
 	ctx = cs.choose(ctx)
74
-	return cs.store.ReaderAt(ctx, desc)
79
+	ra, err := cs.store.ReaderAt(ctx, desc)
80
+	return ra, errors.WithStack(err)
75 81
 }
76 82
 
77 83
 // NewCallerStore creates content.Store from session.Caller with specified storeID
... ...
@@ -14,7 +14,7 @@ import (
14 14
 )
15 15
 
16 16
 func sendDiffCopy(stream grpc.Stream, fs fsutil.FS, progress progressCb) error {
17
-	return fsutil.Send(stream.Context(), stream, fs, progress)
17
+	return errors.WithStack(fsutil.Send(stream.Context(), stream, fs, progress))
18 18
 }
19 19
 
20 20
 func newStreamWriter(stream grpc.ClientStream) io.WriteCloser {
... ...
@@ -29,7 +29,7 @@ type bufferedWriteCloser struct {
29 29
 
30 30
 func (bwc *bufferedWriteCloser) Close() error {
31 31
 	if err := bwc.Writer.Flush(); err != nil {
32
-		return err
32
+		return errors.WithStack(err)
33 33
 	}
34 34
 	return bwc.Closer.Close()
35 35
 }
... ...
@@ -40,19 +40,25 @@ type streamWriterCloser struct {
40 40
 
41 41
 func (wc *streamWriterCloser) Write(dt []byte) (int, error) {
42 42
 	if err := wc.ClientStream.SendMsg(&BytesMessage{Data: dt}); err != nil {
43
-		return 0, err
43
+		// SendMsg return EOF on remote errors
44
+		if errors.Cause(err) == io.EOF {
45
+			if err := errors.WithStack(wc.ClientStream.RecvMsg(struct{}{})); err != nil {
46
+				return 0, err
47
+			}
48
+		}
49
+		return 0, errors.WithStack(err)
44 50
 	}
45 51
 	return len(dt), nil
46 52
 }
47 53
 
48 54
 func (wc *streamWriterCloser) Close() error {
49 55
 	if err := wc.ClientStream.CloseSend(); err != nil {
50
-		return err
56
+		return errors.WithStack(err)
51 57
 	}
52 58
 	// block until receiver is done
53 59
 	var bm BytesMessage
54 60
 	if err := wc.ClientStream.RecvMsg(&bm); err != io.EOF {
55
-		return err
61
+		return errors.WithStack(err)
56 62
 	}
57 63
 	return nil
58 64
 }
... ...
@@ -69,19 +75,19 @@ func recvDiffCopy(ds grpc.Stream, dest string, cu CacheUpdater, progress progres
69 69
 		cf = cu.HandleChange
70 70
 		ch = cu.ContentHasher()
71 71
 	}
72
-	return fsutil.Receive(ds.Context(), ds, dest, fsutil.ReceiveOpt{
72
+	return errors.WithStack(fsutil.Receive(ds.Context(), ds, dest, fsutil.ReceiveOpt{
73 73
 		NotifyHashed:  cf,
74 74
 		ContentHasher: ch,
75 75
 		ProgressCb:    progress,
76 76
 		Filter:        fsutil.FilterFunc(filter),
77
-	})
77
+	}))
78 78
 }
79 79
 
80 80
 func syncTargetDiffCopy(ds grpc.Stream, dest string) error {
81 81
 	if err := os.MkdirAll(dest, 0700); err != nil {
82
-		return err
82
+		return errors.Wrapf(err, "failed to create synctarget dest dir %s", dest)
83 83
 	}
84
-	return fsutil.Receive(ds.Context(), ds, dest, fsutil.ReceiveOpt{
84
+	return errors.WithStack(fsutil.Receive(ds.Context(), ds, dest, fsutil.ReceiveOpt{
85 85
 		Merge: true,
86 86
 		Filter: func() func(string, *fstypes.Stat) bool {
87 87
 			uid := os.Getuid()
... ...
@@ -92,7 +98,7 @@ func syncTargetDiffCopy(ds grpc.Stream, dest string) error {
92 92
 				return true
93 93
 			}
94 94
 		}(),
95
-	})
95
+	}))
96 96
 }
97 97
 
98 98
 func writeTargetFile(ds grpc.Stream, wc io.WriteCloser) error {
... ...
@@ -102,10 +108,10 @@ func writeTargetFile(ds grpc.Stream, wc io.WriteCloser) error {
102 102
 			if errors.Cause(err) == io.EOF {
103 103
 				return nil
104 104
 			}
105
-			return err
105
+			return errors.WithStack(err)
106 106
 		}
107 107
 		if _, err := wc.Write(bm.Data); err != nil {
108
-			return err
108
+			return errors.WithStack(err)
109 109
 		}
110 110
 	}
111 111
 }
... ...
@@ -18,11 +18,12 @@ import (
18 18
 )
19 19
 
20 20
 const (
21
-	keyOverrideExcludes = "override-excludes"
22
-	keyIncludePatterns  = "include-patterns"
23
-	keyExcludePatterns  = "exclude-patterns"
24
-	keyFollowPaths      = "followpaths"
25
-	keyDirName          = "dir-name"
21
+	keyOverrideExcludes   = "override-excludes"
22
+	keyIncludePatterns    = "include-patterns"
23
+	keyExcludePatterns    = "exclude-patterns"
24
+	keyFollowPaths        = "followpaths"
25
+	keyDirName            = "dir-name"
26
+	keyExporterMetaPrefix = "exporter-md-"
26 27
 )
27 28
 
28 29
 type fsSyncProvider struct {
... ...
@@ -238,16 +239,16 @@ func NewFSSyncTargetDir(outdir string) session.Attachable {
238 238
 }
239 239
 
240 240
 // NewFSSyncTarget allows writing into an io.WriteCloser
241
-func NewFSSyncTarget(w io.WriteCloser) session.Attachable {
241
+func NewFSSyncTarget(f func(map[string]string) (io.WriteCloser, error)) session.Attachable {
242 242
 	p := &fsSyncTarget{
243
-		outfile: w,
243
+		f: f,
244 244
 	}
245 245
 	return p
246 246
 }
247 247
 
248 248
 type fsSyncTarget struct {
249
-	outdir  string
250
-	outfile io.WriteCloser
249
+	outdir string
250
+	f      func(map[string]string) (io.WriteCloser, error)
251 251
 }
252 252
 
253 253
 func (sp *fsSyncTarget) Register(server *grpc.Server) {
... ...
@@ -258,11 +259,26 @@ func (sp *fsSyncTarget) DiffCopy(stream FileSend_DiffCopyServer) error {
258 258
 	if sp.outdir != "" {
259 259
 		return syncTargetDiffCopy(stream, sp.outdir)
260 260
 	}
261
-	if sp.outfile == nil {
261
+
262
+	if sp.f == nil {
262 263
 		return errors.New("empty outfile and outdir")
263 264
 	}
264
-	defer sp.outfile.Close()
265
-	return writeTargetFile(stream, sp.outfile)
265
+	opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object
266
+	md := map[string]string{}
267
+	for k, v := range opts {
268
+		if strings.HasPrefix(k, keyExporterMetaPrefix) {
269
+			md[strings.TrimPrefix(k, keyExporterMetaPrefix)] = strings.Join(v, ",")
270
+		}
271
+	}
272
+	wc, err := sp.f(md)
273
+	if err != nil {
274
+		return err
275
+	}
276
+	if wc == nil {
277
+		return status.Errorf(codes.AlreadyExists, "target already exists")
278
+	}
279
+	defer wc.Close()
280
+	return writeTargetFile(stream, wc)
266 281
 }
267 282
 
268 283
 func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress func(int, bool)) error {
... ...
@@ -275,13 +291,13 @@ func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress
275 275
 
276 276
 	cc, err := client.DiffCopy(ctx)
277 277
 	if err != nil {
278
-		return err
278
+		return errors.WithStack(err)
279 279
 	}
280 280
 
281 281
 	return sendDiffCopy(cc, fs, progress)
282 282
 }
283 283
 
284
-func CopyFileWriter(ctx context.Context, c session.Caller) (io.WriteCloser, error) {
284
+func CopyFileWriter(ctx context.Context, md map[string]string, c session.Caller) (io.WriteCloser, error) {
285 285
 	method := session.MethodURL(_FileSend_serviceDesc.ServiceName, "diffcopy")
286 286
 	if !c.Supports(method) {
287 287
 		return nil, errors.Errorf("method %s not supported by the client", method)
... ...
@@ -289,9 +305,16 @@ func CopyFileWriter(ctx context.Context, c session.Caller) (io.WriteCloser, erro
289 289
 
290 290
 	client := NewFileSendClient(c.Conn())
291 291
 
292
+	opts := make(map[string][]string, len(md))
293
+	for k, v := range md {
294
+		opts[keyExporterMetaPrefix+k] = []string{v}
295
+	}
296
+
297
+	ctx = metadata.NewOutgoingContext(ctx, opts)
298
+
292 299
 	cc, err := client.DiffCopy(ctx)
293 300
 	if err != nil {
294
-		return nil, err
301
+		return nil, errors.WithStack(err)
295 302
 	}
296 303
 
297 304
 	return newStreamWriter(cc), nil
... ...
@@ -21,10 +21,10 @@ func GetSecret(ctx context.Context, c session.Caller, id string) ([]byte, error)
21 21
 		ID: id,
22 22
 	})
23 23
 	if err != nil {
24
-		if st, ok := status.FromError(err); ok && (st.Code() == codes.Unimplemented || st.Code() == codes.NotFound) {
24
+		if st, ok := status.FromError(errors.Cause(err)); ok && (st.Code() == codes.Unimplemented || st.Code() == codes.NotFound) {
25 25
 			return nil, errors.Wrapf(ErrNotFound, "secret %s not found", id)
26 26
 		}
27
-		return nil, err
27
+		return nil, errors.WithStack(err)
28 28
 	}
29 29
 	return resp.Data, nil
30 30
 }
... ...
@@ -3,6 +3,7 @@ package sshforward
3 3
 import (
4 4
 	io "io"
5 5
 
6
+	"github.com/pkg/errors"
6 7
 	context "golang.org/x/net/context"
7 8
 	"golang.org/x/sync/errgroup"
8 9
 	"google.golang.org/grpc"
... ...
@@ -19,7 +20,7 @@ func Copy(ctx context.Context, conn io.ReadWriteCloser, stream grpc.Stream) erro
19 19
 					return nil
20 20
 				}
21 21
 				conn.Close()
22
-				return err
22
+				return errors.WithStack(err)
23 23
 			}
24 24
 			select {
25 25
 			case <-ctx.Done():
... ...
@@ -29,7 +30,7 @@ func Copy(ctx context.Context, conn io.ReadWriteCloser, stream grpc.Stream) erro
29 29
 			}
30 30
 			if _, err := conn.Write(p.Data); err != nil {
31 31
 				conn.Close()
32
-				return err
32
+				return errors.WithStack(err)
33 33
 			}
34 34
 			p.Data = p.Data[:0]
35 35
 		}
... ...
@@ -43,7 +44,7 @@ func Copy(ctx context.Context, conn io.ReadWriteCloser, stream grpc.Stream) erro
43 43
 			case err == io.EOF:
44 44
 				return nil
45 45
 			case err != nil:
46
-				return err
46
+				return errors.WithStack(err)
47 47
 			}
48 48
 			select {
49 49
 			case <-ctx.Done():
... ...
@@ -52,7 +53,7 @@ func Copy(ctx context.Context, conn io.ReadWriteCloser, stream grpc.Stream) erro
52 52
 			}
53 53
 			p := &BytesMessage{Data: buf[:n]}
54 54
 			if err := stream.SendMsg(p); err != nil {
55
-				return err
55
+				return errors.WithStack(err)
56 56
 			}
57 57
 		}
58 58
 	})
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"path/filepath"
8 8
 
9 9
 	"github.com/moby/buildkit/session"
10
+	"github.com/pkg/errors"
10 11
 	context "golang.org/x/net/context"
11 12
 	"golang.org/x/sync/errgroup"
12 13
 	"google.golang.org/grpc/metadata"
... ...
@@ -65,7 +66,7 @@ type SocketOpt struct {
65 65
 func MountSSHSocket(ctx context.Context, c session.Caller, opt SocketOpt) (sockPath string, closer func() error, err error) {
66 66
 	dir, err := ioutil.TempDir("", ".buildkit-ssh-sock")
67 67
 	if err != nil {
68
-		return "", nil, err
68
+		return "", nil, errors.WithStack(err)
69 69
 	}
70 70
 
71 71
 	defer func() {
... ...
@@ -78,16 +79,16 @@ func MountSSHSocket(ctx context.Context, c session.Caller, opt SocketOpt) (sockP
78 78
 
79 79
 	l, err := net.Listen("unix", sockPath)
80 80
 	if err != nil {
81
-		return "", nil, err
81
+		return "", nil, errors.WithStack(err)
82 82
 	}
83 83
 
84 84
 	if err := os.Chown(sockPath, opt.UID, opt.GID); err != nil {
85 85
 		l.Close()
86
-		return "", nil, err
86
+		return "", nil, errors.WithStack(err)
87 87
 	}
88 88
 	if err := os.Chmod(sockPath, os.FileMode(opt.Mode)); err != nil {
89 89
 		l.Close()
90
-		return "", nil, err
90
+		return "", nil, errors.WithStack(err)
91 91
 	}
92 92
 
93 93
 	s := &server{caller: c}
... ...
@@ -102,12 +103,12 @@ func MountSSHSocket(ctx context.Context, c session.Caller, opt SocketOpt) (sockP
102 102
 	return sockPath, func() error {
103 103
 		err := l.Close()
104 104
 		os.RemoveAll(sockPath)
105
-		return err
105
+		return errors.WithStack(err)
106 106
 	}, nil
107 107
 }
108 108
 
109 109
 func CheckSSHID(ctx context.Context, c session.Caller, id string) error {
110 110
 	client := NewSSHClient(c.Conn())
111 111
 	_, err := client.CheckAgent(ctx, &CheckAgentRequest{ID: id})
112
-	return err
112
+	return errors.WithStack(err)
113 113
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"net/url"
7 7
 
8 8
 	"github.com/moby/buildkit/session"
9
+	"github.com/pkg/errors"
9 10
 	"google.golang.org/grpc/metadata"
10 11
 )
11 12
 
... ...
@@ -26,7 +27,7 @@ func New(ctx context.Context, c session.Caller, url *url.URL) (*Upload, error) {
26 26
 
27 27
 	cc, err := client.Pull(ctx)
28 28
 	if err != nil {
29
-		return nil, err
29
+		return nil, errors.WithStack(err)
30 30
 	}
31 31
 
32 32
 	return &Upload{cc: cc}, nil
... ...
@@ -44,12 +45,12 @@ func (u *Upload) WriteTo(w io.Writer) (int, error) {
44 44
 			if err == io.EOF {
45 45
 				return n, nil
46 46
 			}
47
-			return n, err
47
+			return n, errors.WithStack(err)
48 48
 		}
49 49
 		nn, err := w.Write(bm.Data)
50 50
 		n += nn
51 51
 		if err != nil {
52
-			return n, err
52
+			return n, errors.WithStack(err)
53 53
 		}
54 54
 	}
55 55
 }
... ...
@@ -331,7 +331,8 @@ func (e *edge) unpark(incoming []pipe.Sender, updates, allPipes []pipe.Receiver,
331 331
 	if e.cacheMapReq == nil && (e.cacheMap == nil || len(e.cacheRecords) == 0) {
332 332
 		index := e.cacheMapIndex
333 333
 		e.cacheMapReq = f.NewFuncRequest(func(ctx context.Context) (interface{}, error) {
334
-			return e.op.CacheMap(ctx, index)
334
+			cm, err := e.op.CacheMap(ctx, index)
335
+			return cm, errors.Wrap(err, "failed to load cache key")
335 336
 		})
336 337
 		cacheMapReq = true
337 338
 	}
... ...
@@ -798,7 +799,8 @@ func (e *edge) createInputRequests(desiredState edgeStatusType, f *pipeFactory,
798 798
 			res := dep.result
799 799
 			func(fn ResultBasedCacheFunc, res Result, index Index) {
800 800
 				dep.slowCacheReq = f.NewFuncRequest(func(ctx context.Context) (interface{}, error) {
801
-					return e.op.CalcSlowCache(ctx, index, fn, res)
801
+					v, err := e.op.CalcSlowCache(ctx, index, fn, res)
802
+					return v, errors.Wrap(err, "failed to compute cache key")
802 803
 				})
803 804
 			}(fn, res, dep.index)
804 805
 			addedNew = true
... ...
@@ -850,7 +852,7 @@ func (e *edge) loadCache(ctx context.Context) (interface{}, error) {
850 850
 	logrus.Debugf("load cache for %s with %s", e.edge.Vertex.Name(), rec.ID)
851 851
 	res, err := e.op.LoadCache(ctx, rec)
852 852
 	if err != nil {
853
-		return nil, err
853
+		return nil, errors.Wrap(err, "failed to load cache")
854 854
 	}
855 855
 
856 856
 	return NewCachedResult(res, []ExportableCacheKey{{CacheKey: rec.key, Exporter: &exporter{k: rec.key, record: rec, edge: e}}}), nil
... ...
@@ -861,7 +863,7 @@ func (e *edge) execOp(ctx context.Context) (interface{}, error) {
861 861
 	cacheKeys, inputs := e.commitOptions()
862 862
 	results, subExporters, err := e.op.Exec(ctx, toResultSlice(inputs))
863 863
 	if err != nil {
864
-		return nil, err
864
+		return nil, errors.WithStack(err)
865 865
 	}
866 866
 
867 867
 	index := e.edge.Index
... ...
@@ -29,6 +29,7 @@ type llbBridge struct {
29 29
 	builder                   solver.Builder
30 30
 	frontends                 map[string]frontend.Frontend
31 31
 	resolveWorker             func() (worker.Worker, error)
32
+	eachWorker                func(func(worker.Worker) error) error
32 33
 	resolveCacheImporterFuncs map[string]remotecache.ResolveCacheImporterFunc
33 34
 	cms                       map[string]solver.CacheManager
34 35
 	cmsMu                     sync.Mutex
... ...
@@ -91,14 +92,28 @@ func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *
91 91
 		if err != nil {
92 92
 			return nil, err
93 93
 		}
94
+		dpc := &detectPrunedCacheID{}
94 95
 
95
-		edge, err := Load(req.Definition, ValidateEntitlements(ent), WithCacheSources(cms), RuntimePlatforms(b.platforms), WithValidateCaps())
96
+		edge, err := Load(req.Definition, dpc.Load, ValidateEntitlements(ent), WithCacheSources(cms), RuntimePlatforms(b.platforms), WithValidateCaps())
96 97
 		if err != nil {
97
-			return nil, err
98
+			return nil, errors.Wrap(err, "failed to load LLB")
99
+		}
100
+
101
+		if len(dpc.ids) > 0 {
102
+			ids := make([]string, 0, len(dpc.ids))
103
+			for id := range dpc.ids {
104
+				ids = append(ids, id)
105
+			}
106
+			if err := b.eachWorker(func(w worker.Worker) error {
107
+				return w.PruneCacheMounts(ctx, ids)
108
+			}); err != nil {
109
+				return nil, err
110
+			}
98 111
 		}
112
+
99 113
 		ref, err := b.builder.Build(ctx, edge)
100 114
 		if err != nil {
101
-			return nil, err
115
+			return nil, errors.Wrap(err, "failed to build LLB")
102 116
 		}
103 117
 
104 118
 		res = &frontend.Result{Ref: ref}
... ...
@@ -109,7 +124,7 @@ func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *
109 109
 		}
110 110
 		res, err = f.Solve(ctx, b, req.FrontendOpt)
111 111
 		if err != nil {
112
-			return nil, err
112
+			return nil, errors.Wrapf(err, "failed to solve with frontend %s", req.Frontend)
113 113
 		}
114 114
 	} else {
115 115
 		return &frontend.Result{}, nil
... ...
@@ -27,13 +27,9 @@ func timestampToTime(ts int64) *time.Time {
27 27
 }
28 28
 
29 29
 func mapUser(user *copy.ChownOpt, idmap *idtools.IdentityMapping) (*copy.ChownOpt, error) {
30
-	if idmap == nil {
30
+	if idmap == nil || user == nil {
31 31
 		return user, nil
32 32
 	}
33
-	if user == nil {
34
-		identity := idmap.RootPair()
35
-		return &copy.ChownOpt{Uid: identity.UID, Gid: identity.GID}, nil
36
-	}
37 33
 	identity, err := idmap.ToHost(idtools.Identity{
38 34
 		UID: user.Uid,
39 35
 		GID: user.Gid,
... ...
@@ -138,7 +134,6 @@ func docopy(ctx context.Context, src, dest string, action pb.FileActionCopy, u *
138 138
 		return nil
139 139
 	}
140 140
 
141
-	// TODO(tonistiigi): this is wrong. fsutil.Copy can't handle non-forced user
142 141
 	u, err := mapUser(u, idmap)
143 142
 	if err != nil {
144 143
 		return err
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"github.com/moby/buildkit/frontend"
11 11
 	"github.com/moby/buildkit/snapshot"
12 12
 	"github.com/moby/buildkit/solver"
13
+	"github.com/moby/buildkit/solver/llbsolver"
13 14
 	"github.com/moby/buildkit/solver/pb"
14 15
 	"github.com/moby/buildkit/worker"
15 16
 	digest "github.com/opencontainers/go-digest"
... ...
@@ -25,6 +26,9 @@ type buildOp struct {
25 25
 }
26 26
 
27 27
 func NewBuildOp(v solver.Vertex, op *pb.Op_Build, b frontend.FrontendLLBBridge, _ worker.Worker) (solver.Op, error) {
28
+	if err := llbsolver.ValidateOp(&pb.Op{Op: op}); err != nil {
29
+		return nil, err
30
+	}
28 31
 	return &buildOp{
29 32
 		op: op.Build,
30 33
 		b:  b,
... ...
@@ -60,6 +60,9 @@ type execOp struct {
60 60
 }
61 61
 
62 62
 func NewExecOp(v solver.Vertex, op *pb.Op_Exec, platform *pb.Platform, cm cache.Manager, sm *session.Manager, md *metadata.Store, exec executor.Executor, w worker.Worker) (solver.Op, error) {
63
+	if err := llbsolver.ValidateOp(&pb.Op{Op: op}); err != nil {
64
+		return nil, err
65
+	}
63 66
 	return &execOp{
64 67
 		op:          op.Exec,
65 68
 		cm:          cm,
... ...
@@ -218,11 +221,13 @@ func (e *execOp) getMountDeps() ([]dep, error) {
218 218
 }
219 219
 
220 220
 func (e *execOp) getRefCacheDir(ctx context.Context, ref cache.ImmutableRef, id string, m *pb.Mount, sharing pb.CacheSharingOpt) (mref cache.MutableRef, err error) {
221
-
222 221
 	key := "cache-dir:" + id
223 222
 	if ref != nil {
224 223
 		key += ":" + ref.ID()
225 224
 	}
225
+	mu := CacheMountsLocker()
226
+	mu.Lock()
227
+	defer mu.Unlock()
226 228
 
227 229
 	if ref, ok := e.cacheMounts[key]; ok {
228 230
 		return ref.clone(), nil
... ...
@@ -324,7 +329,7 @@ func (e *execOp) getSSHMountable(ctx context.Context, m *pb.Mount) (cache.Mounta
324 324
 		if m.SSHOpt.Optional {
325 325
 			return nil, nil
326 326
 		}
327
-		if st, ok := status.FromError(err); ok && st.Code() == codes.Unimplemented {
327
+		if st, ok := status.FromError(errors.Cause(err)); ok && st.Code() == codes.Unimplemented {
328 328
 			return nil, errors.Errorf("no SSH key %q forwarded from the client", m.SSHOpt.ID)
329 329
 		}
330 330
 		return nil, err
... ...
@@ -789,10 +794,17 @@ type cacheRefs struct {
789 789
 	shares map[string]*cacheRefShare
790 790
 }
791 791
 
792
-func (r *cacheRefs) get(key string, fn func() (cache.MutableRef, error)) (cache.MutableRef, error) {
793
-	r.mu.Lock()
794
-	defer r.mu.Unlock()
792
+// ClearActiveCacheMounts clears shared cache mounts currently in use.
793
+// Caller needs to hold CacheMountsLocker before calling
794
+func ClearActiveCacheMounts() {
795
+	sharedCacheRefs.shares = nil
796
+}
795 797
 
798
+func CacheMountsLocker() sync.Locker {
799
+	return &sharedCacheRefs.mu
800
+}
801
+
802
+func (r *cacheRefs) get(key string, fn func() (cache.MutableRef, error)) (cache.MutableRef, error) {
796 803
 	if r.shares == nil {
797 804
 		r.shares = map[string]*cacheRefShare{}
798 805
 	}
... ...
@@ -35,6 +35,9 @@ type fileOp struct {
35 35
 }
36 36
 
37 37
 func NewFileOp(v solver.Vertex, op *pb.Op_File, cm cache.Manager, md *metadata.Store, w worker.Worker) (solver.Op, error) {
38
+	if err := llbsolver.ValidateOp(&pb.Op{Op: op}); err != nil {
39
+		return nil, err
40
+	}
38 41
 	return &fileOp{
39 42
 		op:        op.File,
40 43
 		md:        md,
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/moby/buildkit/session"
9 9
 	"github.com/moby/buildkit/solver"
10
+	"github.com/moby/buildkit/solver/llbsolver"
10 11
 	"github.com/moby/buildkit/solver/pb"
11 12
 	"github.com/moby/buildkit/source"
12 13
 	"github.com/moby/buildkit/worker"
... ...
@@ -26,6 +27,9 @@ type sourceOp struct {
26 26
 }
27 27
 
28 28
 func NewSourceOp(_ solver.Vertex, op *pb.Op_Source, platform *pb.Platform, sm *source.Manager, sessM *session.Manager, w worker.Worker) (solver.Op, error) {
29
+	if err := llbsolver.ValidateOp(&pb.Op{Op: op}); err != nil {
30
+		return nil, err
31
+	}
29 32
 	return &sourceOp{
30 33
 		op:       op,
31 34
 		sm:       sm,
... ...
@@ -39,6 +39,7 @@ type Solver struct {
39 39
 	workerController          *worker.Controller
40 40
 	solver                    *solver.Solver
41 41
 	resolveWorker             ResolveWorkerFunc
42
+	eachWorker                func(func(worker.Worker) error) error
42 43
 	frontends                 map[string]frontend.Frontend
43 44
 	resolveCacheImporterFuncs map[string]remotecache.ResolveCacheImporterFunc
44 45
 	platforms                 []specs.Platform
... ...
@@ -51,6 +52,7 @@ func New(wc *worker.Controller, f map[string]frontend.Frontend, cache solver.Cac
51 51
 	s := &Solver{
52 52
 		workerController:          wc,
53 53
 		resolveWorker:             defaultResolver(wc),
54
+		eachWorker:                allWorkers(wc),
54 55
 		frontends:                 f,
55 56
 		resolveCacheImporterFuncs: resolveCI,
56 57
 		gatewayForwarder:          gatewayForwarder,
... ...
@@ -87,6 +89,7 @@ func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge {
87 87
 		builder:                   b,
88 88
 		frontends:                 s.frontends,
89 89
 		resolveWorker:             s.resolveWorker,
90
+		eachWorker:                s.eachWorker,
90 91
 		resolveCacheImporterFuncs: s.resolveCacheImporterFuncs,
91 92
 		cms:                       map[string]solver.CacheManager{},
92 93
 		platforms:                 s.platforms,
... ...
@@ -285,6 +288,20 @@ func defaultResolver(wc *worker.Controller) ResolveWorkerFunc {
285 285
 		return wc.GetDefault()
286 286
 	}
287 287
 }
288
+func allWorkers(wc *worker.Controller) func(func(w worker.Worker) error) error {
289
+	return func(f func(worker.Worker) error) error {
290
+		all, err := wc.List()
291
+		if err != nil {
292
+			return err
293
+		}
294
+		for _, w := range all {
295
+			if err := f(w); err != nil {
296
+				return err
297
+			}
298
+		}
299
+		return nil
300
+	}
301
+}
288 302
 
289 303
 func oneOffProgress(ctx context.Context, id string) func(err error) error {
290 304
 	pw, _, _ := progress.FromContext(ctx)
... ...
@@ -131,6 +131,34 @@ func ValidateEntitlements(ent entitlements.Set) LoadOpt {
131 131
 	}
132 132
 }
133 133
 
134
+type detectPrunedCacheID struct {
135
+	ids map[string]struct{}
136
+}
137
+
138
+func (dpc *detectPrunedCacheID) Load(op *pb.Op, md *pb.OpMetadata, opt *solver.VertexOptions) error {
139
+	if md == nil || !md.IgnoreCache {
140
+		return nil
141
+	}
142
+	switch op := op.Op.(type) {
143
+	case *pb.Op_Exec:
144
+		for _, m := range op.Exec.GetMounts() {
145
+			if m.MountType == pb.MountType_CACHE {
146
+				if m.CacheOpt != nil {
147
+					id := m.CacheOpt.ID
148
+					if id == "" {
149
+						id = m.Dest
150
+					}
151
+					if dpc.ids == nil {
152
+						dpc.ids = map[string]struct{}{}
153
+					}
154
+					dpc.ids[id] = struct{}{}
155
+				}
156
+			}
157
+		}
158
+	}
159
+	return nil
160
+}
161
+
134 162
 func Load(def *pb.Definition, opts ...LoadOpt) (solver.Edge, error) {
135 163
 	return loadLLB(def, func(dgst digest.Digest, pbOp *pb.Op, load func(digest.Digest) (solver.Vertex, error)) (solver.Vertex, error) {
136 164
 		opMetadata := def.Metadata[dgst]
... ...
@@ -188,8 +216,15 @@ func loadLLB(def *pb.Definition, fn func(digest.Digest, *pb.Op, func(digest.Dige
188 188
 		allOps[dgst] = &op
189 189
 	}
190 190
 
191
+	if len(allOps) < 2 {
192
+		return solver.Edge{}, errors.Errorf("invalid LLB with %d vertexes", len(allOps))
193
+	}
194
+
191 195
 	lastOp := allOps[dgst]
192 196
 	delete(allOps, dgst)
197
+	if len(lastOp.Inputs) == 0 {
198
+		return solver.Edge{}, errors.Errorf("invalid LLB with no inputs on last vertex")
199
+	}
193 200
 	dgst = lastOp.Inputs[0].Digest
194 201
 
195 202
 	cache := make(map[digest.Digest]solver.Vertex)
... ...
@@ -203,6 +238,11 @@ func loadLLB(def *pb.Definition, fn func(digest.Digest, *pb.Op, func(digest.Dige
203 203
 		if !ok {
204 204
 			return nil, errors.Errorf("invalid missing input digest %s", dgst)
205 205
 		}
206
+
207
+		if err := ValidateOp(op); err != nil {
208
+			return nil, err
209
+		}
210
+
206 211
 		v, err := fn(dgst, op, rec)
207 212
 		if err != nil {
208 213
 			return nil, err
... ...
@@ -240,6 +280,55 @@ func llbOpName(op *pb.Op) string {
240 240
 	}
241 241
 }
242 242
 
243
+func ValidateOp(op *pb.Op) error {
244
+	if op == nil {
245
+		return errors.Errorf("invalid nil op")
246
+	}
247
+
248
+	switch op := op.Op.(type) {
249
+	case *pb.Op_Source:
250
+		if op.Source == nil {
251
+			return errors.Errorf("invalid nil source op")
252
+		}
253
+	case *pb.Op_Exec:
254
+		if op.Exec == nil {
255
+			return errors.Errorf("invalid nil exec op")
256
+		}
257
+		if op.Exec.Meta == nil {
258
+			return errors.Errorf("invalid exec op with no meta")
259
+		}
260
+		if len(op.Exec.Meta.Args) == 0 {
261
+			return errors.Errorf("invalid exec op with no args")
262
+		}
263
+		if len(op.Exec.Mounts) == 0 {
264
+			return errors.Errorf("invalid exec op with no mounts")
265
+		}
266
+
267
+		isRoot := false
268
+		for _, m := range op.Exec.Mounts {
269
+			if m.Dest == pb.RootMount {
270
+				isRoot = true
271
+				break
272
+			}
273
+		}
274
+		if !isRoot {
275
+			return errors.Errorf("invalid exec op with no rootfs")
276
+		}
277
+	case *pb.Op_File:
278
+		if op.File == nil {
279
+			return errors.Errorf("invalid nil file op")
280
+		}
281
+		if len(op.File.Actions) == 0 {
282
+			return errors.Errorf("invalid file op with no actions")
283
+		}
284
+	case *pb.Op_Build:
285
+		if op.Build == nil {
286
+			return errors.Errorf("invalid nil build op")
287
+		}
288
+	}
289
+	return nil
290
+}
291
+
243 292
 func fileOpName(actions []*pb.FileAction) string {
244 293
 	names := make([]string, 0, len(actions))
245 294
 	for _, action := range actions {
... ...
@@ -40,9 +40,9 @@ func dup(res Result) (Result, Result) {
40 40
 }
41 41
 
42 42
 type splitResult struct {
43
-	Result
44 43
 	released int64
45 44
 	sem      *int64
45
+	Result
46 46
 }
47 47
 
48 48
 func (r *splitResult) Release(ctx context.Context) error {
49 49
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+// +build !386
1
+
2
+package binfmt_misc
3
+
4
+// This file is generated by running make inside the binfmt_misc package.
5
+// Do not edit manually.
6
+
7
+const Binary386 = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xec\xd8\x31\x6e\xc2\x30\x14\x06\xe0\xdf\x8d\xdb\x26\x6a\x07\x1f\x20\xaa\x3a\x74\xe8\x64\xb5\x52\xae\x00\x2c\x88\x8d\x03\x80\x14\xc1\x94\x44\x89\x91\x60\x22\x47\x60\xe0\x20\x8c\x8c\x5c\x80\x13\x70\x19\xf4\xe2\x67\x91\x81\x25\xfb\xfb\xa4\x5f\x16\xcf\xe6\x29\xeb\x7b\xfb\xd1\x74\xac\x94\x42\xf0\x82\x08\xdd\xaf\x83\x8e\x33\x00\x7f\xc6\xd7\x33\x7c\x23\xc2\x2f\x74\xb8\x27\xad\x8e\x29\x27\x00\x14\x4d\x35\x03\x7f\x6f\x7c\x0f\x4a\x02\x80\xf2\xca\x75\x7a\x77\xa4\xb4\x3a\xa6\xa4\x00\x52\xfe\x7f\xc8\x27\xbf\x9f\xcc\xe6\xd4\xef\x42\xb5\xc7\x57\x0a\x21\x84\x10\x42\x08\x21\x84\x10\x62\x88\x33\x0d\xd5\xff\xb7\x6b\x0b\xdb\xac\x1b\x57\xbb\xc5\x12\xb6\x28\x5d\x6e\x57\xc5\xc6\x56\x75\x59\xe5\xb5\xdb\xc1\xba\x7c\xeb\x86\xf4\xfd\x00\xf0\xde\xed\x13\x78\xce\xe7\x19\x3f\xd0\x7c\x7e\xf1\x5c\xff\xc6\x3b\x07\x18\xbf\x2b\x08\x54\xef\x8c\x7a\xf5\xc4\x00\x3f\x4f\xde\xdd\x03\x00\x00\xff\xff\x8d\xf7\xd2\x72\xd0\x10\x00\x00"
0 8
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !386
1
+
2
+package binfmt_misc
3
+
4
+func i386Supported() error {
5
+	return check(Binary386)
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build 386
1
+
2
+package binfmt_misc
3
+
4
+func i386Supported() error {
5
+	return nil
6
+}
... ...
@@ -24,6 +24,15 @@ func SupportedPlatforms() []string {
24 24
 		if p := "linux/riscv64"; def != p && riscv64Supported() == nil {
25 25
 			arr = append(arr, p)
26 26
 		}
27
+		if p := "linux/ppc64le"; def != p && ppc64leSupported() == nil {
28
+			arr = append(arr, p)
29
+		}
30
+		if p := "linux/s390x"; def != p && s390xSupported() == nil {
31
+			arr = append(arr, p)
32
+		}
33
+		if p := "linux/386"; def != p && i386Supported() == nil {
34
+			arr = append(arr, p)
35
+		}
27 36
 		if !strings.HasPrefix(def, "linux/arm/") && armSupported() == nil {
28 37
 			arr = append(arr, "linux/arm/v7", "linux/arm/v6")
29 38
 		} else if def == "linux/arm/v7" {
... ...
@@ -55,6 +64,21 @@ func WarnIfUnsupported(pfs []string) {
55 55
 					printPlatfromWarning(p, err)
56 56
 				}
57 57
 			}
58
+			if p == "linux/ppc64le" {
59
+				if err := ppc64leSupported(); err != nil {
60
+					printPlatfromWarning(p, err)
61
+				}
62
+			}
63
+			if p == "linux/s390x" {
64
+				if err := s390xSupported(); err != nil {
65
+					printPlatfromWarning(p, err)
66
+				}
67
+			}
68
+			if p == "linux/386" {
69
+				if err := i386Supported(); err != nil {
70
+					printPlatfromWarning(p, err)
71
+				}
72
+			}
58 73
 			if strings.HasPrefix(p, "linux/arm/v6") || strings.HasPrefix(p, "linux/arm/v7") {
59 74
 				if err := armSupported(); err != nil {
60 75
 					printPlatfromWarning(p, err)
61 76
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+// +build !ppc64le
1
+
2
+package binfmt_misc
3
+
4
+// This file is generated by running make inside the binfmt_misc package.
5
+// Do not edit manually.
6
+
7
+const Binaryppc64le = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xaa\x77\xf5\x71\x63\x62\x64\x64\x80\x01\x26\x06\x51\x06\x10\xaf\x82\x81\x41\x00\xc4\x77\x80\x8a\x2f\x80\xcb\x83\xc4\x2c\x18\x18\x19\x1c\x18\x58\x18\x98\xc1\x6a\x59\x19\x50\x80\x00\x32\xdd\x02\xe5\xb4\xc0\xa5\x19\x61\xa4\x05\x03\x43\x82\x05\x13\x03\x83\x0b\x83\x5e\x71\x46\x71\x49\x51\x49\x62\x12\x83\x5e\x49\x6a\x45\x09\x83\x5e\x6a\x46\x7c\x5a\x51\x62\x6e\x2a\x03\xc5\x80\x1b\x6a\x23\x1b\x94\x0f\xf3\x57\x05\x94\xcf\x83\xa6\x9e\x03\x8d\x2f\x08\xd5\xcf\x84\xf0\x87\x00\xaa\x7f\x50\x01\x0b\x1a\x1f\xa4\x97\x19\x8b\x3a\x98\x7e\x69\x2c\xea\x91\x01\x20\x00\x00\xff\xff\xce\xf7\x15\x75\xa0\x01\x00\x00"
0 8
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !ppc64le
1
+
2
+package binfmt_misc
3
+
4
+func ppc64leSupported() error {
5
+	return check(Binaryppc64le)
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build ppc64le
1
+
2
+package binfmt_misc
3
+
4
+func ppc64leSupported() error {
5
+	return nil
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+// +build !s390x
1
+
2
+package binfmt_misc
3
+
4
+// This file is generated by running make inside the binfmt_misc package.
5
+// Do not edit manually.
6
+
7
+const Binarys390x = "\x1f\x8b\x08\x00\x00\x00\x00\x00\x02\xff\xaa\x77\xf5\x71\x63\x62\x62\x64\x80\x03\x26\x06\x31\x06\x06\x06\xb0\x00\x23\x03\x43\x05\x54\xd4\x01\x4a\xcf\x80\xf2\x2c\x18\x18\x19\x1c\x18\x98\x19\x98\xa0\x6a\x59\x19\x90\x00\x23\x1a\xcd\xc0\xc0\xd0\x80\x4a\x0b\x30\x2c\xd7\x64\x60\xe0\x62\x64\x67\x67\xd0\x2b\xce\x28\x2e\x29\x2a\x49\x4c\x62\xd0\x2b\x49\xad\x28\x61\xa0\x1e\xe0\x46\x72\x02\x1b\x9a\x7f\x60\x34\x07\x9a\x1e\x16\x34\x6f\x30\xe3\x30\x1b\xe6\x1f\x41\x34\x71\xb8\x97\x01\x01\x00\x00\xff\xff\x0c\x76\x9a\xe1\x58\x01\x00\x00"
0 8
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build !s390x
1
+
2
+package binfmt_misc
3
+
4
+func s390xSupported() error {
5
+	return check(Binarys390x)
6
+}
0 7
new file mode 100644
... ...
@@ -0,0 +1,7 @@
0
+// +build s390x
1
+
2
+package binfmt_misc
3
+
4
+func s390xSupported() error {
5
+	return nil
6
+}
... ...
@@ -72,16 +72,18 @@ func (g *Group) do(ctx context.Context, key string, fn func(ctx context.Context)
72 72
 		g.mu.Lock()
73 73
 		delete(g.m, key)
74 74
 		g.mu.Unlock()
75
+		close(c.cleaned)
75 76
 	}()
76 77
 	g.mu.Unlock()
77 78
 	return c.wait(ctx)
78 79
 }
79 80
 
80 81
 type call struct {
81
-	mu     sync.Mutex
82
-	result interface{}
83
-	err    error
84
-	ready  chan struct{}
82
+	mu      sync.Mutex
83
+	result  interface{}
84
+	err     error
85
+	ready   chan struct{}
86
+	cleaned chan struct{}
85 87
 
86 88
 	ctx  *sharedContext
87 89
 	ctxs []context.Context
... ...
@@ -97,6 +99,7 @@ func newCall(fn func(ctx context.Context) (interface{}, error)) *call {
97 97
 	c := &call{
98 98
 		fn:            fn,
99 99
 		ready:         make(chan struct{}),
100
+		cleaned:       make(chan struct{}),
100 101
 		progressState: newProgressState(),
101 102
 	}
102 103
 	ctx := newContext(c) // newSharedContext
... ...
@@ -127,6 +130,7 @@ func (c *call) wait(ctx context.Context) (v interface{}, err error) {
127 127
 	select {
128 128
 	case <-c.ready: // could return if no error
129 129
 		c.mu.Unlock()
130
+		<-c.cleaned
130 131
 		return nil, errRetry
131 132
 	default:
132 133
 	}
... ...
@@ -3,20 +3,9 @@ package network
3 3
 import (
4 4
 	"io"
5 5
 
6
-	"github.com/moby/buildkit/solver/pb"
7 6
 	specs "github.com/opencontainers/runtime-spec/specs-go"
8 7
 )
9 8
 
10
-// Default returns the default network provider set
11
-func Default() map[pb.NetMode]Provider {
12
-	return map[pb.NetMode]Provider{
13
-		// FIXME: still uses host if no provider configured
14
-		pb.NetMode_UNSET: NewHostProvider(),
15
-		pb.NetMode_HOST:  NewHostProvider(),
16
-		pb.NetMode_NONE:  NewNoneProvider(),
17
-	}
18
-}
19
-
20 9
 // Provider interface for Network
21 10
 type Provider interface {
22 11
 	New() (Namespace, error)
... ...
@@ -28,10 +17,3 @@ type Namespace interface {
28 28
 	// Set the namespace on the spec
29 29
 	Set(*specs.Spec)
30 30
 }
31
-
32
-// NetworkOpts hold network options
33
-type NetworkOpts struct {
34
-	Type          string
35
-	CNIConfigPath string
36
-	CNIPluginPath string
37
-}
... ...
@@ -33,6 +33,7 @@ type Worker interface {
33 33
 	Prune(ctx context.Context, ch chan client.UsageInfo, opt ...client.PruneInfo) error
34 34
 	GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error)
35 35
 	FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error)
36
+	PruneCacheMounts(ctx context.Context, ids []string) error
36 37
 }
37 38
 
38 39
 // Pre-defined label keys