Browse code

build: buildkit now honors daemon's DNS config

Signed-off-by: Tibor Vass <tibor@docker.com>
(cherry picked from commit a1cdd4bfcc515a862e18ac123836fcaa05d09b32)
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>

Tibor Vass authored on 2019/06/06 10:36:33
Showing 41 changed files
... ...
@@ -75,6 +75,7 @@ type Opt struct {
75 75
 	BuilderConfig       config.BuilderConfig
76 76
 	Rootless            bool
77 77
 	IdentityMapping     *idtools.IdentityMapping
78
+	DNSConfig           config.DNSConfig
78 79
 }
79 80
 
80 81
 // Builder can build using BuildKit backend
... ...
@@ -113,7 +113,9 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) {
113 113
 		return nil, err
114 114
 	}
115 115
 
116
-	exec, err := newExecutor(root, opt.DefaultCgroupParent, opt.NetworkController, opt.Rootless, opt.IdentityMapping)
116
+	dns := getDNSConfig(opt.DNSConfig)
117
+
118
+	exec, err := newExecutor(root, opt.DefaultCgroupParent, opt.NetworkController, dns, opt.Rootless, opt.IdentityMapping)
117 119
 	if err != nil {
118 120
 		return nil, err
119 121
 	}
... ...
@@ -8,9 +8,11 @@ import (
8 8
 	"strconv"
9 9
 	"sync"
10 10
 
11
+	"github.com/docker/docker/daemon/config"
11 12
 	"github.com/docker/docker/pkg/idtools"
12 13
 	"github.com/docker/libnetwork"
13 14
 	"github.com/moby/buildkit/executor"
15
+	"github.com/moby/buildkit/executor/oci"
14 16
 	"github.com/moby/buildkit/executor/runcexecutor"
15 17
 	"github.com/moby/buildkit/identity"
16 18
 	"github.com/moby/buildkit/solver/pb"
... ...
@@ -21,7 +23,7 @@ import (
21 21
 
22 22
 const networkName = "bridge"
23 23
 
24
-func newExecutor(root, cgroupParent string, net libnetwork.NetworkController, rootless bool, idmap *idtools.IdentityMapping) (executor.Executor, error) {
24
+func newExecutor(root, cgroupParent string, net libnetwork.NetworkController, dnsConfig *oci.DNSConfig, rootless bool, idmap *idtools.IdentityMapping) (executor.Executor, error) {
25 25
 	networkProviders := map[pb.NetMode]network.Provider{
26 26
 		pb.NetMode_UNSET: &bridgeProvider{NetworkController: net, Root: filepath.Join(root, "net")},
27 27
 		pb.NetMode_HOST:  network.NewHostProvider(),
... ...
@@ -34,6 +36,7 @@ func newExecutor(root, cgroupParent string, net libnetwork.NetworkController, ro
34 34
 		Rootless:            rootless,
35 35
 		NoPivot:             os.Getenv("DOCKER_RAMDISK") != "",
36 36
 		IdentityMapping:     idmap,
37
+		DNS:                 dnsConfig,
37 38
 	}, networkProviders)
38 39
 }
39 40
 
... ...
@@ -117,3 +120,14 @@ func (iface *lnInterface) Close() error {
117 117
 	}
118 118
 	return iface.err
119 119
 }
120
+
121
+func getDNSConfig(cfg config.DNSConfig) *oci.DNSConfig {
122
+	if cfg.DNS != nil || cfg.DNSSearch != nil || cfg.DNSOptions != nil {
123
+		return &oci.DNSConfig{
124
+			Nameservers:   cfg.DNS,
125
+			SearchDomains: cfg.DNSSearch,
126
+			Options:       cfg.DNSOptions,
127
+		}
128
+	}
129
+	return nil
130
+}
... ...
@@ -5,13 +5,15 @@ import (
5 5
 	"errors"
6 6
 	"io"
7 7
 
8
+	"github.com/docker/docker/daemon/config"
8 9
 	"github.com/docker/docker/pkg/idtools"
9 10
 	"github.com/docker/libnetwork"
10 11
 	"github.com/moby/buildkit/cache"
11 12
 	"github.com/moby/buildkit/executor"
13
+	"github.com/moby/buildkit/executor/oci"
12 14
 )
13 15
 
14
-func newExecutor(_, _ string, _ libnetwork.NetworkController, _ bool, _ *idtools.IdentityMapping) (executor.Executor, error) {
16
+func newExecutor(_, _ string, _ libnetwork.NetworkController, _ *oci.DNSConfig, _ bool, _ *idtools.IdentityMapping) (executor.Executor, error) {
15 17
 	return &winExecutor{}, nil
16 18
 }
17 19
 
... ...
@@ -21,3 +23,7 @@ type winExecutor struct {
21 21
 func (e *winExecutor) Exec(ctx context.Context, meta executor.Meta, rootfs cache.Mountable, mounts []executor.Mount, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
22 22
 	return errors.New("buildkit executor not implemented for windows")
23 23
 }
24
+
25
+func getDNSConfig(config.DNSConfig) *oci.DNSConfig {
26
+	return nil
27
+}
... ...
@@ -319,6 +319,7 @@ func newRouterOptions(config *config.Config, d *daemon.Daemon) (routerOptions, e
319 319
 		BuilderConfig:       config.Builder,
320 320
 		Rootless:            d.Rootless(),
321 321
 		IdentityMapping:     d.IdentityMapping(),
322
+		DNSConfig:           config.DNSConfig,
322 323
 	})
323 324
 	if err != nil {
324 325
 		return opts, err
... ...
@@ -109,6 +109,13 @@ type CommonTLSOptions struct {
109 109
 	KeyFile  string `json:"tlskey,omitempty"`
110 110
 }
111 111
 
112
+// DNSConfig defines the DNS configurations.
113
+type DNSConfig struct {
114
+	DNS        []string `json:"dns,omitempty"`
115
+	DNSOptions []string `json:"dns-opts,omitempty"`
116
+	DNSSearch  []string `json:"dns-search,omitempty"`
117
+}
118
+
112 119
 // CommonConfig defines the configuration of a docker daemon which is
113 120
 // common across platforms.
114 121
 // It includes json tags to deserialize configuration from a file
... ...
@@ -119,9 +126,6 @@ type CommonConfig struct {
119 119
 	AutoRestart           bool                      `json:"-"`
120 120
 	Context               map[string][]string       `json:"-"`
121 121
 	DisableBridge         bool                      `json:"-"`
122
-	DNS                   []string                  `json:"dns,omitempty"`
123
-	DNSOptions            []string                  `json:"dns-opts,omitempty"`
124
-	DNSSearch             []string                  `json:"dns-search,omitempty"`
125 122
 	ExecOptions           []string                  `json:"exec-opts,omitempty"`
126 123
 	GraphDriver           string                    `json:"storage-driver,omitempty"`
127 124
 	GraphOptions          []string                  `json:"storage-opts,omitempty"`
... ...
@@ -200,6 +204,7 @@ type CommonConfig struct {
200 200
 
201 201
 	MetricsAddress string `json:"metrics-addr"`
202 202
 
203
+	DNSConfig
203 204
 	LogConfig
204 205
 	BridgeConfig // bridgeConfig holds bridge network specific configuration.
205 206
 	NetworkConfig
... ...
@@ -244,28 +244,36 @@ func TestValidateConfigurationErrors(t *testing.T) {
244 244
 		{
245 245
 			config: &Config{
246 246
 				CommonConfig: CommonConfig{
247
-					DNS: []string{"1.1.1.1o"},
247
+					DNSConfig: DNSConfig{
248
+						DNS: []string{"1.1.1.1o"},
249
+					},
248 250
 				},
249 251
 			},
250 252
 		},
251 253
 		{
252 254
 			config: &Config{
253 255
 				CommonConfig: CommonConfig{
254
-					DNS: []string{"2.2.2.2", "1.1.1.1o"},
256
+					DNSConfig: DNSConfig{
257
+						DNS: []string{"2.2.2.2", "1.1.1.1o"},
258
+					},
255 259
 				},
256 260
 			},
257 261
 		},
258 262
 		{
259 263
 			config: &Config{
260 264
 				CommonConfig: CommonConfig{
261
-					DNSSearch: []string{"123456"},
265
+					DNSConfig: DNSConfig{
266
+						DNSSearch: []string{"123456"},
267
+					},
262 268
 				},
263 269
 			},
264 270
 		},
265 271
 		{
266 272
 			config: &Config{
267 273
 				CommonConfig: CommonConfig{
268
-					DNSSearch: []string{"a.b.c", "123456"},
274
+					DNSConfig: DNSConfig{
275
+						DNSSearch: []string{"a.b.c", "123456"},
276
+					},
269 277
 				},
270 278
 			},
271 279
 		},
... ...
@@ -329,14 +337,18 @@ func TestValidateConfiguration(t *testing.T) {
329 329
 		{
330 330
 			config: &Config{
331 331
 				CommonConfig: CommonConfig{
332
-					DNS: []string{"1.1.1.1"},
332
+					DNSConfig: DNSConfig{
333
+						DNS: []string{"1.1.1.1"},
334
+					},
333 335
 				},
334 336
 			},
335 337
 		},
336 338
 		{
337 339
 			config: &Config{
338 340
 				CommonConfig: CommonConfig{
339
-					DNSSearch: []string{"a.b.c"},
341
+					DNSConfig: DNSConfig{
342
+						DNSSearch: []string{"a.b.c"},
343
+					},
340 344
 				},
341 345
 			},
342 346
 		},
... ...
@@ -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                            c24275065aca6605bd83c57c6735510f4ebeb6d9
30
+github.com/moby/buildkit                            a258bd18b2c55aac4e8a10a3074757d66d45cef6
31 31
 github.com/tonistiigi/fsutil                        3bbb99cdbd76619ab717299830c60f6f2a533a6b
32 32
 github.com/grpc-ecosystem/grpc-opentracing          8e809c8a86450a29b90dcc9efbf062d0fe6d9746
33 33
 github.com/opentracing/opentracing-go               1361b9cd60be79c4c3a7fa9841b3c132e40066a7
... ...
@@ -299,7 +299,7 @@ Run `make images` to build the images as `moby/buildkit:local` and `moby/buildki
299 299
 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 300
 
301 301
 ```
302
-export BUILDKIT_HOST=docker://<container>
302
+export BUILDKIT_HOST=docker-container://<container>
303 303
 ```
304 304
 
305 305
 ```
... ...
@@ -157,14 +157,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 +222,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 +330,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 +549,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 +590,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
... ...
@@ -283,23 +283,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 +311,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) {
... ...
@@ -341,18 +341,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 +367,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 {
... ...
@@ -190,7 +190,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 +259,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
... ...
@@ -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)
... ...
@@ -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) {
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	"github.com/docker/docker/pkg/idtools"
10 10
 	"github.com/docker/libnetwork/resolvconf"
11
+	"github.com/docker/libnetwork/types"
11 12
 	"github.com/moby/buildkit/util/flightcontrol"
12 13
 )
13 14
 
... ...
@@ -15,7 +16,13 @@ var g flightcontrol.Group
15 15
 var notFirstRun bool
16 16
 var lastNotEmpty bool
17 17
 
18
-func GetResolvConf(ctx context.Context, stateDir string, idmap *idtools.IdentityMapping) (string, error) {
18
+type DNSConfig struct {
19
+	Nameservers   []string
20
+	Options       []string
21
+	SearchDomains []string
22
+}
23
+
24
+func GetResolvConf(ctx context.Context, stateDir string, idmap *idtools.IdentityMapping, dns *DNSConfig) (string, error) {
19 25
 	p := filepath.Join(stateDir, "resolv.conf")
20 26
 	_, err := g.Do(ctx, stateDir, func(ctx context.Context) (interface{}, error) {
21 27
 		generate := !notFirstRun
... ...
@@ -61,9 +68,34 @@ func GetResolvConf(ctx context.Context, stateDir string, idmap *idtools.Identity
61 61
 			dt = f.Content
62 62
 		}
63 63
 
64
-		f, err = resolvconf.FilterResolvDNS(dt, true)
65
-		if err != nil {
66
-			return "", err
64
+		if dns != nil {
65
+			var (
66
+				dnsNameservers   = resolvconf.GetNameservers(dt, types.IP)
67
+				dnsSearchDomains = resolvconf.GetSearchDomains(dt)
68
+				dnsOptions       = resolvconf.GetOptions(dt)
69
+			)
70
+			if len(dns.Nameservers) > 0 {
71
+				dnsNameservers = dns.Nameservers
72
+			}
73
+			if len(dns.SearchDomains) > 0 {
74
+				dnsSearchDomains = dns.SearchDomains
75
+			}
76
+			if len(dns.Options) > 0 {
77
+				dnsOptions = dns.Options
78
+			}
79
+
80
+			f, err = resolvconf.Build(p+".tmp", dnsNameservers, dnsSearchDomains, dnsOptions)
81
+			if err != nil {
82
+				return "", err
83
+			}
84
+		} else {
85
+			// Logic seems odd here: why are we filtering localhost IPs
86
+			// only if neither of the DNS configs were specified?
87
+			// Logic comes from https://github.com/docker/libnetwork/blob/164a77ee6d24fb2b1d61f8ad3403a51d8453899e/sandbox_dns_unix.go#L230-L269
88
+			f, err = resolvconf.FilterResolvDNS(f.Content, true)
89
+			if err != nil {
90
+				return "", err
91
+			}
67 92
 		}
68 93
 
69 94
 		tmpPath := p + ".tmp"
70 95
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) {
... ...
@@ -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) {
... ...
@@ -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,7 +137,7 @@ 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, w.idmap)
137
+	resolvConf, err := oci.GetResolvConf(ctx, w.root, w.idmap, w.dns)
138 138
 	if err != nil {
139 139
 		return err
140 140
 	}
... ...
@@ -50,8 +50,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
... ...
@@ -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,19 @@ 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
+		return 0, errors.WithStack(err)
44 44
 	}
45 45
 	return len(dt), nil
46 46
 }
47 47
 
48 48
 func (wc *streamWriterCloser) Close() error {
49 49
 	if err := wc.ClientStream.CloseSend(); err != nil {
50
-		return err
50
+		return errors.WithStack(err)
51 51
 	}
52 52
 	// block until receiver is done
53 53
 	var bm BytesMessage
54 54
 	if err := wc.ClientStream.RecvMsg(&bm); err != io.EOF {
55
-		return err
55
+		return errors.WithStack(err)
56 56
 	}
57 57
 	return nil
58 58
 }
... ...
@@ -69,19 +69,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 +92,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 +102,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
 }
... ...
@@ -275,7 +275,7 @@ 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)
... ...
@@ -291,7 +291,7 @@ func CopyFileWriter(ctx context.Context, c session.Caller) (io.WriteCloser, erro
291 291
 
292 292
 	cc, err := client.DiffCopy(ctx)
293 293
 	if err != nil {
294
-		return nil, err
294
+		return nil, errors.WithStack(err)
295 295
 	}
296 296
 
297 297
 	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
... ...
@@ -94,11 +94,11 @@ func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *
94 94
 
95 95
 		edge, err := Load(req.Definition, ValidateEntitlements(ent), WithCacheSources(cms), RuntimePlatforms(b.platforms), WithValidateCaps())
96 96
 		if err != nil {
97
-			return nil, err
97
+			return nil, errors.Wrap(err, "failed to load LLB")
98 98
 		}
99 99
 		ref, err := b.builder.Build(ctx, edge)
100 100
 		if err != nil {
101
-			return nil, err
101
+			return nil, errors.Wrap(err, "failed to build LLB")
102 102
 		}
103 103
 
104 104
 		res = &frontend.Result{Ref: ref}
... ...
@@ -109,7 +109,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
... ...
@@ -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,
... ...
@@ -324,7 +327,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
... ...
@@ -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,
... ...
@@ -188,8 +188,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 +210,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 +252,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 {