Browse code

Merge pull request #37350 from tonistiigi/platform-support

Fix platform struct passing

Tibor Vass authored on 2018/06/28 11:23:27
Showing 67 changed files
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"strings"
15 15
 	"sync"
16 16
 
17
+	"github.com/containerd/containerd/platforms"
17 18
 	"github.com/docker/docker/api/server/httputils"
18 19
 	"github.com/docker/docker/api/types"
19 20
 	"github.com/docker/docker/api/types/backend"
... ...
@@ -72,11 +73,16 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
72 72
 	options.RemoteContext = r.FormValue("remote")
73 73
 	if versions.GreaterThanOrEqualTo(version, "1.32") {
74 74
 		apiPlatform := r.FormValue("platform")
75
-		p := system.ParsePlatform(apiPlatform)
76
-		if err := system.ValidatePlatform(p); err != nil {
77
-			return nil, errdefs.InvalidParameter(errors.Errorf("invalid platform: %s", err))
75
+		if apiPlatform != "" {
76
+			sp, err := platforms.Parse(apiPlatform)
77
+			if err != nil {
78
+				return nil, err
79
+			}
80
+			if err := system.ValidatePlatform(sp); err != nil {
81
+				return nil, err
82
+			}
83
+			options.Platform = &sp
78 84
 		}
79
-		options.Platform = p.OS
80 85
 	}
81 86
 
82 87
 	if r.Form.Get("shmsize") != "" {
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/docker/api/types/filters"
9 9
 	"github.com/docker/docker/api/types/image"
10 10
 	"github.com/docker/docker/api/types/registry"
11
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
11 12
 )
12 13
 
13 14
 // Backend is all the methods that need to be implemented
... ...
@@ -34,7 +35,7 @@ type importExportBackend interface {
34 34
 }
35 35
 
36 36
 type registryBackend interface {
37
-	PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
37
+	PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
38 38
 	PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
39 39
 	SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
40 40
 }
... ...
@@ -4,11 +4,11 @@ import (
4 4
 	"context"
5 5
 	"encoding/base64"
6 6
 	"encoding/json"
7
-	"fmt"
8 7
 	"net/http"
9 8
 	"strconv"
10 9
 	"strings"
11 10
 
11
+	"github.com/containerd/containerd/platforms"
12 12
 	"github.com/docker/docker/api/server/httputils"
13 13
 	"github.com/docker/docker/api/types"
14 14
 	"github.com/docker/docker/api/types/filters"
... ...
@@ -36,7 +36,7 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
36 36
 		message  = r.Form.Get("message")
37 37
 		err      error
38 38
 		output   = ioutils.NewWriteFlusher(w)
39
-		platform = &specs.Platform{}
39
+		platform *specs.Platform
40 40
 	)
41 41
 	defer output.Close()
42 42
 
... ...
@@ -45,9 +45,15 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
45 45
 	version := httputils.VersionFromContext(ctx)
46 46
 	if versions.GreaterThanOrEqualTo(version, "1.32") {
47 47
 		apiPlatform := r.FormValue("platform")
48
-		platform = system.ParsePlatform(apiPlatform)
49
-		if err = system.ValidatePlatform(platform); err != nil {
50
-			err = fmt.Errorf("invalid platform: %s", err)
48
+		if apiPlatform != "" {
49
+			sp, err := platforms.Parse(apiPlatform)
50
+			if err != nil {
51
+				return err
52
+			}
53
+			if err := system.ValidatePlatform(sp); err != nil {
54
+				return err
55
+			}
56
+			platform = &sp
51 57
 		}
52 58
 	}
53 59
 
... ...
@@ -70,13 +76,17 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
70 70
 					authConfig = &types.AuthConfig{}
71 71
 				}
72 72
 			}
73
-			err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output)
73
+			err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output)
74 74
 		} else { //import
75 75
 			src := r.Form.Get("fromSrc")
76 76
 			// 'err' MUST NOT be defined within this block, we need any error
77 77
 			// generated from the download to be available to the output
78 78
 			// stream processing below
79
-			err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"])
79
+			os := ""
80
+			if platform != nil {
81
+				os = platform.OS
82
+			}
83
+			err = s.backend.ImportImage(src, repo, os, tag, message, r.Body, output, r.Form["changes"])
80 84
 		}
81 85
 	}
82 86
 	if err != nil {
... ...
@@ -5,6 +5,7 @@ import (
5 5
 
6 6
 	"github.com/docker/docker/api/types"
7 7
 	"github.com/docker/docker/pkg/streamformatter"
8
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
8 9
 )
9 10
 
10 11
 // PullOption defines different modes for accessing images
... ...
@@ -40,5 +41,5 @@ type GetImageAndLayerOptions struct {
40 40
 	PullOption PullOption
41 41
 	AuthConfig map[string]types.AuthConfig
42 42
 	Output     io.Writer
43
-	OS         string
43
+	Platform   *specs.Platform
44 44
 }
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/docker/api/types/container"
9 9
 	"github.com/docker/docker/api/types/filters"
10 10
 	"github.com/docker/go-units"
11
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
11 12
 )
12 13
 
13 14
 // CheckpointCreateOptions holds parameters to create a checkpoint from a container
... ...
@@ -180,7 +181,7 @@ type ImageBuildOptions struct {
180 180
 	ExtraHosts  []string // List of extra hosts
181 181
 	Target      string
182 182
 	SessionID   string
183
-	Platform    string
183
+	Platform    *specs.Platform
184 184
 	// Version specifies the version of the unerlying builder to use
185 185
 	Version BuilderVersion
186 186
 	// BuildID is an optional identifier that can be passed together with the
... ...
@@ -113,7 +113,7 @@ func (is *imageSource) resolveLocal(refStr string) ([]byte, error) {
113 113
 	return img.RawJSON(), nil
114 114
 }
115 115
 
116
-func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error) {
116
+func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) {
117 117
 	if preferLocal {
118 118
 		dt, err := is.resolveLocal(ref)
119 119
 		if err == nil {
... ...
@@ -126,7 +126,7 @@ func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string) (dige
126 126
 		dt   []byte
127 127
 	}
128 128
 	res, err := is.g.Do(ctx, ref, func(ctx context.Context) (interface{}, error) {
129
-		dgst, dt, err := imageutil.Config(ctx, ref, is.getResolver(ctx), is.ContentStore, "")
129
+		dgst, dt, err := imageutil.Config(ctx, ref, is.getResolver(ctx), is.ContentStore, platform)
130 130
 		if err != nil {
131 131
 			return nil, err
132 132
 		}
... ...
@@ -145,10 +145,16 @@ func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (sourc
145 145
 		return nil, errors.Errorf("invalid image identifier %v", id)
146 146
 	}
147 147
 
148
+	platform := platforms.DefaultSpec()
149
+	if imageIdentifier.Platform != nil {
150
+		platform = *imageIdentifier.Platform
151
+	}
152
+
148 153
 	p := &puller{
149 154
 		src:      imageIdentifier,
150 155
 		is:       is,
151 156
 		resolver: is.getResolver(ctx),
157
+		platform: platform,
152 158
 	}
153 159
 	return p, nil
154 160
 }
... ...
@@ -163,17 +169,20 @@ type puller struct {
163 163
 	resolveErr       error
164 164
 	resolver         remotes.Resolver
165 165
 	config           []byte
166
+	platform         ocispec.Platform
166 167
 }
167 168
 
168
-func (p *puller) mainManifestKey(dgst digest.Digest) (digest.Digest, error) {
169
+func (p *puller) mainManifestKey(dgst digest.Digest, platform ocispec.Platform) (digest.Digest, error) {
169 170
 	dt, err := json.Marshal(struct {
170
-		Digest digest.Digest
171
-		OS     string
172
-		Arch   string
171
+		Digest  digest.Digest
172
+		OS      string
173
+		Arch    string
174
+		Variant string `json:",omitempty"`
173 175
 	}{
174
-		Digest: p.desc.Digest,
175
-		OS:     runtime.GOOS,
176
-		Arch:   runtime.GOARCH,
176
+		Digest:  p.desc.Digest,
177
+		OS:      platform.OS,
178
+		Arch:    platform.Architecture,
179
+		Variant: platform.Variant,
177 180
 	})
178 181
 	if err != nil {
179 182
 		return "", err
... ...
@@ -248,7 +257,7 @@ func (p *puller) resolve(ctx context.Context) error {
248 248
 				return
249 249
 			}
250 250
 
251
-			_, dt, err := p.is.ResolveImageConfig(ctx, ref.String())
251
+			_, dt, err := p.is.ResolveImageConfig(ctx, ref.String(), &p.platform)
252 252
 			if err != nil {
253 253
 				p.resolveErr = err
254 254
 				resolveProgressDone(err)
... ...
@@ -266,7 +275,7 @@ func (p *puller) CacheKey(ctx context.Context, index int) (string, bool, error)
266 266
 	p.resolveLocal()
267 267
 
268 268
 	if p.desc.Digest != "" && index == 0 {
269
-		dgst, err := p.mainManifestKey(p.desc.Digest)
269
+		dgst, err := p.mainManifestKey(p.desc.Digest, p.platform)
270 270
 		if err != nil {
271 271
 			return "", false, err
272 272
 		}
... ...
@@ -282,7 +291,7 @@ func (p *puller) CacheKey(ctx context.Context, index int) (string, bool, error)
282 282
 	}
283 283
 
284 284
 	if p.desc.Digest != "" && index == 0 {
285
-		dgst, err := p.mainManifestKey(p.desc.Digest)
285
+		dgst, err := p.mainManifestKey(p.desc.Digest, p.platform)
286 286
 		if err != nil {
287 287
 			return "", false, err
288 288
 		}
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"time"
10 10
 
11 11
 	"github.com/containerd/containerd/content"
12
+	"github.com/containerd/containerd/platforms"
12 13
 	"github.com/docker/docker/api/types"
13 14
 	"github.com/docker/docker/api/types/backend"
14 15
 	"github.com/docker/docker/builder"
... ...
@@ -208,6 +209,10 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.
208 208
 		frontendAttrs["no-cache"] = ""
209 209
 	}
210 210
 
211
+	if opt.Options.Platform != nil {
212
+		frontendAttrs["platform"] = platforms.Format(*opt.Options.Platform)
213
+	}
214
+
211 215
 	exporterAttrs := map[string]string{}
212 216
 
213 217
 	if len(opt.Options.Tags) > 0 {
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"time"
11 11
 
12 12
 	"github.com/containerd/containerd/content"
13
+	"github.com/containerd/containerd/platforms"
13 14
 	"github.com/containerd/containerd/rootfs"
14 15
 	"github.com/docker/docker/distribution"
15 16
 	distmetadata "github.com/docker/docker/distribution/metadata"
... ...
@@ -122,6 +123,12 @@ func (w *Worker) Labels() map[string]string {
122 122
 	return w.Opt.Labels
123 123
 }
124 124
 
125
+// Platforms returns one or more platforms supported by the image.
126
+func (w *Worker) Platforms() []ocispec.Platform {
127
+	// does not handle lcow
128
+	return []ocispec.Platform{platforms.DefaultSpec()}
129
+}
130
+
125 131
 // LoadRef loads a reference by ID
126 132
 func (w *Worker) LoadRef(id string) (cache.ImmutableRef, error) {
127 133
 	return w.CacheManager.Get(context.TODO(), id)
... ...
@@ -129,26 +136,27 @@ func (w *Worker) LoadRef(id string) (cache.ImmutableRef, error) {
129 129
 
130 130
 // ResolveOp converts a LLB vertex into a LLB operation
131 131
 func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error) {
132
-	switch op := v.Sys().(type) {
133
-	case *pb.Op_Source:
134
-		return ops.NewSourceOp(v, op, w.SourceManager, w)
135
-	case *pb.Op_Exec:
136
-		return ops.NewExecOp(v, op, w.CacheManager, w.MetadataStore, w.Executor, w)
137
-	case *pb.Op_Build:
138
-		return ops.NewBuildOp(v, op, s, w)
139
-	default:
140
-		return nil, errors.Errorf("could not resolve %v", v)
132
+	if baseOp, ok := v.Sys().(*pb.Op); ok {
133
+		switch op := baseOp.Op.(type) {
134
+		case *pb.Op_Source:
135
+			return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, w)
136
+		case *pb.Op_Exec:
137
+			return ops.NewExecOp(v, op, w.CacheManager, w.MetadataStore, w.Executor, w)
138
+		case *pb.Op_Build:
139
+			return ops.NewBuildOp(v, op, s, w)
140
+		}
141 141
 	}
142
+	return nil, errors.Errorf("could not resolve %v", v)
142 143
 }
143 144
 
144 145
 // ResolveImageConfig returns image config for an image
145
-func (w *Worker) ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error) {
146
+func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) {
146 147
 	// ImageSource is typically source/containerimage
147 148
 	resolveImageConfig, ok := w.ImageSource.(resolveImageConfig)
148 149
 	if !ok {
149 150
 		return "", nil, errors.Errorf("worker %q does not implement ResolveImageConfig", w.ID())
150 151
 	}
151
-	return resolveImageConfig.ResolveImageConfig(ctx, ref)
152
+	return resolveImageConfig.ResolveImageConfig(ctx, ref, platform)
152 153
 }
153 154
 
154 155
 // Exec executes a process directly on a worker
... ...
@@ -319,5 +327,5 @@ func oneOffProgress(ctx context.Context, id string) func(err error) error {
319 319
 }
320 320
 
321 321
 type resolveImageConfig interface {
322
-	ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error)
322
+	ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error)
323 323
 }
... ...
@@ -104,13 +104,6 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
104 104
 		source = src
105 105
 	}
106 106
 
107
-	os := ""
108
-	apiPlatform := system.ParsePlatform(config.Options.Platform)
109
-	if apiPlatform.OS != "" {
110
-		os = apiPlatform.OS
111
-	}
112
-	config.Options.Platform = os
113
-
114 107
 	builderOptions := builderOptions{
115 108
 		Options:        config.Options,
116 109
 		ProgressWriter: config.ProgressWriter,
... ...
@@ -24,6 +24,7 @@ import (
24 24
 	"github.com/docker/docker/pkg/streamformatter"
25 25
 	"github.com/docker/docker/pkg/system"
26 26
 	"github.com/docker/docker/pkg/urlutil"
27
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
27 28
 	"github.com/pkg/errors"
28 29
 )
29 30
 
... ...
@@ -72,7 +73,7 @@ type copier struct {
72 72
 	source      builder.Source
73 73
 	pathCache   pathCache
74 74
 	download    sourceDownloader
75
-	platform    string
75
+	platform    *specs.Platform
76 76
 	// for cleanup. TODO: having copier.cleanup() is error prone and hard to
77 77
 	// follow. Code calling performCopy should manage the lifecycle of its params.
78 78
 	// Copier should take override source as input, not imageMount.
... ...
@@ -95,8 +96,14 @@ func (o *copier) createCopyInstruction(args []string, cmdName string) (copyInstr
95 95
 	last := len(args) - 1
96 96
 
97 97
 	// Work in platform-specific filepath semantics
98
-	inst.dest = fromSlash(args[last], o.platform)
99
-	separator := string(separator(o.platform))
98
+	// TODO: This OS switch for paths is NOT correct and should not be supported.
99
+	// Maintained for backwards compatibility
100
+	pathOS := runtime.GOOS
101
+	if o.platform != nil {
102
+		pathOS = o.platform.OS
103
+	}
104
+	inst.dest = fromSlash(args[last], pathOS)
105
+	separator := string(separator(pathOS))
100 106
 	infos, err := o.getCopyInfosForSourcePaths(args[0:last], inst.dest)
101 107
 	if err != nil {
102 108
 		return inst, errors.Wrapf(err, "%s failed", cmdName)
... ...
@@ -14,6 +14,7 @@ import (
14 14
 	"sort"
15 15
 	"strings"
16 16
 
17
+	"github.com/containerd/containerd/platforms"
17 18
 	"github.com/docker/docker/api"
18 19
 	"github.com/docker/docker/api/types/container"
19 20
 	"github.com/docker/docker/api/types/strslice"
... ...
@@ -27,6 +28,7 @@ import (
27 27
 	"github.com/moby/buildkit/frontend/dockerfile/instructions"
28 28
 	"github.com/moby/buildkit/frontend/dockerfile/parser"
29 29
 	"github.com/moby/buildkit/frontend/dockerfile/shell"
30
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
30 31
 	"github.com/pkg/errors"
31 32
 )
32 33
 
... ...
@@ -102,7 +104,7 @@ func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error {
102 102
 	copyInstruction.chownStr = c.Chown
103 103
 	copyInstruction.allowLocalDecompression = true
104 104
 
105
-	return d.builder.performCopy(d.state, copyInstruction)
105
+	return d.builder.performCopy(d, copyInstruction)
106 106
 }
107 107
 
108 108
 // COPY foo /path
... ...
@@ -126,7 +128,7 @@ func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error {
126 126
 	}
127 127
 	copyInstruction.chownStr = c.Chown
128 128
 
129
-	return d.builder.performCopy(d.state, copyInstruction)
129
+	return d.builder.performCopy(d, copyInstruction)
130 130
 }
131 131
 
132 132
 func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error) {
... ...
@@ -144,17 +146,32 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
144 144
 		imageRefOrID = stage.Image
145 145
 		localOnly = true
146 146
 	}
147
-	return d.builder.imageSources.Get(imageRefOrID, localOnly, d.state.operatingSystem)
147
+	return d.builder.imageSources.Get(imageRefOrID, localOnly, d.builder.options.Platform)
148 148
 }
149 149
 
150 150
 // FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
151 151
 //
152 152
 func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
153 153
 	d.builder.imageProber.Reset()
154
-	if err := system.ValidatePlatform(&cmd.Platform); err != nil {
155
-		return err
154
+
155
+	var platform *specs.Platform
156
+	if v := cmd.Platform; v != "" {
157
+		v, err := d.getExpandedString(d.shlex, v)
158
+		if err != nil {
159
+			return errors.Wrapf(err, "failed to process arguments for platform %s", v)
160
+		}
161
+
162
+		p, err := platforms.Parse(v)
163
+		if err != nil {
164
+			return errors.Wrapf(err, "failed to parse platform %s", v)
165
+		}
166
+		if err := system.ValidatePlatform(p); err != nil {
167
+			return err
168
+		}
169
+		platform = &p
156 170
 	}
157
-	image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.Platform.OS)
171
+
172
+	image, err := d.getFromImage(d.shlex, cmd.BaseName, platform)
158 173
 	if err != nil {
159 174
 		return err
160 175
 	}
... ...
@@ -200,82 +217,72 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
200 200
 	return nil
201 201
 }
202 202
 
203
-func (d *dispatchRequest) getExpandedImageName(shlex *shell.Lex, name string) (string, error) {
203
+func (d *dispatchRequest) getExpandedString(shlex *shell.Lex, str string) (string, error) {
204 204
 	substitutionArgs := []string{}
205 205
 	for key, value := range d.state.buildArgs.GetAllMeta() {
206 206
 		substitutionArgs = append(substitutionArgs, key+"="+value)
207 207
 	}
208 208
 
209
-	name, err := shlex.ProcessWord(name, substitutionArgs)
209
+	name, err := shlex.ProcessWord(str, substitutionArgs)
210 210
 	if err != nil {
211 211
 		return "", err
212 212
 	}
213 213
 	return name, nil
214 214
 }
215 215
 
216
-// getOsFromFlagsAndStage calculates the operating system if we need to pull an image.
217
-// stagePlatform contains the value supplied by optional `--platform=` on
218
-// a current FROM statement. b.builder.options.Platform contains the operating
219
-// system part of the optional flag passed in the API call (or CLI flag
220
-// through `docker build --platform=...`). Precedence is for an explicit
221
-// platform indication in the FROM statement.
222
-func (d *dispatchRequest) getOsFromFlagsAndStage(stageOS string) string {
223
-	switch {
224
-	case stageOS != "":
225
-		return stageOS
226
-	case d.builder.options.Platform != "":
227
-		// Note this is API "platform", but by this point, as the daemon is not
228
-		// multi-arch aware yet, it is guaranteed to only hold the OS part here.
229
-		return d.builder.options.Platform
230
-	default:
231
-		return runtime.GOOS
232
-	}
233
-}
234
-
235
-func (d *dispatchRequest) getImageOrStage(name string, stageOS string) (builder.Image, error) {
216
+func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform) (builder.Image, error) {
236 217
 	var localOnly bool
237 218
 	if im, ok := d.stages.getByName(name); ok {
238 219
 		name = im.Image
239 220
 		localOnly = true
240 221
 	}
241 222
 
242
-	os := d.getOsFromFlagsAndStage(stageOS)
223
+	if platform == nil {
224
+		platform = d.builder.options.Platform
225
+	}
243 226
 
244 227
 	// Windows cannot support a container with no base image unless it is LCOW.
245 228
 	if name == api.NoBaseImageSpecifier {
229
+		p := platforms.DefaultSpec()
230
+		if platform != nil {
231
+			p = *platform
232
+		}
246 233
 		imageImage := &image.Image{}
247
-		imageImage.OS = runtime.GOOS
234
+		imageImage.OS = p.OS
235
+
236
+		// old windows scratch handling
237
+		// TODO: scratch should not have an os. It should be nil image.
238
+		// Windows supports scratch. What is not supported is running containers
239
+		// from it.
248 240
 		if runtime.GOOS == "windows" {
249
-			switch os {
250
-			case "windows", "":
251
-				return nil, errors.New("Windows does not support FROM scratch")
252
-			case "linux":
241
+			if platform == nil || platform.OS == "linux" {
253 242
 				if !system.LCOWSupported() {
254 243
 					return nil, errors.New("Linux containers are not supported on this system")
255 244
 				}
256 245
 				imageImage.OS = "linux"
257
-			default:
258
-				return nil, errors.Errorf("operating system %q is not supported", os)
246
+			} else if platform.OS == "windows" {
247
+				return nil, errors.New("Windows does not support FROM scratch")
248
+			} else {
249
+				return nil, errors.Errorf("platform %s is not supported", platforms.Format(p))
259 250
 			}
260 251
 		}
261 252
 		return builder.Image(imageImage), nil
262 253
 	}
263
-	imageMount, err := d.builder.imageSources.Get(name, localOnly, os)
254
+	imageMount, err := d.builder.imageSources.Get(name, localOnly, platform)
264 255
 	if err != nil {
265 256
 		return nil, err
266 257
 	}
267 258
 	return imageMount.Image(), nil
268 259
 }
269
-func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, stageOS string) (builder.Image, error) {
270
-	name, err := d.getExpandedImageName(shlex, name)
260
+func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, platform *specs.Platform) (builder.Image, error) {
261
+	name, err := d.getExpandedString(shlex, name)
271 262
 	if err != nil {
272 263
 		return nil, err
273 264
 	}
274
-	return d.getImageOrStage(name, stageOS)
265
+	return d.getImageOrStage(name, platform)
275 266
 }
276 267
 
277 268
 func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
278
-
279 269
 	d.state.runConfig.OnBuild = append(d.state.runConfig.OnBuild, c.Expression)
280 270
 	return d.builder.commit(d.state, "ONBUILD "+c.Expression)
281 271
 }
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"runtime"
7 7
 	"testing"
8 8
 
9
+	"github.com/containerd/containerd/platforms"
9 10
 	"github.com/docker/docker/api/types"
10 11
 	"github.com/docker/docker/api/types/backend"
11 12
 	"github.com/docker/docker/api/types/container"
... ...
@@ -22,15 +23,17 @@ import (
22 22
 
23 23
 func newBuilderWithMockBackend() *Builder {
24 24
 	mockBackend := &MockBackend{}
25
+	defaultPlatform := platforms.DefaultSpec()
26
+	opts := &types.ImageBuildOptions{Platform: &defaultPlatform}
25 27
 	ctx := context.Background()
26 28
 	b := &Builder{
27
-		options:       &types.ImageBuildOptions{Platform: runtime.GOOS},
29
+		options:       opts,
28 30
 		docker:        mockBackend,
29 31
 		Stdout:        new(bytes.Buffer),
30 32
 		clientCtx:     ctx,
31 33
 		disableCommit: true,
32 34
 		imageSources: newImageSources(ctx, builderOptions{
33
-			Options: &types.ImageBuildOptions{Platform: runtime.GOOS},
35
+			Options: opts,
34 36
 			Backend: mockBackend,
35 37
 		}),
36 38
 		imageProber:      newImageProber(mockBackend, nil, false),
... ...
@@ -7,11 +7,12 @@ import (
7 7
 	"github.com/docker/docker/api/types/backend"
8 8
 	"github.com/docker/docker/builder"
9 9
 	dockerimage "github.com/docker/docker/image"
10
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
10 11
 	"github.com/pkg/errors"
11 12
 	"github.com/sirupsen/logrus"
12 13
 )
13 14
 
14
-type getAndMountFunc func(string, bool, string) (builder.Image, builder.ROLayer, error)
15
+type getAndMountFunc func(string, bool, *specs.Platform) (builder.Image, builder.ROLayer, error)
15 16
 
16 17
 // imageSources mounts images and provides a cache for mounted images. It tracks
17 18
 // all images so they can be unmounted at the end of the build.
... ...
@@ -22,7 +23,7 @@ type imageSources struct {
22 22
 }
23 23
 
24 24
 func newImageSources(ctx context.Context, options builderOptions) *imageSources {
25
-	getAndMount := func(idOrRef string, localOnly bool, osForPull string) (builder.Image, builder.ROLayer, error) {
25
+	getAndMount := func(idOrRef string, localOnly bool, platform *specs.Platform) (builder.Image, builder.ROLayer, error) {
26 26
 		pullOption := backend.PullOptionNoPull
27 27
 		if !localOnly {
28 28
 			if options.Options.PullParent {
... ...
@@ -35,7 +36,7 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
35 35
 			PullOption: pullOption,
36 36
 			AuthConfig: options.Options.AuthConfigs,
37 37
 			Output:     options.ProgressWriter.Output,
38
-			OS:         osForPull,
38
+			Platform:   platform,
39 39
 		})
40 40
 	}
41 41
 
... ...
@@ -45,12 +46,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
45 45
 	}
46 46
 }
47 47
 
48
-func (m *imageSources) Get(idOrRef string, localOnly bool, osForPull string) (*imageMount, error) {
48
+func (m *imageSources) Get(idOrRef string, localOnly bool, platform *specs.Platform) (*imageMount, error) {
49 49
 	if im, ok := m.byImageID[idOrRef]; ok {
50 50
 		return im, nil
51 51
 	}
52 52
 
53
-	image, layer, err := m.getImage(idOrRef, localOnly, osForPull)
53
+	image, layer, err := m.getImage(idOrRef, localOnly, platform)
54 54
 	if err != nil {
55 55
 		return nil, err
56 56
 	}
... ...
@@ -150,7 +150,8 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren
150 150
 	return nil
151 151
 }
152 152
 
153
-func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error {
153
+func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
154
+	state := req.state
154 155
 	srcHash := getSourceHashFromInfos(inst.infos)
155 156
 
156 157
 	var chownComment string
... ...
@@ -168,7 +169,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
168 168
 		return err
169 169
 	}
170 170
 
171
-	imageMount, err := b.imageSources.Get(state.imageID, true, state.operatingSystem)
171
+	imageMount, err := b.imageSources.Get(state.imageID, true, req.builder.options.Platform)
172 172
 	if err != nil {
173 173
 		return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
174 174
 	}
... ...
@@ -456,7 +457,7 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf
456 456
 	// is too small for builder scenarios where many users are
457 457
 	// using RUN statements to install large amounts of data.
458 458
 	// Use 127GB as that's the default size of a VHD in Hyper-V.
459
-	if runtime.GOOS == "windows" && options.Platform == "windows" {
459
+	if runtime.GOOS == "windows" && options.Platform != nil && options.Platform.OS == "windows" {
460 460
 		hc.StorageOpt = make(map[string]string)
461 461
 		hc.StorageOpt["size"] = "127GB"
462 462
 	}
... ...
@@ -8,8 +8,8 @@ import (
8 8
 	"net/http"
9 9
 	"net/url"
10 10
 	"strconv"
11
-	"strings"
12 11
 
12
+	"github.com/containerd/containerd/platforms"
13 13
 	"github.com/docker/docker/api/types"
14 14
 	"github.com/docker/docker/api/types/container"
15 15
 )
... ...
@@ -30,11 +30,11 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
30 30
 	}
31 31
 	headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
32 32
 
33
-	if options.Platform != "" {
33
+	if options.Platform != nil {
34 34
 		if err := cli.NewVersionError("1.32", "platform"); err != nil {
35 35
 			return types.ImageBuildResponse{}, err
36 36
 		}
37
-		query.Set("platform", options.Platform)
37
+		query.Set("platform", platforms.Format(*options.Platform))
38 38
 	}
39 39
 	headers.Set("Content-Type", "application/x-tar")
40 40
 
... ...
@@ -130,8 +130,8 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
130 130
 	if options.SessionID != "" {
131 131
 		query.Set("session", options.SessionID)
132 132
 	}
133
-	if options.Platform != "" {
134
-		query.Set("platform", strings.ToLower(options.Platform))
133
+	if options.Platform != nil {
134
+		query.Set("platform", platforms.Format(*options.Platform))
135 135
 	}
136 136
 	if options.BuildID != "" {
137 137
 		query.Set("buildid", options.BuildID)
... ...
@@ -23,6 +23,7 @@ import (
23 23
 	"github.com/docker/libnetwork/cluster"
24 24
 	networktypes "github.com/docker/libnetwork/types"
25 25
 	"github.com/docker/swarmkit/agent/exec"
26
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
26 27
 )
27 28
 
28 29
 // Backend defines the executor component for a swarm agent.
... ...
@@ -69,7 +70,7 @@ type VolumeBackend interface {
69 69
 
70 70
 // ImageBackend is used by an executor to perform image operations
71 71
 type ImageBackend interface {
72
-	PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
72
+	PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
73 73
 	GetRepository(context.Context, reference.Named, *types.AuthConfig) (distribution.Repository, bool, error)
74 74
 	LookupImage(name string) (*types.ImageInspect, error)
75 75
 }
... ...
@@ -8,7 +8,6 @@ import (
8 8
 	"fmt"
9 9
 	"io"
10 10
 	"os"
11
-	"runtime"
12 11
 	"strings"
13 12
 	"syscall"
14 13
 	"time"
... ...
@@ -97,8 +96,7 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
97 97
 	go func() {
98 98
 		// TODO @jhowardmsft LCOW Support: This will need revisiting as
99 99
 		// the stack is built up to include LCOW support for swarm.
100
-		platform := runtime.GOOS
101
-		err := c.imageBackend.PullImage(ctx, c.container.image(), "", platform, metaHeaders, authConfig, pw)
100
+		err := c.imageBackend.PullImage(ctx, c.container.image(), "", nil, metaHeaders, authConfig, pw)
102 101
 		pw.CloseWithError(err)
103 102
 	}()
104 103
 
... ...
@@ -3,6 +3,7 @@ package images // import "github.com/docker/docker/daemon/images"
3 3
 import (
4 4
 	"context"
5 5
 	"io"
6
+	"runtime"
6 7
 
7 8
 	"github.com/docker/distribution/reference"
8 9
 	"github.com/docker/docker/api/types"
... ...
@@ -14,6 +15,7 @@ import (
14 14
 	"github.com/docker/docker/pkg/stringid"
15 15
 	"github.com/docker/docker/pkg/system"
16 16
 	"github.com/docker/docker/registry"
17
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
17 18
 	"github.com/pkg/errors"
18 19
 )
19 20
 
... ...
@@ -137,7 +139,7 @@ func newROLayerForImage(img *image.Image, layerStore layer.Store) (builder.ROLay
137 137
 }
138 138
 
139 139
 // TODO: could this use the regular daemon PullImage ?
140
-func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) {
140
+func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, platform *specs.Platform) (*image.Image, error) {
141 141
 	ref, err := reference.ParseNormalizedNamed(name)
142 142
 	if err != nil {
143 143
 		return nil, err
... ...
@@ -156,7 +158,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf
156 156
 		pullRegistryAuth = &resolvedConfig
157 157
 	}
158 158
 
159
-	if err := i.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil {
159
+	if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
160 160
 		return nil, err
161 161
 	}
162 162
 	return i.GetImage(name)
... ...
@@ -166,11 +168,15 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf
166 166
 // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
167 167
 // leaking of layers.
168 168
 func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
169
-	if refOrID == "" {
170
-		if !system.IsOSSupported(opts.OS) {
169
+	if refOrID == "" { // ie FROM scratch
170
+		os := runtime.GOOS
171
+		if opts.Platform != nil {
172
+			os = opts.Platform.OS
173
+		}
174
+		if !system.IsOSSupported(os) {
171 175
 			return nil, nil, system.ErrNotSupportedOperatingSystem
172 176
 		}
173
-		layer, err := newROLayerForImage(nil, i.layerStores[opts.OS])
177
+		layer, err := newROLayerForImage(nil, i.layerStores[os])
174 178
 		return nil, layer, err
175 179
 	}
176 180
 
... ...
@@ -189,7 +195,7 @@ func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID s
189 189
 		}
190 190
 	}
191 191
 
192
-	image, err := i.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS)
192
+	image, err := i.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.Platform)
193 193
 	if err != nil {
194 194
 		return nil, nil, err
195 195
 	}
... ...
@@ -3,7 +3,6 @@ package images // import "github.com/docker/docker/daemon/images"
3 3
 import (
4 4
 	"context"
5 5
 	"io"
6
-	"runtime"
7 6
 	"strings"
8 7
 	"time"
9 8
 
... ...
@@ -16,11 +15,12 @@ import (
16 16
 	"github.com/docker/docker/pkg/progress"
17 17
 	"github.com/docker/docker/registry"
18 18
 	"github.com/opencontainers/go-digest"
19
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
19 20
 )
20 21
 
21 22
 // PullImage initiates a pull operation. image is the repository name to pull, and
22 23
 // tag may be either empty, or indicate a specific tag to pull.
23
-func (i *ImageService) PullImage(ctx context.Context, image, tag, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
24
+func (i *ImageService) PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
24 25
 	start := time.Now()
25 26
 	// Special case: "pull -a" may send an image name with a
26 27
 	// trailing :. This is ugly, but let's not break API
... ...
@@ -46,12 +46,12 @@ func (i *ImageService) PullImage(ctx context.Context, image, tag, os string, met
46 46
 		}
47 47
 	}
48 48
 
49
-	err = i.pullImageWithReference(ctx, ref, os, metaHeaders, authConfig, outStream)
49
+	err = i.pullImageWithReference(ctx, ref, platform, metaHeaders, authConfig, outStream)
50 50
 	imageActions.WithValues("pull").UpdateSince(start)
51 51
 	return err
52 52
 }
53 53
 
54
-func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, os string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
54
+func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference.Named, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error {
55 55
 	// Include a buffer so that slow client connections don't affect
56 56
 	// transfer performance.
57 57
 	progressChan := make(chan progress.Progress, 100)
... ...
@@ -65,11 +65,6 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference
65 65
 		close(writesDone)
66 66
 	}()
67 67
 
68
-	// Default to the host OS platform in case it hasn't been populated with an explicit value.
69
-	if os == "" {
70
-		os = runtime.GOOS
71
-	}
72
-
73 68
 	imagePullConfig := &distribution.ImagePullConfig{
74 69
 		Config: distribution.Config{
75 70
 			MetaHeaders:      metaHeaders,
... ...
@@ -83,7 +78,7 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference
83 83
 		},
84 84
 		DownloadManager: i.downloadManager,
85 85
 		Schema2Types:    distribution.ImageTypes,
86
-		OS:              os,
86
+		Platform:        platform,
87 87
 	}
88 88
 
89 89
 	err := distribution.Pull(ctx, ref, imagePullConfig)
... ...
@@ -60,9 +60,8 @@ type ImagePullConfig struct {
60 60
 	// Schema2Types is the valid schema2 configuration types allowed
61 61
 	// by the pull operation.
62 62
 	Schema2Types []string
63
-	// OS is the requested operating system of the image being pulled to ensure it can be validated
64
-	// when the host OS supports multiple image operating systems.
65
-	OS string
63
+	// Platform is the requested platform of the image being pulled
64
+	Platform *specs.Platform
66 65
 }
67 66
 
68 67
 // ImagePushConfig stores push configuration.
... ...
@@ -171,7 +170,7 @@ func (s *imageConfigStore) PlatformFromConfig(c []byte) (*specs.Platform, error)
171 171
 	if !system.IsOSSupported(os) {
172 172
 		return nil, system.ErrNotSupportedOperatingSystem
173 173
 	}
174
-	return &specs.Platform{OS: os, OSVersion: unmarshalledConfig.OSVersion}, nil
174
+	return &specs.Platform{OS: os, Architecture: unmarshalledConfig.Architecture, OSVersion: unmarshalledConfig.OSVersion}, nil
175 175
 }
176 176
 
177 177
 type storeLayerProvider struct {
... ...
@@ -3,7 +3,6 @@ package distribution // import "github.com/docker/docker/distribution"
3 3
 import (
4 4
 	"context"
5 5
 	"fmt"
6
-	"runtime"
7 6
 
8 7
 	"github.com/docker/distribution/reference"
9 8
 	"github.com/docker/docker/api"
... ...
@@ -12,6 +11,7 @@ import (
12 12
 	refstore "github.com/docker/docker/reference"
13 13
 	"github.com/docker/docker/registry"
14 14
 	"github.com/opencontainers/go-digest"
15
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
15 16
 	"github.com/pkg/errors"
16 17
 	"github.com/sirupsen/logrus"
17 18
 )
... ...
@@ -21,7 +21,7 @@ type Puller interface {
21 21
 	// Pull tries to pull the image referenced by `tag`
22 22
 	// Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
23 23
 	//
24
-	Pull(ctx context.Context, ref reference.Named, os string) error
24
+	Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) error
25 25
 }
26 26
 
27 27
 // newPuller returns a Puller interface that will pull from either a v1 or v2
... ...
@@ -115,12 +115,7 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
115 115
 			continue
116 116
 		}
117 117
 
118
-		// Make sure we default the OS if it hasn't been supplied
119
-		if imagePullConfig.OS == "" {
120
-			imagePullConfig.OS = runtime.GOOS
121
-		}
122
-
123
-		if err := puller.Pull(ctx, ref, imagePullConfig.OS); err != nil {
118
+		if err := puller.Pull(ctx, ref, imagePullConfig.Platform); err != nil {
124 119
 			// Was this pull cancelled? If so, don't try to fall
125 120
 			// back.
126 121
 			fallback := false
... ...
@@ -25,6 +25,7 @@ import (
25 25
 	"github.com/docker/docker/pkg/progress"
26 26
 	"github.com/docker/docker/pkg/stringid"
27 27
 	"github.com/docker/docker/registry"
28
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
28 29
 	"github.com/sirupsen/logrus"
29 30
 )
30 31
 
... ...
@@ -36,7 +37,7 @@ type v1Puller struct {
36 36
 	session     *registry.Session
37 37
 }
38 38
 
39
-func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, os string) error {
39
+func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, _ *specs.Platform) error {
40 40
 	if _, isCanonical := ref.(reference.Canonical); isCanonical {
41 41
 		// Allowing fallback, because HTTPS v1 is before HTTP v2
42 42
 		return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}}
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"runtime"
12 12
 	"strings"
13 13
 
14
+	"github.com/containerd/containerd/platforms"
14 15
 	"github.com/docker/distribution"
15 16
 	"github.com/docker/distribution/manifest/manifestlist"
16 17
 	"github.com/docker/distribution/manifest/schema1"
... ...
@@ -63,7 +64,7 @@ type v2Puller struct {
63 63
 	confirmedV2 bool
64 64
 }
65 65
 
66
-func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (err error) {
66
+func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
67 67
 	// TODO(tiborvass): was ReceiveTimeout
68 68
 	p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
69 69
 	if err != nil {
... ...
@@ -71,7 +72,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (er
71 71
 		return err
72 72
 	}
73 73
 
74
-	if err = p.pullV2Repository(ctx, ref, os); err != nil {
74
+	if err = p.pullV2Repository(ctx, ref, platform); err != nil {
75 75
 		if _, ok := err.(fallbackError); ok {
76 76
 			return err
77 77
 		}
... ...
@@ -86,10 +87,10 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, os string) (er
86 86
 	return err
87 87
 }
88 88
 
89
-func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os string) (err error) {
89
+func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform *specs.Platform) (err error) {
90 90
 	var layersDownloaded bool
91 91
 	if !reference.IsNameOnly(ref) {
92
-		layersDownloaded, err = p.pullV2Tag(ctx, ref, os)
92
+		layersDownloaded, err = p.pullV2Tag(ctx, ref, platform)
93 93
 		if err != nil {
94 94
 			return err
95 95
 		}
... ...
@@ -111,7 +112,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, os
111 111
 			if err != nil {
112 112
 				return err
113 113
 			}
114
-			pulledNew, err := p.pullV2Tag(ctx, tagRef, os)
114
+			pulledNew, err := p.pullV2Tag(ctx, tagRef, platform)
115 115
 			if err != nil {
116 116
 				// Since this is the pull-all-tags case, don't
117 117
 				// allow an error pulling a particular tag to
... ...
@@ -327,7 +328,7 @@ func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
327 327
 	ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
328 328
 }
329 329
 
330
-func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string) (tagUpdated bool, err error) {
330
+func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, platform *specs.Platform) (tagUpdated bool, err error) {
331 331
 	manSvc, err := p.repo.Manifests(ctx)
332 332
 	if err != nil {
333 333
 		return false, err
... ...
@@ -391,17 +392,17 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string
391 391
 		if p.config.RequireSchema2 {
392 392
 			return false, fmt.Errorf("invalid manifest: not schema2")
393 393
 		}
394
-		id, manifestDigest, err = p.pullSchema1(ctx, ref, v, os)
394
+		id, manifestDigest, err = p.pullSchema1(ctx, ref, v, platform)
395 395
 		if err != nil {
396 396
 			return false, err
397 397
 		}
398 398
 	case *schema2.DeserializedManifest:
399
-		id, manifestDigest, err = p.pullSchema2(ctx, ref, v, os)
399
+		id, manifestDigest, err = p.pullSchema2(ctx, ref, v, platform)
400 400
 		if err != nil {
401 401
 			return false, err
402 402
 		}
403 403
 	case *manifestlist.DeserializedManifestList:
404
-		id, manifestDigest, err = p.pullManifestList(ctx, ref, v, os)
404
+		id, manifestDigest, err = p.pullManifestList(ctx, ref, v, platform)
405 405
 		if err != nil {
406 406
 			return false, err
407 407
 		}
... ...
@@ -437,7 +438,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string
437 437
 	return true, nil
438 438
 }
439 439
 
440
-func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) {
440
+func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
441 441
 	var verifiedManifest *schema1.Manifest
442 442
 	verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
443 443
 	if err != nil {
... ...
@@ -509,6 +510,17 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
509 509
 		}
510 510
 	}
511 511
 
512
+	// In the situation that the API call didn't specify an OS explicitly, but
513
+	// we support the operating system, switch to that operating system.
514
+	// eg FROM supertest2014/nyan with no platform specifier, and docker build
515
+	// with no --platform= flag under LCOW.
516
+	requestedOS := ""
517
+	if platform != nil {
518
+		requestedOS = platform.OS
519
+	} else if system.IsOSSupported(configOS) {
520
+		requestedOS = configOS
521
+	}
522
+
512 523
 	// Early bath if the requested OS doesn't match that of the configuration.
513 524
 	// This avoids doing the download, only to potentially fail later.
514 525
 	if !strings.EqualFold(configOS, requestedOS) {
... ...
@@ -536,7 +548,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
536 536
 	return imageID, manifestDigest, nil
537 537
 }
538 538
 
539
-func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) {
539
+func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, platform *specs.Platform) (id digest.Digest, manifestDigest digest.Digest, err error) {
540 540
 	manifestDigest, err = schema2ManifestDigest(ref, mfst)
541 541
 	if err != nil {
542 542
 		return "", "", err
... ...
@@ -592,6 +604,11 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
592 592
 		configPlatform   *specs.Platform // for LCOW when registering downloaded layers
593 593
 	)
594 594
 
595
+	layerStoreOS := runtime.GOOS
596
+	if platform != nil {
597
+		layerStoreOS = platform.OS
598
+	}
599
+
595 600
 	// https://github.com/docker/docker/issues/24766 - Err on the side of caution,
596 601
 	// explicitly blocking images intended for linux from the Windows daemon. On
597 602
 	// Windows, we do this before the attempt to download, effectively serialising
... ...
@@ -615,11 +632,13 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
615 615
 		if len(descriptors) != len(configRootFS.DiffIDs) {
616 616
 			return "", "", errRootFSMismatch
617 617
 		}
618
-
619
-		// Early bath if the requested OS doesn't match that of the configuration.
620
-		// This avoids doing the download, only to potentially fail later.
621
-		if !strings.EqualFold(configPlatform.OS, requestedOS) {
622
-			return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, requestedOS)
618
+		if platform == nil {
619
+			// Early bath if the requested OS doesn't match that of the configuration.
620
+			// This avoids doing the download, only to potentially fail later.
621
+			if !system.IsOSSupported(configPlatform.OS) {
622
+				return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configPlatform.OS, layerStoreOS)
623
+			}
624
+			layerStoreOS = configPlatform.OS
623 625
 		}
624 626
 
625 627
 		// Populate diff ids in descriptors to avoid downloading foreign layers
... ...
@@ -636,7 +655,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
636 636
 				rootFS image.RootFS
637 637
 			)
638 638
 			downloadRootFS := *image.NewRootFS()
639
-			rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, requestedOS, descriptors, p.config.ProgressOutput)
639
+			rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layerStoreOS, descriptors, p.config.ProgressOutput)
640 640
 			if err != nil {
641 641
 				// Intentionally do not cancel the config download here
642 642
 				// as the error from config download (if there is one)
... ...
@@ -722,18 +741,22 @@ func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan
722 722
 
723 723
 // pullManifestList handles "manifest lists" which point to various
724 724
 // platform-specific manifests.
725
-func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, os string) (id digest.Digest, manifestListDigest digest.Digest, err error) {
725
+func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, pp *specs.Platform) (id digest.Digest, manifestListDigest digest.Digest, err error) {
726 726
 	manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
727 727
 	if err != nil {
728 728
 		return "", "", err
729 729
 	}
730 730
 
731
-	logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), os, runtime.GOARCH)
731
+	var platform specs.Platform
732
+	if pp != nil {
733
+		platform = *pp
734
+	}
735
+	logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), platforms.Format(platform), runtime.GOARCH)
732 736
 
733
-	manifestMatches := filterManifests(mfstList.Manifests, os)
737
+	manifestMatches := filterManifests(mfstList.Manifests, platform)
734 738
 
735 739
 	if len(manifestMatches) == 0 {
736
-		errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", os, runtime.GOARCH)
740
+		errMsg := fmt.Sprintf("no matching manifest for %s in the manifest list entries", platforms.Format(platform))
737 741
 		logrus.Debugf(errMsg)
738 742
 		return "", "", errors.New(errMsg)
739 743
 	}
... ...
@@ -764,12 +787,14 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
764 764
 
765 765
 	switch v := manifest.(type) {
766 766
 	case *schema1.SignedManifest:
767
-		id, _, err = p.pullSchema1(ctx, manifestRef, v, os)
767
+		platform := toOCIPlatform(manifestMatches[0].Platform)
768
+		id, _, err = p.pullSchema1(ctx, manifestRef, v, &platform)
768 769
 		if err != nil {
769 770
 			return "", "", err
770 771
 		}
771 772
 	case *schema2.DeserializedManifest:
772
-		id, _, err = p.pullSchema2(ctx, manifestRef, v, os)
773
+		platform := toOCIPlatform(manifestMatches[0].Platform)
774
+		id, _, err = p.pullSchema2(ctx, manifestRef, v, &platform)
773 775
 		if err != nil {
774 776
 			return "", "", err
775 777
 		}
... ...
@@ -939,3 +964,13 @@ func fixManifestLayers(m *schema1.Manifest) error {
939 939
 func createDownloadFile() (*os.File, error) {
940 940
 	return ioutil.TempFile("", "GetImageBlob")
941 941
 }
942
+
943
+func toOCIPlatform(p manifestlist.PlatformSpec) specs.Platform {
944
+	return specs.Platform{
945
+		OS:           p.OS,
946
+		Architecture: p.Architecture,
947
+		Variant:      p.Variant,
948
+		OSFeatures:   p.OSFeatures,
949
+		OSVersion:    p.OSVersion,
950
+	}
951
+}
... ...
@@ -4,10 +4,11 @@ package distribution // import "github.com/docker/docker/distribution"
4 4
 
5 5
 import (
6 6
 	"context"
7
-	"runtime"
8 7
 
8
+	"github.com/containerd/containerd/platforms"
9 9
 	"github.com/docker/distribution"
10 10
 	"github.com/docker/distribution/manifest/manifestlist"
11
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
11 12
 	"github.com/sirupsen/logrus"
12 13
 )
13 14
 
... ...
@@ -16,15 +17,28 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
16 16
 	return blobs.Open(ctx, ld.digest)
17 17
 }
18 18
 
19
-func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor {
19
+func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platform) []manifestlist.ManifestDescriptor {
20
+	p = platforms.Normalize(withDefault(p))
21
+	m := platforms.NewMatcher(p)
20 22
 	var matches []manifestlist.ManifestDescriptor
21
-	for _, manifestDescriptor := range manifests {
22
-		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os {
23
-			matches = append(matches, manifestDescriptor)
23
+	for _, desc := range manifests {
24
+		if m.Match(toOCIPlatform(desc.Platform)) {
25
+			matches = append(matches, desc)
26
+			logrus.Debugf("found match for %s with media type %s, digest %s", platforms.Format(p), desc.MediaType, desc.Digest.String())
27
+		}
28
+	}
24 29
 
25
-			logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
30
+	// deprecated: backwards compatibility with older versions that didn't compare variant
31
+	if len(matches) == 0 && p.Architecture == "arm" {
32
+		p = platforms.Normalize(p)
33
+		for _, desc := range manifests {
34
+			if desc.Platform.OS == p.OS && desc.Platform.Architecture == p.Architecture {
35
+				matches = append(matches, desc)
36
+				logrus.Debugf("found deprecated partial match for %s with media type %s, digest %s", platforms.Format(p), desc.MediaType, desc.Digest.String())
37
+			}
26 38
 		}
27 39
 	}
40
+
28 41
 	return matches
29 42
 }
30 43
 
... ...
@@ -32,3 +46,15 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []m
32 32
 func checkImageCompatibility(imageOS, imageOSVersion string) error {
33 33
 	return nil
34 34
 }
35
+
36
+func withDefault(p specs.Platform) specs.Platform {
37
+	def := platforms.DefaultSpec()
38
+	if p.OS == "" {
39
+		p.OS = def.OS
40
+	}
41
+	if p.Architecture == "" {
42
+		p.Architecture = def.Architecture
43
+		p.Variant = def.Variant
44
+	}
45
+	return p
46
+}
... ...
@@ -16,6 +16,7 @@ import (
16 16
 	"github.com/docker/distribution/manifest/schema2"
17 17
 	"github.com/docker/distribution/registry/client/transport"
18 18
 	"github.com/docker/docker/pkg/system"
19
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
19 20
 	"github.com/sirupsen/logrus"
20 21
 )
21 22
 
... ...
@@ -62,24 +63,27 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
62 62
 	return rsc, err
63 63
 }
64 64
 
65
-func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor {
66
-	osVersion := ""
67
-	if os == "windows" {
68
-		version := system.GetOSVersion()
69
-		osVersion = fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
70
-		logrus.Debugf("will prefer entries with version %s", osVersion)
71
-	}
65
+func filterManifests(manifests []manifestlist.ManifestDescriptor, p specs.Platform) []manifestlist.ManifestDescriptor {
66
+	version := system.GetOSVersion()
67
+	osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
68
+	logrus.Debugf("will prefer Windows entries with version %s", osVersion)
72 69
 
73 70
 	var matches []manifestlist.ManifestDescriptor
71
+	foundWindowsMatch := false
74 72
 	for _, manifestDescriptor := range manifests {
75
-		if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os {
73
+		if (manifestDescriptor.Platform.Architecture == runtime.GOARCH) &&
74
+			((p.OS != "" && manifestDescriptor.Platform.OS == p.OS) || // Explicit user request for an OS we know we support
75
+				(p.OS == "" && system.IsOSSupported(manifestDescriptor.Platform.OS))) { // No user requested OS, but one we can support
76 76
 			matches = append(matches, manifestDescriptor)
77
-			logrus.Debugf("found match for %s/%s %s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
77
+			logrus.Debugf("found match %s/%s %s with media type %s, digest %s", manifestDescriptor.Platform.OS, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
78
+			if strings.EqualFold("windows", manifestDescriptor.Platform.OS) {
79
+				foundWindowsMatch = true
80
+			}
78 81
 		} else {
79
-			logrus.Debugf("ignoring %s/%s %s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
82
+			logrus.Debugf("ignoring %s/%s %s with media type %s, digest %s", manifestDescriptor.Platform.OS, manifestDescriptor.Platform.Architecture, manifestDescriptor.Platform.OSVersion, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
80 83
 		}
81 84
 	}
82
-	if os == "windows" {
85
+	if foundWindowsMatch {
83 86
 		sort.Stable(manifestsByVersion{osVersion, matches})
84 87
 	}
85 88
 	return matches
... ...
@@ -5,7 +5,6 @@ import (
5 5
 	"net/http"
6 6
 	"net/http/httptest"
7 7
 	"net/url"
8
-	"runtime"
9 8
 	"strings"
10 9
 	"testing"
11 10
 
... ...
@@ -84,7 +83,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) {
84 84
 	logrus.Debug("About to pull")
85 85
 	// We expect it to fail, since we haven't mock'd the full registry exchange in our handler above
86 86
 	tag, _ := reference.WithTag(n, "tag_goes_here")
87
-	_ = p.pullV2Repository(ctx, tag, runtime.GOOS)
87
+	_ = p.pullV2Repository(ctx, tag, nil)
88 88
 }
89 89
 
90 90
 func TestTokenPassThru(t *testing.T) {
... ...
@@ -76,7 +76,8 @@ func (is *store) restore() error {
76 76
 		var l layer.Layer
77 77
 		if chainID := img.RootFS.ChainID(); chainID != "" {
78 78
 			if !system.IsOSSupported(img.OperatingSystem()) {
79
-				return system.ErrNotSupportedOperatingSystem
79
+				logrus.Errorf("not restoring image with unsupported operating system %v, %v, %s", dgst, chainID, img.OperatingSystem())
80
+				return nil
80 81
 			}
81 82
 			l, err = is.lss[img.OperatingSystem()].Get(chainID)
82 83
 			if err != nil {
... ...
@@ -11,6 +11,7 @@ import (
11 11
 	"reflect"
12 12
 	"runtime"
13 13
 
14
+	"github.com/containerd/containerd/platforms"
14 15
 	"github.com/docker/distribution"
15 16
 	"github.com/docker/distribution/reference"
16 17
 	"github.com/docker/docker/image"
... ...
@@ -421,9 +422,11 @@ func checkCompatibleOS(imageOS string) error {
421 421
 	if runtime.GOOS != "windows" && imageOS == "windows" {
422 422
 		return fmt.Errorf("cannot load %s image on %s", imageOS, runtime.GOOS)
423 423
 	}
424
-	// Finally, check the image OS is supported for the platform.
425
-	if err := system.ValidatePlatform(system.ParsePlatform(imageOS)); err != nil {
426
-		return fmt.Errorf("cannot load %s image on %s: %s", imageOS, runtime.GOOS, err)
424
+
425
+	p, err := platforms.Parse(imageOS)
426
+	if err != nil {
427
+		return err
427 428
 	}
428
-	return nil
429
+
430
+	return system.ValidatePlatform(p)
429 431
 }
... ...
@@ -1,69 +1,32 @@
1 1
 package system // import "github.com/docker/docker/pkg/system"
2 2
 
3 3
 import (
4
-	"fmt"
5 4
 	"runtime"
6 5
 	"strings"
7 6
 
8 7
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
8
+	"github.com/pkg/errors"
9 9
 )
10 10
 
11
-// ValidatePlatform determines if a platform structure is valid.
12
-// TODO This is a temporary function - can be replaced by parsing from
13
-// https://github.com/containerd/containerd/pull/1403/files at a later date.
14
-// @jhowardmsft
15
-func ValidatePlatform(platform *specs.Platform) error {
16
-	platform.Architecture = strings.ToLower(platform.Architecture)
17
-	platform.OS = strings.ToLower(platform.OS)
18
-	// Based on https://github.com/moby/moby/pull/34642#issuecomment-330375350, do
19
-	// not support anything except operating system.
20
-	if platform.Architecture != "" {
21
-		return fmt.Errorf("invalid platform architecture %q", platform.Architecture)
22
-	}
23
-	if platform.OS != "" {
24
-		if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) {
25
-			return fmt.Errorf("invalid platform os %q", platform.OS)
26
-		}
27
-	}
28
-	if len(platform.OSFeatures) != 0 {
29
-		return fmt.Errorf("invalid platform osfeatures %q", platform.OSFeatures)
30
-	}
31
-	if platform.OSVersion != "" {
32
-		return fmt.Errorf("invalid platform osversion %q", platform.OSVersion)
33
-	}
34
-	if platform.Variant != "" {
35
-		return fmt.Errorf("invalid platform variant %q", platform.Variant)
36
-	}
37
-	return nil
38
-}
39
-
40
-// ParsePlatform parses a platform string in the format os[/arch[/variant]
41
-// into an OCI image-spec platform structure.
42
-// TODO This is a temporary function - can be replaced by parsing from
43
-// https://github.com/containerd/containerd/pull/1403/files at a later date.
44
-// @jhowardmsft
45
-func ParsePlatform(in string) *specs.Platform {
46
-	p := &specs.Platform{}
47
-	elements := strings.SplitN(strings.ToLower(in), "/", 3)
48
-	if len(elements) == 3 {
49
-		p.Variant = elements[2]
50
-	}
51
-	if len(elements) >= 2 {
52
-		p.Architecture = elements[1]
53
-	}
54
-	if len(elements) >= 1 {
55
-		p.OS = elements[0]
56
-	}
57
-	return p
58
-}
59
-
60 11
 // IsOSSupported determines if an operating system is supported by the host
61 12
 func IsOSSupported(os string) bool {
62
-	if runtime.GOOS == os {
13
+	if strings.EqualFold(runtime.GOOS, os) {
63 14
 		return true
64 15
 	}
65
-	if LCOWSupported() && os == "linux" {
16
+	if LCOWSupported() && strings.EqualFold(os, "linux") {
66 17
 		return true
67 18
 	}
68 19
 	return false
69 20
 }
21
+
22
+// ValidatePlatform determines if a platform structure is valid.
23
+// TODO This is a temporary windows-only function, should be replaced by
24
+// comparison of worker capabilities
25
+func ValidatePlatform(platform specs.Platform) error {
26
+	if runtime.GOOS == "windows" {
27
+		if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) {
28
+			return errors.Errorf("unsupported os %s", platform.OS)
29
+		}
30
+	}
31
+	return nil
32
+}
... ...
@@ -26,7 +26,7 @@ github.com/imdario/mergo v0.3.5
26 26
 golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
27 27
 
28 28
 # buildkit
29
-github.com/moby/buildkit dbf67a691ce77023a0a5ce9b005298631f8bbb4e
29
+github.com/moby/buildkit cce2080ddbe4698912f2290892b247c83627efa8
30 30
 github.com/tonistiigi/fsutil 8abad97ee3969cdf5e9c367f46adba2c212b3ddb
31 31
 github.com/grpc-ecosystem/grpc-opentracing 8e809c8a86450a29b90dcc9efbf062d0fe6d9746
32 32
 github.com/opentracing/opentracing-go 1361b9cd60be79c4c3a7fa9841b3c132e40066a7
... ...
@@ -542,8 +542,9 @@ func (m *ListWorkersResponse) GetRecord() []*WorkerRecord {
542 542
 }
543 543
 
544 544
 type WorkerRecord struct {
545
-	ID     string            `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
546
-	Labels map[string]string `protobuf:"bytes,2,rep,name=Labels" json:"Labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
545
+	ID        string            `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
546
+	Labels    map[string]string `protobuf:"bytes,2,rep,name=Labels" json:"Labels,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
547
+	Platforms []pb.Platform     `protobuf:"bytes,3,rep,name=platforms" json:"platforms"`
547 548
 }
548 549
 
549 550
 func (m *WorkerRecord) Reset()                    { *m = WorkerRecord{} }
... ...
@@ -565,6 +566,13 @@ func (m *WorkerRecord) GetLabels() map[string]string {
565 565
 	return nil
566 566
 }
567 567
 
568
+func (m *WorkerRecord) GetPlatforms() []pb.Platform {
569
+	if m != nil {
570
+		return m.Platforms
571
+	}
572
+	return nil
573
+}
574
+
568 575
 func init() {
569 576
 	proto.RegisterType((*PruneRequest)(nil), "moby.buildkit.v1.PruneRequest")
570 577
 	proto.RegisterType((*DiskUsageRequest)(nil), "moby.buildkit.v1.DiskUsageRequest")
... ...
@@ -1650,6 +1658,18 @@ func (m *WorkerRecord) MarshalTo(dAtA []byte) (int, error) {
1650 1650
 			i += copy(dAtA[i:], v)
1651 1651
 		}
1652 1652
 	}
1653
+	if len(m.Platforms) > 0 {
1654
+		for _, msg := range m.Platforms {
1655
+			dAtA[i] = 0x1a
1656
+			i++
1657
+			i = encodeVarintControl(dAtA, i, uint64(msg.Size()))
1658
+			n, err := msg.MarshalTo(dAtA[i:])
1659
+			if err != nil {
1660
+				return 0, err
1661
+			}
1662
+			i += n
1663
+		}
1664
+	}
1653 1665
 	return i, nil
1654 1666
 }
1655 1667
 
... ...
@@ -1979,6 +1999,12 @@ func (m *WorkerRecord) Size() (n int) {
1979 1979
 			n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize))
1980 1980
 		}
1981 1981
 	}
1982
+	if len(m.Platforms) > 0 {
1983
+		for _, e := range m.Platforms {
1984
+			l = e.Size()
1985
+			n += 1 + l + sovControl(uint64(l))
1986
+		}
1987
+	}
1982 1988
 	return n
1983 1989
 }
1984 1990
 
... ...
@@ -4663,6 +4689,37 @@ func (m *WorkerRecord) Unmarshal(dAtA []byte) error {
4663 4663
 			}
4664 4664
 			m.Labels[mapkey] = mapvalue
4665 4665
 			iNdEx = postIndex
4666
+		case 3:
4667
+			if wireType != 2 {
4668
+				return fmt.Errorf("proto: wrong wireType = %d for field Platforms", wireType)
4669
+			}
4670
+			var msglen int
4671
+			for shift := uint(0); ; shift += 7 {
4672
+				if shift >= 64 {
4673
+					return ErrIntOverflowControl
4674
+				}
4675
+				if iNdEx >= l {
4676
+					return io.ErrUnexpectedEOF
4677
+				}
4678
+				b := dAtA[iNdEx]
4679
+				iNdEx++
4680
+				msglen |= (int(b) & 0x7F) << shift
4681
+				if b < 0x80 {
4682
+					break
4683
+				}
4684
+			}
4685
+			if msglen < 0 {
4686
+				return ErrInvalidLengthControl
4687
+			}
4688
+			postIndex := iNdEx + msglen
4689
+			if postIndex > l {
4690
+				return io.ErrUnexpectedEOF
4691
+			}
4692
+			m.Platforms = append(m.Platforms, pb.Platform{})
4693
+			if err := m.Platforms[len(m.Platforms)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
4694
+				return err
4695
+			}
4696
+			iNdEx = postIndex
4666 4697
 		default:
4667 4698
 			iNdEx = preIndex
4668 4699
 			skippy, err := skipControl(dAtA[iNdEx:])
... ...
@@ -4792,80 +4849,81 @@ var (
4792 4792
 func init() { proto.RegisterFile("control.proto", fileDescriptorControl) }
4793 4793
 
4794 4794
 var fileDescriptorControl = []byte{
4795
-	// 1192 bytes of a gzipped FileDescriptorProto
4796
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcd, 0x6e, 0x23, 0x45,
4797
-	0x10, 0x66, 0x6c, 0xc7, 0x3f, 0x65, 0x27, 0x0a, 0x0d, 0xac, 0x46, 0x03, 0x24, 0x66, 0x00, 0xc9,
4798
-	0x8a, 0x76, 0xc7, 0xd9, 0xc0, 0x22, 0xc8, 0x61, 0xb5, 0xeb, 0x78, 0x11, 0x89, 0x12, 0xb1, 0x74,
4799
-	0x36, 0xac, 0xc4, 0x6d, 0x6c, 0x77, 0xbc, 0xa3, 0xd8, 0xd3, 0xa6, 0xbb, 0x27, 0xda, 0xf0, 0x14,
4800
-	0x1c, 0xb8, 0xf2, 0x14, 0x1c, 0x38, 0x73, 0x40, 0xda, 0x23, 0x67, 0x0e, 0x59, 0x94, 0x3b, 0x3c,
4801
-	0x03, 0xea, 0x9f, 0xb1, 0xdb, 0x1e, 0xe7, 0xc7, 0xd9, 0x53, 0xba, 0x3a, 0x5f, 0x7d, 0x53, 0x5d,
4802
-	0x5f, 0xb9, 0xaa, 0x60, 0xb9, 0x4b, 0x63, 0xc1, 0xe8, 0x20, 0x18, 0x31, 0x2a, 0x28, 0x5a, 0x1d,
4803
-	0xd2, 0xce, 0x59, 0xd0, 0x49, 0xa2, 0x41, 0xef, 0x24, 0x12, 0xc1, 0xe9, 0x7d, 0xef, 0x5e, 0x3f,
4804
-	0x12, 0x2f, 0x92, 0x4e, 0xd0, 0xa5, 0xc3, 0x66, 0x9f, 0xf6, 0x69, 0x53, 0x01, 0x3b, 0xc9, 0xb1,
4805
-	0xb2, 0x94, 0xa1, 0x4e, 0x9a, 0xc0, 0x5b, 0xef, 0x53, 0xda, 0x1f, 0x90, 0x09, 0x4a, 0x44, 0x43,
4806
-	0xc2, 0x45, 0x38, 0x1c, 0x19, 0xc0, 0x5d, 0x8b, 0x4f, 0x7e, 0xac, 0x99, 0x7e, 0xac, 0xc9, 0xe9,
4807
-	0xe0, 0x94, 0xb0, 0xe6, 0xa8, 0xd3, 0xa4, 0x23, 0xae, 0xd1, 0xfe, 0x0a, 0xd4, 0x9e, 0xb2, 0x24,
4808
-	0x26, 0x98, 0xfc, 0x98, 0x10, 0x2e, 0xfc, 0x0d, 0x58, 0x6d, 0x47, 0xfc, 0xe4, 0x88, 0x87, 0xfd,
4809
-	0xf4, 0x0e, 0xdd, 0x81, 0xe2, 0x71, 0x34, 0x10, 0x84, 0xb9, 0x4e, 0xdd, 0x69, 0x54, 0xb0, 0xb1,
4810
-	0xfc, 0x3d, 0x78, 0xdb, 0xc2, 0xf2, 0x11, 0x8d, 0x39, 0x41, 0x0f, 0xa0, 0xc8, 0x48, 0x97, 0xb2,
4811
-	0x9e, 0xeb, 0xd4, 0xf3, 0x8d, 0xea, 0xd6, 0x87, 0xc1, 0xec, 0x8b, 0x03, 0xe3, 0x20, 0x41, 0xd8,
4812
-	0x80, 0xfd, 0x3f, 0x72, 0x50, 0xb5, 0xee, 0xd1, 0x0a, 0xe4, 0x76, 0xdb, 0xe6, 0x7b, 0xb9, 0xdd,
4813
-	0x36, 0x72, 0xa1, 0x74, 0x90, 0x88, 0xb0, 0x33, 0x20, 0x6e, 0xae, 0xee, 0x34, 0xca, 0x38, 0x35,
4814
-	0xd1, 0xbb, 0xb0, 0xb4, 0x1b, 0x1f, 0x71, 0xe2, 0xe6, 0xd5, 0xbd, 0x36, 0x10, 0x82, 0xc2, 0x61,
4815
-	0xf4, 0x13, 0x71, 0x0b, 0x75, 0xa7, 0x91, 0xc7, 0xea, 0x2c, 0xdf, 0xf1, 0x34, 0x64, 0x24, 0x16,
4816
-	0xee, 0x92, 0x7e, 0x87, 0xb6, 0x50, 0x0b, 0x2a, 0x3b, 0x8c, 0x84, 0x82, 0xf4, 0x1e, 0x0b, 0xb7,
4817
-	0x58, 0x77, 0x1a, 0xd5, 0x2d, 0x2f, 0xd0, 0x69, 0x0e, 0xd2, 0x34, 0x07, 0xcf, 0xd2, 0x34, 0xb7,
4818
-	0xca, 0xaf, 0xce, 0xd7, 0xdf, 0xfa, 0xf9, 0xf5, 0xba, 0x83, 0x27, 0x6e, 0xe8, 0x11, 0xc0, 0x7e,
4819
-	0xc8, 0xc5, 0x11, 0x57, 0x24, 0xa5, 0x6b, 0x49, 0x0a, 0x8a, 0xc0, 0xf2, 0x41, 0x6b, 0x00, 0x2a,
4820
-	0x01, 0x3b, 0x34, 0x89, 0x85, 0x5b, 0x56, 0x71, 0x5b, 0x37, 0xa8, 0x0e, 0xd5, 0x36, 0xe1, 0x5d,
4821
-	0x16, 0x8d, 0x44, 0x44, 0x63, 0xb7, 0xa2, 0x9e, 0x60, 0x5f, 0xf9, 0xbf, 0x14, 0xa0, 0x76, 0x28,
4822
-	0x35, 0x4e, 0x85, 0x5b, 0x85, 0x3c, 0x26, 0xc7, 0x26, 0x8b, 0xf2, 0x88, 0x02, 0x80, 0x36, 0x39,
4823
-	0x8e, 0xe2, 0x48, 0x71, 0xe4, 0x54, 0x98, 0x2b, 0xc1, 0xa8, 0x13, 0x4c, 0x6e, 0xb1, 0x85, 0x40,
4824
-	0x1e, 0x94, 0x9f, 0xbc, 0x1c, 0x51, 0x26, 0xc5, 0xcf, 0x2b, 0x9a, 0xb1, 0x8d, 0x9e, 0xc3, 0x72,
4825
-	0x7a, 0x7e, 0x2c, 0x04, 0xe3, 0x6e, 0x41, 0x09, 0x7e, 0x3f, 0x2b, 0xb8, 0x1d, 0x54, 0x30, 0xe5,
4826
-	0xf3, 0x24, 0x16, 0xec, 0x0c, 0x4f, 0xf3, 0x48, 0xad, 0x0f, 0x09, 0xe7, 0x32, 0x42, 0x2d, 0x54,
4827
-	0x6a, 0xca, 0x70, 0xbe, 0x66, 0x34, 0x16, 0x24, 0xee, 0x29, 0xa1, 0x2a, 0x78, 0x6c, 0xcb, 0x70,
4828
-	0xd2, 0xb3, 0x0e, 0xa7, 0x74, 0xa3, 0x70, 0xa6, 0x7c, 0x4c, 0x38, 0x53, 0x77, 0x68, 0x1b, 0x96,
4829
-	0x76, 0xc2, 0xee, 0x0b, 0xa2, 0x34, 0xa9, 0x6e, 0xad, 0x65, 0x09, 0xd5, 0xbf, 0xbf, 0x55, 0x22,
4830
-	0xf0, 0x56, 0x41, 0x96, 0x07, 0xd6, 0x2e, 0xde, 0x23, 0x40, 0xd9, 0xf7, 0x4a, 0x5d, 0x4e, 0xc8,
4831
-	0x59, 0xaa, 0xcb, 0x09, 0x39, 0x93, 0x45, 0x7c, 0x1a, 0x0e, 0x12, 0x5d, 0xdc, 0x15, 0xac, 0x8d,
4832
-	0xed, 0xdc, 0x97, 0x8e, 0x64, 0xc8, 0x86, 0xb8, 0x08, 0x83, 0xff, 0xda, 0x81, 0x9a, 0x1d, 0x21,
4833
-	0xfa, 0x00, 0x2a, 0x3a, 0xa8, 0x49, 0x71, 0x4c, 0x2e, 0x64, 0x1d, 0xee, 0x0e, 0x8d, 0xc1, 0xdd,
4834
-	0x5c, 0x3d, 0xdf, 0xa8, 0x60, 0xeb, 0x06, 0x7d, 0x07, 0x55, 0x0d, 0xd6, 0x59, 0xce, 0xab, 0x2c,
4835
-	0x37, 0xaf, 0x4e, 0x4a, 0x60, 0x79, 0xe8, 0x1c, 0xdb, 0x1c, 0xde, 0x43, 0x58, 0x9d, 0x05, 0x2c,
4836
-	0xf4, 0xc2, 0xdf, 0x1d, 0x58, 0x36, 0xa2, 0x9a, 0x2e, 0x14, 0xa6, 0x8c, 0x84, 0xa5, 0x77, 0xa6,
4837
-	0x1f, 0x3d, 0xb8, 0xb4, 0x1e, 0x34, 0x2c, 0x98, 0xf5, 0xd3, 0xf1, 0x66, 0xe8, 0xbc, 0x1d, 0x78,
4838
-	0x6f, 0x2e, 0x74, 0xa1, 0xc8, 0x3f, 0x82, 0xe5, 0x43, 0x11, 0x8a, 0x84, 0x5f, 0xfa, 0x93, 0xf5,
4839
-	0x7f, 0x73, 0x60, 0x25, 0xc5, 0x98, 0xd7, 0x7d, 0x0e, 0xe5, 0x53, 0xc2, 0x04, 0x79, 0x49, 0xb8,
4840
-	0x79, 0x95, 0x9b, 0x7d, 0xd5, 0xf7, 0x0a, 0x81, 0xc7, 0x48, 0xb4, 0x0d, 0x65, 0xae, 0x78, 0x88,
4841
-	0x96, 0x75, 0x6e, 0x29, 0x6b, 0x2f, 0xf3, 0xbd, 0x31, 0x1e, 0x35, 0xa1, 0x30, 0xa0, 0xfd, 0x54,
4842
-	0xed, 0xf7, 0x2f, 0xf3, 0xdb, 0xa7, 0x7d, 0xac, 0x80, 0xfe, 0x79, 0x0e, 0x8a, 0xfa, 0x0e, 0xed,
4843
-	0x41, 0xb1, 0x17, 0xf5, 0x09, 0x17, 0xfa, 0x55, 0xad, 0x2d, 0xf9, 0x03, 0xf9, 0xfb, 0x7c, 0x7d,
4844
-	0xc3, 0x1a, 0x54, 0x74, 0x44, 0x62, 0x39, 0x28, 0xc3, 0x28, 0x26, 0x8c, 0x37, 0xfb, 0xf4, 0x9e,
4845
-	0x76, 0x09, 0xda, 0xea, 0x0f, 0x36, 0x0c, 0x92, 0x2b, 0x8a, 0x47, 0x89, 0x30, 0x85, 0x79, 0x3b,
4846
-	0x2e, 0xcd, 0x20, 0x47, 0x44, 0x1c, 0x0e, 0x89, 0xe9, 0x6b, 0xea, 0x2c, 0x47, 0x44, 0x57, 0xd6,
4847
-	0x6d, 0x4f, 0x0d, 0x8e, 0x32, 0x36, 0x16, 0xda, 0x86, 0x12, 0x17, 0x21, 0x13, 0xa4, 0xa7, 0x5a,
4848
-	0xd2, 0x4d, 0x7a, 0x7b, 0xea, 0x80, 0x1e, 0x42, 0xa5, 0x4b, 0x87, 0xa3, 0x01, 0x91, 0xde, 0xc5,
4849
-	0x1b, 0x7a, 0x4f, 0x5c, 0x64, 0xf5, 0x10, 0xc6, 0x28, 0x53, 0x53, 0xa5, 0x82, 0xb5, 0xe1, 0xff,
4850
-	0x97, 0x83, 0x9a, 0x2d, 0x56, 0x66, 0x62, 0xee, 0x41, 0x51, 0x4b, 0xaf, 0xab, 0xee, 0x76, 0xa9,
4851
-	0xd2, 0x0c, 0x73, 0x53, 0xe5, 0x42, 0xa9, 0x9b, 0x30, 0x35, 0x4e, 0xf5, 0x90, 0x4d, 0x4d, 0x19,
4852
-	0xb0, 0xa0, 0x22, 0x1c, 0xa8, 0x54, 0xe5, 0xb1, 0x36, 0xe4, 0x94, 0x1d, 0xaf, 0x2a, 0x8b, 0x4d,
4853
-	0xd9, 0xb1, 0x9b, 0x2d, 0x43, 0xe9, 0x8d, 0x64, 0x28, 0x2f, 0x2c, 0x83, 0xff, 0xa7, 0x03, 0x95,
4854
-	0x71, 0x95, 0x5b, 0xd9, 0x75, 0xde, 0x38, 0xbb, 0x53, 0x99, 0xc9, 0xdd, 0x2e, 0x33, 0x77, 0xa0,
4855
-	0xc8, 0x05, 0x23, 0xe1, 0x50, 0x69, 0x94, 0xc7, 0xc6, 0x92, 0xfd, 0x64, 0xc8, 0xfb, 0x4a, 0xa1,
4856
-	0x1a, 0x96, 0x47, 0xdf, 0x87, 0x5a, 0xeb, 0x4c, 0x10, 0x7e, 0x40, 0xb8, 0x5c, 0x2e, 0xa4, 0xb6,
4857
-	0xbd, 0x50, 0x84, 0xea, 0x1d, 0x35, 0xac, 0xce, 0xfe, 0x5d, 0x40, 0xfb, 0x11, 0x17, 0xcf, 0x29,
4858
-	0x3b, 0x21, 0x8c, 0xcf, 0xdb, 0x03, 0xf3, 0xd6, 0x1e, 0x78, 0x00, 0xef, 0x4c, 0xa1, 0x4d, 0x97,
4859
-	0xfa, 0x62, 0x66, 0x13, 0x9c, 0xd3, 0x6d, 0xb4, 0xcb, 0xcc, 0x2a, 0xf8, 0xab, 0x03, 0x35, 0xfb,
4860
-	0x1f, 0x99, 0xca, 0x6e, 0x41, 0x71, 0x3f, 0xec, 0x90, 0x41, 0xda, 0xc6, 0x36, 0xae, 0x26, 0x0e,
4861
-	0x34, 0x58, 0xf7, 0x71, 0xe3, 0xe9, 0x7d, 0x05, 0x55, 0xeb, 0x7a, 0x91, 0x9e, 0xbd, 0xf5, 0x6f,
4862
-	0x1e, 0x4a, 0x3b, 0x7a, 0xa9, 0x47, 0xcf, 0xa0, 0x32, 0x5e, 0x81, 0x91, 0x9f, 0x8d, 0x63, 0x76,
4863
-	0x97, 0xf6, 0x3e, 0xbe, 0x12, 0x63, 0x32, 0xf7, 0x0d, 0x2c, 0xa9, 0xa5, 0x1c, 0xcd, 0x49, 0x99,
4864
-	0xbd, 0xad, 0x7b, 0x57, 0x2f, 0xd7, 0x9b, 0x8e, 0x64, 0x52, 0xd3, 0x6d, 0x1e, 0x93, 0xbd, 0x06,
4865
-	0x79, 0xeb, 0xd7, 0x8c, 0x45, 0x74, 0x00, 0x45, 0xd3, 0x68, 0xe6, 0x41, 0xed, 0x19, 0xe6, 0xd5,
4866
-	0x2f, 0x07, 0x68, 0xb2, 0x4d, 0x07, 0x1d, 0x8c, 0x77, 0xbc, 0x79, 0xa1, 0xd9, 0x05, 0xea, 0x5d,
4867
-	0xf3, 0xff, 0x86, 0xb3, 0xe9, 0xa0, 0x1f, 0xa0, 0x6a, 0x95, 0x20, 0xfa, 0x24, 0xeb, 0x92, 0xad,
4868
-	0x67, 0xef, 0xd3, 0x6b, 0x50, 0x3a, 0xd8, 0x56, 0xed, 0xd5, 0xc5, 0x9a, 0xf3, 0xd7, 0xc5, 0x9a,
4869
-	0xf3, 0xcf, 0xc5, 0x9a, 0xd3, 0x29, 0xaa, 0x5f, 0xe4, 0x67, 0xff, 0x07, 0x00, 0x00, 0xff, 0xff,
4870
-	0x4d, 0x94, 0x5a, 0xb6, 0xd8, 0x0d, 0x00, 0x00,
4795
+	// 1214 bytes of a gzipped FileDescriptorProto
4796
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x57, 0x4f, 0x6f, 0x1b, 0x55,
4797
+	0x10, 0x67, 0x6d, 0xc7, 0xf6, 0x8e, 0x9d, 0x28, 0x3c, 0xa0, 0x5a, 0x2d, 0x90, 0x98, 0x05, 0x24,
4798
+	0xab, 0x6a, 0xd7, 0x69, 0xa0, 0x08, 0x72, 0xa8, 0x5a, 0xc7, 0x45, 0x24, 0x4a, 0x44, 0xd8, 0x34,
4799
+	0x54, 0xe2, 0xb6, 0xb6, 0x5f, 0xdc, 0x55, 0xd6, 0xfb, 0x96, 0xf7, 0x9e, 0xa3, 0x86, 0x4f, 0xc1,
4800
+	0x81, 0x6f, 0xc2, 0x81, 0x33, 0x07, 0xa4, 0xde, 0xe0, 0xcc, 0x21, 0x45, 0xb9, 0xc3, 0x67, 0x40,
4801
+	0xef, 0xcf, 0xda, 0xcf, 0x5e, 0xe7, 0x8f, 0xd3, 0x93, 0xdf, 0xcc, 0xfe, 0xe6, 0xb7, 0xf3, 0x66,
4802
+	0x66, 0x67, 0xc6, 0xb0, 0xdc, 0x23, 0x09, 0xa7, 0x24, 0xf6, 0x53, 0x4a, 0x38, 0x41, 0xab, 0x43,
4803
+	0xd2, 0x3d, 0xf3, 0xbb, 0xa3, 0x28, 0xee, 0x9f, 0x44, 0xdc, 0x3f, 0x7d, 0xe0, 0xde, 0x1f, 0x44,
4804
+	0xfc, 0xc5, 0xa8, 0xeb, 0xf7, 0xc8, 0xb0, 0x35, 0x20, 0x03, 0xd2, 0x92, 0xc0, 0xee, 0xe8, 0x58,
4805
+	0x4a, 0x52, 0x90, 0x27, 0x45, 0xe0, 0xae, 0x0f, 0x08, 0x19, 0xc4, 0x78, 0x82, 0xe2, 0xd1, 0x10,
4806
+	0x33, 0x1e, 0x0e, 0x53, 0x0d, 0xb8, 0x67, 0xf0, 0x89, 0x97, 0xb5, 0xb2, 0x97, 0xb5, 0x18, 0x89,
4807
+	0x4f, 0x31, 0x6d, 0xa5, 0xdd, 0x16, 0x49, 0x99, 0x42, 0x7b, 0x2b, 0x50, 0x3f, 0xa0, 0xa3, 0x04,
4808
+	0x07, 0xf8, 0xc7, 0x11, 0x66, 0xdc, 0xbb, 0x0b, 0xab, 0x9d, 0x88, 0x9d, 0x1c, 0xb1, 0x70, 0x90,
4809
+	0xe9, 0xd0, 0x1d, 0x28, 0x1f, 0x47, 0x31, 0xc7, 0xd4, 0xb1, 0x1a, 0x56, 0xd3, 0x0e, 0xb4, 0xe4,
4810
+	0xed, 0xc2, 0xdb, 0x06, 0x96, 0xa5, 0x24, 0x61, 0x18, 0x3d, 0x84, 0x32, 0xc5, 0x3d, 0x42, 0xfb,
4811
+	0x8e, 0xd5, 0x28, 0x36, 0x6b, 0x9b, 0x1f, 0xfa, 0xb3, 0x37, 0xf6, 0xb5, 0x81, 0x00, 0x05, 0x1a,
4812
+	0xec, 0xfd, 0x5e, 0x80, 0x9a, 0xa1, 0x47, 0x2b, 0x50, 0xd8, 0xe9, 0xe8, 0xf7, 0x15, 0x76, 0x3a,
4813
+	0xc8, 0x81, 0xca, 0xfe, 0x88, 0x87, 0xdd, 0x18, 0x3b, 0x85, 0x86, 0xd5, 0xac, 0x06, 0x99, 0x88,
4814
+	0xde, 0x85, 0xa5, 0x9d, 0xe4, 0x88, 0x61, 0xa7, 0x28, 0xf5, 0x4a, 0x40, 0x08, 0x4a, 0x87, 0xd1,
4815
+	0x4f, 0xd8, 0x29, 0x35, 0xac, 0x66, 0x31, 0x90, 0x67, 0x71, 0x8f, 0x83, 0x90, 0xe2, 0x84, 0x3b,
4816
+	0x4b, 0xea, 0x1e, 0x4a, 0x42, 0x6d, 0xb0, 0xb7, 0x29, 0x0e, 0x39, 0xee, 0x3f, 0xe1, 0x4e, 0xb9,
4817
+	0x61, 0x35, 0x6b, 0x9b, 0xae, 0xaf, 0xc2, 0xec, 0x67, 0x61, 0xf6, 0x9f, 0x65, 0x61, 0x6e, 0x57,
4818
+	0x5f, 0x9d, 0xaf, 0xbf, 0xf5, 0xf3, 0xeb, 0x75, 0x2b, 0x98, 0x98, 0xa1, 0xc7, 0x00, 0x7b, 0x21,
4819
+	0xe3, 0x47, 0x4c, 0x92, 0x54, 0xae, 0x25, 0x29, 0x49, 0x02, 0xc3, 0x06, 0xad, 0x01, 0xc8, 0x00,
4820
+	0x6c, 0x93, 0x51, 0xc2, 0x9d, 0xaa, 0xf4, 0xdb, 0xd0, 0xa0, 0x06, 0xd4, 0x3a, 0x98, 0xf5, 0x68,
4821
+	0x94, 0xf2, 0x88, 0x24, 0x8e, 0x2d, 0xaf, 0x60, 0xaa, 0xbc, 0x5f, 0x4a, 0x50, 0x3f, 0x14, 0x39,
4822
+	0xce, 0x12, 0xb7, 0x0a, 0xc5, 0x00, 0x1f, 0xeb, 0x28, 0x8a, 0x23, 0xf2, 0x01, 0x3a, 0xf8, 0x38,
4823
+	0x4a, 0x22, 0xc9, 0x51, 0x90, 0x6e, 0xae, 0xf8, 0x69, 0xd7, 0x9f, 0x68, 0x03, 0x03, 0x81, 0x5c,
4824
+	0xa8, 0x3e, 0x7d, 0x99, 0x12, 0x2a, 0x92, 0x5f, 0x94, 0x34, 0x63, 0x19, 0x3d, 0x87, 0xe5, 0xec,
4825
+	0xfc, 0x84, 0x73, 0xca, 0x9c, 0x92, 0x4c, 0xf8, 0x83, 0x7c, 0xc2, 0x4d, 0xa7, 0xfc, 0x29, 0x9b,
4826
+	0xa7, 0x09, 0xa7, 0x67, 0xc1, 0x34, 0x8f, 0xc8, 0xf5, 0x21, 0x66, 0x4c, 0x78, 0xa8, 0x12, 0x95,
4827
+	0x89, 0xc2, 0x9d, 0xaf, 0x29, 0x49, 0x38, 0x4e, 0xfa, 0x32, 0x51, 0x76, 0x30, 0x96, 0x85, 0x3b,
4828
+	0xd9, 0x59, 0xb9, 0x53, 0xb9, 0x91, 0x3b, 0x53, 0x36, 0xda, 0x9d, 0x29, 0x1d, 0xda, 0x82, 0xa5,
4829
+	0xed, 0xb0, 0xf7, 0x02, 0xcb, 0x9c, 0xd4, 0x36, 0xd7, 0xf2, 0x84, 0xf2, 0xf1, 0xb7, 0x32, 0x09,
4830
+	0xac, 0x5d, 0x12, 0xe5, 0x11, 0x28, 0x13, 0xf7, 0x31, 0xa0, 0xfc, 0x7d, 0x45, 0x5e, 0x4e, 0xf0,
4831
+	0x59, 0x96, 0x97, 0x13, 0x7c, 0x26, 0x8a, 0xf8, 0x34, 0x8c, 0x47, 0xaa, 0xb8, 0xed, 0x40, 0x09,
4832
+	0x5b, 0x85, 0x2f, 0x2d, 0xc1, 0x90, 0x77, 0x71, 0x11, 0x06, 0xef, 0xb5, 0x05, 0x75, 0xd3, 0x43,
4833
+	0xf4, 0x01, 0xd8, 0xca, 0xa9, 0x49, 0x71, 0x4c, 0x14, 0xa2, 0x0e, 0x77, 0x86, 0x5a, 0x60, 0x4e,
4834
+	0xa1, 0x51, 0x6c, 0xda, 0x81, 0xa1, 0x41, 0xdf, 0x41, 0x4d, 0x81, 0x55, 0x94, 0x8b, 0x32, 0xca,
4835
+	0xad, 0xab, 0x83, 0xe2, 0x1b, 0x16, 0x2a, 0xc6, 0x26, 0x87, 0xfb, 0x08, 0x56, 0x67, 0x01, 0x0b,
4836
+	0xdd, 0xf0, 0x37, 0x0b, 0x96, 0x75, 0x52, 0x75, 0x17, 0x0a, 0x33, 0x46, 0x4c, 0x33, 0x9d, 0xee,
4837
+	0x47, 0x0f, 0x2f, 0xad, 0x07, 0x05, 0xf3, 0x67, 0xed, 0x94, 0xbf, 0x39, 0x3a, 0x77, 0x1b, 0xde,
4838
+	0x9b, 0x0b, 0x5d, 0xc8, 0xf3, 0x8f, 0x60, 0xf9, 0x90, 0x87, 0x7c, 0xc4, 0x2e, 0xfd, 0x64, 0xbd,
4839
+	0x5f, 0x2d, 0x58, 0xc9, 0x30, 0xfa, 0x76, 0x9f, 0x43, 0xf5, 0x14, 0x53, 0x8e, 0x5f, 0x62, 0xa6,
4840
+	0x6f, 0xe5, 0xe4, 0x6f, 0xf5, 0xbd, 0x44, 0x04, 0x63, 0x24, 0xda, 0x82, 0x2a, 0x93, 0x3c, 0x58,
4841
+	0xa5, 0x75, 0x6e, 0x29, 0x2b, 0x2b, 0xfd, 0xbe, 0x31, 0x1e, 0xb5, 0xa0, 0x14, 0x93, 0x41, 0x96,
4842
+	0xed, 0xf7, 0x2f, 0xb3, 0xdb, 0x23, 0x83, 0x40, 0x02, 0xbd, 0xf3, 0x02, 0x94, 0x95, 0x0e, 0xed,
4843
+	0x42, 0xb9, 0x1f, 0x0d, 0x30, 0xe3, 0xea, 0x56, 0xed, 0x4d, 0xf1, 0x81, 0xfc, 0x7d, 0xbe, 0x7e,
4844
+	0xd7, 0x18, 0x54, 0x24, 0xc5, 0x89, 0x18, 0x94, 0x61, 0x94, 0x60, 0xca, 0x5a, 0x03, 0x72, 0x5f,
4845
+	0x99, 0xf8, 0x1d, 0xf9, 0x13, 0x68, 0x06, 0xc1, 0x15, 0x25, 0xe9, 0x88, 0xeb, 0xc2, 0xbc, 0x1d,
4846
+	0x97, 0x62, 0x10, 0x23, 0x22, 0x09, 0x87, 0x58, 0xf7, 0x35, 0x79, 0x16, 0x23, 0xa2, 0x27, 0xea,
4847
+	0xb6, 0x2f, 0x07, 0x47, 0x35, 0xd0, 0x12, 0xda, 0x82, 0x0a, 0xe3, 0x21, 0xe5, 0xb8, 0x2f, 0x5b,
4848
+	0xd2, 0x4d, 0x7a, 0x7b, 0x66, 0x80, 0x1e, 0x81, 0xdd, 0x23, 0xc3, 0x34, 0xc6, 0xc2, 0xba, 0x7c,
4849
+	0x43, 0xeb, 0x89, 0x89, 0xa8, 0x1e, 0x4c, 0x29, 0xa1, 0x72, 0xaa, 0xd8, 0x81, 0x12, 0xbc, 0xff,
4850
+	0x0a, 0x50, 0x37, 0x93, 0x95, 0x9b, 0x98, 0xbb, 0x50, 0x56, 0xa9, 0x57, 0x55, 0x77, 0xbb, 0x50,
4851
+	0x29, 0x86, 0xb9, 0xa1, 0x72, 0xa0, 0xd2, 0x1b, 0x51, 0x39, 0x4e, 0xd5, 0x90, 0xcd, 0x44, 0xe1,
4852
+	0x30, 0x27, 0x3c, 0x8c, 0x65, 0xa8, 0x8a, 0x81, 0x12, 0xc4, 0x94, 0x1d, 0xaf, 0x2a, 0x8b, 0x4d,
4853
+	0xd9, 0xb1, 0x99, 0x99, 0x86, 0xca, 0x1b, 0xa5, 0xa1, 0xba, 0x70, 0x1a, 0xbc, 0x3f, 0x2c, 0xb0,
4854
+	0xc7, 0x55, 0x6e, 0x44, 0xd7, 0x7a, 0xe3, 0xe8, 0x4e, 0x45, 0xa6, 0x70, 0xbb, 0xc8, 0xdc, 0x81,
4855
+	0x32, 0xe3, 0x14, 0x87, 0x43, 0x99, 0xa3, 0x62, 0xa0, 0x25, 0xd1, 0x4f, 0x86, 0x6c, 0x20, 0x33,
4856
+	0x54, 0x0f, 0xc4, 0xd1, 0xf3, 0xa0, 0xde, 0x3e, 0xe3, 0x98, 0xed, 0x63, 0x26, 0x96, 0x0b, 0x91,
4857
+	0xdb, 0x7e, 0xc8, 0x43, 0x79, 0x8f, 0x7a, 0x20, 0xcf, 0xde, 0x3d, 0x40, 0x7b, 0x11, 0xe3, 0xcf,
4858
+	0x09, 0x3d, 0xc1, 0x94, 0xcd, 0xdb, 0x03, 0x8b, 0xc6, 0x1e, 0xb8, 0x0f, 0xef, 0x4c, 0xa1, 0x75,
4859
+	0x97, 0xfa, 0x62, 0x66, 0x13, 0x9c, 0xd3, 0x6d, 0x94, 0xc9, 0xcc, 0x2a, 0xf8, 0xa7, 0x05, 0x75,
4860
+	0xf3, 0x41, 0xae, 0xb2, 0xdb, 0x50, 0xde, 0x0b, 0xbb, 0x38, 0xce, 0xda, 0xd8, 0xdd, 0xab, 0x89,
4861
+	0x7d, 0x05, 0x56, 0x7d, 0x5c, 0x5b, 0xa2, 0x0d, 0xb0, 0xd3, 0x38, 0xe4, 0xc7, 0x84, 0x0e, 0xb3,
4862
+	0xae, 0x56, 0x17, 0x7b, 0xd0, 0x81, 0x56, 0xea, 0x31, 0x3e, 0x01, 0xb9, 0x5f, 0x41, 0xcd, 0x20,
4863
+	0x5a, 0xa4, 0xcb, 0x6f, 0xfe, 0x5b, 0x84, 0xca, 0xb6, 0xfa, 0x1b, 0x80, 0x9e, 0x81, 0x3d, 0x5e,
4864
+	0x9a, 0x91, 0x97, 0xf7, 0x7c, 0x76, 0xfb, 0x76, 0x3f, 0xbe, 0x12, 0xa3, 0x63, 0xfd, 0x0d, 0x2c,
4865
+	0xc9, 0x35, 0x1e, 0xcd, 0x09, 0xb2, 0xb9, 0xdf, 0xbb, 0x57, 0xaf, 0xe3, 0x1b, 0x96, 0x60, 0x92,
4866
+	0xf3, 0x70, 0x1e, 0x93, 0xb9, 0x38, 0xb9, 0xeb, 0xd7, 0x0c, 0x52, 0xb4, 0x0f, 0x65, 0xdd, 0x9a,
4867
+	0xe6, 0x41, 0xcd, 0xa9, 0xe7, 0x36, 0x2e, 0x07, 0x28, 0xb2, 0x0d, 0x0b, 0xed, 0x8f, 0xb7, 0xc2,
4868
+	0x79, 0xae, 0x99, 0x25, 0xed, 0x5e, 0xf3, 0xbc, 0x69, 0x6d, 0x58, 0xe8, 0x07, 0xa8, 0x19, 0x45,
4869
+	0x8b, 0x3e, 0xc9, 0x9b, 0xe4, 0xbf, 0x00, 0xf7, 0xd3, 0x6b, 0x50, 0xca, 0xd9, 0x76, 0xfd, 0xd5,
4870
+	0xc5, 0x9a, 0xf5, 0xd7, 0xc5, 0x9a, 0xf5, 0xcf, 0xc5, 0x9a, 0xd5, 0x2d, 0xcb, 0x6f, 0xf8, 0xb3,
4871
+	0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x86, 0xd4, 0x0f, 0xa1, 0x0a, 0x0e, 0x00, 0x00,
4871 4872
 }
... ...
@@ -118,4 +118,5 @@ message ListWorkersResponse {
118 118
 message WorkerRecord {
119 119
 	string ID = 1;
120 120
 	map<string, string> Labels = 2;
121
+	repeated pb.Platform platforms = 3 [(gogoproto.nullable) = false];
121 122
 }
... ...
@@ -17,7 +17,7 @@ import (
17 17
 )
18 18
 
19 19
 var (
20
-	errLocked   = errors.New("locked")
20
+	ErrLocked   = errors.New("locked")
21 21
 	errNotFound = errors.New("not found")
22 22
 	errInvalid  = errors.New("invalid")
23 23
 )
... ...
@@ -122,7 +122,7 @@ func (cm *cacheManager) get(ctx context.Context, id string, fromSnapshotter bool
122 122
 
123 123
 	if rec.mutable {
124 124
 		if len(rec.refs) != 0 {
125
-			return nil, errors.Wrapf(errLocked, "%s is locked", id)
125
+			return nil, errors.Wrapf(ErrLocked, "%s is locked", id)
126 126
 		}
127 127
 		if rec.equalImmutable != nil {
128 128
 			return rec.equalImmutable.ref(), nil
... ...
@@ -279,12 +279,12 @@ func (cm *cacheManager) GetMutable(ctx context.Context, id string) (MutableRef,
279 279
 	}
280 280
 
281 281
 	if len(rec.refs) != 0 {
282
-		return nil, errors.Wrapf(errLocked, "%s is locked", id)
282
+		return nil, errors.Wrapf(ErrLocked, "%s is locked", id)
283 283
 	}
284 284
 
285 285
 	if rec.equalImmutable != nil {
286 286
 		if len(rec.equalImmutable.refs) != 0 {
287
-			return nil, errors.Wrapf(errLocked, "%s is locked", id)
287
+			return nil, errors.Wrapf(ErrLocked, "%s is locked", id)
288 288
 		}
289 289
 		delete(cm.records, rec.equalImmutable.ID())
290 290
 		if err := rec.equalImmutable.remove(ctx, false); err != nil {
... ...
@@ -513,7 +513,7 @@ func (cm *cacheManager) DiskUsage(ctx context.Context, opt client.DiskUsageInfo)
513 513
 }
514 514
 
515 515
 func IsLocked(err error) bool {
516
-	return errors.Cause(err) == errLocked
516
+	return errors.Cause(err) == ErrLocked
517 517
 }
518 518
 
519 519
 func IsNotFound(err error) bool {
... ...
@@ -5,7 +5,6 @@ import (
5 5
 	"crypto/tls"
6 6
 	"crypto/x509"
7 7
 	"io/ioutil"
8
-	"time"
9 8
 
10 9
 	"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
11 10
 	controlapi "github.com/moby/buildkit/api/services/control"
... ...
@@ -23,7 +22,7 @@ type Client struct {
23 23
 type ClientOpt interface{}
24 24
 
25 25
 // New returns a new buildkit client. Address can be empty for the system-default address.
26
-func New(address string, opts ...ClientOpt) (*Client, error) {
26
+func New(ctx context.Context, address string, opts ...ClientOpt) (*Client, error) {
27 27
 	gopts := []grpc.DialOption{
28 28
 		grpc.WithDialer(dialer),
29 29
 		grpc.FailOnNonTempDialError(true),
... ...
@@ -54,9 +53,6 @@ func New(address string, opts ...ClientOpt) (*Client, error) {
54 54
 		address = appdefaults.Address
55 55
 	}
56 56
 
57
-	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
58
-	defer cancel()
59
-
60 57
 	conn, err := grpc.DialContext(ctx, address, gopts...)
61 58
 	if err != nil {
62 59
 		return nil, errors.Wrapf(err, "failed to dial %q . make sure buildkitd is running", address)
... ...
@@ -17,8 +17,8 @@ type Meta struct {
17 17
 	ProxyEnv *ProxyEnv
18 18
 }
19 19
 
20
-func NewExecOp(root Output, meta Meta, readOnly bool, md OpMetadata) *ExecOp {
21
-	e := &ExecOp{meta: meta, cachedOpMetadata: md}
20
+func NewExecOp(root Output, meta Meta, readOnly bool, c Constraints) *ExecOp {
21
+	e := &ExecOp{meta: meta, constraints: c}
22 22
 	rootMount := &mount{
23 23
 		target:   pb.RootMount,
24 24
 		source:   root,
... ...
@@ -28,32 +28,35 @@ func NewExecOp(root Output, meta Meta, readOnly bool, md OpMetadata) *ExecOp {
28 28
 	if readOnly {
29 29
 		e.root = root
30 30
 	} else {
31
-		e.root = &output{vertex: e, getIndex: e.getMountIndexFn(rootMount)}
31
+		o := &output{vertex: e, getIndex: e.getMountIndexFn(rootMount)}
32
+		if p := c.Platform; p != nil {
33
+			o.platform = p
34
+		}
35
+		e.root = o
32 36
 	}
33 37
 	rootMount.output = e.root
34
-
35 38
 	return e
36 39
 }
37 40
 
38 41
 type mount struct {
39
-	target   string
40
-	readonly bool
41
-	source   Output
42
-	output   Output
43
-	selector string
44
-	cacheID  string
45
-	tmpfs    bool
42
+	target       string
43
+	readonly     bool
44
+	source       Output
45
+	output       Output
46
+	selector     string
47
+	cacheID      string
48
+	tmpfs        bool
49
+	cacheSharing CacheMountSharingMode
46 50
 	// hasOutput bool
47 51
 }
48 52
 
49 53
 type ExecOp struct {
50
-	root             Output
51
-	mounts           []*mount
52
-	meta             Meta
53
-	cachedPBDigest   digest.Digest
54
-	cachedPB         []byte
55
-	cachedOpMetadata OpMetadata
56
-	isValidated      bool
54
+	MarshalCache
55
+	root        Output
56
+	mounts      []*mount
57
+	meta        Meta
58
+	constraints Constraints
59
+	isValidated bool
57 60
 }
58 61
 
59 62
 func (e *ExecOp) AddMount(target string, source Output, opt ...MountOption) Output {
... ...
@@ -70,9 +73,13 @@ func (e *ExecOp) AddMount(target string, source Output, opt ...MountOption) Outp
70 70
 	} else if m.tmpfs {
71 71
 		m.output = &output{vertex: e, err: errors.Errorf("tmpfs mount for %s can't be used as a parent", target)}
72 72
 	} else {
73
-		m.output = &output{vertex: e, getIndex: e.getMountIndexFn(m)}
73
+		o := &output{vertex: e, getIndex: e.getMountIndexFn(m)}
74
+		if p := e.constraints.Platform; p != nil {
75
+			o.platform = p
76
+		}
77
+		m.output = o
74 78
 	}
75
-	e.cachedPB = nil
79
+	e.Store(nil, nil, nil)
76 80
 	e.isValidated = false
77 81
 	return m.output
78 82
 }
... ...
@@ -107,9 +114,9 @@ func (e *ExecOp) Validate() error {
107 107
 	return nil
108 108
 }
109 109
 
110
-func (e *ExecOp) Marshal() (digest.Digest, []byte, *OpMetadata, error) {
111
-	if e.cachedPB != nil {
112
-		return e.cachedPBDigest, e.cachedPB, &e.cachedOpMetadata, nil
110
+func (e *ExecOp) Marshal(c *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
111
+	if e.Cached(c) {
112
+		return e.Load()
113 113
 	}
114 114
 	if err := e.Validate(); err != nil {
115 115
 		return "", nil, nil, err
... ...
@@ -137,10 +144,9 @@ func (e *ExecOp) Marshal() (digest.Digest, []byte, *OpMetadata, error) {
137 137
 		}
138 138
 	}
139 139
 
140
-	pop := &pb.Op{
141
-		Op: &pb.Op_Exec{
142
-			Exec: peo,
143
-		},
140
+	pop, md := MarshalConstraints(c, &e.constraints)
141
+	pop.Op = &pb.Op_Exec{
142
+		Exec: peo,
144 143
 	}
145 144
 
146 145
 	outIndex := 0
... ...
@@ -150,7 +156,7 @@ func (e *ExecOp) Marshal() (digest.Digest, []byte, *OpMetadata, error) {
150 150
 			if m.tmpfs {
151 151
 				return "", nil, nil, errors.Errorf("tmpfs mounts must use scratch")
152 152
 			}
153
-			inp, err := m.source.ToInput()
153
+			inp, err := m.source.ToInput(c)
154 154
 			if err != nil {
155 155
 				return "", nil, nil, err
156 156
 			}
... ...
@@ -190,6 +196,14 @@ func (e *ExecOp) Marshal() (digest.Digest, []byte, *OpMetadata, error) {
190 190
 			pm.CacheOpt = &pb.CacheOpt{
191 191
 				ID: m.cacheID,
192 192
 			}
193
+			switch m.cacheSharing {
194
+			case CacheMountShared:
195
+				pm.CacheOpt.Sharing = pb.CacheSharingOpt_SHARED
196
+			case CacheMountPrivate:
197
+				pm.CacheOpt.Sharing = pb.CacheSharingOpt_PRIVATE
198
+			case CacheMountLocked:
199
+				pm.CacheOpt.Sharing = pb.CacheSharingOpt_LOCKED
200
+			}
193 201
 		}
194 202
 		if m.tmpfs {
195 203
 			pm.MountType = pb.MountType_TMPFS
... ...
@@ -201,9 +215,8 @@ func (e *ExecOp) Marshal() (digest.Digest, []byte, *OpMetadata, error) {
201 201
 	if err != nil {
202 202
 		return "", nil, nil, err
203 203
 	}
204
-	e.cachedPBDigest = digest.FromBytes(dt)
205
-	e.cachedPB = dt
206
-	return e.cachedPBDigest, dt, &e.cachedOpMetadata, nil
204
+	e.Store(dt, md, c)
205
+	return e.Load()
207 206
 }
208 207
 
209 208
 func (e *ExecOp) Output() Output {
... ...
@@ -273,9 +286,10 @@ func SourcePath(src string) MountOption {
273 273
 	}
274 274
 }
275 275
 
276
-func AsPersistentCacheDir(id string) MountOption {
276
+func AsPersistentCacheDir(id string, sharing CacheMountSharingMode) MountOption {
277 277
 	return func(m *mount) {
278 278
 		m.cacheID = id
279
+		m.cacheSharing = sharing
279 280
 	}
280 281
 }
281 282
 
... ...
@@ -366,7 +380,7 @@ func WithProxy(ps ProxyEnv) RunOption {
366 366
 }
367 367
 
368 368
 type ExecInfo struct {
369
-	opMetaWrapper
369
+	constraintsWrapper
370 370
 	State          State
371 371
 	Mounts         []MountInfo
372 372
 	ReadonlyRootFS bool
... ...
@@ -385,3 +399,11 @@ type ProxyEnv struct {
385 385
 	FtpProxy   string
386 386
 	NoProxy    string
387 387
 }
388
+
389
+type CacheMountSharingMode int
390
+
391
+const (
392
+	CacheMountShared CacheMountSharingMode = iota
393
+	CacheMountPrivate
394
+	CacheMountLocked
395
+)
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"github.com/moby/buildkit/util/contentutil"
13 13
 	"github.com/moby/buildkit/util/imageutil"
14 14
 	digest "github.com/opencontainers/go-digest"
15
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
15 16
 )
16 17
 
17 18
 var defaultImageMetaResolver llb.ImageMetaResolver
... ...
@@ -22,12 +23,12 @@ var WithDefault = llb.ImageOptionFunc(func(ii *llb.ImageInfo) {
22 22
 })
23 23
 
24 24
 type imageMetaResolverOpts struct {
25
-	platform string
25
+	platform *specs.Platform
26 26
 }
27 27
 
28 28
 type ImageMetaResolverOpt func(o *imageMetaResolverOpts)
29 29
 
30
-func WithPlatform(p string) ImageMetaResolverOpt {
30
+func WithDefaultPlatform(p *specs.Platform) ImageMetaResolverOpt {
31 31
 	return func(o *imageMetaResolverOpts) {
32 32
 		o.platform = p
33 33
 	}
... ...
@@ -59,7 +60,7 @@ func Default() llb.ImageMetaResolver {
59 59
 type imageMetaResolver struct {
60 60
 	resolver remotes.Resolver
61 61
 	buffer   contentutil.Buffer
62
-	platform string
62
+	platform *specs.Platform
63 63
 	locker   *locker.Locker
64 64
 	cache    map[string]resolveResult
65 65
 }
... ...
@@ -69,7 +70,7 @@ type resolveResult struct {
69 69
 	dgst   digest.Digest
70 70
 }
71 71
 
72
-func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error) {
72
+func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) {
73 73
 	imr.locker.Lock(ref)
74 74
 	defer imr.locker.Unlock(ref)
75 75
 
... ...
@@ -77,7 +78,11 @@ func (imr *imageMetaResolver) ResolveImageConfig(ctx context.Context, ref string
77 77
 		return res.dgst, res.config, nil
78 78
 	}
79 79
 
80
-	dgst, config, err := imageutil.Config(ctx, ref, imr.resolver, imr.buffer, imr.platform)
80
+	if platform == nil {
81
+		platform = imr.platform
82
+	}
83
+
84
+	dgst, config, err := imageutil.Config(ctx, ref, imr.resolver, imr.buffer, platform)
81 85
 	if err != nil {
82 86
 		return "", nil, err
83 87
 	}
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"io"
5 5
 	"io/ioutil"
6 6
 
7
+	"github.com/containerd/containerd/platforms"
7 8
 	"github.com/moby/buildkit/solver/pb"
8 9
 	digest "github.com/opencontainers/go-digest"
9 10
 )
... ...
@@ -12,11 +13,11 @@ import (
12 12
 // Corresponds to the Definition structure defined in solver/pb.Definition.
13 13
 type Definition struct {
14 14
 	Def      [][]byte
15
-	Metadata map[digest.Digest]OpMetadata
15
+	Metadata map[digest.Digest]pb.OpMetadata
16 16
 }
17 17
 
18 18
 func (def *Definition) ToPB() *pb.Definition {
19
-	md := make(map[digest.Digest]OpMetadata)
19
+	md := make(map[digest.Digest]pb.OpMetadata)
20 20
 	for k, v := range def.Metadata {
21 21
 		md[k] = v
22 22
 	}
... ...
@@ -28,14 +29,12 @@ func (def *Definition) ToPB() *pb.Definition {
28 28
 
29 29
 func (def *Definition) FromPB(x *pb.Definition) {
30 30
 	def.Def = x.Def
31
-	def.Metadata = make(map[digest.Digest]OpMetadata)
31
+	def.Metadata = make(map[digest.Digest]pb.OpMetadata)
32 32
 	for k, v := range x.Metadata {
33 33
 		def.Metadata[k] = v
34 34
 	}
35 35
 }
36 36
 
37
-type OpMetadata = pb.OpMetadata
38
-
39 37
 func WriteTo(def *Definition, w io.Writer) error {
40 38
 	b, err := def.ToPB().Marshal()
41 39
 	if err != nil {
... ...
@@ -58,3 +57,56 @@ func ReadFrom(r io.Reader) (*Definition, error) {
58 58
 	def.FromPB(&pbDef)
59 59
 	return &def, nil
60 60
 }
61
+
62
+func MarshalConstraints(base, override *Constraints) (*pb.Op, *pb.OpMetadata) {
63
+	c := *base
64
+	c.WorkerConstraints = append([]string{}, c.WorkerConstraints...)
65
+
66
+	if p := override.Platform; p != nil {
67
+		c.Platform = p
68
+	}
69
+
70
+	for _, wc := range override.WorkerConstraints {
71
+		c.WorkerConstraints = append(c.WorkerConstraints, wc)
72
+	}
73
+
74
+	c.Metadata = mergeMetadata(c.Metadata, override.Metadata)
75
+
76
+	if c.Platform == nil {
77
+		defaultPlatform := platforms.Normalize(platforms.DefaultSpec())
78
+		c.Platform = &defaultPlatform
79
+	}
80
+
81
+	return &pb.Op{
82
+		Platform: &pb.Platform{
83
+			OS:           c.Platform.OS,
84
+			Architecture: c.Platform.Architecture,
85
+			Variant:      c.Platform.Variant,
86
+			OSVersion:    c.Platform.OSVersion,
87
+			OSFeatures:   c.Platform.OSFeatures,
88
+		},
89
+		Constraints: &pb.WorkerConstraints{
90
+			Filter: c.WorkerConstraints,
91
+		},
92
+	}, &c.Metadata
93
+}
94
+
95
+type MarshalCache struct {
96
+	digest      digest.Digest
97
+	dt          []byte
98
+	md          *pb.OpMetadata
99
+	constraints *Constraints
100
+}
101
+
102
+func (mc *MarshalCache) Cached(c *Constraints) bool {
103
+	return mc.dt != nil && mc.constraints == c
104
+}
105
+func (mc *MarshalCache) Load() (digest.Digest, []byte, *pb.OpMetadata, error) {
106
+	return mc.digest, mc.dt, mc.md, nil
107
+}
108
+func (mc *MarshalCache) Store(dt []byte, md *pb.OpMetadata, c *Constraints) {
109
+	mc.digest = digest.FromBytes(dt)
110
+	mc.dt = dt
111
+	mc.md = md
112
+	mc.constraints = c
113
+}
... ...
@@ -4,16 +4,19 @@ import (
4 4
 	"fmt"
5 5
 	"path"
6 6
 
7
+	"github.com/containerd/containerd/platforms"
7 8
 	"github.com/google/shlex"
9
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
8 10
 )
9 11
 
10 12
 type contextKeyT string
11 13
 
12 14
 var (
13
-	keyArgs = contextKeyT("llb.exec.args")
14
-	keyDir  = contextKeyT("llb.exec.dir")
15
-	keyEnv  = contextKeyT("llb.exec.env")
16
-	keyUser = contextKeyT("llb.exec.user")
15
+	keyArgs     = contextKeyT("llb.exec.args")
16
+	keyDir      = contextKeyT("llb.exec.dir")
17
+	keyEnv      = contextKeyT("llb.exec.env")
18
+	keyUser     = contextKeyT("llb.exec.user")
19
+	keyPlatform = contextKeyT("llb.platform")
17 20
 )
18 21
 
19 22
 func addEnv(key, value string) StateOption {
... ...
@@ -106,6 +109,21 @@ func shlexf(str string, v ...interface{}) StateOption {
106 106
 	}
107 107
 }
108 108
 
109
+func platform(p specs.Platform) StateOption {
110
+	return func(s State) State {
111
+		return s.WithValue(keyPlatform, platforms.Normalize(p))
112
+	}
113
+}
114
+
115
+func getPlatform(s State) *specs.Platform {
116
+	v := s.Value(keyPlatform)
117
+	if v != nil {
118
+		p := v.(specs.Platform)
119
+		return &p
120
+	}
121
+	return nil
122
+}
123
+
109 124
 type EnvList []KeyValue
110 125
 
111 126
 type KeyValue struct {
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"context"
5 5
 
6 6
 	digest "github.com/opencontainers/go-digest"
7
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
7 8
 )
8 9
 
9 10
 func WithMetaResolver(mr ImageMetaResolver) ImageOption {
... ...
@@ -13,5 +14,5 @@ func WithMetaResolver(mr ImageMetaResolver) ImageOption {
13 13
 }
14 14
 
15 15
 type ImageMetaResolver interface {
16
-	ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error)
16
+	ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error)
17 17
 }
... ...
@@ -15,22 +15,21 @@ import (
15 15
 )
16 16
 
17 17
 type SourceOp struct {
18
-	id               string
19
-	attrs            map[string]string
20
-	output           Output
21
-	cachedPBDigest   digest.Digest
22
-	cachedPB         []byte
23
-	cachedOpMetadata OpMetadata
24
-	err              error
18
+	MarshalCache
19
+	id          string
20
+	attrs       map[string]string
21
+	output      Output
22
+	constraints Constraints
23
+	err         error
25 24
 }
26 25
 
27
-func NewSource(id string, attrs map[string]string, md OpMetadata) *SourceOp {
26
+func NewSource(id string, attrs map[string]string, c Constraints) *SourceOp {
28 27
 	s := &SourceOp{
29
-		id:               id,
30
-		attrs:            attrs,
31
-		cachedOpMetadata: md,
28
+		id:          id,
29
+		attrs:       attrs,
30
+		constraints: c,
32 31
 	}
33
-	s.output = &output{vertex: s}
32
+	s.output = &output{vertex: s, platform: c.Platform}
34 33
 	return s
35 34
 }
36 35
 
... ...
@@ -44,26 +43,26 @@ func (s *SourceOp) Validate() error {
44 44
 	return nil
45 45
 }
46 46
 
47
-func (s *SourceOp) Marshal() (digest.Digest, []byte, *OpMetadata, error) {
48
-	if s.cachedPB != nil {
49
-		return s.cachedPBDigest, s.cachedPB, &s.cachedOpMetadata, nil
47
+func (s *SourceOp) Marshal(constraints *Constraints) (digest.Digest, []byte, *pb.OpMetadata, error) {
48
+	if s.Cached(constraints) {
49
+		return s.Load()
50 50
 	}
51 51
 	if err := s.Validate(); err != nil {
52 52
 		return "", nil, nil, err
53 53
 	}
54 54
 
55
-	proto := &pb.Op{
56
-		Op: &pb.Op_Source{
57
-			Source: &pb.SourceOp{Identifier: s.id, Attrs: s.attrs},
58
-		},
55
+	proto, md := MarshalConstraints(constraints, &s.constraints)
56
+
57
+	proto.Op = &pb.Op_Source{
58
+		Source: &pb.SourceOp{Identifier: s.id, Attrs: s.attrs},
59 59
 	}
60 60
 	dt, err := proto.Marshal()
61 61
 	if err != nil {
62 62
 		return "", nil, nil, err
63 63
 	}
64
-	s.cachedPB = dt
65
-	s.cachedPBDigest = digest.FromBytes(dt)
66
-	return s.cachedPBDigest, dt, &s.cachedOpMetadata, nil
64
+
65
+	s.Store(dt, md, constraints)
66
+	return s.Load()
67 67
 }
68 68
 
69 69
 func (s *SourceOp) Output() Output {
... ...
@@ -74,10 +73,6 @@ func (s *SourceOp) Inputs() []Output {
74 74
 	return nil
75 75
 }
76 76
 
77
-func Source(id string) State {
78
-	return NewState(NewSource(id, nil, OpMetadata{}).Output())
79
-}
80
-
81 77
 func Image(ref string, opts ...ImageOption) State {
82 78
 	r, err := reference.ParseNormalizedNamed(ref)
83 79
 	if err == nil {
... ...
@@ -87,12 +82,12 @@ func Image(ref string, opts ...ImageOption) State {
87 87
 	for _, opt := range opts {
88 88
 		opt.SetImageOption(&info)
89 89
 	}
90
-	src := NewSource("docker-image://"+ref, nil, info.Metadata()) // controversial
90
+	src := NewSource("docker-image://"+ref, nil, info.Constraints) // controversial
91 91
 	if err != nil {
92 92
 		src.err = err
93 93
 	}
94 94
 	if info.metaResolver != nil {
95
-		_, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref)
95
+		_, dt, err := info.metaResolver.ResolveImageConfig(context.TODO(), ref, info.Constraints.Platform)
96 96
 		if err != nil {
97 97
 			src.err = err
98 98
 		} else {
... ...
@@ -136,7 +131,7 @@ func (fn ImageOptionFunc) SetImageOption(ii *ImageInfo) {
136 136
 }
137 137
 
138 138
 type ImageInfo struct {
139
-	opMetaWrapper
139
+	constraintsWrapper
140 140
 	metaResolver ImageMetaResolver
141 141
 }
142 142
 
... ...
@@ -169,7 +164,7 @@ func Git(remote, ref string, opts ...GitOption) State {
169 169
 	if url != "" {
170 170
 		attrs[pb.AttrFullRemoteURL] = url
171 171
 	}
172
-	source := NewSource("git://"+id, attrs, gi.Metadata())
172
+	source := NewSource("git://"+id, attrs, gi.Constraints)
173 173
 	return NewState(source.Output())
174 174
 }
175 175
 
... ...
@@ -183,7 +178,7 @@ func (fn gitOptionFunc) SetGitOption(gi *GitInfo) {
183 183
 }
184 184
 
185 185
 type GitInfo struct {
186
-	opMetaWrapper
186
+	constraintsWrapper
187 187
 	KeepGitDir bool
188 188
 }
189 189
 
... ...
@@ -220,7 +215,7 @@ func Local(name string, opts ...LocalOption) State {
220 220
 		attrs[pb.AttrSharedKeyHint] = gi.SharedKeyHint
221 221
 	}
222 222
 
223
-	source := NewSource("local://"+name, attrs, gi.Metadata())
223
+	source := NewSource("local://"+name, attrs, gi.Constraints)
224 224
 	return NewState(source.Output())
225 225
 }
226 226
 
... ...
@@ -280,7 +275,7 @@ func SharedKeyHint(h string) LocalOption {
280 280
 }
281 281
 
282 282
 type LocalInfo struct {
283
-	opMetaWrapper
283
+	constraintsWrapper
284 284
 	SessionID       string
285 285
 	IncludePatterns string
286 286
 	ExcludePatterns string
... ...
@@ -310,12 +305,12 @@ func HTTP(url string, opts ...HTTPOption) State {
310 310
 		attrs[pb.AttrHTTPGID] = strconv.Itoa(hi.GID)
311 311
 	}
312 312
 
313
-	source := NewSource(url, attrs, hi.Metadata())
313
+	source := NewSource(url, attrs, hi.Constraints)
314 314
 	return NewState(source.Output())
315 315
 }
316 316
 
317 317
 type HTTPInfo struct {
318
-	opMetaWrapper
318
+	constraintsWrapper
319 319
 	Checksum digest.Digest
320 320
 	Filename string
321 321
 	Perm     int
... ...
@@ -3,21 +3,23 @@ package llb
3 3
 import (
4 4
 	"context"
5 5
 
6
+	"github.com/containerd/containerd/platforms"
6 7
 	"github.com/moby/buildkit/solver/pb"
7 8
 	"github.com/moby/buildkit/util/system"
8 9
 	digest "github.com/opencontainers/go-digest"
10
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
9 11
 )
10 12
 
11 13
 type StateOption func(State) State
12 14
 
13 15
 type Output interface {
14
-	ToInput() (*pb.Input, error)
16
+	ToInput(*Constraints) (*pb.Input, error)
15 17
 	Vertex() Vertex
16 18
 }
17 19
 
18 20
 type Vertex interface {
19 21
 	Validate() error
20
-	Marshal() (digest.Digest, []byte, *OpMetadata, error)
22
+	Marshal(*Constraints) (digest.Digest, []byte, *pb.OpMetadata, error)
21 23
 	Output() Output
22 24
 	Inputs() []Output
23 25
 }
... ...
@@ -29,12 +31,25 @@ func NewState(o Output) State {
29 29
 	}
30 30
 	s = dir("/")(s)
31 31
 	s = addEnv("PATH", system.DefaultPathEnv)(s)
32
+	s = s.ensurePlatform()
32 33
 	return s
33 34
 }
34 35
 
35 36
 type State struct {
36
-	out Output
37
-	ctx context.Context
37
+	out  Output
38
+	ctx  context.Context
39
+	opts []ConstraintsOpt
40
+}
41
+
42
+func (s State) ensurePlatform() State {
43
+	if o, ok := s.out.(interface {
44
+		Platform() *specs.Platform
45
+	}); ok {
46
+		if p := o.Platform(); p != nil {
47
+			s = platform(*p)(s)
48
+		}
49
+	}
50
+	return s
38 51
 }
39 52
 
40 53
 func (s State) WithValue(k, v interface{}) State {
... ...
@@ -48,18 +63,32 @@ func (s State) Value(k interface{}) interface{} {
48 48
 	return s.ctx.Value(k)
49 49
 }
50 50
 
51
-func (s State) Marshal(md ...MetadataOpt) (*Definition, error) {
51
+func (s State) SetMarhalDefaults(co ...ConstraintsOpt) State {
52
+	s.opts = co
53
+	return s
54
+}
55
+
56
+func (s State) Marshal(co ...ConstraintsOpt) (*Definition, error) {
52 57
 	def := &Definition{
53
-		Metadata: make(map[digest.Digest]OpMetadata, 0),
58
+		Metadata: make(map[digest.Digest]pb.OpMetadata, 0),
54 59
 	}
55 60
 	if s.Output() == nil {
56 61
 		return def, nil
57 62
 	}
58
-	def, err := marshal(s.Output().Vertex(), def, map[digest.Digest]struct{}{}, map[Vertex]struct{}{}, md)
63
+
64
+	defaultPlatform := platforms.Normalize(platforms.DefaultSpec())
65
+	c := &Constraints{
66
+		Platform: &defaultPlatform,
67
+	}
68
+	for _, o := range append(s.opts, co...) {
69
+		o.SetConstraintsOption(c)
70
+	}
71
+
72
+	def, err := marshal(s.Output().Vertex(), def, map[digest.Digest]struct{}{}, map[Vertex]struct{}{}, c)
59 73
 	if err != nil {
60 74
 		return def, err
61 75
 	}
62
-	inp, err := s.Output().ToInput()
76
+	inp, err := s.Output().ToInput(c)
63 77
 	if err != nil {
64 78
 		return def, err
65 79
 	}
... ...
@@ -72,29 +101,25 @@ func (s State) Marshal(md ...MetadataOpt) (*Definition, error) {
72 72
 	return def, nil
73 73
 }
74 74
 
75
-func marshal(v Vertex, def *Definition, cache map[digest.Digest]struct{}, vertexCache map[Vertex]struct{}, md []MetadataOpt) (*Definition, error) {
75
+func marshal(v Vertex, def *Definition, cache map[digest.Digest]struct{}, vertexCache map[Vertex]struct{}, c *Constraints) (*Definition, error) {
76 76
 	if _, ok := vertexCache[v]; ok {
77 77
 		return def, nil
78 78
 	}
79 79
 	for _, inp := range v.Inputs() {
80 80
 		var err error
81
-		def, err = marshal(inp.Vertex(), def, cache, vertexCache, md)
81
+		def, err = marshal(inp.Vertex(), def, cache, vertexCache, c)
82 82
 		if err != nil {
83 83
 			return def, err
84 84
 		}
85 85
 	}
86 86
 
87
-	dgst, dt, opMeta, err := v.Marshal()
87
+	dgst, dt, opMeta, err := v.Marshal(c)
88 88
 	if err != nil {
89 89
 		return def, err
90 90
 	}
91 91
 	vertexCache[v] = struct{}{}
92 92
 	if opMeta != nil {
93
-		m := mergeMetadata(def.Metadata[dgst], *opMeta)
94
-		for _, f := range md {
95
-			f.SetMetadataOption(&m)
96
-		}
97
-		def.Metadata[dgst] = m
93
+		def.Metadata[dgst] = mergeMetadata(def.Metadata[dgst], *opMeta)
98 94
 	}
99 95
 	if _, ok := cache[dgst]; ok {
100 96
 		return def, nil
... ...
@@ -113,14 +138,19 @@ func (s State) Output() Output {
113 113
 }
114 114
 
115 115
 func (s State) WithOutput(o Output) State {
116
-	return State{
116
+	s = State{
117 117
 		out: o,
118 118
 		ctx: s.ctx,
119 119
 	}
120
+	s = s.ensurePlatform()
121
+	return s
120 122
 }
121 123
 
122 124
 func (s State) Run(ro ...RunOption) ExecState {
123 125
 	ei := &ExecInfo{State: s}
126
+	if p := s.GetPlatform(); p != nil {
127
+		ei.Constraints.Platform = p
128
+	}
124 129
 	for _, o := range ro {
125 130
 		o.SetRunOption(ei)
126 131
 	}
... ...
@@ -132,7 +162,7 @@ func (s State) Run(ro ...RunOption) ExecState {
132 132
 		ProxyEnv: ei.ProxyEnv,
133 133
 	}
134 134
 
135
-	exec := NewExecOp(s.Output(), meta, ei.ReadonlyRootFS, ei.Metadata())
135
+	exec := NewExecOp(s.Output(), meta, ei.ReadonlyRootFS, ei.Constraints)
136 136
 	for _, m := range ei.Mounts {
137 137
 		exec.AddMount(m.Target, m.Source, m.Opts...)
138 138
 	}
... ...
@@ -178,6 +208,14 @@ func (s State) User(v string) State {
178 178
 	return user(v)(s)
179 179
 }
180 180
 
181
+func (s State) Platform(p specs.Platform) State {
182
+	return platform(p)(s)
183
+}
184
+
185
+func (s State) GetPlatform() *specs.Platform {
186
+	return getPlatform(s)
187
+}
188
+
181 189
 func (s State) With(so ...StateOption) State {
182 190
 	for _, o := range so {
183 191
 		s = o(s)
... ...
@@ -189,9 +227,10 @@ type output struct {
189 189
 	vertex   Vertex
190 190
 	getIndex func() (pb.OutputIndex, error)
191 191
 	err      error
192
+	platform *specs.Platform
192 193
 }
193 194
 
194
-func (o *output) ToInput() (*pb.Input, error) {
195
+func (o *output) ToInput(c *Constraints) (*pb.Input, error) {
195 196
 	if o.err != nil {
196 197
 		return nil, o.err
197 198
 	}
... ...
@@ -203,7 +242,7 @@ func (o *output) ToInput() (*pb.Input, error) {
203 203
 			return nil, err
204 204
 		}
205 205
 	}
206
-	dgst, _, _, err := o.vertex.Marshal()
206
+	dgst, _, _, err := o.vertex.Marshal(c)
207 207
 	if err != nil {
208 208
 		return nil, err
209 209
 	}
... ...
@@ -214,8 +253,12 @@ func (o *output) Vertex() Vertex {
214 214
 	return o.vertex
215 215
 }
216 216
 
217
-type MetadataOpt interface {
218
-	SetMetadataOption(*OpMetadata)
217
+func (o *output) Platform() *specs.Platform {
218
+	return o.platform
219
+}
220
+
221
+type ConstraintsOpt interface {
222
+	SetConstraintsOption(*Constraints)
219 223
 	RunOption
220 224
 	LocalOption
221 225
 	HTTPOption
... ...
@@ -223,33 +266,33 @@ type MetadataOpt interface {
223 223
 	GitOption
224 224
 }
225 225
 
226
-type metadataOptFunc func(m *OpMetadata)
226
+type constraintsOptFunc func(m *Constraints)
227 227
 
228
-func (fn metadataOptFunc) SetMetadataOption(m *OpMetadata) {
228
+func (fn constraintsOptFunc) SetConstraintsOption(m *Constraints) {
229 229
 	fn(m)
230 230
 }
231 231
 
232
-func (fn metadataOptFunc) SetRunOption(ei *ExecInfo) {
233
-	ei.ApplyMetadata(fn)
232
+func (fn constraintsOptFunc) SetRunOption(ei *ExecInfo) {
233
+	ei.applyConstraints(fn)
234 234
 }
235 235
 
236
-func (fn metadataOptFunc) SetLocalOption(li *LocalInfo) {
237
-	li.ApplyMetadata(fn)
236
+func (fn constraintsOptFunc) SetLocalOption(li *LocalInfo) {
237
+	li.applyConstraints(fn)
238 238
 }
239 239
 
240
-func (fn metadataOptFunc) SetHTTPOption(hi *HTTPInfo) {
241
-	hi.ApplyMetadata(fn)
240
+func (fn constraintsOptFunc) SetHTTPOption(hi *HTTPInfo) {
241
+	hi.applyConstraints(fn)
242 242
 }
243 243
 
244
-func (fn metadataOptFunc) SetImageOption(ii *ImageInfo) {
245
-	ii.ApplyMetadata(fn)
244
+func (fn constraintsOptFunc) SetImageOption(ii *ImageInfo) {
245
+	ii.applyConstraints(fn)
246 246
 }
247 247
 
248
-func (fn metadataOptFunc) SetGitOption(gi *GitInfo) {
249
-	gi.ApplyMetadata(fn)
248
+func (fn constraintsOptFunc) SetGitOption(gi *GitInfo) {
249
+	gi.applyConstraints(fn)
250 250
 }
251 251
 
252
-func mergeMetadata(m1, m2 OpMetadata) OpMetadata {
252
+func mergeMetadata(m1, m2 pb.OpMetadata) pb.OpMetadata {
253 253
 	if m2.IgnoreCache {
254 254
 		m1.IgnoreCache = true
255 255
 	}
... ...
@@ -268,49 +311,77 @@ func mergeMetadata(m1, m2 OpMetadata) OpMetadata {
268 268
 	return m1
269 269
 }
270 270
 
271
-var IgnoreCache = metadataOptFunc(func(md *OpMetadata) {
272
-	md.IgnoreCache = true
271
+var IgnoreCache = constraintsOptFunc(func(c *Constraints) {
272
+	c.Metadata.IgnoreCache = true
273 273
 })
274 274
 
275
-func WithDescription(m map[string]string) MetadataOpt {
276
-	return metadataOptFunc(func(md *OpMetadata) {
277
-		md.Description = m
275
+func WithDescription(m map[string]string) ConstraintsOpt {
276
+	return constraintsOptFunc(func(c *Constraints) {
277
+		c.Metadata.Description = m
278 278
 	})
279 279
 }
280 280
 
281 281
 // WithExportCache forces results for this vertex to be exported with the cache
282
-func WithExportCache() MetadataOpt {
283
-	return metadataOptFunc(func(md *OpMetadata) {
284
-		md.ExportCache = &pb.ExportCache{Value: true}
282
+func WithExportCache() ConstraintsOpt {
283
+	return constraintsOptFunc(func(c *Constraints) {
284
+		c.Metadata.ExportCache = &pb.ExportCache{Value: true}
285 285
 	})
286 286
 }
287 287
 
288 288
 // WithoutExportCache sets results for this vertex to be not exported with
289 289
 // the cache
290
-func WithoutExportCache() MetadataOpt {
291
-	return metadataOptFunc(func(md *OpMetadata) {
290
+func WithoutExportCache() ConstraintsOpt {
291
+	return constraintsOptFunc(func(c *Constraints) {
292 292
 		// ExportCache with value false means to disable exporting
293
-		md.ExportCache = &pb.ExportCache{Value: false}
293
+		c.Metadata.ExportCache = &pb.ExportCache{Value: false}
294 294
 	})
295 295
 }
296 296
 
297 297
 // WithoutDefaultExportCache resets the cache export for the vertex to use
298 298
 // the default defined by the build configuration.
299
-func WithoutDefaultExportCache() MetadataOpt {
300
-	return metadataOptFunc(func(md *OpMetadata) {
299
+func WithoutDefaultExportCache() ConstraintsOpt {
300
+	return constraintsOptFunc(func(c *Constraints) {
301 301
 		// nil means no vertex based config has been set
302
-		md.ExportCache = nil
302
+		c.Metadata.ExportCache = nil
303 303
 	})
304 304
 }
305 305
 
306
-type opMetaWrapper struct {
307
-	OpMetadata
306
+type constraintsWrapper struct {
307
+	Constraints
308
+}
309
+
310
+func (cw *constraintsWrapper) applyConstraints(f func(c *Constraints)) {
311
+	f(&cw.Constraints)
308 312
 }
309 313
 
310
-func (mw *opMetaWrapper) ApplyMetadata(f func(m *OpMetadata)) {
311
-	f(&mw.OpMetadata)
314
+type Constraints struct {
315
+	Platform          *specs.Platform
316
+	WorkerConstraints []string
317
+	Metadata          pb.OpMetadata
312 318
 }
313 319
 
314
-func (mw *opMetaWrapper) Metadata() OpMetadata {
315
-	return mw.OpMetadata
320
+func Platform(p specs.Platform) ConstraintsOpt {
321
+	return constraintsOptFunc(func(c *Constraints) {
322
+		c.Platform = &p
323
+	})
324
+}
325
+
326
+var (
327
+	LinuxAmd64   = Platform(specs.Platform{OS: "linux", Architecture: "amd64"})
328
+	LinuxArmhf   = Platform(specs.Platform{OS: "linux", Architecture: "arm", Variant: "v7"})
329
+	LinuxArm     = LinuxArmhf
330
+	LinuxArmel   = Platform(specs.Platform{OS: "linux", Architecture: "arm", Variant: "v6"})
331
+	LinuxArm64   = Platform(specs.Platform{OS: "linux", Architecture: "arm64"})
332
+	LinuxS390x   = Platform(specs.Platform{OS: "linux", Architecture: "s390x"})
333
+	LinuxPpc64le = Platform(specs.Platform{OS: "linux", Architecture: "ppc64le"})
334
+	Darwin       = Platform(specs.Platform{OS: "darwin", Architecture: "amd64"})
335
+	Windows      = Platform(specs.Platform{OS: "windows", Architecture: "amd64"})
336
+)
337
+
338
+func Require(filters ...string) ConstraintsOpt {
339
+	return constraintsOptFunc(func(c *Constraints) {
340
+		for _, f := range filters {
341
+			c.WorkerConstraints = append(c.WorkerConstraints, f)
342
+		}
343
+	})
316 344
 }
... ...
@@ -4,12 +4,15 @@ import (
4 4
 	"context"
5 5
 
6 6
 	controlapi "github.com/moby/buildkit/api/services/control"
7
+	"github.com/moby/buildkit/solver/pb"
8
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
7 9
 	"github.com/pkg/errors"
8 10
 )
9 11
 
10 12
 type WorkerInfo struct {
11
-	ID     string
12
-	Labels map[string]string
13
+	ID        string
14
+	Labels    map[string]string
15
+	Platforms []specs.Platform
13 16
 }
14 17
 
15 18
 func (c *Client) ListWorkers(ctx context.Context, opts ...ListWorkersOption) ([]*WorkerInfo, error) {
... ...
@@ -28,8 +31,9 @@ func (c *Client) ListWorkers(ctx context.Context, opts ...ListWorkersOption) ([]
28 28
 
29 29
 	for _, w := range resp.Record {
30 30
 		wi = append(wi, &WorkerInfo{
31
-			ID:     w.ID,
32
-			Labels: w.Labels,
31
+			ID:        w.ID,
32
+			Labels:    w.Labels,
33
+			Platforms: toClientPlatforms(w.Platforms),
33 34
 		})
34 35
 	}
35 36
 
... ...
@@ -47,3 +51,17 @@ func WithWorkerFilter(f []string) ListWorkersOption {
47 47
 		wi.Filter = f
48 48
 	}
49 49
 }
50
+
51
+func toClientPlatforms(p []pb.Platform) []specs.Platform {
52
+	out := make([]specs.Platform, 0, len(p))
53
+	for _, pp := range p {
54
+		out = append(out, specs.Platform{
55
+			OS:           pp.OS,
56
+			Architecture: pp.Architecture,
57
+			Variant:      pp.Variant,
58
+			OSVersion:    pp.OSVersion,
59
+			OSFeatures:   pp.OSFeatures,
60
+		})
61
+	}
62
+	return out
63
+}
... ...
@@ -13,7 +13,9 @@ import (
13 13
 	"github.com/moby/buildkit/session/grpchijack"
14 14
 	"github.com/moby/buildkit/solver"
15 15
 	"github.com/moby/buildkit/solver/llbsolver"
16
+	"github.com/moby/buildkit/solver/pb"
16 17
 	"github.com/moby/buildkit/worker"
18
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
17 19
 	"github.com/pkg/errors"
18 20
 	"github.com/sirupsen/logrus"
19 21
 	"golang.org/x/sync/errgroup"
... ...
@@ -35,7 +37,10 @@ type Controller struct { // TODO: ControlService
35 35
 }
36 36
 
37 37
 func NewController(opt Opt) (*Controller, error) {
38
-	solver := llbsolver.New(opt.WorkerController, opt.Frontends, opt.CacheKeyStorage, opt.CacheImporter)
38
+	solver, err := llbsolver.New(opt.WorkerController, opt.Frontends, opt.CacheKeyStorage, opt.CacheImporter)
39
+	if err != nil {
40
+		return nil, errors.Wrap(err, "failed to create solver")
41
+	}
39 42
 
40 43
 	c := &Controller{
41 44
 		opt:    opt,
... ...
@@ -265,8 +270,9 @@ func (c *Controller) ListWorkers(ctx context.Context, r *controlapi.ListWorkersR
265 265
 	}
266 266
 	for _, w := range workers {
267 267
 		resp.Record = append(resp.Record, &controlapi.WorkerRecord{
268
-			ID:     w.ID(),
269
-			Labels: w.Labels(),
268
+			ID:        w.ID(),
269
+			Labels:    w.Labels(),
270
+			Platforms: toPBPlatforms(w.Platforms()),
270 271
 		})
271 272
 	}
272 273
 	return resp, nil
... ...
@@ -290,3 +296,17 @@ func parseCacheExporterOpt(opt map[string]string) solver.CacheExportMode {
290 290
 	}
291 291
 	return solver.CacheExportModeMin
292 292
 }
293
+
294
+func toPBPlatforms(p []specs.Platform) []pb.Platform {
295
+	out := make([]pb.Platform, 0, len(p))
296
+	for _, pp := range p {
297
+		out = append(out, pb.Platform{
298
+			OS:           pp.OS,
299
+			Architecture: pp.Architecture,
300
+			Variant:      pp.Variant,
301
+			OSVersion:    pp.OSVersion,
302
+			OSFeatures:   pp.OSFeatures,
303
+		})
304
+	}
305
+	return out
306
+}
... ...
@@ -8,10 +8,12 @@ import (
8 8
 	"regexp"
9 9
 	"strings"
10 10
 
11
+	"github.com/containerd/containerd/platforms"
11 12
 	"github.com/docker/docker/builder/dockerignore"
12 13
 	"github.com/moby/buildkit/client/llb"
13 14
 	"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
14 15
 	"github.com/moby/buildkit/frontend/gateway/client"
16
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
15 17
 	"github.com/pkg/errors"
16 18
 	"golang.org/x/sync/errgroup"
17 19
 )
... ...
@@ -28,14 +30,26 @@ const (
28 28
 	buildArgPrefix        = "build-arg:"
29 29
 	labelPrefix           = "label:"
30 30
 	keyNoCache            = "no-cache"
31
+	keyTargetPlatform     = "platform"
31 32
 )
32 33
 
33 34
 var httpPrefix = regexp.MustCompile("^https?://")
34
-var gitUrlPathWithFragmentSuffix = regexp.MustCompile(".git(?:#.+)?$")
35
+var gitUrlPathWithFragmentSuffix = regexp.MustCompile("\\.git(?:#.+)?$")
35 36
 
36 37
 func Build(ctx context.Context, c client.Client) error {
37 38
 	opts := c.Opts()
38 39
 
40
+	// TODO: read buildPlatforms from workers
41
+	buildPlatforms := []specs.Platform{platforms.DefaultSpec()}
42
+	targetPlatform := platforms.DefaultSpec()
43
+	if v := opts[keyTargetPlatform]; v != "" {
44
+		var err error
45
+		targetPlatform, err = platforms.Parse(v)
46
+		if err != nil {
47
+			return errors.Wrapf(err, "failed to parse target platform %s", v)
48
+		}
49
+	}
50
+
39 51
 	filename := opts[keyFilename]
40 52
 	if filename == "" {
41 53
 		filename = defaultDockerfileName
... ...
@@ -166,14 +180,16 @@ func Build(ctx context.Context, c client.Client) error {
166 166
 	}
167 167
 
168 168
 	st, img, err := dockerfile2llb.Dockerfile2LLB(ctx, dtDockerfile, dockerfile2llb.ConvertOpt{
169
-		Target:       opts[keyTarget],
170
-		MetaResolver: c,
171
-		BuildArgs:    filter(opts, buildArgPrefix),
172
-		Labels:       filter(opts, labelPrefix),
173
-		SessionID:    c.SessionID(),
174
-		BuildContext: buildContext,
175
-		Excludes:     excludes,
176
-		IgnoreCache:  ignoreCache,
169
+		Target:         opts[keyTarget],
170
+		MetaResolver:   c,
171
+		BuildArgs:      filter(opts, buildArgPrefix),
172
+		Labels:         filter(opts, labelPrefix),
173
+		SessionID:      c.SessionID(),
174
+		BuildContext:   buildContext,
175
+		Excludes:       excludes,
176
+		IgnoreCache:    ignoreCache,
177
+		TargetPlatform: &targetPlatform,
178
+		BuildPlatforms: buildPlatforms,
177 179
 	})
178 180
 
179 181
 	if err != nil {
... ...
@@ -12,6 +12,7 @@ import (
12 12
 	"strconv"
13 13
 	"strings"
14 14
 
15
+	"github.com/containerd/containerd/platforms"
15 16
 	"github.com/docker/distribution/reference"
16 17
 	"github.com/docker/docker/pkg/signal"
17 18
 	"github.com/docker/go-connections/nat"
... ...
@@ -20,7 +21,7 @@ import (
20 20
 	"github.com/moby/buildkit/frontend/dockerfile/instructions"
21 21
 	"github.com/moby/buildkit/frontend/dockerfile/parser"
22 22
 	"github.com/moby/buildkit/frontend/dockerfile/shell"
23
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
23
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
24 24
 	"github.com/pkg/errors"
25 25
 	"golang.org/x/sync/errgroup"
26 26
 )
... ...
@@ -30,7 +31,7 @@ const (
30 30
 	localNameContext = "context"
31 31
 	historyComment   = "buildkit.dockerfile.v0"
32 32
 
33
-	CopyImage = "tonistiigi/copy:v0.1.3@sha256:87c46e7b413cdd2c2702902b481b390ce263ac9d942253d366f3b1a3c16f96d6"
33
+	CopyImage = "tonistiigi/copy:v0.1.3@sha256:e57a3b4d6240f55bac26b655d2cfb751f8b9412d6f7bb1f787e946391fb4b21b"
34 34
 )
35 35
 
36 36
 type ConvertOpt struct {
... ...
@@ -46,6 +47,8 @@ type ConvertOpt struct {
46 46
 	IgnoreCache []string
47 47
 	// CacheIDNamespace scopes the IDs for different cache mounts
48 48
 	CacheIDNamespace string
49
+	TargetPlatform   *specs.Platform
50
+	BuildPlatforms   []specs.Platform
49 51
 }
50 52
 
51 53
 func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State, *Image, error) {
... ...
@@ -53,6 +56,18 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
53 53
 		return nil, nil, errors.Errorf("the Dockerfile cannot be empty")
54 54
 	}
55 55
 
56
+	if opt.TargetPlatform != nil && opt.BuildPlatforms == nil {
57
+		opt.BuildPlatforms = []specs.Platform{*opt.TargetPlatform}
58
+	}
59
+	if len(opt.BuildPlatforms) == 0 {
60
+		opt.BuildPlatforms = []specs.Platform{platforms.DefaultSpec()}
61
+	}
62
+	implicitTargetPlatform := false
63
+	if opt.TargetPlatform == nil {
64
+		implicitTargetPlatform = true
65
+		opt.TargetPlatform = &opt.BuildPlatforms[0]
66
+	}
67
+
56 68
 	dockerfile, err := parser.Parse(bytes.NewReader(dt))
57 69
 	if err != nil {
58 70
 		return nil, nil, err
... ...
@@ -92,6 +107,20 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
92 92
 			deps:     make(map[*dispatchState]struct{}),
93 93
 			ctxPaths: make(map[string]struct{}),
94 94
 		}
95
+
96
+		if v := st.Platform; v != "" {
97
+			v, err := shlex.ProcessWord(v, toEnvList(metaArgs, nil))
98
+			if err != nil {
99
+				return nil, nil, errors.Wrapf(err, "failed to process arguments for platform %s", v)
100
+			}
101
+
102
+			p, err := platforms.Parse(v)
103
+			if err != nil {
104
+				return nil, nil, errors.Wrapf(err, "failed to parse platform %s", v)
105
+			}
106
+			ds.platform = &p
107
+		}
108
+
95 109
 		if d, ok := dispatchStatesByName[st.BaseName]; ok {
96 110
 			ds.base = d
97 111
 		}
... ...
@@ -150,7 +179,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
150 150
 		if d.base == nil {
151 151
 			if d.stage.BaseName == emptyImageName {
152 152
 				d.state = llb.Scratch()
153
-				d.image = emptyImage()
153
+				d.image = emptyImage(*opt.TargetPlatform)
154 154
 				continue
155 155
 			}
156 156
 			func(i int, d *dispatchState) {
... ...
@@ -159,16 +188,25 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
159 159
 					if err != nil {
160 160
 						return err
161 161
 					}
162
+					platform := d.platform
163
+					if platform == nil {
164
+						platform = opt.TargetPlatform
165
+					}
162 166
 					d.stage.BaseName = reference.TagNameOnly(ref).String()
163 167
 					var isScratch bool
164 168
 					if metaResolver != nil && reachable {
165
-						dgst, dt, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName)
169
+						dgst, dt, err := metaResolver.ResolveImageConfig(ctx, d.stage.BaseName, platform)
166 170
 						if err == nil { // handle the error while builder is actually running
167 171
 							var img Image
168 172
 							if err := json.Unmarshal(dt, &img); err != nil {
169 173
 								return err
170 174
 							}
171 175
 							img.Created = nil
176
+							// if there is no explicit target platform, try to match based on image config
177
+							if d.platform == nil && implicitTargetPlatform {
178
+								p := autoDetectPlatform(img, *platform, opt.BuildPlatforms)
179
+								platform = &p
180
+							}
172 181
 							d.image = img
173 182
 							if dgst != "" {
174 183
 								ref, err = reference.WithDigest(ref, dgst)
... ...
@@ -186,7 +224,7 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
186 186
 					if isScratch {
187 187
 						d.state = llb.Scratch()
188 188
 					} else {
189
-						d.state = llb.Image(d.stage.BaseName, dfCmd(d.stage.SourceCode))
189
+						d.state = llb.Image(d.stage.BaseName, dfCmd(d.stage.SourceCode), llb.Platform(*platform))
190 190
 					}
191 191
 					return nil
192 192
 				})
... ...
@@ -242,6 +280,8 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
242 242
 			buildContext:         llb.NewState(buildContext),
243 243
 			proxyEnv:             proxyEnv,
244 244
 			cacheIDNamespace:     opt.CacheIDNamespace,
245
+			buildPlatforms:       opt.BuildPlatforms,
246
+			targetPlatform:       *opt.TargetPlatform,
245 247
 		}
246 248
 
247 249
 		if err = dispatchOnBuild(d, d.image.Config.OnBuild, opt); err != nil {
... ...
@@ -280,7 +320,14 @@ func Dockerfile2LLB(ctx context.Context, dt []byte, opt ConvertOpt) (*llb.State,
280 280
 	}
281 281
 	buildContext.Output = bc.Output()
282 282
 
283
-	return &target.state, &target.image, nil
283
+	st := target.state.SetMarhalDefaults(llb.Platform(*opt.TargetPlatform))
284
+
285
+	if !implicitTargetPlatform {
286
+		target.image.OS = opt.TargetPlatform.OS
287
+		target.image.Architecture = opt.TargetPlatform.Architecture
288
+	}
289
+
290
+	return &st, &target.image, nil
284 291
 }
285 292
 
286 293
 func toCommand(ic instructions.Command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) (command, error) {
... ...
@@ -325,6 +372,8 @@ type dispatchOpt struct {
325 325
 	buildContext         llb.State
326 326
 	proxyEnv             *llb.ProxyEnv
327 327
 	cacheIDNamespace     string
328
+	targetPlatform       specs.Platform
329
+	buildPlatforms       []specs.Platform
328 330
 }
329 331
 
330 332
 func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
... ...
@@ -348,7 +397,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
348 348
 	case *instructions.WorkdirCommand:
349 349
 		err = dispatchWorkdir(d, c, true)
350 350
 	case *instructions.AddCommand:
351
-		err = dispatchCopy(d, c.SourcesAndDest, opt.buildContext, true, c, "")
351
+		err = dispatchCopy(d, c.SourcesAndDest, opt.buildContext, true, c, "", opt)
352 352
 		if err == nil {
353 353
 			for _, src := range c.Sources() {
354 354
 				d.ctxPaths[path.Join("/", filepath.ToSlash(src))] = struct{}{}
... ...
@@ -381,7 +430,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
381 381
 		if len(cmd.sources) != 0 {
382 382
 			l = cmd.sources[0].state
383 383
 		}
384
-		err = dispatchCopy(d, c.SourcesAndDest, l, false, c, c.Chown)
384
+		err = dispatchCopy(d, c.SourcesAndDest, l, false, c, c.Chown, opt)
385 385
 		if err == nil && len(cmd.sources) == 0 {
386 386
 			for _, src := range c.Sources() {
387 387
 				d.ctxPaths[path.Join("/", filepath.ToSlash(src))] = struct{}{}
... ...
@@ -395,6 +444,7 @@ func dispatch(d *dispatchState, cmd command, opt dispatchOpt) error {
395 395
 type dispatchState struct {
396 396
 	state        llb.State
397 397
 	image        Image
398
+	platform     *specs.Platform
398 399
 	stage        instructions.Stage
399 400
 	base         *dispatchState
400 401
 	deps         map[*dispatchState]struct{}
... ...
@@ -467,7 +517,11 @@ func dispatchRun(d *dispatchState, c *instructions.RunCommand, proxy *llb.ProxyE
467 467
 		opt = append(opt, llb.WithProxy(*proxy))
468 468
 	}
469 469
 
470
-	opt = append(opt, dispatchRunMounts(d, c, sources, dopt)...)
470
+	runMounts, err := dispatchRunMounts(d, c, sources, dopt)
471
+	if err != nil {
472
+		return err
473
+	}
474
+	opt = append(opt, runMounts...)
471 475
 
472 476
 	d.state = d.state.Run(opt...).Root()
473 477
 	return commitToHistory(&d.image, "RUN "+runCommandString(args, d.buildArgs), true, &d.state)
... ...
@@ -486,9 +540,9 @@ func dispatchWorkdir(d *dispatchState, c *instructions.WorkdirCommand, commit bo
486 486
 	return nil
487 487
 }
488 488
 
489
-func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState llb.State, isAddCommand bool, cmdToPrint interface{}, chown string) error {
489
+func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState llb.State, isAddCommand bool, cmdToPrint interface{}, chown string, opt dispatchOpt) error {
490 490
 	// TODO: this should use CopyOp instead. Current implementation is inefficient
491
-	img := llb.Image(CopyImage)
491
+	img := llb.Image(CopyImage, llb.Platform(opt.buildPlatforms[0]))
492 492
 
493 493
 	dest := path.Join(".", pathRelativeToWorkingDir(d.state, c.Dest()))
494 494
 	if c.Dest() == "." || c.Dest()[len(c.Dest())-1] == filepath.Separator {
... ...
@@ -554,12 +608,12 @@ func dispatchCopy(d *dispatchState, c instructions.SourcesAndDest, sourceState l
554 554
 		args = append(args[:1], append([]string{"--unpack"}, args[1:]...)...)
555 555
 	}
556 556
 
557
-	opt := []llb.RunOption{llb.Args(args), llb.Dir("/dest"), llb.ReadonlyRootFS(), dfCmd(cmdToPrint)}
557
+	runOpt := []llb.RunOption{llb.Args(args), llb.Dir("/dest"), llb.ReadonlyRootFS(), dfCmd(cmdToPrint)}
558 558
 	if d.ignoreCache {
559
-		opt = append(opt, llb.IgnoreCache)
559
+		runOpt = append(runOpt, llb.IgnoreCache)
560 560
 	}
561
-	run := img.Run(append(opt, mounts...)...)
562
-	d.state = run.AddMount("/dest", d.state)
561
+	run := img.Run(append(runOpt, mounts...)...)
562
+	d.state = run.AddMount("/dest", d.state).Platform(opt.targetPlatform)
563 563
 
564 564
 	return commitToHistory(&d.image, commitMessage.String(), true, &d.state)
565 565
 }
... ...
@@ -767,7 +821,7 @@ func getArgValue(arg instructions.ArgCommand) string {
767 767
 	return v
768 768
 }
769 769
 
770
-func dfCmd(cmd interface{}) llb.MetadataOpt {
770
+func dfCmd(cmd interface{}) llb.ConstraintsOpt {
771 771
 	// TODO: add fmt.Stringer to instructions.Command to remove interface{}
772 772
 	var cmdStr string
773 773
 	if cmd, ok := cmd.(fmt.Stringer); ok {
... ...
@@ -798,7 +852,7 @@ func commitToHistory(img *Image, msg string, withLayer bool, st *llb.State) erro
798 798
 		msg += " # buildkit"
799 799
 	}
800 800
 
801
-	img.History = append(img.History, ocispec.History{
801
+	img.History = append(img.History, specs.History{
802 802
 		CreatedBy:  msg,
803 803
 		Comment:    historyComment,
804 804
 		EmptyLayer: !withLayer,
... ...
@@ -930,3 +984,17 @@ func withShell(img Image, args []string) []string {
930 930
 	}
931 931
 	return append(shell, strings.Join(args, " "))
932 932
 }
933
+
934
+func autoDetectPlatform(img Image, target specs.Platform, supported []specs.Platform) specs.Platform {
935
+	os := img.OS
936
+	arch := img.Architecture
937
+	if target.OS == os && target.Architecture == arch {
938
+		return target
939
+	}
940
+	for _, p := range supported {
941
+		if p.OS == os && p.Architecture == arch {
942
+			return p
943
+		}
944
+	}
945
+	return target
946
+}
... ...
@@ -11,6 +11,6 @@ func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState
11 11
 	return false
12 12
 }
13 13
 
14
-func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) []llb.RunOption {
15
-	return nil
14
+func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) {
15
+	return nil, nil
16 16
 }
... ...
@@ -9,6 +9,7 @@ import (
9 9
 
10 10
 	"github.com/moby/buildkit/client/llb"
11 11
 	"github.com/moby/buildkit/frontend/dockerfile/instructions"
12
+	"github.com/pkg/errors"
12 13
 )
13 14
 
14 15
 func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState, allDispatchStates []*dispatchState) bool {
... ...
@@ -40,7 +41,7 @@ func detectRunMount(cmd *command, dispatchStatesByName map[string]*dispatchState
40 40
 	return false
41 41
 }
42 42
 
43
-func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) []llb.RunOption {
43
+func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*dispatchState, opt dispatchOpt) ([]llb.RunOption, error) {
44 44
 	var out []llb.RunOption
45 45
 	mounts := instructions.GetMounts(c)
46 46
 
... ...
@@ -61,14 +62,25 @@ func dispatchRunMounts(d *dispatchState, c *instructions.RunCommand, sources []*
61 61
 			mountOpts = append(mountOpts, llb.Readonly)
62 62
 		}
63 63
 		if mount.Type == instructions.MountTypeCache {
64
-			mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID))
64
+			sharing := llb.CacheMountShared
65
+			if mount.CacheSharing == instructions.MountSharingPrivate {
66
+				sharing = llb.CacheMountPrivate
67
+			}
68
+			if mount.CacheSharing == instructions.MountSharingLocked {
69
+				sharing = llb.CacheMountLocked
70
+			}
71
+			mountOpts = append(mountOpts, llb.AsPersistentCacheDir(opt.cacheIDNamespace+"/"+mount.CacheID, sharing))
72
+		}
73
+		target := path.Join("/", mount.Target)
74
+		if target == "/" {
75
+			return nil, errors.Errorf("invalid mount target %q", mount.Target)
65 76
 		}
66 77
 		if src := path.Join("/", mount.Source); src != "/" {
67 78
 			mountOpts = append(mountOpts, llb.SourcePath(src))
68 79
 		}
69
-		out = append(out, llb.AddMount(path.Join("/", mount.Target), st, mountOpts...))
80
+		out = append(out, llb.AddMount(target, st, mountOpts...))
70 81
 
71 82
 		d.ctxPaths[path.Join("/", filepath.ToSlash(mount.Source))] = struct{}{}
72 83
 	}
73
-	return out
84
+	return out, nil
74 85
 }
... ...
@@ -1,12 +1,11 @@
1 1
 package dockerfile2llb
2 2
 
3 3
 import (
4
-	"runtime"
5 4
 	"time"
6 5
 
7 6
 	"github.com/docker/docker/api/types/strslice"
8 7
 	"github.com/moby/buildkit/util/system"
9
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
8
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
10 9
 )
11 10
 
12 11
 // HealthConfig holds configuration settings for the HEALTHCHECK feature.
... ...
@@ -31,7 +30,7 @@ type HealthConfig struct {
31 31
 }
32 32
 
33 33
 type ImageConfig struct {
34
-	ocispec.ImageConfig
34
+	specs.ImageConfig
35 35
 
36 36
 	Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
37 37
 	ArgsEscaped bool          `json:",omitempty"` // True if command is already escaped (Windows specific)
... ...
@@ -46,7 +45,7 @@ type ImageConfig struct {
46 46
 // Image is the JSON structure which describes some basic information about the image.
47 47
 // This provides the `application/vnd.oci.image.config.v1+json` mediatype when marshalled to JSON.
48 48
 type Image struct {
49
-	ocispec.Image
49
+	specs.Image
50 50
 
51 51
 	// Config defines the execution parameters which should be used as a base when running a container using the image.
52 52
 	Config ImageConfig `json:"config,omitempty"`
... ...
@@ -61,11 +60,11 @@ func clone(src Image) Image {
61 61
 	return img
62 62
 }
63 63
 
64
-func emptyImage() Image {
64
+func emptyImage(platform specs.Platform) Image {
65 65
 	img := Image{
66
-		Image: ocispec.Image{
67
-			Architecture: runtime.GOARCH,
68
-			OS:           runtime.GOOS,
66
+		Image: specs.Image{
67
+			Architecture: platform.Architecture,
68
+			OS:           platform.OS,
69 69
 		},
70 70
 	}
71 71
 	img.RootFS.Type = "layers"
... ...
@@ -6,7 +6,6 @@ import (
6 6
 
7 7
 	"github.com/docker/docker/api/types/container"
8 8
 	"github.com/docker/docker/api/types/strslice"
9
-	specs "github.com/opencontainers/image-spec/specs-go/v1"
10 9
 )
11 10
 
12 11
 // KeyValuePair represent an arbitrary named value (useful in slice instead of map[string] string to preserve ordering)
... ...
@@ -382,7 +381,7 @@ type Stage struct {
382 382
 	Commands   []Command
383 383
 	BaseName   string
384 384
 	SourceCode string
385
-	Platform   specs.Platform
385
+	Platform   string
386 386
 }
387 387
 
388 388
 // AddCommand to the stage
... ...
@@ -20,6 +20,16 @@ var allowedMountTypes = map[string]struct{}{
20 20
 	MountTypeTmpfs: {},
21 21
 }
22 22
 
23
+const MountSharingShared = "shared"
24
+const MountSharingPrivate = "private"
25
+const MountSharingLocked = "locked"
26
+
27
+var allowedSharingTypes = map[string]struct{}{
28
+	MountSharingShared:  {},
29
+	MountSharingPrivate: {},
30
+	MountSharingLocked:  {},
31
+}
32
+
23 33
 type mountsKeyT string
24 34
 
25 35
 var mountsKey = mountsKeyT("dockerfile/run/mounts")
... ...
@@ -76,12 +86,13 @@ type mountState struct {
76 76
 }
77 77
 
78 78
 type Mount struct {
79
-	Type     string
80
-	From     string
81
-	Source   string
82
-	Target   string
83
-	ReadOnly bool
84
-	CacheID  string
79
+	Type         string
80
+	From         string
81
+	Source       string
82
+	Target       string
83
+	ReadOnly     bool
84
+	CacheID      string
85
+	CacheSharing string
85 86
 }
86 87
 
87 88
 func parseMount(value string) (*Mount, error) {
... ...
@@ -120,7 +131,7 @@ func parseMount(value string) (*Mount, error) {
120 120
 		switch key {
121 121
 		case "type":
122 122
 			if !isValidMountType(strings.ToLower(value)) {
123
-				return nil, errors.Errorf("invalid mount type %q", value)
123
+				return nil, errors.Errorf("unsupported mount type %q", value)
124 124
 			}
125 125
 			m.Type = strings.ToLower(value)
126 126
 		case "from":
... ...
@@ -144,6 +155,11 @@ func parseMount(value string) (*Mount, error) {
144 144
 			roAuto = false
145 145
 		case "id":
146 146
 			m.CacheID = value
147
+		case "sharing":
148
+			if _, ok := allowedSharingTypes[strings.ToLower(value)]; !ok {
149
+				return nil, errors.Errorf("unsupported sharing value %q", value)
150
+			}
151
+			m.CacheSharing = strings.ToLower(value)
147 152
 		default:
148 153
 			return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field)
149 154
 		}
... ...
@@ -157,5 +173,9 @@ func parseMount(value string) (*Mount, error) {
157 157
 		}
158 158
 	}
159 159
 
160
+	if m.CacheSharing != "" && m.Type != MountTypeCache {
161
+		return nil, errors.Errorf("invalid cache sharing set for %v mount", m.Type)
162
+	}
163
+
160 164
 	return m, nil
161 165
 }
... ...
@@ -10,7 +10,6 @@ import (
10 10
 
11 11
 	"github.com/docker/docker/api/types/container"
12 12
 	"github.com/docker/docker/api/types/strslice"
13
-	"github.com/docker/docker/pkg/system"
14 13
 	"github.com/moby/buildkit/frontend/dockerfile/command"
15 14
 	"github.com/moby/buildkit/frontend/dockerfile/parser"
16 15
 	"github.com/pkg/errors"
... ...
@@ -279,13 +278,14 @@ func parseFrom(req parseRequest) (*Stage, error) {
279 279
 	if err := req.flags.Parse(); err != nil {
280 280
 		return nil, err
281 281
 	}
282
+
282 283
 	code := strings.TrimSpace(req.original)
283 284
 	return &Stage{
284 285
 		BaseName:   req.args[0],
285 286
 		Name:       stageName,
286 287
 		SourceCode: code,
287 288
 		Commands:   []Command{},
288
-		Platform:   *system.ParsePlatform(flPlatform.Value),
289
+		Platform:   flPlatform.Value,
289 290
 	}, nil
290 291
 
291 292
 }
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/moby/buildkit/solver"
10 10
 	"github.com/moby/buildkit/solver/pb"
11 11
 	digest "github.com/opencontainers/go-digest"
12
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
12 13
 )
13 14
 
14 15
 type Frontend interface {
... ...
@@ -17,7 +18,7 @@ type Frontend interface {
17 17
 
18 18
 type FrontendLLBBridge interface {
19 19
 	Solve(ctx context.Context, req SolveRequest) (solver.CachedResult, map[string][]byte, error)
20
-	ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error)
20
+	ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error)
21 21
 	Exec(ctx context.Context, meta executor.Meta, rootfs cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error
22 22
 }
23 23
 
... ...
@@ -5,12 +5,13 @@ import (
5 5
 
6 6
 	"github.com/moby/buildkit/solver/pb"
7 7
 	digest "github.com/opencontainers/go-digest"
8
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
8 9
 )
9 10
 
10 11
 // TODO: make this take same options as LLBBridge. Add Return()
11 12
 type Client interface {
12 13
 	Solve(ctx context.Context, req SolveRequest, exporterAttr map[string][]byte, final bool) (Reference, error)
13
-	ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error)
14
+	ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error)
14 15
 	Opts() map[string]string
15 16
 	SessionID() string
16 17
 }
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"net"
9 9
 	"os"
10 10
 	"strings"
11
+	"sync"
11 12
 	"time"
12 13
 
13 14
 	"github.com/docker/distribution/reference"
... ...
@@ -21,7 +22,7 @@ import (
21 21
 	"github.com/moby/buildkit/solver"
22 22
 	"github.com/moby/buildkit/util/tracing"
23 23
 	"github.com/moby/buildkit/worker"
24
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
24
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
25 25
 	"github.com/pkg/errors"
26 26
 	"github.com/sirupsen/logrus"
27 27
 	"golang.org/x/net/http2"
... ...
@@ -62,7 +63,7 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten
62 62
 	sid := session.FromContext(ctx)
63 63
 
64 64
 	_, isDevel := opts[keyDevel]
65
-	var img ocispec.Image
65
+	var img specs.Image
66 66
 	var rootFS cache.ImmutableRef
67 67
 	var readonly bool // TODO: try to switch to read-only by default.
68 68
 
... ...
@@ -94,7 +95,7 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten
94 94
 			return nil, nil, err
95 95
 		}
96 96
 
97
-		dgst, config, err := llbBridge.ResolveImageConfig(ctx, reference.TagNameOnly(sourceRef).String())
97
+		dgst, config, err := llbBridge.ResolveImageConfig(ctx, reference.TagNameOnly(sourceRef).String(), nil) // TODO:
98 98
 		if err != nil {
99 99
 			return nil, nil, err
100 100
 		}
... ...
@@ -103,9 +104,11 @@ func (gf *gatewayFrontend) Solve(ctx context.Context, llbBridge frontend.Fronten
103 103
 			return nil, nil, err
104 104
 		}
105 105
 
106
-		sourceRef, err = reference.WithDigest(sourceRef, dgst)
107
-		if err != nil {
108
-			return nil, nil, err
106
+		if dgst != "" {
107
+			sourceRef, err = reference.WithDigest(sourceRef, dgst)
108
+			if err != nil {
109
+				return nil, nil, err
110
+			}
109 111
 		}
110 112
 
111 113
 		src := llb.Image(sourceRef.String())
... ...
@@ -248,6 +251,7 @@ func (d dummyAddr) String() string {
248 248
 }
249 249
 
250 250
 type llbBridgeForwarder struct {
251
+	mu           sync.Mutex
251 252
 	callCtx      context.Context
252 253
 	llbBridge    frontend.FrontendLLBBridge
253 254
 	refs         map[string]solver.Result
... ...
@@ -258,7 +262,17 @@ type llbBridgeForwarder struct {
258 258
 
259 259
 func (lbf *llbBridgeForwarder) ResolveImageConfig(ctx context.Context, req *pb.ResolveImageConfigRequest) (*pb.ResolveImageConfigResponse, error) {
260 260
 	ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx)
261
-	dgst, dt, err := lbf.llbBridge.ResolveImageConfig(ctx, req.Ref)
261
+	var platform *specs.Platform
262
+	if p := req.Platform; p != nil {
263
+		platform = &specs.Platform{
264
+			OS:           p.OS,
265
+			Architecture: p.Architecture,
266
+			Variant:      p.Variant,
267
+			OSVersion:    p.OSVersion,
268
+			OSFeatures:   p.OSFeatures,
269
+		}
270
+	}
271
+	dgst, dt, err := lbf.llbBridge.ResolveImageConfig(ctx, req.Ref, platform)
262 272
 	if err != nil {
263 273
 		return nil, err
264 274
 	}
... ...
@@ -292,7 +306,9 @@ func (lbf *llbBridgeForwarder) Solve(ctx context.Context, req *pb.SolveRequest)
292 292
 	}
293 293
 
294 294
 	id := identity.NewID()
295
+	lbf.mu.Lock()
295 296
 	lbf.refs[id] = ref
297
+	lbf.mu.Unlock()
296 298
 	if req.Final {
297 299
 		lbf.lastRef = ref
298 300
 		lbf.exporterAttr = exp
... ...
@@ -304,7 +320,9 @@ func (lbf *llbBridgeForwarder) Solve(ctx context.Context, req *pb.SolveRequest)
304 304
 }
305 305
 func (lbf *llbBridgeForwarder) ReadFile(ctx context.Context, req *pb.ReadFileRequest) (*pb.ReadFileResponse, error) {
306 306
 	ctx = tracing.ContextWithSpanFromContext(ctx, lbf.callCtx)
307
+	lbf.mu.Lock()
307 308
 	ref, ok := lbf.refs[req.Ref]
309
+	lbf.mu.Unlock()
308 310
 	if !ok {
309 311
 		return nil, errors.Errorf("no such ref: %v", req.Ref)
310 312
 	}
... ...
@@ -45,7 +45,8 @@ var _ = math.Inf
45 45
 const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
46 46
 
47 47
 type ResolveImageConfigRequest struct {
48
-	Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
48
+	Ref      string       `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"`
49
+	Platform *pb.Platform `protobuf:"bytes,2,opt,name=Platform" json:"Platform,omitempty"`
49 50
 }
50 51
 
51 52
 func (m *ResolveImageConfigRequest) Reset()                    { *m = ResolveImageConfigRequest{} }
... ...
@@ -60,6 +61,13 @@ func (m *ResolveImageConfigRequest) GetRef() string {
60 60
 	return ""
61 61
 }
62 62
 
63
+func (m *ResolveImageConfigRequest) GetPlatform() *pb.Platform {
64
+	if m != nil {
65
+		return m.Platform
66
+	}
67
+	return nil
68
+}
69
+
63 70
 type ResolveImageConfigResponse struct {
64 71
 	Digest github_com_opencontainers_go_digest.Digest `protobuf:"bytes,1,opt,name=Digest,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"Digest"`
65 72
 	Config []byte                                     `protobuf:"bytes,2,opt,name=Config,proto3" json:"Config,omitempty"`
... ...
@@ -451,6 +459,16 @@ func (m *ResolveImageConfigRequest) MarshalTo(dAtA []byte) (int, error) {
451 451
 		i = encodeVarintGateway(dAtA, i, uint64(len(m.Ref)))
452 452
 		i += copy(dAtA[i:], m.Ref)
453 453
 	}
454
+	if m.Platform != nil {
455
+		dAtA[i] = 0x12
456
+		i++
457
+		i = encodeVarintGateway(dAtA, i, uint64(m.Platform.Size()))
458
+		n1, err := m.Platform.MarshalTo(dAtA[i:])
459
+		if err != nil {
460
+			return 0, err
461
+		}
462
+		i += n1
463
+	}
454 464
 	return i, nil
455 465
 }
456 466
 
... ...
@@ -503,11 +521,11 @@ func (m *SolveRequest) MarshalTo(dAtA []byte) (int, error) {
503 503
 		dAtA[i] = 0xa
504 504
 		i++
505 505
 		i = encodeVarintGateway(dAtA, i, uint64(m.Definition.Size()))
506
-		n1, err := m.Definition.MarshalTo(dAtA[i:])
506
+		n2, err := m.Definition.MarshalTo(dAtA[i:])
507 507
 		if err != nil {
508 508
 			return 0, err
509 509
 		}
510
-		i += n1
510
+		i += n2
511 511
 	}
512 512
 	if len(m.Frontend) > 0 {
513 513
 		dAtA[i] = 0x12
... ...
@@ -627,11 +645,11 @@ func (m *ReadFileRequest) MarshalTo(dAtA []byte) (int, error) {
627 627
 		dAtA[i] = 0x1a
628 628
 		i++
629 629
 		i = encodeVarintGateway(dAtA, i, uint64(m.Range.Size()))
630
-		n2, err := m.Range.MarshalTo(dAtA[i:])
630
+		n3, err := m.Range.MarshalTo(dAtA[i:])
631 631
 		if err != nil {
632 632
 			return 0, err
633 633
 		}
634
-		i += n2
634
+		i += n3
635 635
 	}
636 636
 	return i, nil
637 637
 }
... ...
@@ -740,6 +758,10 @@ func (m *ResolveImageConfigRequest) Size() (n int) {
740 740
 	if l > 0 {
741 741
 		n += 1 + l + sovGateway(uint64(l))
742 742
 	}
743
+	if m.Platform != nil {
744
+		l = m.Platform.Size()
745
+		n += 1 + l + sovGateway(uint64(l))
746
+	}
743 747
 	return n
744 748
 }
745 749
 
... ...
@@ -929,6 +951,39 @@ func (m *ResolveImageConfigRequest) Unmarshal(dAtA []byte) error {
929 929
 			}
930 930
 			m.Ref = string(dAtA[iNdEx:postIndex])
931 931
 			iNdEx = postIndex
932
+		case 2:
933
+			if wireType != 2 {
934
+				return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType)
935
+			}
936
+			var msglen int
937
+			for shift := uint(0); ; shift += 7 {
938
+				if shift >= 64 {
939
+					return ErrIntOverflowGateway
940
+				}
941
+				if iNdEx >= l {
942
+					return io.ErrUnexpectedEOF
943
+				}
944
+				b := dAtA[iNdEx]
945
+				iNdEx++
946
+				msglen |= (int(b) & 0x7F) << shift
947
+				if b < 0x80 {
948
+					break
949
+				}
950
+			}
951
+			if msglen < 0 {
952
+				return ErrInvalidLengthGateway
953
+			}
954
+			postIndex := iNdEx + msglen
955
+			if postIndex > l {
956
+				return io.ErrUnexpectedEOF
957
+			}
958
+			if m.Platform == nil {
959
+				m.Platform = &pb.Platform{}
960
+			}
961
+			if err := m.Platform.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
962
+				return err
963
+			}
964
+			iNdEx = postIndex
932 965
 		default:
933 966
 			iNdEx = preIndex
934 967
 			skippy, err := skipGateway(dAtA[iNdEx:])
... ...
@@ -1998,45 +2053,46 @@ var (
1998 1998
 func init() { proto.RegisterFile("gateway.proto", fileDescriptorGateway) }
1999 1999
 
2000 2000
 var fileDescriptorGateway = []byte{
2001
-	// 629 bytes of a gzipped FileDescriptorProto
2002
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x4f, 0xdb, 0x40,
2003
-	0x10, 0xad, 0x63, 0x40, 0x64, 0x12, 0x3e, 0xb4, 0xaa, 0x2a, 0xe3, 0x03, 0x44, 0x56, 0x45, 0x2d,
2004
-	0x5a, 0x6c, 0x35, 0x6d, 0x25, 0x44, 0xa5, 0x4a, 0x0d, 0x1f, 0x12, 0x15, 0x12, 0x68, 0x7b, 0xa8,
2005
-	0xc4, 0xcd, 0x4e, 0xc6, 0x66, 0x45, 0xb2, 0xeb, 0xda, 0x1b, 0xda, 0xa8, 0x97, 0xf6, 0xe7, 0xf4,
2006
-	0x9f, 0x70, 0xec, 0x99, 0x03, 0xaa, 0xf8, 0x25, 0x95, 0xd7, 0xeb, 0x60, 0x48, 0x49, 0xe9, 0x6d,
2007
-	0xdf, 0x78, 0xe6, 0xed, 0x9b, 0x79, 0xb3, 0x86, 0x85, 0x38, 0x90, 0xf8, 0x25, 0x18, 0x79, 0x49,
2008
-	0x2a, 0xa4, 0x20, 0x2b, 0x03, 0x11, 0x8e, 0xbc, 0x70, 0xc8, 0xfa, 0xbd, 0x33, 0x26, 0xbd, 0xf3,
2009
-	0x97, 0x5e, 0x94, 0x0a, 0x2e, 0x91, 0xf7, 0xec, 0xcd, 0x98, 0xc9, 0xd3, 0x61, 0xe8, 0x75, 0xc5,
2010
-	0xc0, 0x8f, 0x45, 0x2c, 0x7c, 0x55, 0x11, 0x0e, 0x23, 0x85, 0x14, 0x50, 0xa7, 0x82, 0xc9, 0x7e,
2011
-	0x51, 0x49, 0xcf, 0x49, 0xfd, 0x92, 0xd4, 0xcf, 0x44, 0xff, 0x1c, 0x53, 0x3f, 0x09, 0x7d, 0x91,
2012
-	0x64, 0x45, 0xb6, 0xb3, 0x09, 0x2b, 0x14, 0xd5, 0x87, 0x83, 0x41, 0x10, 0xe3, 0x8e, 0xe0, 0x11,
2013
-	0x8b, 0x29, 0x7e, 0x1e, 0x62, 0x26, 0xc9, 0x32, 0x98, 0x14, 0x23, 0xcb, 0x68, 0x19, 0x6e, 0x9d,
2014
-	0xe6, 0x47, 0xe7, 0xbb, 0x01, 0xf6, 0xdf, 0xf2, 0xb3, 0x44, 0xf0, 0x0c, 0xc9, 0x07, 0x98, 0xdb,
2015
-	0x65, 0x31, 0x66, 0xb2, 0xa8, 0xe9, 0xb4, 0x2f, 0xae, 0xd6, 0x1e, 0x5d, 0x5e, 0xad, 0x6d, 0x54,
2016
-	0x34, 0x89, 0x04, 0x79, 0x57, 0x70, 0x19, 0x30, 0x8e, 0x69, 0xe6, 0xc7, 0x62, 0xb3, 0xa7, 0x4a,
2017
-	0xbc, 0xa2, 0x92, 0x6a, 0x06, 0xf2, 0x04, 0xe6, 0x0a, 0x76, 0xab, 0xd6, 0x32, 0xdc, 0x26, 0xd5,
2018
-	0xc8, 0xb9, 0xac, 0x41, 0xf3, 0x63, 0x2e, 0xa0, 0x54, 0xe9, 0x01, 0xec, 0x62, 0xc4, 0x38, 0x93,
2019
-	0x4c, 0x70, 0x75, 0x71, 0xa3, 0xbd, 0xe8, 0x25, 0xa1, 0x77, 0x13, 0xa5, 0x95, 0x0c, 0x62, 0xc3,
2020
-	0xfc, 0xbe, 0x9e, 0xad, 0xa2, 0xae, 0xd3, 0x31, 0x26, 0x27, 0xd0, 0x28, 0xcf, 0x47, 0x89, 0xb4,
2021
-	0xcc, 0x96, 0xe9, 0x36, 0xda, 0x5b, 0xde, 0xbd, 0xe6, 0x78, 0x55, 0x25, 0x5e, 0xa5, 0x74, 0x8f,
2022
-	0xcb, 0x74, 0x44, 0xab, 0x64, 0xc4, 0x85, 0xa5, 0x83, 0x41, 0x22, 0x52, 0xb9, 0x13, 0x74, 0x4f,
2023
-	0x91, 0x62, 0x94, 0x59, 0x33, 0x2d, 0xd3, 0xad, 0xd3, 0xbb, 0x61, 0xf2, 0x18, 0x66, 0xf7, 0x19,
2024
-	0x0f, 0xfa, 0x16, 0xb4, 0x0c, 0x77, 0x9e, 0x16, 0x80, 0x38, 0xd0, 0xdc, 0xfb, 0x9a, 0x27, 0x62,
2025
-	0xfa, 0x5e, 0xca, 0xd4, 0x6a, 0xa8, 0xb1, 0xdc, 0x8a, 0xd9, 0xef, 0x60, 0xf9, 0xae, 0x88, 0xdc,
2026
-	0xc5, 0x33, 0x1c, 0x95, 0x2e, 0x9e, 0xe1, 0x28, 0xe7, 0x3f, 0x0f, 0xfa, 0x43, 0xd4, 0xed, 0x17,
2027
-	0x60, 0xbb, 0xb6, 0x65, 0x38, 0x7b, 0xb0, 0xa0, 0x3b, 0xd2, 0x8e, 0x4e, 0xac, 0xc0, 0x84, 0x8c,
2028
-	0xda, 0xa4, 0x0c, 0xe7, 0x1b, 0x2c, 0x51, 0x0c, 0x7a, 0xfb, 0xac, 0x8f, 0xf7, 0xee, 0x92, 0xf2,
2029
-	0x81, 0xf5, 0xf1, 0x38, 0x90, 0xa7, 0x63, 0x1f, 0x34, 0x26, 0xdb, 0x30, 0x4b, 0x03, 0x1e, 0xa3,
2030
-	0x65, 0x2a, 0x3b, 0x9f, 0x4e, 0x71, 0x40, 0x5d, 0x92, 0xe7, 0xd2, 0xa2, 0xc4, 0x79, 0x0b, 0xf5,
2031
-	0x71, 0x2c, 0xdf, 0xa2, 0xa3, 0x28, 0xca, 0xb0, 0xd8, 0x48, 0x93, 0x6a, 0x94, 0xc7, 0x0f, 0x91,
2032
-	0xc7, 0xfa, 0x6a, 0x93, 0x6a, 0xe4, 0xac, 0xc3, 0xf2, 0x8d, 0x72, 0x3d, 0x03, 0x02, 0x33, 0xbb,
2033
-	0x81, 0x0c, 0x14, 0x43, 0x93, 0xaa, 0xb3, 0xb3, 0x00, 0x8d, 0x63, 0xc6, 0xcb, 0x97, 0xe2, 0x2c,
2034
-	0x42, 0xf3, 0x58, 0xf0, 0xf1, 0x43, 0x68, 0xff, 0x34, 0xa1, 0x7e, 0x78, 0xd8, 0xe9, 0xa4, 0xac,
2035
-	0x17, 0x23, 0xf9, 0x61, 0x00, 0x99, 0x7c, 0x35, 0xe4, 0xf5, 0x94, 0xae, 0xee, 0x7d, 0x94, 0xf6,
2036
-	0x9b, 0xff, 0xac, 0xd2, 0x4d, 0x9c, 0xc0, 0xac, 0x72, 0x96, 0x3c, 0x7b, 0xe0, 0x36, 0xdb, 0xee,
2037
-	0xbf, 0x13, 0x35, 0x77, 0x17, 0xe6, 0xcb, 0xa1, 0x91, 0x8d, 0xa9, 0xf2, 0x6e, 0xed, 0x84, 0xfd,
2038
-	0xfc, 0x41, 0xb9, 0xfa, 0x92, 0x4f, 0x30, 0x93, 0x4f, 0x9c, 0xac, 0x4f, 0x29, 0xaa, 0x58, 0x62,
2039
-	0x4f, 0xeb, 0xb3, 0xea, 0x55, 0xa7, 0x79, 0x71, 0xbd, 0x6a, 0xfc, 0xba, 0x5e, 0x35, 0x7e, 0x5f,
2040
-	0xaf, 0x1a, 0xe1, 0x9c, 0xfa, 0x2f, 0xbe, 0xfa, 0x13, 0x00, 0x00, 0xff, 0xff, 0xd8, 0x21, 0xd1,
2041
-	0x98, 0xa0, 0x05, 0x00, 0x00,
2001
+	// 652 bytes of a gzipped FileDescriptorProto
2002
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0x4d, 0x4f, 0xdb, 0x4a,
2003
+	0x14, 0x7d, 0x8e, 0x01, 0x25, 0x37, 0xe6, 0x43, 0xa3, 0xa7, 0x27, 0xe3, 0x05, 0x44, 0xd6, 0x13,
2004
+	0xcf, 0xe2, 0x15, 0x5b, 0x4d, 0x5b, 0x09, 0x51, 0xa9, 0x52, 0xc3, 0x87, 0x44, 0x85, 0x44, 0x34,
2005
+	0x5d, 0x20, 0xb1, 0x1b, 0x27, 0x63, 0x33, 0xc2, 0x99, 0x71, 0xed, 0x09, 0x6d, 0xd4, 0x4d, 0xfb,
2006
+	0x73, 0xfa, 0x4f, 0x58, 0x76, 0xcd, 0x02, 0x55, 0xfc, 0x92, 0xca, 0xe3, 0x71, 0x30, 0x50, 0x52,
2007
+	0xba, 0x9b, 0x73, 0x7d, 0xef, 0x99, 0x73, 0xe7, 0xdc, 0x6b, 0x58, 0x8c, 0x89, 0xa4, 0x1f, 0xc9,
2008
+	0xc4, 0x4f, 0x33, 0x21, 0x05, 0x5a, 0x1d, 0x89, 0x70, 0xe2, 0x87, 0x63, 0x96, 0x0c, 0xcf, 0x99,
2009
+	0xf4, 0x2f, 0x9e, 0xfb, 0x51, 0x26, 0xb8, 0xa4, 0x7c, 0xe8, 0x6c, 0xc5, 0x4c, 0x9e, 0x8d, 0x43,
2010
+	0x7f, 0x20, 0x46, 0x41, 0x2c, 0x62, 0x11, 0xa8, 0x8a, 0x70, 0x1c, 0x29, 0xa4, 0x80, 0x3a, 0x95,
2011
+	0x4c, 0xce, 0xb3, 0x5a, 0x7a, 0x41, 0x1a, 0x54, 0xa4, 0x41, 0x2e, 0x92, 0x0b, 0x9a, 0x05, 0x69,
2012
+	0x18, 0x88, 0x34, 0x2f, 0xb3, 0xdd, 0x13, 0x58, 0xc5, 0x54, 0x7d, 0x38, 0x1c, 0x91, 0x98, 0xee,
2013
+	0x0a, 0x1e, 0xb1, 0x18, 0xd3, 0x0f, 0x63, 0x9a, 0x4b, 0xb4, 0x02, 0x26, 0xa6, 0x91, 0x6d, 0x74,
2014
+	0x0c, 0xaf, 0x85, 0x8b, 0x23, 0xf2, 0xa0, 0xd9, 0x4f, 0x88, 0x8c, 0x44, 0x36, 0xb2, 0x1b, 0x1d,
2015
+	0xc3, 0x6b, 0x77, 0x2d, 0x3f, 0x0d, 0xfd, 0x2a, 0x86, 0xa7, 0x5f, 0xdd, 0x2f, 0x06, 0x38, 0xbf,
2016
+	0x62, 0xce, 0x53, 0xc1, 0x73, 0x8a, 0xde, 0xc1, 0xc2, 0x1e, 0x8b, 0x69, 0x2e, 0x4b, 0xf6, 0x5e,
2017
+	0xf7, 0xf2, 0x7a, 0xfd, 0xaf, 0xab, 0xeb, 0xf5, 0xcd, 0x9a, 0x7a, 0x91, 0x52, 0x3e, 0x10, 0x5c,
2018
+	0x12, 0xc6, 0x69, 0x96, 0x07, 0xb1, 0xd8, 0x1a, 0xaa, 0x12, 0xbf, 0xac, 0xc4, 0x9a, 0x01, 0xfd,
2019
+	0x03, 0x0b, 0x25, 0xbb, 0x92, 0x64, 0x61, 0x8d, 0xdc, 0xab, 0x06, 0x58, 0xef, 0x0b, 0x01, 0x55,
2020
+	0x3f, 0x3e, 0xc0, 0x1e, 0x8d, 0x18, 0x67, 0x92, 0x09, 0xae, 0x2e, 0x6e, 0x77, 0x97, 0x0a, 0xfd,
2021
+	0xb7, 0x51, 0x5c, 0xcb, 0x40, 0x0e, 0x34, 0x0f, 0xb4, 0x0b, 0x8a, 0xba, 0x85, 0xa7, 0x18, 0x9d,
2022
+	0x42, 0xbb, 0x3a, 0x1f, 0xa7, 0xd2, 0x36, 0x3b, 0xa6, 0xd7, 0xee, 0x6e, 0xfb, 0x8f, 0xda, 0xe8,
2023
+	0xd7, 0x95, 0xf8, 0xb5, 0xd2, 0x7d, 0x2e, 0xb3, 0x09, 0xae, 0x93, 0x21, 0x0f, 0x96, 0x0f, 0x47,
2024
+	0xa9, 0xc8, 0xe4, 0x2e, 0x19, 0x9c, 0x51, 0x4c, 0xa3, 0xdc, 0x9e, 0xeb, 0x98, 0x5e, 0x0b, 0xdf,
2025
+	0x0f, 0xa3, 0xbf, 0x61, 0xfe, 0x80, 0x71, 0x92, 0xd8, 0xd0, 0x31, 0xbc, 0x26, 0x2e, 0x01, 0x72,
2026
+	0xc1, 0xda, 0xff, 0x54, 0x24, 0xd2, 0xec, 0xad, 0x94, 0x99, 0xdd, 0x56, 0xcf, 0x72, 0x27, 0xe6,
2027
+	0xbc, 0x81, 0x95, 0xfb, 0x22, 0x0a, 0xbf, 0xcf, 0xe9, 0xa4, 0xf2, 0xfb, 0x9c, 0x4e, 0x0a, 0xfe,
2028
+	0x0b, 0x92, 0x8c, 0xa9, 0x6e, 0xbf, 0x04, 0x3b, 0x8d, 0x6d, 0xc3, 0xdd, 0x87, 0x45, 0xdd, 0x91,
2029
+	0x76, 0xf4, 0xe1, 0xb0, 0xdc, 0x97, 0xd1, 0x78, 0x28, 0xc3, 0xfd, 0x0c, 0xcb, 0x98, 0x92, 0xe1,
2030
+	0x01, 0x4b, 0xe8, 0xe3, 0x53, 0x57, 0xf8, 0xc0, 0x12, 0xda, 0x27, 0xf2, 0x6c, 0xea, 0x83, 0xc6,
2031
+	0x68, 0x07, 0xe6, 0x31, 0xe1, 0x31, 0xb5, 0x4d, 0x65, 0xe7, 0xbf, 0x33, 0x1c, 0x50, 0x97, 0x14,
2032
+	0xb9, 0xb8, 0x2c, 0x71, 0x5f, 0x43, 0x6b, 0x1a, 0x2b, 0xa6, 0xe8, 0x38, 0x8a, 0x72, 0x5a, 0x4e,
2033
+	0xa4, 0x89, 0x35, 0x2a, 0xe2, 0x47, 0x94, 0xc7, 0xfa, 0x6a, 0x13, 0x6b, 0xe4, 0x6e, 0xc0, 0xca,
2034
+	0xad, 0x72, 0xfd, 0x06, 0x08, 0xe6, 0xf6, 0x88, 0x24, 0x8a, 0xc1, 0xc2, 0xea, 0xec, 0x2e, 0x42,
2035
+	0xbb, 0xcf, 0x78, 0xb5, 0x53, 0xee, 0x12, 0x58, 0x7d, 0xc1, 0xa7, 0x8b, 0xd0, 0xfd, 0x66, 0x42,
2036
+	0xeb, 0xe8, 0xa8, 0xd7, 0xcb, 0xd8, 0x30, 0xa6, 0xe8, 0xab, 0x01, 0xe8, 0xe1, 0xd6, 0xa0, 0x97,
2037
+	0x33, 0xba, 0x7a, 0x74, 0x7d, 0x9d, 0x57, 0x7f, 0x58, 0xa5, 0x9b, 0x38, 0x85, 0x79, 0xe5, 0x2c,
2038
+	0xfa, 0xef, 0x89, 0xd3, 0xec, 0x78, 0xbf, 0x4f, 0xd4, 0xdc, 0x03, 0x68, 0x56, 0x8f, 0x86, 0x36,
2039
+	0x67, 0xca, 0xbb, 0x33, 0x13, 0xce, 0xff, 0x4f, 0xca, 0xd5, 0x97, 0x9c, 0xc0, 0x5c, 0xf1, 0xe2,
2040
+	0x68, 0x63, 0x46, 0x51, 0xcd, 0x12, 0x67, 0x56, 0x9f, 0x75, 0xaf, 0x7a, 0xd6, 0xe5, 0xcd, 0x9a,
2041
+	0xf1, 0xfd, 0x66, 0xcd, 0xf8, 0x71, 0xb3, 0x66, 0x84, 0x0b, 0xea, 0x0f, 0xfa, 0xe2, 0x67, 0x00,
2042
+	0x00, 0x00, 0xff, 0xff, 0xbc, 0x68, 0x1b, 0xf0, 0xca, 0x05, 0x00, 0x00,
2042 2043
 }
... ...
@@ -18,6 +18,7 @@ service LLBBridge {
18 18
 
19 19
 message ResolveImageConfigRequest {
20 20
 	string Ref = 1;
21
+	pb.Platform Platform = 2;
21 22
 }
22 23
 
23 24
 message ResolveImageConfigResponse {
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/moby/buildkit/util/tracing"
16 16
 	"github.com/moby/buildkit/worker"
17 17
 	digest "github.com/opencontainers/go-digest"
18
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
18 19
 	"github.com/pkg/errors"
19 20
 )
20 21
 
... ...
@@ -25,6 +26,7 @@ type llbBridge struct {
25 25
 	ci            *remotecache.CacheImporter
26 26
 	cms           map[string]solver.CacheManager
27 27
 	cmsMu         sync.Mutex
28
+	platforms     []specs.Platform
28 29
 }
29 30
 
30 31
 func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res solver.CachedResult, exp map[string][]byte, err error) {
... ...
@@ -59,7 +61,7 @@ func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res s
59 59
 	}
60 60
 
61 61
 	if req.Definition != nil && req.Definition.Def != nil {
62
-		edge, err := Load(req.Definition, WithCacheSources(cms))
62
+		edge, err := Load(req.Definition, WithCacheSources(cms), RuntimePlatforms(b.platforms))
63 63
 		if err != nil {
64 64
 			return nil, nil, err
65 65
 		}
... ...
@@ -108,12 +110,12 @@ func (s *llbBridge) Exec(ctx context.Context, meta executor.Meta, root cache.Imm
108 108
 	return err
109 109
 }
110 110
 
111
-func (s *llbBridge) ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error) {
111
+func (s *llbBridge) ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error) {
112 112
 	w, err := s.resolveWorker()
113 113
 	if err != nil {
114 114
 		return "", nil, err
115 115
 	}
116
-	return w.ResolveImageConfig(ctx, ref)
116
+	return w.ResolveImageConfig(ctx, ref, platform)
117 117
 }
118 118
 
119 119
 type lazyCacheManager struct {
... ...
@@ -11,9 +11,11 @@ import (
11 11
 	"sort"
12 12
 	"strings"
13 13
 	"sync"
14
+	"time"
14 15
 
15 16
 	"github.com/boltdb/bolt"
16 17
 	"github.com/containerd/containerd/mount"
18
+	"github.com/docker/docker/pkg/locker"
17 19
 	"github.com/moby/buildkit/cache"
18 20
 	"github.com/moby/buildkit/cache/metadata"
19 21
 	"github.com/moby/buildkit/executor"
... ...
@@ -37,16 +39,19 @@ type execOp struct {
37 37
 	exec      executor.Executor
38 38
 	w         worker.Worker
39 39
 	numInputs int
40
+
41
+	cacheMounts map[string]*cacheRefShare
40 42
 }
41 43
 
42 44
 func NewExecOp(v solver.Vertex, op *pb.Op_Exec, cm cache.Manager, md *metadata.Store, exec executor.Executor, w worker.Worker) (solver.Op, error) {
43 45
 	return &execOp{
44
-		op:        op.Exec,
45
-		cm:        cm,
46
-		md:        md,
47
-		exec:      exec,
48
-		numInputs: len(v.Inputs()),
49
-		w:         w,
46
+		op:          op.Exec,
47
+		cm:          cm,
48
+		md:          md,
49
+		exec:        exec,
50
+		numInputs:   len(v.Inputs()),
51
+		w:           w,
52
+		cacheMounts: map[string]*cacheRefShare{},
50 53
 	}, nil
51 54
 }
52 55
 
... ...
@@ -165,32 +170,72 @@ func (e *execOp) getMountDeps() ([]dep, error) {
165 165
 	return deps, nil
166 166
 }
167 167
 
168
-func (e *execOp) getRefCacheDir(ctx context.Context, ref cache.ImmutableRef, id string, m *pb.Mount) (cache.MutableRef, error) {
168
+func (e *execOp) getRefCacheDir(ctx context.Context, ref cache.ImmutableRef, id string, m *pb.Mount, sharing pb.CacheSharingOpt) (mref cache.MutableRef, err error) {
169 169
 
170 170
 	key := "cache-dir:" + id
171 171
 	if ref != nil {
172 172
 		key += ":" + ref.ID()
173 173
 	}
174 174
 
175
-	return sharedCacheRefs.get(key, func() (cache.MutableRef, error) {
176
-		return e.getRefCacheDirNoCache(ctx, key, ref, id, m)
177
-	})
175
+	if ref, ok := e.cacheMounts[key]; ok {
176
+		return ref.clone(), nil
177
+	}
178
+	defer func() {
179
+		if err == nil {
180
+			share := &cacheRefShare{MutableRef: mref, refs: map[*cacheRef]struct{}{}}
181
+			e.cacheMounts[key] = share
182
+			mref = share.clone()
183
+		}
184
+	}()
185
+
186
+	switch sharing {
187
+	case pb.CacheSharingOpt_SHARED:
188
+		return sharedCacheRefs.get(key, func() (cache.MutableRef, error) {
189
+			return e.getRefCacheDirNoCache(ctx, key, ref, id, m, false)
190
+		})
191
+	case pb.CacheSharingOpt_PRIVATE:
192
+		return e.getRefCacheDirNoCache(ctx, key, ref, id, m, false)
193
+	case pb.CacheSharingOpt_LOCKED:
194
+		return e.getRefCacheDirNoCache(ctx, key, ref, id, m, true)
195
+	default:
196
+		return nil, errors.Errorf("invalid cache sharing option: %s", sharing.String())
197
+	}
198
+
178 199
 }
179 200
 
180
-func (e *execOp) getRefCacheDirNoCache(ctx context.Context, key string, ref cache.ImmutableRef, id string, m *pb.Mount) (cache.MutableRef, error) {
201
+func (e *execOp) getRefCacheDirNoCache(ctx context.Context, key string, ref cache.ImmutableRef, id string, m *pb.Mount, block bool) (cache.MutableRef, error) {
181 202
 	makeMutable := func(cache.ImmutableRef) (cache.MutableRef, error) {
182 203
 		desc := fmt.Sprintf("cached mount %s from exec %s", m.Dest, strings.Join(e.op.Meta.Args, " "))
183 204
 		return e.cm.New(ctx, ref, cache.WithDescription(desc), cache.CachePolicyRetain)
184 205
 	}
185 206
 
186
-	sis, err := e.md.Search(key)
187
-	if err != nil {
188
-		return nil, err
189
-	}
190
-	for _, si := range sis {
191
-		if mRef, err := e.cm.GetMutable(ctx, si.ID()); err == nil {
192
-			logrus.Debugf("reusing ref for cache dir: %s", mRef.ID())
193
-			return mRef, nil
207
+	cacheRefsLocker.Lock(key)
208
+	defer cacheRefsLocker.Unlock(key)
209
+	for {
210
+		sis, err := e.md.Search(key)
211
+		if err != nil {
212
+			return nil, err
213
+		}
214
+		locked := false
215
+		for _, si := range sis {
216
+			if mRef, err := e.cm.GetMutable(ctx, si.ID()); err == nil {
217
+				logrus.Debugf("reusing ref for cache dir: %s", mRef.ID())
218
+				return mRef, nil
219
+			} else if errors.Cause(err) == cache.ErrLocked {
220
+				locked = true
221
+			}
222
+		}
223
+		if block && locked {
224
+			cacheRefsLocker.Unlock(key)
225
+			select {
226
+			case <-ctx.Done():
227
+				cacheRefsLocker.Lock(key)
228
+				return nil, ctx.Err()
229
+			case <-time.After(100 * time.Millisecond):
230
+				cacheRefsLocker.Lock(key)
231
+			}
232
+		} else {
233
+			break
194 234
 		}
195 235
 	}
196 236
 	mRef, err := makeMutable(ref)
... ...
@@ -287,7 +332,7 @@ func (e *execOp) Exec(ctx context.Context, inputs []solver.Result) ([]solver.Res
287 287
 			if m.CacheOpt == nil {
288 288
 				return nil, errors.Errorf("missing cache mount options")
289 289
 			}
290
-			mRef, err := e.getRefCacheDir(ctx, ref, m.CacheOpt.ID, m)
290
+			mRef, err := e.getRefCacheDir(ctx, ref, m.CacheOpt.ID, m, m.CacheOpt.Sharing)
291 291
 			if err != nil {
292 292
 				return nil, err
293 293
 			}
... ...
@@ -418,6 +463,7 @@ func (m *tmpfsMount) Release() error {
418 418
 	return nil
419 419
 }
420 420
 
421
+var cacheRefsLocker = locker.New()
421 422
 var sharedCacheRefs = &cacheRefs{}
422 423
 
423 424
 type cacheRefs struct {
... ...
@@ -466,9 +512,11 @@ func (r *cacheRefShare) clone() cache.MutableRef {
466 466
 }
467 467
 
468 468
 func (r *cacheRefShare) release(ctx context.Context) error {
469
-	r.main.mu.Lock()
470
-	defer r.main.mu.Unlock()
471
-	delete(r.main.shares, r.key)
469
+	if r.main != nil {
470
+		r.main.mu.Lock()
471
+		defer r.main.mu.Unlock()
472
+		delete(r.main.shares, r.key)
473
+	}
472 474
 	return r.MutableRef.Release(ctx)
473 475
 }
474 476
 
... ...
@@ -14,18 +14,20 @@ import (
14 14
 const sourceCacheType = "buildkit.source.v0"
15 15
 
16 16
 type sourceOp struct {
17
-	mu  sync.Mutex
18
-	op  *pb.Op_Source
19
-	sm  *source.Manager
20
-	src source.SourceInstance
21
-	w   worker.Worker
17
+	mu       sync.Mutex
18
+	op       *pb.Op_Source
19
+	platform *pb.Platform
20
+	sm       *source.Manager
21
+	src      source.SourceInstance
22
+	w        worker.Worker
22 23
 }
23 24
 
24
-func NewSourceOp(_ solver.Vertex, op *pb.Op_Source, sm *source.Manager, w worker.Worker) (solver.Op, error) {
25
+func NewSourceOp(_ solver.Vertex, op *pb.Op_Source, platform *pb.Platform, sm *source.Manager, w worker.Worker) (solver.Op, error) {
25 26
 	return &sourceOp{
26
-		op: op,
27
-		sm: sm,
28
-		w:  w,
27
+		op:       op,
28
+		sm:       sm,
29
+		w:        w,
30
+		platform: platform,
29 31
 	}, nil
30 32
 }
31 33
 
... ...
@@ -35,7 +37,7 @@ func (s *sourceOp) instance(ctx context.Context) (source.SourceInstance, error)
35 35
 	if s.src != nil {
36 36
 		return s.src, nil
37 37
 	}
38
-	id, err := source.FromLLB(s.op)
38
+	id, err := source.FromLLB(s.op, s.platform)
39 39
 	if err != nil {
40 40
 		return nil, err
41 41
 	}
... ...
@@ -13,6 +13,7 @@ import (
13 13
 	"github.com/moby/buildkit/solver"
14 14
 	"github.com/moby/buildkit/util/progress"
15 15
 	"github.com/moby/buildkit/worker"
16
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
16 17
 	"github.com/pkg/errors"
17 18
 )
18 19
 
... ...
@@ -30,9 +31,10 @@ type Solver struct {
30 30
 	resolveWorker ResolveWorkerFunc
31 31
 	frontends     map[string]frontend.Frontend
32 32
 	ci            *remotecache.CacheImporter
33
+	platforms     []specs.Platform
33 34
 }
34 35
 
35
-func New(wc *worker.Controller, f map[string]frontend.Frontend, cacheStore solver.CacheKeyStorage, ci *remotecache.CacheImporter) *Solver {
36
+func New(wc *worker.Controller, f map[string]frontend.Frontend, cacheStore solver.CacheKeyStorage, ci *remotecache.CacheImporter) (*Solver, error) {
36 37
 	s := &Solver{
37 38
 		resolveWorker: defaultResolver(wc),
38 39
 		frontends:     f,
... ...
@@ -43,11 +45,18 @@ func New(wc *worker.Controller, f map[string]frontend.Frontend, cacheStore solve
43 43
 
44 44
 	cache := solver.NewCacheManager("local", cacheStore, results)
45 45
 
46
+	// executing is currently only allowed on default worker
47
+	w, err := wc.GetDefault()
48
+	if err != nil {
49
+		return nil, err
50
+	}
51
+	s.platforms = w.Platforms()
52
+
46 53
 	s.solver = solver.NewSolver(solver.SolverOpt{
47 54
 		ResolveOpFunc: s.resolver(),
48 55
 		DefaultCache:  cache,
49 56
 	})
50
-	return s
57
+	return s, nil
51 58
 }
52 59
 
53 60
 func (s *Solver) resolver() solver.ResolveOpFunc {
... ...
@@ -67,6 +76,7 @@ func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge {
67 67
 		resolveWorker: s.resolveWorker,
68 68
 		ci:            s.ci,
69 69
 		cms:           map[string]solver.CacheManager{},
70
+		platforms:     s.platforms,
70 71
 	}
71 72
 }
72 73
 
... ...
@@ -3,10 +3,12 @@ package llbsolver
3 3
 import (
4 4
 	"strings"
5 5
 
6
+	"github.com/containerd/containerd/platforms"
6 7
 	"github.com/moby/buildkit/solver"
7 8
 	"github.com/moby/buildkit/solver/pb"
8 9
 	"github.com/moby/buildkit/source"
9 10
 	digest "github.com/opencontainers/go-digest"
11
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
10 12
 	"github.com/pkg/errors"
11 13
 )
12 14
 
... ...
@@ -38,11 +40,44 @@ func (v *vertex) Name() string {
38 38
 	return v.name
39 39
 }
40 40
 
41
-type LoadOpt func(*solver.VertexOptions)
41
+type LoadOpt func(*pb.Op, *pb.OpMetadata, *solver.VertexOptions) error
42 42
 
43 43
 func WithCacheSources(cms []solver.CacheManager) LoadOpt {
44
-	return func(opt *solver.VertexOptions) {
44
+	return func(_ *pb.Op, _ *pb.OpMetadata, opt *solver.VertexOptions) error {
45 45
 		opt.CacheSources = cms
46
+		return nil
47
+	}
48
+}
49
+
50
+func RuntimePlatforms(p []specs.Platform) LoadOpt {
51
+	var defaultPlatform *pb.Platform
52
+	for i := range p {
53
+		p[i] = platforms.Normalize(p[i])
54
+	}
55
+	return func(op *pb.Op, _ *pb.OpMetadata, opt *solver.VertexOptions) error {
56
+		if op.Platform == nil {
57
+			if defaultPlatform == nil {
58
+				p := platforms.DefaultSpec()
59
+				defaultPlatform = &pb.Platform{
60
+					OS:           p.OS,
61
+					Architecture: p.Architecture,
62
+				}
63
+			}
64
+			op.Platform = defaultPlatform
65
+		}
66
+		if _, ok := op.Op.(*pb.Op_Exec); ok {
67
+			var found bool
68
+			for _, pp := range p {
69
+				if pp.OS == op.Platform.OS && pp.Architecture == op.Platform.Architecture && pp.Variant == op.Platform.Variant {
70
+					found = true
71
+					break
72
+				}
73
+			}
74
+			if !found {
75
+				return errors.Errorf("runtime execution on platform %s not supported", platforms.Format(specs.Platform{OS: op.Platform.OS, Architecture: op.Platform.Architecture, Variant: op.Platform.Variant}))
76
+			}
77
+		}
78
+		return nil
46 79
 	}
47 80
 }
48 81
 
... ...
@@ -67,9 +102,11 @@ func newVertex(dgst digest.Digest, op *pb.Op, opMeta *pb.OpMetadata, load func(d
67 67
 		}
68 68
 	}
69 69
 	for _, fn := range opts {
70
-		fn(&opt)
70
+		if err := fn(op, opMeta, &opt); err != nil {
71
+			return nil, err
72
+		}
71 73
 	}
72
-	vtx := &vertex{sys: op.Op, options: opt, digest: dgst, name: llbOpName(op)}
74
+	vtx := &vertex{sys: op, options: opt, digest: dgst, name: llbOpName(op)}
73 75
 	for _, in := range op.Inputs {
74 76
 		sub, err := load(in.Digest)
75 77
 		if err != nil {
... ...
@@ -129,7 +166,7 @@ func loadLLB(def *pb.Definition, fn func(digest.Digest, *pb.Op, func(digest.Dige
129 129
 func llbOpName(op *pb.Op) string {
130 130
 	switch op := op.Op.(type) {
131 131
 	case *pb.Op_Source:
132
-		if id, err := source.FromLLB(op); err == nil {
132
+		if id, err := source.FromLLB(op, nil); err == nil {
133 133
 			if id, ok := id.(*source.LocalIdentifier); ok {
134 134
 				if len(id.IncludePatterns) == 1 {
135 135
 					return op.Source.Identifier + " (" + id.IncludePatterns[0] + ")"
... ...
@@ -12,6 +12,7 @@
12 12
 
13 13
 	It has these top-level messages:
14 14
 		Op
15
+		Platform
15 16
 		Input
16 17
 		ExecOp
17 18
 		Meta
... ...
@@ -25,7 +26,7 @@
25 25
 		OpMetadata
26 26
 		ExportCache
27 27
 		ProxyEnv
28
-		WorkerConstraint
28
+		WorkerConstraints
29 29
 		Definition
30 30
 */
31 31
 package pb
... ...
@@ -50,6 +51,7 @@ var _ = math.Inf
50 50
 // proto package needs to be updated.
51 51
 const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
52 52
 
53
+// MountType defines a type of a mount from a supported set
53 54
 type MountType int32
54 55
 
55 56
 const (
... ...
@@ -80,6 +82,34 @@ func (x MountType) String() string {
80 80
 }
81 81
 func (MountType) EnumDescriptor() ([]byte, []int) { return fileDescriptorOps, []int{0} }
82 82
 
83
+// CacheSharingOpt defines different sharing modes for cache mount
84
+type CacheSharingOpt int32
85
+
86
+const (
87
+	// SHARED cache mount can be used concurrently by multiple writers
88
+	CacheSharingOpt_SHARED CacheSharingOpt = 0
89
+	// PRIVATE creates a new mount if there are multiple writers
90
+	CacheSharingOpt_PRIVATE CacheSharingOpt = 1
91
+	// LOCKED pauses second writer until first one releases the mount
92
+	CacheSharingOpt_LOCKED CacheSharingOpt = 2
93
+)
94
+
95
+var CacheSharingOpt_name = map[int32]string{
96
+	0: "SHARED",
97
+	1: "PRIVATE",
98
+	2: "LOCKED",
99
+}
100
+var CacheSharingOpt_value = map[string]int32{
101
+	"SHARED":  0,
102
+	"PRIVATE": 1,
103
+	"LOCKED":  2,
104
+}
105
+
106
+func (x CacheSharingOpt) String() string {
107
+	return proto.EnumName(CacheSharingOpt_name, int32(x))
108
+}
109
+func (CacheSharingOpt) EnumDescriptor() ([]byte, []int) { return fileDescriptorOps, []int{1} }
110
+
83 111
 // Op represents a vertex of the LLB DAG.
84 112
 type Op struct {
85 113
 	// inputs is a set of input edges.
... ...
@@ -89,7 +119,9 @@ type Op struct {
89 89
 	//	*Op_Source
90 90
 	//	*Op_Copy
91 91
 	//	*Op_Build
92
-	Op isOp_Op `protobuf_oneof:"op"`
92
+	Op          isOp_Op            `protobuf_oneof:"op"`
93
+	Platform    *Platform          `protobuf:"bytes,10,opt,name=platform" json:"platform,omitempty"`
94
+	Constraints *WorkerConstraints `protobuf:"bytes,11,opt,name=constraints" json:"constraints,omitempty"`
93 95
 }
94 96
 
95 97
 func (m *Op) Reset()                    { *m = Op{} }
... ...
@@ -163,6 +195,20 @@ func (m *Op) GetBuild() *BuildOp {
163 163
 	return nil
164 164
 }
165 165
 
166
+func (m *Op) GetPlatform() *Platform {
167
+	if m != nil {
168
+		return m.Platform
169
+	}
170
+	return nil
171
+}
172
+
173
+func (m *Op) GetConstraints() *WorkerConstraints {
174
+	if m != nil {
175
+		return m.Constraints
176
+	}
177
+	return nil
178
+}
179
+
166 180
 // XXX_OneofFuncs is for the internal use of the proto package.
167 181
 func (*Op) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
168 182
 	return _Op_OneofMarshaler, _Op_OneofUnmarshaler, _Op_OneofSizer, []interface{}{
... ...
@@ -275,6 +321,55 @@ func _Op_OneofSizer(msg proto.Message) (n int) {
275 275
 	return n
276 276
 }
277 277
 
278
+// Platform is github.com/opencontainers/image-spec/specs-go/v1.Platform
279
+type Platform struct {
280
+	Architecture string   `protobuf:"bytes,1,opt,name=Architecture,proto3" json:"Architecture,omitempty"`
281
+	OS           string   `protobuf:"bytes,2,opt,name=OS,proto3" json:"OS,omitempty"`
282
+	Variant      string   `protobuf:"bytes,3,opt,name=Variant,proto3" json:"Variant,omitempty"`
283
+	OSVersion    string   `protobuf:"bytes,4,opt,name=OSVersion,proto3" json:"OSVersion,omitempty"`
284
+	OSFeatures   []string `protobuf:"bytes,5,rep,name=OSFeatures" json:"OSFeatures,omitempty"`
285
+}
286
+
287
+func (m *Platform) Reset()                    { *m = Platform{} }
288
+func (m *Platform) String() string            { return proto.CompactTextString(m) }
289
+func (*Platform) ProtoMessage()               {}
290
+func (*Platform) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{1} }
291
+
292
+func (m *Platform) GetArchitecture() string {
293
+	if m != nil {
294
+		return m.Architecture
295
+	}
296
+	return ""
297
+}
298
+
299
+func (m *Platform) GetOS() string {
300
+	if m != nil {
301
+		return m.OS
302
+	}
303
+	return ""
304
+}
305
+
306
+func (m *Platform) GetVariant() string {
307
+	if m != nil {
308
+		return m.Variant
309
+	}
310
+	return ""
311
+}
312
+
313
+func (m *Platform) GetOSVersion() string {
314
+	if m != nil {
315
+		return m.OSVersion
316
+	}
317
+	return ""
318
+}
319
+
320
+func (m *Platform) GetOSFeatures() []string {
321
+	if m != nil {
322
+		return m.OSFeatures
323
+	}
324
+	return nil
325
+}
326
+
278 327
 // Input represents an input edge for an Op.
279 328
 type Input struct {
280 329
 	// digest of the marshaled input Op
... ...
@@ -286,7 +381,7 @@ type Input struct {
286 286
 func (m *Input) Reset()                    { *m = Input{} }
287 287
 func (m *Input) String() string            { return proto.CompactTextString(m) }
288 288
 func (*Input) ProtoMessage()               {}
289
-func (*Input) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{1} }
289
+func (*Input) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{2} }
290 290
 
291 291
 // ExecOp executes a command in a container.
292 292
 type ExecOp struct {
... ...
@@ -297,7 +392,7 @@ type ExecOp struct {
297 297
 func (m *ExecOp) Reset()                    { *m = ExecOp{} }
298 298
 func (m *ExecOp) String() string            { return proto.CompactTextString(m) }
299 299
 func (*ExecOp) ProtoMessage()               {}
300
-func (*ExecOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{2} }
300
+func (*ExecOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{3} }
301 301
 
302 302
 func (m *ExecOp) GetMeta() *Meta {
303 303
 	if m != nil {
... ...
@@ -327,7 +422,7 @@ type Meta struct {
327 327
 func (m *Meta) Reset()                    { *m = Meta{} }
328 328
 func (m *Meta) String() string            { return proto.CompactTextString(m) }
329 329
 func (*Meta) ProtoMessage()               {}
330
-func (*Meta) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{3} }
330
+func (*Meta) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{4} }
331 331
 
332 332
 func (m *Meta) GetArgs() []string {
333 333
 	if m != nil {
... ...
@@ -378,7 +473,7 @@ type Mount struct {
378 378
 func (m *Mount) Reset()                    { *m = Mount{} }
379 379
 func (m *Mount) String() string            { return proto.CompactTextString(m) }
380 380
 func (*Mount) ProtoMessage()               {}
381
-func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{4} }
381
+func (*Mount) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{5} }
382 382
 
383 383
 func (m *Mount) GetSelector() string {
384 384
 	if m != nil {
... ...
@@ -415,14 +510,18 @@ func (m *Mount) GetCacheOpt() *CacheOpt {
415 415
 	return nil
416 416
 }
417 417
 
418
+// CacheOpt defines options specific to cache mounts
418 419
 type CacheOpt struct {
420
+	// ID is an optional namespace for the mount
419 421
 	ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
422
+	// Sharing is the sharing mode for the mount
423
+	Sharing CacheSharingOpt `protobuf:"varint,2,opt,name=sharing,proto3,enum=pb.CacheSharingOpt" json:"sharing,omitempty"`
420 424
 }
421 425
 
422 426
 func (m *CacheOpt) Reset()                    { *m = CacheOpt{} }
423 427
 func (m *CacheOpt) String() string            { return proto.CompactTextString(m) }
424 428
 func (*CacheOpt) ProtoMessage()               {}
425
-func (*CacheOpt) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{5} }
429
+func (*CacheOpt) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{6} }
426 430
 
427 431
 func (m *CacheOpt) GetID() string {
428 432
 	if m != nil {
... ...
@@ -431,6 +530,13 @@ func (m *CacheOpt) GetID() string {
431 431
 	return ""
432 432
 }
433 433
 
434
+func (m *CacheOpt) GetSharing() CacheSharingOpt {
435
+	if m != nil {
436
+		return m.Sharing
437
+	}
438
+	return CacheSharingOpt_SHARED
439
+}
440
+
434 441
 // CopyOp copies files across Ops.
435 442
 type CopyOp struct {
436 443
 	Src  []*CopySource `protobuf:"bytes,1,rep,name=src" json:"src,omitempty"`
... ...
@@ -440,7 +546,7 @@ type CopyOp struct {
440 440
 func (m *CopyOp) Reset()                    { *m = CopyOp{} }
441 441
 func (m *CopyOp) String() string            { return proto.CompactTextString(m) }
442 442
 func (*CopyOp) ProtoMessage()               {}
443
-func (*CopyOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{6} }
443
+func (*CopyOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{7} }
444 444
 
445 445
 func (m *CopyOp) GetSrc() []*CopySource {
446 446
 	if m != nil {
... ...
@@ -465,7 +571,7 @@ type CopySource struct {
465 465
 func (m *CopySource) Reset()                    { *m = CopySource{} }
466 466
 func (m *CopySource) String() string            { return proto.CompactTextString(m) }
467 467
 func (*CopySource) ProtoMessage()               {}
468
-func (*CopySource) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{7} }
468
+func (*CopySource) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{8} }
469 469
 
470 470
 func (m *CopySource) GetSelector() string {
471 471
 	if m != nil {
... ...
@@ -486,7 +592,7 @@ type SourceOp struct {
486 486
 func (m *SourceOp) Reset()                    { *m = SourceOp{} }
487 487
 func (m *SourceOp) String() string            { return proto.CompactTextString(m) }
488 488
 func (*SourceOp) ProtoMessage()               {}
489
-func (*SourceOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{8} }
489
+func (*SourceOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{9} }
490 490
 
491 491
 func (m *SourceOp) GetIdentifier() string {
492 492
 	if m != nil {
... ...
@@ -513,7 +619,7 @@ type BuildOp struct {
513 513
 func (m *BuildOp) Reset()                    { *m = BuildOp{} }
514 514
 func (m *BuildOp) String() string            { return proto.CompactTextString(m) }
515 515
 func (*BuildOp) ProtoMessage()               {}
516
-func (*BuildOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{9} }
516
+func (*BuildOp) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{10} }
517 517
 
518 518
 func (m *BuildOp) GetInputs() map[string]*BuildInput {
519 519
 	if m != nil {
... ...
@@ -544,22 +650,23 @@ type BuildInput struct {
544 544
 func (m *BuildInput) Reset()                    { *m = BuildInput{} }
545 545
 func (m *BuildInput) String() string            { return proto.CompactTextString(m) }
546 546
 func (*BuildInput) ProtoMessage()               {}
547
-func (*BuildInput) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{10} }
547
+func (*BuildInput) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{11} }
548 548
 
549 549
 // OpMetadata is a per-vertex metadata entry, which can be defined for arbitrary Op vertex and overridable on the run time.
550 550
 type OpMetadata struct {
551 551
 	// ignore_cache specifies to ignore the cache for this Op.
552 552
 	IgnoreCache bool `protobuf:"varint,1,opt,name=ignore_cache,json=ignoreCache,proto3" json:"ignore_cache,omitempty"`
553 553
 	// Description can be used for keeping any text fields that builder doesn't parse
554
-	Description      map[string]string `protobuf:"bytes,2,rep,name=description" json:"description,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
555
-	WorkerConstraint *WorkerConstraint `protobuf:"bytes,3,opt,name=worker_constraint,json=workerConstraint" json:"worker_constraint,omitempty"`
556
-	ExportCache      *ExportCache      `protobuf:"bytes,4,opt,name=export_cache,json=exportCache" json:"export_cache,omitempty"`
554
+	Description map[string]string `protobuf:"bytes,2,rep,name=description" json:"description,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
555
+	// index 3 reserved for WorkerConstraint in previous versions
556
+	// WorkerConstraint worker_constraint = 3;
557
+	ExportCache *ExportCache `protobuf:"bytes,4,opt,name=export_cache,json=exportCache" json:"export_cache,omitempty"`
557 558
 }
558 559
 
559 560
 func (m *OpMetadata) Reset()                    { *m = OpMetadata{} }
560 561
 func (m *OpMetadata) String() string            { return proto.CompactTextString(m) }
561 562
 func (*OpMetadata) ProtoMessage()               {}
562
-func (*OpMetadata) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{11} }
563
+func (*OpMetadata) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{12} }
563 564
 
564 565
 func (m *OpMetadata) GetIgnoreCache() bool {
565 566
 	if m != nil {
... ...
@@ -575,13 +682,6 @@ func (m *OpMetadata) GetDescription() map[string]string {
575 575
 	return nil
576 576
 }
577 577
 
578
-func (m *OpMetadata) GetWorkerConstraint() *WorkerConstraint {
579
-	if m != nil {
580
-		return m.WorkerConstraint
581
-	}
582
-	return nil
583
-}
584
-
585 578
 func (m *OpMetadata) GetExportCache() *ExportCache {
586 579
 	if m != nil {
587 580
 		return m.ExportCache
... ...
@@ -596,7 +696,7 @@ type ExportCache struct {
596 596
 func (m *ExportCache) Reset()                    { *m = ExportCache{} }
597 597
 func (m *ExportCache) String() string            { return proto.CompactTextString(m) }
598 598
 func (*ExportCache) ProtoMessage()               {}
599
-func (*ExportCache) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{12} }
599
+func (*ExportCache) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{13} }
600 600
 
601 601
 func (m *ExportCache) GetValue() bool {
602 602
 	if m != nil {
... ...
@@ -615,7 +715,7 @@ type ProxyEnv struct {
615 615
 func (m *ProxyEnv) Reset()                    { *m = ProxyEnv{} }
616 616
 func (m *ProxyEnv) String() string            { return proto.CompactTextString(m) }
617 617
 func (*ProxyEnv) ProtoMessage()               {}
618
-func (*ProxyEnv) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{13} }
618
+func (*ProxyEnv) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{14} }
619 619
 
620 620
 func (m *ProxyEnv) GetHttpProxy() string {
621 621
 	if m != nil {
... ...
@@ -645,17 +745,17 @@ func (m *ProxyEnv) GetNoProxy() string {
645 645
 	return ""
646 646
 }
647 647
 
648
-// WorkerConstraint is experimental and likely to be changed.
649
-type WorkerConstraint struct {
648
+// WorkerConstraints defines conditions for the worker
649
+type WorkerConstraints struct {
650 650
 	Filter []string `protobuf:"bytes,1,rep,name=filter" json:"filter,omitempty"`
651 651
 }
652 652
 
653
-func (m *WorkerConstraint) Reset()                    { *m = WorkerConstraint{} }
654
-func (m *WorkerConstraint) String() string            { return proto.CompactTextString(m) }
655
-func (*WorkerConstraint) ProtoMessage()               {}
656
-func (*WorkerConstraint) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{14} }
653
+func (m *WorkerConstraints) Reset()                    { *m = WorkerConstraints{} }
654
+func (m *WorkerConstraints) String() string            { return proto.CompactTextString(m) }
655
+func (*WorkerConstraints) ProtoMessage()               {}
656
+func (*WorkerConstraints) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{15} }
657 657
 
658
-func (m *WorkerConstraint) GetFilter() []string {
658
+func (m *WorkerConstraints) GetFilter() []string {
659 659
 	if m != nil {
660 660
 		return m.Filter
661 661
 	}
... ...
@@ -674,7 +774,7 @@ type Definition struct {
674 674
 func (m *Definition) Reset()                    { *m = Definition{} }
675 675
 func (m *Definition) String() string            { return proto.CompactTextString(m) }
676 676
 func (*Definition) ProtoMessage()               {}
677
-func (*Definition) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{15} }
677
+func (*Definition) Descriptor() ([]byte, []int) { return fileDescriptorOps, []int{16} }
678 678
 
679 679
 func (m *Definition) GetDef() [][]byte {
680 680
 	if m != nil {
... ...
@@ -692,6 +792,7 @@ func (m *Definition) GetMetadata() map[github_com_opencontainers_go_digest.Diges
692 692
 
693 693
 func init() {
694 694
 	proto.RegisterType((*Op)(nil), "pb.Op")
695
+	proto.RegisterType((*Platform)(nil), "pb.Platform")
695 696
 	proto.RegisterType((*Input)(nil), "pb.Input")
696 697
 	proto.RegisterType((*ExecOp)(nil), "pb.ExecOp")
697 698
 	proto.RegisterType((*Meta)(nil), "pb.Meta")
... ...
@@ -705,9 +806,10 @@ func init() {
705 705
 	proto.RegisterType((*OpMetadata)(nil), "pb.OpMetadata")
706 706
 	proto.RegisterType((*ExportCache)(nil), "pb.ExportCache")
707 707
 	proto.RegisterType((*ProxyEnv)(nil), "pb.ProxyEnv")
708
-	proto.RegisterType((*WorkerConstraint)(nil), "pb.WorkerConstraint")
708
+	proto.RegisterType((*WorkerConstraints)(nil), "pb.WorkerConstraints")
709 709
 	proto.RegisterType((*Definition)(nil), "pb.Definition")
710 710
 	proto.RegisterEnum("pb.MountType", MountType_name, MountType_value)
711
+	proto.RegisterEnum("pb.CacheSharingOpt", CacheSharingOpt_name, CacheSharingOpt_value)
711 712
 }
712 713
 func (m *Op) Marshal() (dAtA []byte, err error) {
713 714
 	size := m.Size()
... ...
@@ -743,6 +845,26 @@ func (m *Op) MarshalTo(dAtA []byte) (int, error) {
743 743
 		}
744 744
 		i += nn1
745 745
 	}
746
+	if m.Platform != nil {
747
+		dAtA[i] = 0x52
748
+		i++
749
+		i = encodeVarintOps(dAtA, i, uint64(m.Platform.Size()))
750
+		n2, err := m.Platform.MarshalTo(dAtA[i:])
751
+		if err != nil {
752
+			return 0, err
753
+		}
754
+		i += n2
755
+	}
756
+	if m.Constraints != nil {
757
+		dAtA[i] = 0x5a
758
+		i++
759
+		i = encodeVarintOps(dAtA, i, uint64(m.Constraints.Size()))
760
+		n3, err := m.Constraints.MarshalTo(dAtA[i:])
761
+		if err != nil {
762
+			return 0, err
763
+		}
764
+		i += n3
765
+	}
746 766
 	return i, nil
747 767
 }
748 768
 
... ...
@@ -752,11 +874,11 @@ func (m *Op_Exec) MarshalTo(dAtA []byte) (int, error) {
752 752
 		dAtA[i] = 0x12
753 753
 		i++
754 754
 		i = encodeVarintOps(dAtA, i, uint64(m.Exec.Size()))
755
-		n2, err := m.Exec.MarshalTo(dAtA[i:])
755
+		n4, err := m.Exec.MarshalTo(dAtA[i:])
756 756
 		if err != nil {
757 757
 			return 0, err
758 758
 		}
759
-		i += n2
759
+		i += n4
760 760
 	}
761 761
 	return i, nil
762 762
 }
... ...
@@ -766,11 +888,11 @@ func (m *Op_Source) MarshalTo(dAtA []byte) (int, error) {
766 766
 		dAtA[i] = 0x1a
767 767
 		i++
768 768
 		i = encodeVarintOps(dAtA, i, uint64(m.Source.Size()))
769
-		n3, err := m.Source.MarshalTo(dAtA[i:])
769
+		n5, err := m.Source.MarshalTo(dAtA[i:])
770 770
 		if err != nil {
771 771
 			return 0, err
772 772
 		}
773
-		i += n3
773
+		i += n5
774 774
 	}
775 775
 	return i, nil
776 776
 }
... ...
@@ -780,11 +902,11 @@ func (m *Op_Copy) MarshalTo(dAtA []byte) (int, error) {
780 780
 		dAtA[i] = 0x22
781 781
 		i++
782 782
 		i = encodeVarintOps(dAtA, i, uint64(m.Copy.Size()))
783
-		n4, err := m.Copy.MarshalTo(dAtA[i:])
783
+		n6, err := m.Copy.MarshalTo(dAtA[i:])
784 784
 		if err != nil {
785 785
 			return 0, err
786 786
 		}
787
-		i += n4
787
+		i += n6
788 788
 	}
789 789
 	return i, nil
790 790
 }
... ...
@@ -794,14 +916,71 @@ func (m *Op_Build) MarshalTo(dAtA []byte) (int, error) {
794 794
 		dAtA[i] = 0x2a
795 795
 		i++
796 796
 		i = encodeVarintOps(dAtA, i, uint64(m.Build.Size()))
797
-		n5, err := m.Build.MarshalTo(dAtA[i:])
797
+		n7, err := m.Build.MarshalTo(dAtA[i:])
798 798
 		if err != nil {
799 799
 			return 0, err
800 800
 		}
801
-		i += n5
801
+		i += n7
802
+	}
803
+	return i, nil
804
+}
805
+func (m *Platform) Marshal() (dAtA []byte, err error) {
806
+	size := m.Size()
807
+	dAtA = make([]byte, size)
808
+	n, err := m.MarshalTo(dAtA)
809
+	if err != nil {
810
+		return nil, err
811
+	}
812
+	return dAtA[:n], nil
813
+}
814
+
815
+func (m *Platform) MarshalTo(dAtA []byte) (int, error) {
816
+	var i int
817
+	_ = i
818
+	var l int
819
+	_ = l
820
+	if len(m.Architecture) > 0 {
821
+		dAtA[i] = 0xa
822
+		i++
823
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Architecture)))
824
+		i += copy(dAtA[i:], m.Architecture)
825
+	}
826
+	if len(m.OS) > 0 {
827
+		dAtA[i] = 0x12
828
+		i++
829
+		i = encodeVarintOps(dAtA, i, uint64(len(m.OS)))
830
+		i += copy(dAtA[i:], m.OS)
831
+	}
832
+	if len(m.Variant) > 0 {
833
+		dAtA[i] = 0x1a
834
+		i++
835
+		i = encodeVarintOps(dAtA, i, uint64(len(m.Variant)))
836
+		i += copy(dAtA[i:], m.Variant)
837
+	}
838
+	if len(m.OSVersion) > 0 {
839
+		dAtA[i] = 0x22
840
+		i++
841
+		i = encodeVarintOps(dAtA, i, uint64(len(m.OSVersion)))
842
+		i += copy(dAtA[i:], m.OSVersion)
843
+	}
844
+	if len(m.OSFeatures) > 0 {
845
+		for _, s := range m.OSFeatures {
846
+			dAtA[i] = 0x2a
847
+			i++
848
+			l = len(s)
849
+			for l >= 1<<7 {
850
+				dAtA[i] = uint8(uint64(l)&0x7f | 0x80)
851
+				l >>= 7
852
+				i++
853
+			}
854
+			dAtA[i] = uint8(l)
855
+			i++
856
+			i += copy(dAtA[i:], s)
857
+		}
802 858
 	}
803 859
 	return i, nil
804 860
 }
861
+
805 862
 func (m *Input) Marshal() (dAtA []byte, err error) {
806 863
 	size := m.Size()
807 864
 	dAtA = make([]byte, size)
... ...
@@ -850,11 +1029,11 @@ func (m *ExecOp) MarshalTo(dAtA []byte) (int, error) {
850 850
 		dAtA[i] = 0xa
851 851
 		i++
852 852
 		i = encodeVarintOps(dAtA, i, uint64(m.Meta.Size()))
853
-		n6, err := m.Meta.MarshalTo(dAtA[i:])
853
+		n8, err := m.Meta.MarshalTo(dAtA[i:])
854 854
 		if err != nil {
855 855
 			return 0, err
856 856
 		}
857
-		i += n6
857
+		i += n8
858 858
 	}
859 859
 	if len(m.Mounts) > 0 {
860 860
 		for _, msg := range m.Mounts {
... ...
@@ -932,11 +1111,11 @@ func (m *Meta) MarshalTo(dAtA []byte) (int, error) {
932 932
 		dAtA[i] = 0x2a
933 933
 		i++
934 934
 		i = encodeVarintOps(dAtA, i, uint64(m.ProxyEnv.Size()))
935
-		n7, err := m.ProxyEnv.MarshalTo(dAtA[i:])
935
+		n9, err := m.ProxyEnv.MarshalTo(dAtA[i:])
936 936
 		if err != nil {
937 937
 			return 0, err
938 938
 		}
939
-		i += n7
939
+		i += n9
940 940
 	}
941 941
 	return i, nil
942 942
 }
... ...
@@ -999,11 +1178,11 @@ func (m *Mount) MarshalTo(dAtA []byte) (int, error) {
999 999
 		dAtA[i] = 0x1
1000 1000
 		i++
1001 1001
 		i = encodeVarintOps(dAtA, i, uint64(m.CacheOpt.Size()))
1002
-		n8, err := m.CacheOpt.MarshalTo(dAtA[i:])
1002
+		n10, err := m.CacheOpt.MarshalTo(dAtA[i:])
1003 1003
 		if err != nil {
1004 1004
 			return 0, err
1005 1005
 		}
1006
-		i += n8
1006
+		i += n10
1007 1007
 	}
1008 1008
 	return i, nil
1009 1009
 }
... ...
@@ -1029,6 +1208,11 @@ func (m *CacheOpt) MarshalTo(dAtA []byte) (int, error) {
1029 1029
 		i = encodeVarintOps(dAtA, i, uint64(len(m.ID)))
1030 1030
 		i += copy(dAtA[i:], m.ID)
1031 1031
 	}
1032
+	if m.Sharing != 0 {
1033
+		dAtA[i] = 0x10
1034
+		i++
1035
+		i = encodeVarintOps(dAtA, i, uint64(m.Sharing))
1036
+	}
1032 1037
 	return i, nil
1033 1038
 }
1034 1039
 
... ...
@@ -1178,11 +1362,11 @@ func (m *BuildOp) MarshalTo(dAtA []byte) (int, error) {
1178 1178
 				dAtA[i] = 0x12
1179 1179
 				i++
1180 1180
 				i = encodeVarintOps(dAtA, i, uint64(v.Size()))
1181
-				n9, err := v.MarshalTo(dAtA[i:])
1181
+				n11, err := v.MarshalTo(dAtA[i:])
1182 1182
 				if err != nil {
1183 1183
 					return 0, err
1184 1184
 				}
1185
-				i += n9
1185
+				i += n11
1186 1186
 			}
1187 1187
 		}
1188 1188
 	}
... ...
@@ -1190,11 +1374,11 @@ func (m *BuildOp) MarshalTo(dAtA []byte) (int, error) {
1190 1190
 		dAtA[i] = 0x1a
1191 1191
 		i++
1192 1192
 		i = encodeVarintOps(dAtA, i, uint64(m.Def.Size()))
1193
-		n10, err := m.Def.MarshalTo(dAtA[i:])
1193
+		n12, err := m.Def.MarshalTo(dAtA[i:])
1194 1194
 		if err != nil {
1195 1195
 			return 0, err
1196 1196
 		}
1197
-		i += n10
1197
+		i += n12
1198 1198
 	}
1199 1199
 	if len(m.Attrs) > 0 {
1200 1200
 		for k, _ := range m.Attrs {
... ...
@@ -1281,25 +1465,15 @@ func (m *OpMetadata) MarshalTo(dAtA []byte) (int, error) {
1281 1281
 			i += copy(dAtA[i:], v)
1282 1282
 		}
1283 1283
 	}
1284
-	if m.WorkerConstraint != nil {
1285
-		dAtA[i] = 0x1a
1286
-		i++
1287
-		i = encodeVarintOps(dAtA, i, uint64(m.WorkerConstraint.Size()))
1288
-		n11, err := m.WorkerConstraint.MarshalTo(dAtA[i:])
1289
-		if err != nil {
1290
-			return 0, err
1291
-		}
1292
-		i += n11
1293
-	}
1294 1284
 	if m.ExportCache != nil {
1295 1285
 		dAtA[i] = 0x22
1296 1286
 		i++
1297 1287
 		i = encodeVarintOps(dAtA, i, uint64(m.ExportCache.Size()))
1298
-		n12, err := m.ExportCache.MarshalTo(dAtA[i:])
1288
+		n13, err := m.ExportCache.MarshalTo(dAtA[i:])
1299 1289
 		if err != nil {
1300 1290
 			return 0, err
1301 1291
 		}
1302
-		i += n12
1292
+		i += n13
1303 1293
 	}
1304 1294
 	return i, nil
1305 1295
 }
... ...
@@ -1374,7 +1548,7 @@ func (m *ProxyEnv) MarshalTo(dAtA []byte) (int, error) {
1374 1374
 	return i, nil
1375 1375
 }
1376 1376
 
1377
-func (m *WorkerConstraint) Marshal() (dAtA []byte, err error) {
1377
+func (m *WorkerConstraints) Marshal() (dAtA []byte, err error) {
1378 1378
 	size := m.Size()
1379 1379
 	dAtA = make([]byte, size)
1380 1380
 	n, err := m.MarshalTo(dAtA)
... ...
@@ -1384,7 +1558,7 @@ func (m *WorkerConstraint) Marshal() (dAtA []byte, err error) {
1384 1384
 	return dAtA[:n], nil
1385 1385
 }
1386 1386
 
1387
-func (m *WorkerConstraint) MarshalTo(dAtA []byte) (int, error) {
1387
+func (m *WorkerConstraints) MarshalTo(dAtA []byte) (int, error) {
1388 1388
 	var i int
1389 1389
 	_ = i
1390 1390
 	var l int
... ...
@@ -1449,11 +1623,11 @@ func (m *Definition) MarshalTo(dAtA []byte) (int, error) {
1449 1449
 			dAtA[i] = 0x12
1450 1450
 			i++
1451 1451
 			i = encodeVarintOps(dAtA, i, uint64((&v).Size()))
1452
-			n13, err := (&v).MarshalTo(dAtA[i:])
1452
+			n14, err := (&v).MarshalTo(dAtA[i:])
1453 1453
 			if err != nil {
1454 1454
 				return 0, err
1455 1455
 			}
1456
-			i += n13
1456
+			i += n14
1457 1457
 		}
1458 1458
 	}
1459 1459
 	return i, nil
... ...
@@ -1480,6 +1654,14 @@ func (m *Op) Size() (n int) {
1480 1480
 	if m.Op != nil {
1481 1481
 		n += m.Op.Size()
1482 1482
 	}
1483
+	if m.Platform != nil {
1484
+		l = m.Platform.Size()
1485
+		n += 1 + l + sovOps(uint64(l))
1486
+	}
1487
+	if m.Constraints != nil {
1488
+		l = m.Constraints.Size()
1489
+		n += 1 + l + sovOps(uint64(l))
1490
+	}
1483 1491
 	return n
1484 1492
 }
1485 1493
 
... ...
@@ -1519,6 +1701,34 @@ func (m *Op_Build) Size() (n int) {
1519 1519
 	}
1520 1520
 	return n
1521 1521
 }
1522
+func (m *Platform) Size() (n int) {
1523
+	var l int
1524
+	_ = l
1525
+	l = len(m.Architecture)
1526
+	if l > 0 {
1527
+		n += 1 + l + sovOps(uint64(l))
1528
+	}
1529
+	l = len(m.OS)
1530
+	if l > 0 {
1531
+		n += 1 + l + sovOps(uint64(l))
1532
+	}
1533
+	l = len(m.Variant)
1534
+	if l > 0 {
1535
+		n += 1 + l + sovOps(uint64(l))
1536
+	}
1537
+	l = len(m.OSVersion)
1538
+	if l > 0 {
1539
+		n += 1 + l + sovOps(uint64(l))
1540
+	}
1541
+	if len(m.OSFeatures) > 0 {
1542
+		for _, s := range m.OSFeatures {
1543
+			l = len(s)
1544
+			n += 1 + l + sovOps(uint64(l))
1545
+		}
1546
+	}
1547
+	return n
1548
+}
1549
+
1522 1550
 func (m *Input) Size() (n int) {
1523 1551
 	var l int
1524 1552
 	_ = l
... ...
@@ -1615,6 +1825,9 @@ func (m *CacheOpt) Size() (n int) {
1615 1615
 	if l > 0 {
1616 1616
 		n += 1 + l + sovOps(uint64(l))
1617 1617
 	}
1618
+	if m.Sharing != 0 {
1619
+		n += 1 + sovOps(uint64(m.Sharing))
1620
+	}
1618 1621
 	return n
1619 1622
 }
1620 1623
 
... ...
@@ -1722,10 +1935,6 @@ func (m *OpMetadata) Size() (n int) {
1722 1722
 			n += mapEntrySize + 1 + sovOps(uint64(mapEntrySize))
1723 1723
 		}
1724 1724
 	}
1725
-	if m.WorkerConstraint != nil {
1726
-		l = m.WorkerConstraint.Size()
1727
-		n += 1 + l + sovOps(uint64(l))
1728
-	}
1729 1725
 	if m.ExportCache != nil {
1730 1726
 		l = m.ExportCache.Size()
1731 1727
 		n += 1 + l + sovOps(uint64(l))
... ...
@@ -1764,7 +1973,7 @@ func (m *ProxyEnv) Size() (n int) {
1764 1764
 	return n
1765 1765
 }
1766 1766
 
1767
-func (m *WorkerConstraint) Size() (n int) {
1767
+func (m *WorkerConstraints) Size() (n int) {
1768 1768
 	var l int
1769 1769
 	_ = l
1770 1770
 	if len(m.Filter) > 0 {
... ...
@@ -1998,6 +2207,267 @@ func (m *Op) Unmarshal(dAtA []byte) error {
1998 1998
 			}
1999 1999
 			m.Op = &Op_Build{v}
2000 2000
 			iNdEx = postIndex
2001
+		case 10:
2002
+			if wireType != 2 {
2003
+				return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType)
2004
+			}
2005
+			var msglen int
2006
+			for shift := uint(0); ; shift += 7 {
2007
+				if shift >= 64 {
2008
+					return ErrIntOverflowOps
2009
+				}
2010
+				if iNdEx >= l {
2011
+					return io.ErrUnexpectedEOF
2012
+				}
2013
+				b := dAtA[iNdEx]
2014
+				iNdEx++
2015
+				msglen |= (int(b) & 0x7F) << shift
2016
+				if b < 0x80 {
2017
+					break
2018
+				}
2019
+			}
2020
+			if msglen < 0 {
2021
+				return ErrInvalidLengthOps
2022
+			}
2023
+			postIndex := iNdEx + msglen
2024
+			if postIndex > l {
2025
+				return io.ErrUnexpectedEOF
2026
+			}
2027
+			if m.Platform == nil {
2028
+				m.Platform = &Platform{}
2029
+			}
2030
+			if err := m.Platform.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
2031
+				return err
2032
+			}
2033
+			iNdEx = postIndex
2034
+		case 11:
2035
+			if wireType != 2 {
2036
+				return fmt.Errorf("proto: wrong wireType = %d for field Constraints", wireType)
2037
+			}
2038
+			var msglen int
2039
+			for shift := uint(0); ; shift += 7 {
2040
+				if shift >= 64 {
2041
+					return ErrIntOverflowOps
2042
+				}
2043
+				if iNdEx >= l {
2044
+					return io.ErrUnexpectedEOF
2045
+				}
2046
+				b := dAtA[iNdEx]
2047
+				iNdEx++
2048
+				msglen |= (int(b) & 0x7F) << shift
2049
+				if b < 0x80 {
2050
+					break
2051
+				}
2052
+			}
2053
+			if msglen < 0 {
2054
+				return ErrInvalidLengthOps
2055
+			}
2056
+			postIndex := iNdEx + msglen
2057
+			if postIndex > l {
2058
+				return io.ErrUnexpectedEOF
2059
+			}
2060
+			if m.Constraints == nil {
2061
+				m.Constraints = &WorkerConstraints{}
2062
+			}
2063
+			if err := m.Constraints.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
2064
+				return err
2065
+			}
2066
+			iNdEx = postIndex
2067
+		default:
2068
+			iNdEx = preIndex
2069
+			skippy, err := skipOps(dAtA[iNdEx:])
2070
+			if err != nil {
2071
+				return err
2072
+			}
2073
+			if skippy < 0 {
2074
+				return ErrInvalidLengthOps
2075
+			}
2076
+			if (iNdEx + skippy) > l {
2077
+				return io.ErrUnexpectedEOF
2078
+			}
2079
+			iNdEx += skippy
2080
+		}
2081
+	}
2082
+
2083
+	if iNdEx > l {
2084
+		return io.ErrUnexpectedEOF
2085
+	}
2086
+	return nil
2087
+}
2088
+func (m *Platform) Unmarshal(dAtA []byte) error {
2089
+	l := len(dAtA)
2090
+	iNdEx := 0
2091
+	for iNdEx < l {
2092
+		preIndex := iNdEx
2093
+		var wire uint64
2094
+		for shift := uint(0); ; shift += 7 {
2095
+			if shift >= 64 {
2096
+				return ErrIntOverflowOps
2097
+			}
2098
+			if iNdEx >= l {
2099
+				return io.ErrUnexpectedEOF
2100
+			}
2101
+			b := dAtA[iNdEx]
2102
+			iNdEx++
2103
+			wire |= (uint64(b) & 0x7F) << shift
2104
+			if b < 0x80 {
2105
+				break
2106
+			}
2107
+		}
2108
+		fieldNum := int32(wire >> 3)
2109
+		wireType := int(wire & 0x7)
2110
+		if wireType == 4 {
2111
+			return fmt.Errorf("proto: Platform: wiretype end group for non-group")
2112
+		}
2113
+		if fieldNum <= 0 {
2114
+			return fmt.Errorf("proto: Platform: illegal tag %d (wire type %d)", fieldNum, wire)
2115
+		}
2116
+		switch fieldNum {
2117
+		case 1:
2118
+			if wireType != 2 {
2119
+				return fmt.Errorf("proto: wrong wireType = %d for field Architecture", wireType)
2120
+			}
2121
+			var stringLen uint64
2122
+			for shift := uint(0); ; shift += 7 {
2123
+				if shift >= 64 {
2124
+					return ErrIntOverflowOps
2125
+				}
2126
+				if iNdEx >= l {
2127
+					return io.ErrUnexpectedEOF
2128
+				}
2129
+				b := dAtA[iNdEx]
2130
+				iNdEx++
2131
+				stringLen |= (uint64(b) & 0x7F) << shift
2132
+				if b < 0x80 {
2133
+					break
2134
+				}
2135
+			}
2136
+			intStringLen := int(stringLen)
2137
+			if intStringLen < 0 {
2138
+				return ErrInvalidLengthOps
2139
+			}
2140
+			postIndex := iNdEx + intStringLen
2141
+			if postIndex > l {
2142
+				return io.ErrUnexpectedEOF
2143
+			}
2144
+			m.Architecture = string(dAtA[iNdEx:postIndex])
2145
+			iNdEx = postIndex
2146
+		case 2:
2147
+			if wireType != 2 {
2148
+				return fmt.Errorf("proto: wrong wireType = %d for field OS", wireType)
2149
+			}
2150
+			var stringLen uint64
2151
+			for shift := uint(0); ; shift += 7 {
2152
+				if shift >= 64 {
2153
+					return ErrIntOverflowOps
2154
+				}
2155
+				if iNdEx >= l {
2156
+					return io.ErrUnexpectedEOF
2157
+				}
2158
+				b := dAtA[iNdEx]
2159
+				iNdEx++
2160
+				stringLen |= (uint64(b) & 0x7F) << shift
2161
+				if b < 0x80 {
2162
+					break
2163
+				}
2164
+			}
2165
+			intStringLen := int(stringLen)
2166
+			if intStringLen < 0 {
2167
+				return ErrInvalidLengthOps
2168
+			}
2169
+			postIndex := iNdEx + intStringLen
2170
+			if postIndex > l {
2171
+				return io.ErrUnexpectedEOF
2172
+			}
2173
+			m.OS = string(dAtA[iNdEx:postIndex])
2174
+			iNdEx = postIndex
2175
+		case 3:
2176
+			if wireType != 2 {
2177
+				return fmt.Errorf("proto: wrong wireType = %d for field Variant", wireType)
2178
+			}
2179
+			var stringLen uint64
2180
+			for shift := uint(0); ; shift += 7 {
2181
+				if shift >= 64 {
2182
+					return ErrIntOverflowOps
2183
+				}
2184
+				if iNdEx >= l {
2185
+					return io.ErrUnexpectedEOF
2186
+				}
2187
+				b := dAtA[iNdEx]
2188
+				iNdEx++
2189
+				stringLen |= (uint64(b) & 0x7F) << shift
2190
+				if b < 0x80 {
2191
+					break
2192
+				}
2193
+			}
2194
+			intStringLen := int(stringLen)
2195
+			if intStringLen < 0 {
2196
+				return ErrInvalidLengthOps
2197
+			}
2198
+			postIndex := iNdEx + intStringLen
2199
+			if postIndex > l {
2200
+				return io.ErrUnexpectedEOF
2201
+			}
2202
+			m.Variant = string(dAtA[iNdEx:postIndex])
2203
+			iNdEx = postIndex
2204
+		case 4:
2205
+			if wireType != 2 {
2206
+				return fmt.Errorf("proto: wrong wireType = %d for field OSVersion", wireType)
2207
+			}
2208
+			var stringLen uint64
2209
+			for shift := uint(0); ; shift += 7 {
2210
+				if shift >= 64 {
2211
+					return ErrIntOverflowOps
2212
+				}
2213
+				if iNdEx >= l {
2214
+					return io.ErrUnexpectedEOF
2215
+				}
2216
+				b := dAtA[iNdEx]
2217
+				iNdEx++
2218
+				stringLen |= (uint64(b) & 0x7F) << shift
2219
+				if b < 0x80 {
2220
+					break
2221
+				}
2222
+			}
2223
+			intStringLen := int(stringLen)
2224
+			if intStringLen < 0 {
2225
+				return ErrInvalidLengthOps
2226
+			}
2227
+			postIndex := iNdEx + intStringLen
2228
+			if postIndex > l {
2229
+				return io.ErrUnexpectedEOF
2230
+			}
2231
+			m.OSVersion = string(dAtA[iNdEx:postIndex])
2232
+			iNdEx = postIndex
2233
+		case 5:
2234
+			if wireType != 2 {
2235
+				return fmt.Errorf("proto: wrong wireType = %d for field OSFeatures", wireType)
2236
+			}
2237
+			var stringLen uint64
2238
+			for shift := uint(0); ; shift += 7 {
2239
+				if shift >= 64 {
2240
+					return ErrIntOverflowOps
2241
+				}
2242
+				if iNdEx >= l {
2243
+					return io.ErrUnexpectedEOF
2244
+				}
2245
+				b := dAtA[iNdEx]
2246
+				iNdEx++
2247
+				stringLen |= (uint64(b) & 0x7F) << shift
2248
+				if b < 0x80 {
2249
+					break
2250
+				}
2251
+			}
2252
+			intStringLen := int(stringLen)
2253
+			if intStringLen < 0 {
2254
+				return ErrInvalidLengthOps
2255
+			}
2256
+			postIndex := iNdEx + intStringLen
2257
+			if postIndex > l {
2258
+				return io.ErrUnexpectedEOF
2259
+			}
2260
+			m.OSFeatures = append(m.OSFeatures, string(dAtA[iNdEx:postIndex]))
2261
+			iNdEx = postIndex
2001 2262
 		default:
2002 2263
 			iNdEx = preIndex
2003 2264
 			skippy, err := skipOps(dAtA[iNdEx:])
... ...
@@ -2706,6 +3176,25 @@ func (m *CacheOpt) Unmarshal(dAtA []byte) error {
2706 2706
 			}
2707 2707
 			m.ID = string(dAtA[iNdEx:postIndex])
2708 2708
 			iNdEx = postIndex
2709
+		case 2:
2710
+			if wireType != 0 {
2711
+				return fmt.Errorf("proto: wrong wireType = %d for field Sharing", wireType)
2712
+			}
2713
+			m.Sharing = 0
2714
+			for shift := uint(0); ; shift += 7 {
2715
+				if shift >= 64 {
2716
+					return ErrIntOverflowOps
2717
+				}
2718
+				if iNdEx >= l {
2719
+					return io.ErrUnexpectedEOF
2720
+				}
2721
+				b := dAtA[iNdEx]
2722
+				iNdEx++
2723
+				m.Sharing |= (CacheSharingOpt(b) & 0x7F) << shift
2724
+				if b < 0x80 {
2725
+					break
2726
+				}
2727
+			}
2709 2728
 		default:
2710 2729
 			iNdEx = preIndex
2711 2730
 			skippy, err := skipOps(dAtA[iNdEx:])
... ...
@@ -3711,39 +4200,6 @@ func (m *OpMetadata) Unmarshal(dAtA []byte) error {
3711 3711
 			}
3712 3712
 			m.Description[mapkey] = mapvalue
3713 3713
 			iNdEx = postIndex
3714
-		case 3:
3715
-			if wireType != 2 {
3716
-				return fmt.Errorf("proto: wrong wireType = %d for field WorkerConstraint", wireType)
3717
-			}
3718
-			var msglen int
3719
-			for shift := uint(0); ; shift += 7 {
3720
-				if shift >= 64 {
3721
-					return ErrIntOverflowOps
3722
-				}
3723
-				if iNdEx >= l {
3724
-					return io.ErrUnexpectedEOF
3725
-				}
3726
-				b := dAtA[iNdEx]
3727
-				iNdEx++
3728
-				msglen |= (int(b) & 0x7F) << shift
3729
-				if b < 0x80 {
3730
-					break
3731
-				}
3732
-			}
3733
-			if msglen < 0 {
3734
-				return ErrInvalidLengthOps
3735
-			}
3736
-			postIndex := iNdEx + msglen
3737
-			if postIndex > l {
3738
-				return io.ErrUnexpectedEOF
3739
-			}
3740
-			if m.WorkerConstraint == nil {
3741
-				m.WorkerConstraint = &WorkerConstraint{}
3742
-			}
3743
-			if err := m.WorkerConstraint.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
3744
-				return err
3745
-			}
3746
-			iNdEx = postIndex
3747 3714
 		case 4:
3748 3715
 			if wireType != 2 {
3749 3716
 				return fmt.Errorf("proto: wrong wireType = %d for field ExportCache", wireType)
... ...
@@ -4034,7 +4490,7 @@ func (m *ProxyEnv) Unmarshal(dAtA []byte) error {
4034 4034
 	}
4035 4035
 	return nil
4036 4036
 }
4037
-func (m *WorkerConstraint) Unmarshal(dAtA []byte) error {
4037
+func (m *WorkerConstraints) Unmarshal(dAtA []byte) error {
4038 4038
 	l := len(dAtA)
4039 4039
 	iNdEx := 0
4040 4040
 	for iNdEx < l {
... ...
@@ -4057,10 +4513,10 @@ func (m *WorkerConstraint) Unmarshal(dAtA []byte) error {
4057 4057
 		fieldNum := int32(wire >> 3)
4058 4058
 		wireType := int(wire & 0x7)
4059 4059
 		if wireType == 4 {
4060
-			return fmt.Errorf("proto: WorkerConstraint: wiretype end group for non-group")
4060
+			return fmt.Errorf("proto: WorkerConstraints: wiretype end group for non-group")
4061 4061
 		}
4062 4062
 		if fieldNum <= 0 {
4063
-			return fmt.Errorf("proto: WorkerConstraint: illegal tag %d (wire type %d)", fieldNum, wire)
4063
+			return fmt.Errorf("proto: WorkerConstraints: illegal tag %d (wire type %d)", fieldNum, wire)
4064 4064
 		}
4065 4065
 		switch fieldNum {
4066 4066
 		case 1:
... ...
@@ -4423,72 +4879,81 @@ var (
4423 4423
 func init() { proto.RegisterFile("ops.proto", fileDescriptorOps) }
4424 4424
 
4425 4425
 var fileDescriptorOps = []byte{
4426
-	// 1062 bytes of a gzipped FileDescriptorProto
4427
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0x1b, 0xc5,
4428
-	0x17, 0xcf, 0xae, 0x3f, 0xb2, 0x7b, 0x36, 0xed, 0xdf, 0xff, 0x21, 0x2a, 0xc6, 0x94, 0xc4, 0x6c,
4429
-	0x11, 0x72, 0xd3, 0xc6, 0x91, 0x8c, 0x84, 0x2a, 0x2e, 0x2a, 0xe2, 0x0f, 0x14, 0x83, 0x42, 0xaa,
4430
-	0x49, 0x04, 0x97, 0x91, 0xbd, 0x1e, 0x3b, 0xab, 0x3a, 0x3b, 0xab, 0xdd, 0xd9, 0x24, 0xbe, 0x00,
4431
-	0x89, 0x3e, 0x01, 0x12, 0x4f, 0xc1, 0x43, 0xc0, 0x75, 0x2f, 0xb9, 0x85, 0x8b, 0x82, 0xc2, 0x8b,
4432
-	0xa0, 0x73, 0x66, 0xbc, 0xeb, 0x86, 0x22, 0xb5, 0x82, 0x2b, 0xcf, 0x9c, 0xf3, 0x3b, 0x67, 0xce,
4433
-	0xf9, 0x9d, 0x8f, 0x35, 0xb8, 0x32, 0x4e, 0xdb, 0x71, 0x22, 0x95, 0x64, 0x76, 0x3c, 0x6e, 0xec,
4434
-	0xce, 0x42, 0x75, 0x96, 0x8d, 0xdb, 0x81, 0x3c, 0xdf, 0x9b, 0xc9, 0x99, 0xdc, 0x23, 0xd5, 0x38,
4435
-	0x9b, 0xd2, 0x8d, 0x2e, 0x74, 0xd2, 0x26, 0xfe, 0xcf, 0x16, 0xd8, 0x47, 0x31, 0x7b, 0x1f, 0xaa,
4436
-	0x61, 0x14, 0x67, 0x2a, 0xad, 0x5b, 0xcd, 0x52, 0xcb, 0xeb, 0xb8, 0xed, 0x78, 0xdc, 0x1e, 0xa2,
4437
-	0x84, 0x1b, 0x05, 0x6b, 0x42, 0x59, 0x5c, 0x89, 0xa0, 0x6e, 0x37, 0xad, 0x96, 0xd7, 0x01, 0x04,
4438
-	0x0c, 0xae, 0x44, 0x70, 0x14, 0x1f, 0xac, 0x71, 0xd2, 0xb0, 0x0f, 0xa1, 0x9a, 0xca, 0x2c, 0x09,
4439
-	0x44, 0xbd, 0x44, 0x98, 0x0d, 0xc4, 0x1c, 0x93, 0x84, 0x50, 0x46, 0x8b, 0x9e, 0x02, 0x19, 0x2f,
4440
-	0xea, 0xe5, 0xc2, 0x53, 0x4f, 0xc6, 0x0b, 0xed, 0x09, 0x35, 0xec, 0x1e, 0x54, 0xc6, 0x59, 0x38,
4441
-	0x9f, 0xd4, 0x2b, 0x04, 0xf1, 0x10, 0xd2, 0x45, 0x01, 0x61, 0xb4, 0xae, 0x5b, 0x06, 0x5b, 0xc6,
4442
-	0xfe, 0xb7, 0x50, 0xa1, 0x38, 0xd9, 0xe7, 0x50, 0x9d, 0x84, 0x33, 0x91, 0xaa, 0xba, 0xd5, 0xb4,
4443
-	0x5a, 0x6e, 0xb7, 0xf3, 0xfc, 0xc5, 0xf6, 0xda, 0x6f, 0x2f, 0xb6, 0x77, 0x56, 0x08, 0x91, 0xb1,
4444
-	0x88, 0x02, 0x19, 0xa9, 0x51, 0x18, 0x89, 0x24, 0xdd, 0x9b, 0xc9, 0x5d, 0x6d, 0xd2, 0xee, 0xd3,
4445
-	0x0f, 0x37, 0x1e, 0xd8, 0x7d, 0xa8, 0x84, 0xd1, 0x44, 0x5c, 0x51, 0xb2, 0xa5, 0xee, 0x5b, 0xc6,
4446
-	0x95, 0x77, 0x94, 0xa9, 0x38, 0x53, 0x43, 0x54, 0x71, 0x8d, 0xf0, 0x87, 0x50, 0xd5, 0x34, 0xb0,
4447
-	0xbb, 0x50, 0x3e, 0x17, 0x6a, 0x44, 0xcf, 0x7b, 0x1d, 0x07, 0x63, 0x3e, 0x14, 0x6a, 0xc4, 0x49,
4448
-	0x8a, 0x0c, 0x9f, 0xcb, 0x2c, 0x52, 0x69, 0xdd, 0x2e, 0x18, 0x3e, 0x44, 0x09, 0x37, 0x0a, 0xff,
4449
-	0x1b, 0x28, 0xa3, 0x01, 0x63, 0x50, 0x1e, 0x25, 0x33, 0x5d, 0x0a, 0x97, 0xd3, 0x99, 0xd5, 0xa0,
4450
-	0x24, 0xa2, 0x0b, 0xb2, 0x75, 0x39, 0x1e, 0x51, 0x12, 0x5c, 0x4e, 0x88, 0x6a, 0x97, 0xe3, 0x11,
4451
-	0xed, 0xb2, 0x54, 0x24, 0xc4, 0xab, 0xcb, 0xe9, 0xcc, 0xee, 0x83, 0x1b, 0x27, 0xf2, 0x6a, 0x71,
4452
-	0x8a, 0xd6, 0x95, 0xa2, 0x2c, 0x4f, 0x50, 0x38, 0x88, 0x2e, 0xb8, 0x13, 0x9b, 0x93, 0xff, 0x9d,
4453
-	0x0d, 0x15, 0x0a, 0x88, 0xb5, 0x30, 0xfd, 0x38, 0xd3, 0x4c, 0x96, 0xba, 0xcc, 0xa4, 0x0f, 0x44,
4454
-	0x74, 0x9e, 0x3d, 0x92, 0xde, 0x00, 0x27, 0x15, 0x73, 0x11, 0x28, 0x99, 0x10, 0x57, 0x2e, 0xcf,
4455
-	0xef, 0x18, 0xce, 0x04, 0xcb, 0xa1, 0x23, 0xa4, 0x33, 0x7b, 0x00, 0x55, 0x49, 0x1c, 0x52, 0x90,
4456
-	0xff, 0xc0, 0xac, 0x81, 0xa0, 0xf3, 0x44, 0x8c, 0x26, 0x32, 0x9a, 0x2f, 0x28, 0x74, 0x87, 0xe7,
4457
-	0x77, 0xf6, 0x00, 0x5c, 0x62, 0xed, 0x64, 0x11, 0x8b, 0x7a, 0xb5, 0x69, 0xb5, 0x6e, 0x77, 0x6e,
4458
-	0xe5, 0x8c, 0xa2, 0x90, 0x17, 0x7a, 0xd6, 0x02, 0x27, 0x18, 0x05, 0x67, 0xe2, 0x28, 0x56, 0xf5,
4459
-	0xcd, 0x82, 0x83, 0x9e, 0x91, 0xf1, 0x5c, 0xeb, 0x37, 0xc0, 0x59, 0x4a, 0xd9, 0x6d, 0xb0, 0x87,
4460
-	0x7d, 0xdd, 0x4c, 0xdc, 0x1e, 0xf6, 0xfd, 0xc7, 0x50, 0xd5, 0x6d, 0xca, 0x9a, 0x50, 0x4a, 0x93,
4461
-	0xc0, 0x8c, 0xca, 0xed, 0x65, 0xff, 0xea, 0x4e, 0xe7, 0xa8, 0xca, 0x73, 0xb7, 0x8b, 0xdc, 0x7d,
4462
-	0x0e, 0x50, 0xc0, 0xfe, 0x1b, 0x8e, 0xfd, 0x1f, 0x2c, 0x70, 0x96, 0x13, 0xc6, 0xb6, 0x00, 0xc2,
4463
-	0x89, 0x88, 0x54, 0x38, 0x0d, 0x45, 0x62, 0x02, 0x5f, 0x91, 0xb0, 0x5d, 0xa8, 0x8c, 0x94, 0x4a,
4464
-	0x96, 0x1d, 0xf8, 0xf6, 0xea, 0x78, 0xb6, 0xf7, 0x51, 0x33, 0x88, 0x54, 0xb2, 0xe0, 0x1a, 0xd5,
4465
-	0x78, 0x04, 0x50, 0x08, 0xb1, 0xdd, 0x9e, 0x8a, 0x85, 0xf1, 0x8a, 0x47, 0xb6, 0x09, 0x95, 0x8b,
4466
-	0xd1, 0x3c, 0x13, 0x26, 0x28, 0x7d, 0xf9, 0xc4, 0x7e, 0x64, 0xf9, 0x3f, 0xd9, 0xb0, 0x6e, 0xc6,
4467
-	0x95, 0x3d, 0x84, 0x75, 0x1a, 0x57, 0x13, 0xd1, 0xab, 0x33, 0x5d, 0x42, 0xd8, 0x5e, 0xbe, 0x87,
4468
-	0x56, 0x62, 0x34, 0xae, 0xf4, 0x3e, 0x32, 0x31, 0x16, 0x5b, 0xa9, 0x34, 0x11, 0x53, 0xb3, 0x70,
4469
-	0xa8, 0x14, 0x7d, 0x31, 0x0d, 0xa3, 0x50, 0x85, 0x32, 0xe2, 0xa8, 0x62, 0x0f, 0x97, 0x59, 0x97,
4470
-	0xc9, 0xe3, 0x9d, 0x55, 0x8f, 0x7f, 0x4f, 0x7a, 0x08, 0xde, 0xca, 0x33, 0xaf, 0xc8, 0xfa, 0x83,
4471
-	0xd5, 0xac, 0xcd, 0x93, 0xe4, 0x4e, 0x6f, 0xcb, 0x82, 0x85, 0x7f, 0xc1, 0xdf, 0xc7, 0x00, 0x85,
4472
-	0xcb, 0xd7, 0xef, 0x14, 0xff, 0x47, 0x1b, 0xe0, 0x28, 0xc6, 0x1d, 0x32, 0x19, 0xd1, 0xca, 0xd9,
4473
-	0x08, 0x67, 0x91, 0x4c, 0xc4, 0x29, 0xf5, 0x37, 0xd9, 0x3b, 0xdc, 0xd3, 0x32, 0x6a, 0x73, 0xb6,
4474
-	0x0f, 0xde, 0x44, 0xa4, 0x41, 0x12, 0xc6, 0x48, 0x98, 0x21, 0x7d, 0x1b, 0x73, 0x2a, 0xfc, 0xb4,
4475
-	0xfb, 0x05, 0x42, 0x73, 0xb5, 0x6a, 0xc3, 0xf6, 0xe1, 0xff, 0x97, 0x32, 0x79, 0x2a, 0x92, 0xd3,
4476
-	0x40, 0x46, 0xa9, 0x4a, 0x46, 0x61, 0xa4, 0x4c, 0x3d, 0x36, 0xd1, 0xd1, 0xd7, 0xa4, 0xec, 0xe5,
4477
-	0x3a, 0x5e, 0xbb, 0xbc, 0x21, 0x61, 0x1d, 0xd8, 0x10, 0x57, 0xb1, 0x4c, 0x94, 0x09, 0x54, 0x7f,
4478
-	0x18, 0xfe, 0xa7, 0x3f, 0x31, 0x28, 0xa7, 0x60, 0xb9, 0x27, 0x8a, 0x4b, 0xe3, 0x31, 0xd4, 0x6e,
4479
-	0xc6, 0xf5, 0x46, 0x1c, 0xdf, 0x03, 0x6f, 0xc5, 0x37, 0x02, 0xbf, 0x22, 0xa0, 0x26, 0x49, 0x5f,
4480
-	0xfc, 0x67, 0x16, 0x38, 0xcb, 0x4d, 0xc9, 0xde, 0x03, 0x38, 0x53, 0x2a, 0x3e, 0xa5, 0x85, 0x69,
4481
-	0x1e, 0x71, 0x51, 0x42, 0x08, 0xb6, 0x0d, 0x1e, 0x5e, 0x52, 0xa3, 0xd7, 0x0f, 0x92, 0x45, 0xaa,
4482
-	0x01, 0xef, 0x82, 0x3b, 0xcd, 0xcd, 0xf5, 0x52, 0x74, 0xa6, 0x4b, 0xeb, 0x77, 0xc0, 0x89, 0xa4,
4483
-	0xd1, 0xe9, 0xfd, 0xbd, 0x1e, 0x49, 0x52, 0xf9, 0x3b, 0x50, 0xbb, 0xc9, 0x21, 0xbb, 0x03, 0xd5,
4484
-	0x69, 0x38, 0x57, 0x34, 0x54, 0xf8, 0x45, 0x30, 0x37, 0xff, 0x57, 0x0b, 0xa0, 0x18, 0x00, 0x24,
4485
-	0x04, 0xa7, 0x03, 0x31, 0x1b, 0x7a, 0x1a, 0xe6, 0xe0, 0x9c, 0x9b, 0xba, 0x9a, 0x6a, 0xdf, 0x7d,
4486
-	0x79, 0x68, 0xda, 0xcb, 0xb2, 0x13, 0xa5, 0xfa, 0x2b, 0xfa, 0xec, 0xf7, 0x37, 0xfa, 0x8a, 0xe6,
4487
-	0x2f, 0x34, 0xbe, 0x80, 0x5b, 0x2f, 0xb9, 0x7b, 0xcd, 0x79, 0x2a, 0x7a, 0x6f, 0xa5, 0x62, 0x3b,
4488
-	0x9f, 0x82, 0x9b, 0x6f, 0x77, 0xe6, 0x40, 0xb9, 0x3b, 0xfc, 0xb2, 0x5f, 0x5b, 0x63, 0x00, 0xd5,
4489
-	0xe3, 0x41, 0x8f, 0x0f, 0x4e, 0x6a, 0x16, 0x5b, 0x87, 0xd2, 0xf1, 0xf1, 0x41, 0xcd, 0x66, 0x2e,
4490
-	0x54, 0x7a, 0xfb, 0xbd, 0x83, 0x41, 0xad, 0x84, 0xc7, 0x93, 0xc3, 0x27, 0x9f, 0x1d, 0xd7, 0xca,
4491
-	0xdd, 0xda, 0xf3, 0xeb, 0x2d, 0xeb, 0x97, 0xeb, 0x2d, 0xeb, 0x8f, 0xeb, 0x2d, 0xeb, 0xfb, 0x3f,
4492
-	0xb7, 0xd6, 0xc6, 0x55, 0xfa, 0x17, 0xf4, 0xd1, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x76,
4493
-	0x25, 0x54, 0x45, 0x09, 0x00, 0x00,
4426
+	// 1203 bytes of a gzipped FileDescriptorProto
4427
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4d, 0x8f, 0x1b, 0x45,
4428
+	0x13, 0xde, 0x19, 0x7f, 0xcd, 0xd4, 0x6c, 0x36, 0x7e, 0x3b, 0x79, 0x83, 0x59, 0xc2, 0xae, 0x99,
4429
+	0x20, 0xe4, 0x7c, 0xac, 0x57, 0x32, 0x52, 0x88, 0x38, 0x44, 0xac, 0x3f, 0xa2, 0x35, 0x21, 0x38,
4430
+	0x6a, 0xaf, 0x96, 0x63, 0x34, 0x1e, 0xb7, 0xbd, 0xa3, 0x78, 0xa7, 0x47, 0x3d, 0xed, 0xb0, 0x3e,
4431
+	0x80, 0x44, 0x7e, 0x01, 0x12, 0x12, 0x77, 0x7e, 0x08, 0xf7, 0x1c, 0xb9, 0xc2, 0x21, 0xa0, 0x20,
4432
+	0xf1, 0x3b, 0x50, 0x75, 0xb7, 0x67, 0x66, 0x93, 0x20, 0x25, 0x82, 0x93, 0xbb, 0xab, 0x9e, 0x7a,
4433
+	0xba, 0xea, 0xe9, 0x9a, 0x6a, 0x83, 0xcb, 0x93, 0xb4, 0x9d, 0x08, 0x2e, 0x39, 0xb1, 0x93, 0xc9,
4434
+	0xf6, 0xde, 0x3c, 0x92, 0x27, 0xcb, 0x49, 0x3b, 0xe4, 0xa7, 0xfb, 0x73, 0x3e, 0xe7, 0xfb, 0xca,
4435
+	0x35, 0x59, 0xce, 0xd4, 0x4e, 0x6d, 0xd4, 0x4a, 0x87, 0xf8, 0x3f, 0xd9, 0x60, 0x8f, 0x12, 0xf2,
4436
+	0x01, 0x54, 0xa3, 0x38, 0x59, 0xca, 0xb4, 0x61, 0x35, 0x4b, 0x2d, 0xaf, 0xe3, 0xb6, 0x93, 0x49,
4437
+	0x7b, 0x88, 0x16, 0x6a, 0x1c, 0xa4, 0x09, 0x65, 0x76, 0xc6, 0xc2, 0x86, 0xdd, 0xb4, 0x5a, 0x5e,
4438
+	0x07, 0x10, 0x30, 0x38, 0x63, 0xe1, 0x28, 0x39, 0xdc, 0xa0, 0xca, 0x43, 0x3e, 0x82, 0x6a, 0xca,
4439
+	0x97, 0x22, 0x64, 0x8d, 0x92, 0xc2, 0x6c, 0x22, 0x66, 0xac, 0x2c, 0x0a, 0x65, 0xbc, 0xc8, 0x14,
4440
+	0xf2, 0x64, 0xd5, 0x28, 0xe7, 0x4c, 0x3d, 0x9e, 0xac, 0x34, 0x13, 0x7a, 0xc8, 0x35, 0xa8, 0x4c,
4441
+	0x96, 0xd1, 0x62, 0xda, 0xa8, 0x28, 0x88, 0x87, 0x90, 0x2e, 0x1a, 0x14, 0x46, 0xfb, 0x48, 0x0b,
4442
+	0x9c, 0x64, 0x11, 0xc8, 0x19, 0x17, 0xa7, 0x0d, 0xc8, 0x0f, 0x7c, 0x68, 0x6c, 0x34, 0xf3, 0x92,
4443
+	0x4f, 0xc0, 0x0b, 0x79, 0x9c, 0x4a, 0x11, 0x44, 0xb1, 0x4c, 0x1b, 0x9e, 0x02, 0xff, 0x1f, 0xc1,
4444
+	0x5f, 0x71, 0xf1, 0x98, 0x89, 0x5e, 0xee, 0xa4, 0x45, 0x64, 0xb7, 0x0c, 0x36, 0x4f, 0xfc, 0x1f,
4445
+	0x2d, 0x70, 0xd6, 0xac, 0xc4, 0x87, 0xcd, 0x03, 0x11, 0x9e, 0x44, 0x92, 0x85, 0x72, 0x29, 0x58,
4446
+	0xc3, 0x6a, 0x5a, 0x2d, 0x97, 0x9e, 0xb3, 0x91, 0x2d, 0xb0, 0x47, 0x63, 0x25, 0x94, 0x4b, 0xed,
4447
+	0xd1, 0x98, 0x34, 0xa0, 0x76, 0x1c, 0x88, 0x28, 0x88, 0xa5, 0x52, 0xc6, 0xa5, 0xeb, 0x2d, 0xb9,
4448
+	0x0a, 0xee, 0x68, 0x7c, 0xcc, 0x44, 0x1a, 0xf1, 0x58, 0xe9, 0xe1, 0xd2, 0xdc, 0x40, 0x76, 0x00,
4449
+	0x46, 0xe3, 0x7b, 0x2c, 0x40, 0xd2, 0xb4, 0x51, 0x69, 0x96, 0x5a, 0x2e, 0x2d, 0x58, 0xfc, 0x6f,
4450
+	0xa1, 0xa2, 0xee, 0x88, 0x7c, 0x0e, 0xd5, 0x69, 0x34, 0x67, 0xa9, 0xd4, 0xe9, 0x74, 0x3b, 0xcf,
4451
+	0x9e, 0xef, 0x6e, 0xfc, 0xf6, 0x7c, 0xf7, 0x46, 0xa1, 0x19, 0x78, 0xc2, 0xe2, 0x90, 0xc7, 0x32,
4452
+	0x88, 0x62, 0x26, 0xd2, 0xfd, 0x39, 0xdf, 0xd3, 0x21, 0xed, 0xbe, 0xfa, 0xa1, 0x86, 0x81, 0x5c,
4453
+	0x87, 0x4a, 0x14, 0x4f, 0xd9, 0x99, 0xca, 0xbf, 0xd4, 0xbd, 0x64, 0xa8, 0xbc, 0xd1, 0x52, 0x26,
4454
+	0x4b, 0x39, 0x44, 0x17, 0xd5, 0x08, 0x7f, 0x08, 0x55, 0xdd, 0x02, 0xe4, 0x2a, 0x94, 0x4f, 0x99,
4455
+	0x0c, 0xd4, 0xf1, 0x5e, 0xc7, 0x41, 0x69, 0x1f, 0x30, 0x19, 0x50, 0x65, 0xc5, 0xee, 0x3a, 0xe5,
4456
+	0x4b, 0x94, 0xde, 0xce, 0xbb, 0xeb, 0x01, 0x5a, 0xa8, 0x71, 0xf8, 0xdf, 0x40, 0x19, 0x03, 0x08,
4457
+	0x81, 0x72, 0x20, 0xe6, 0xba, 0x0d, 0x5d, 0xaa, 0xd6, 0xa4, 0x0e, 0x25, 0x16, 0x3f, 0x51, 0xb1,
4458
+	0x2e, 0xc5, 0x25, 0x5a, 0xc2, 0xaf, 0xa7, 0x46, 0x4c, 0x5c, 0x62, 0xdc, 0x32, 0x65, 0xc2, 0x68,
4459
+	0xa8, 0xd6, 0xe4, 0x3a, 0xb8, 0x89, 0xe0, 0x67, 0xab, 0x47, 0x18, 0x5d, 0x29, 0x74, 0x08, 0x1a,
4460
+	0x07, 0xf1, 0x13, 0xea, 0x24, 0x66, 0xe5, 0x7f, 0x67, 0x43, 0x45, 0x25, 0x44, 0x5a, 0x58, 0x7e,
4461
+	0xb2, 0xd4, 0x4a, 0x96, 0xba, 0xc4, 0x94, 0x0f, 0x4a, 0xe8, 0xac, 0x7a, 0x14, 0x7d, 0x1b, 0x9c,
4462
+	0x94, 0x2d, 0x58, 0x28, 0xb9, 0x30, 0x77, 0x9d, 0xed, 0x31, 0x9d, 0x29, 0x5e, 0x87, 0xce, 0x50,
4463
+	0xad, 0xc9, 0x4d, 0xa8, 0x72, 0xa5, 0xa1, 0x4a, 0xf2, 0x1f, 0x94, 0x35, 0x10, 0x24, 0x17, 0x2c,
4464
+	0x98, 0xf2, 0x78, 0xb1, 0x52, 0xa9, 0x3b, 0x34, 0xdb, 0x93, 0x9b, 0xe0, 0x2a, 0xd5, 0x8e, 0x56,
4465
+	0x09, 0x6b, 0x54, 0x9b, 0x56, 0x6b, 0xab, 0x73, 0x21, 0x53, 0x14, 0x8d, 0x34, 0xf7, 0xe3, 0x57,
4466
+	0x12, 0x06, 0xe1, 0x09, 0x1b, 0x25, 0xb2, 0x71, 0x39, 0xd7, 0xa0, 0x67, 0x6c, 0x34, 0xf3, 0xfa,
4467
+	0x43, 0x70, 0xd6, 0x56, 0xec, 0xe0, 0x61, 0xdf, 0xf4, 0xb6, 0x3d, 0xec, 0x93, 0x3d, 0xa8, 0xa5,
4468
+	0x27, 0x81, 0x88, 0xe2, 0xb9, 0x2a, 0x75, 0xab, 0x73, 0x29, 0x23, 0x19, 0x6b, 0x3b, 0x72, 0xad,
4469
+	0x31, 0xfe, 0x5d, 0xa8, 0xea, 0x2f, 0x9a, 0x34, 0xa1, 0x94, 0x8a, 0xd0, 0x4c, 0x95, 0xad, 0xf5,
4470
+	0xa7, 0xae, 0x87, 0x02, 0x45, 0x57, 0x26, 0x95, 0x9d, 0x4b, 0xe5, 0x53, 0x80, 0x1c, 0xf6, 0xdf,
4471
+	0x5c, 0x89, 0xff, 0x83, 0x05, 0xce, 0x7a, 0x18, 0xe1, 0x97, 0x15, 0x4d, 0x59, 0x2c, 0xa3, 0x59,
4472
+	0xc4, 0x84, 0xa9, 0xb3, 0x60, 0x21, 0x7b, 0x50, 0x09, 0xa4, 0x14, 0xeb, 0x86, 0x7d, 0xa7, 0x38,
4473
+	0xc9, 0xda, 0x07, 0xe8, 0x19, 0xc4, 0x52, 0xac, 0xa8, 0x46, 0x6d, 0xdf, 0x01, 0xc8, 0x8d, 0xd8,
4474
+	0x9d, 0x8f, 0xd9, 0xca, 0xb0, 0xe2, 0x92, 0x5c, 0x86, 0xca, 0x93, 0x60, 0xb1, 0x64, 0x26, 0x29,
4475
+	0xbd, 0xf9, 0xd4, 0xbe, 0x63, 0xf9, 0x3f, 0xdb, 0x50, 0x33, 0x93, 0x8d, 0xdc, 0x82, 0x9a, 0x9a,
4476
+	0x6c, 0x26, 0xa3, 0xd7, 0x57, 0xba, 0x86, 0x90, 0xfd, 0x6c, 0x64, 0x17, 0x72, 0x34, 0x54, 0x7a,
4477
+	0x74, 0x9b, 0x1c, 0xf3, 0x01, 0x5e, 0x9a, 0xb2, 0x99, 0x99, 0xcd, 0xea, 0x2a, 0xfa, 0x6c, 0x16,
4478
+	0xc5, 0x91, 0x8c, 0x78, 0x4c, 0xd1, 0x45, 0x6e, 0xad, 0xab, 0x2e, 0x2b, 0xc6, 0x2b, 0x45, 0xc6,
4479
+	0x57, 0x8b, 0x1e, 0x82, 0x57, 0x38, 0xe6, 0x35, 0x55, 0x7f, 0x58, 0xac, 0xda, 0x1c, 0xa9, 0xe8,
4480
+	0xf4, 0xc3, 0x92, 0xab, 0xf0, 0x2f, 0xf4, 0xbb, 0x0d, 0x90, 0x53, 0xbe, 0x79, 0xa7, 0xf8, 0x7f,
4481
+	0x59, 0x00, 0xa3, 0x04, 0x47, 0xce, 0x34, 0x50, 0x13, 0x6a, 0x33, 0x9a, 0xc7, 0x5c, 0xb0, 0x47,
4482
+	0xea, 0x73, 0x50, 0xf1, 0x0e, 0xf5, 0xb4, 0x4d, 0xb5, 0x39, 0x39, 0x00, 0x6f, 0xca, 0xd2, 0x50,
4483
+	0x44, 0x09, 0x0a, 0x66, 0x44, 0xdf, 0xc5, 0x9a, 0x72, 0x9e, 0x76, 0x3f, 0x47, 0x68, 0xad, 0x8a,
4484
+	0x31, 0xa4, 0x03, 0x9b, 0xec, 0x2c, 0xe1, 0x42, 0x9a, 0x53, 0xf4, 0x03, 0x78, 0x51, 0x3f, 0xa5,
4485
+	0x68, 0x57, 0x27, 0x51, 0x8f, 0xe5, 0x9b, 0xed, 0xbb, 0x50, 0x7f, 0x99, 0xf4, 0xad, 0x04, 0xba,
4486
+	0x06, 0x5e, 0x81, 0x1b, 0x81, 0xc7, 0x0a, 0xa8, 0x2b, 0xd4, 0x1b, 0xff, 0x29, 0xbe, 0x70, 0x66,
4487
+	0x16, 0x92, 0xf7, 0x01, 0x4e, 0xa4, 0x4c, 0x1e, 0xa9, 0xe1, 0x68, 0x0e, 0x71, 0xd1, 0xa2, 0x10,
4488
+	0x64, 0x17, 0x3c, 0xdc, 0xa4, 0xc6, 0xaf, 0x0f, 0x54, 0x11, 0xa9, 0x06, 0xbc, 0x07, 0xee, 0x2c,
4489
+	0x0b, 0xd7, 0x03, 0xd0, 0x99, 0xad, 0xa3, 0xdf, 0x05, 0x27, 0xe6, 0xc6, 0xa7, 0x67, 0x75, 0x2d,
4490
+	0xe6, 0xca, 0xe5, 0xdf, 0x84, 0xff, 0xbd, 0xf2, 0x1c, 0x93, 0x2b, 0x50, 0x9d, 0x45, 0x0b, 0xa9,
4491
+	0x3e, 0x09, 0x1c, 0xff, 0x66, 0xe7, 0xff, 0x6a, 0x01, 0xe4, 0xed, 0x8b, 0x8a, 0x60, 0x6f, 0x23,
4492
+	0x66, 0x53, 0xf7, 0xf2, 0x02, 0x9c, 0x53, 0x73, 0x2b, 0xe6, 0xae, 0xae, 0x9e, 0x6f, 0xf9, 0xf6,
4493
+	0xfa, 0xd2, 0x94, 0xa6, 0xfa, 0xc9, 0x7c, 0xfa, 0xfb, 0x5b, 0x3d, 0x99, 0xd9, 0x09, 0xdb, 0xf7,
4494
+	0xe1, 0xc2, 0x39, 0xba, 0x37, 0xfc, 0x1a, 0xf2, 0xce, 0x29, 0x5c, 0xd9, 0x8d, 0xcf, 0xc0, 0xcd,
4495
+	0x46, 0x39, 0x71, 0xa0, 0xdc, 0x1d, 0x7e, 0xd9, 0xaf, 0x6f, 0x10, 0x80, 0xea, 0x78, 0xd0, 0xa3,
4496
+	0x83, 0xa3, 0xba, 0x45, 0x6a, 0x50, 0x1a, 0x8f, 0x0f, 0xeb, 0x36, 0x71, 0xa1, 0xd2, 0x3b, 0xe8,
4497
+	0x1d, 0x0e, 0xea, 0x25, 0x5c, 0x1e, 0x3d, 0x78, 0x78, 0x6f, 0x5c, 0x2f, 0xdf, 0xb8, 0x0d, 0x17,
4498
+	0x5f, 0x9a, 0xcd, 0x2a, 0xfa, 0xf0, 0x80, 0x0e, 0x90, 0xc9, 0x83, 0xda, 0x43, 0x3a, 0x3c, 0x3e,
4499
+	0x38, 0x1a, 0xd4, 0x2d, 0x74, 0x7c, 0x31, 0xea, 0xdd, 0x1f, 0xf4, 0xeb, 0x76, 0xb7, 0xfe, 0xec,
4500
+	0xc5, 0x8e, 0xf5, 0xcb, 0x8b, 0x1d, 0xeb, 0x8f, 0x17, 0x3b, 0xd6, 0xf7, 0x7f, 0xee, 0x6c, 0x4c,
4501
+	0xaa, 0xea, 0x6f, 0xe2, 0xc7, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x42, 0x80, 0x11, 0x1b, 0x66,
4502
+	0x0a, 0x00, 0x00,
4494 4503
 }
... ...
@@ -15,7 +15,18 @@ message Op {
15 15
 		SourceOp source = 3;
16 16
 		CopyOp copy = 4;
17 17
 		BuildOp build = 5;
18
-	 }
18
+	}
19
+	Platform platform = 10;
20
+	WorkerConstraints constraints = 11;
21
+}
22
+
23
+// Platform is github.com/opencontainers/image-spec/specs-go/v1.Platform
24
+message Platform {
25
+	string Architecture = 1;
26
+	string OS = 2;
27
+	string Variant = 3;
28
+	string OSVersion = 4; // unused
29
+	repeated string OSFeatures = 5; // unused
19 30
 }
20 31
 
21 32
 // Input represents an input edge for an Op.
... ...
@@ -54,6 +65,7 @@ message Mount {
54 54
 	CacheOpt cacheOpt = 20;
55 55
 }
56 56
 
57
+// MountType defines a type of a mount from a supported set
57 58
 enum MountType {
58 59
 	BIND = 0;
59 60
 	SECRET = 1;
... ...
@@ -62,8 +74,22 @@ enum MountType {
62 62
 	TMPFS = 4;
63 63
 }
64 64
 
65
+// CacheOpt defines options specific to cache mounts
65 66
 message CacheOpt {
67
+	// ID is an optional namespace for the mount
66 68
 	string ID = 1;
69
+	// Sharing is the sharing mode for the mount 
70
+	CacheSharingOpt sharing = 2;
71
+}
72
+
73
+// CacheSharingOpt defines different sharing modes for cache mount
74
+enum CacheSharingOpt {
75
+	// SHARED cache mount can be used concurrently by multiple writers
76
+	SHARED = 0;
77
+	// PRIVATE creates a new mount if there are multiple writers
78
+	PRIVATE = 1;
79
+	// LOCKED pauses second writer until first one releases the mount
80
+	LOCKED = 2;
67 81
 }
68 82
 
69 83
 // CopyOp copies files across Ops.
... ...
@@ -106,8 +132,9 @@ message OpMetadata {
106 106
 	// ignore_cache specifies to ignore the cache for this Op.
107 107
 	bool ignore_cache = 1;
108 108
 	// Description can be used for keeping any text fields that builder doesn't parse
109
-	map<string, string> description = 2;  
110
-	WorkerConstraint worker_constraint = 3;
109
+	map<string, string> description = 2; 
110
+	// index 3 reserved for WorkerConstraint in previous versions
111
+	// WorkerConstraint worker_constraint = 3;
111 112
 	ExportCache export_cache = 4;
112 113
 }
113 114
 
... ...
@@ -122,8 +149,8 @@ message ProxyEnv {
122 122
 	string no_proxy = 4;
123 123
 }
124 124
 
125
-// WorkerConstraint is experimental and likely to be changed.
126
-message WorkerConstraint {
125
+// WorkerConstraints defines conditions for the worker
126
+message WorkerConstraints {
127 127
 	repeated string filter = 1; // containerd-style filter
128 128
 }
129 129
 
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/containerd/containerd/reference"
9 9
 	"github.com/moby/buildkit/solver/pb"
10 10
 	digest "github.com/opencontainers/go-digest"
11
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
11 12
 	"github.com/pkg/errors"
12 13
 )
13 14
 
... ...
@@ -50,11 +51,20 @@ func FromString(s string) (Identifier, error) {
50 50
 		return nil, errors.Wrapf(errNotFound, "unknown schema %s", parts[0])
51 51
 	}
52 52
 }
53
-func FromLLB(op *pb.Op_Source) (Identifier, error) {
53
+func FromLLB(op *pb.Op_Source, platform *pb.Platform) (Identifier, error) {
54 54
 	id, err := FromString(op.Source.Identifier)
55 55
 	if err != nil {
56 56
 		return nil, err
57 57
 	}
58
+	if id, ok := id.(*ImageIdentifier); ok && platform != nil {
59
+		id.Platform = &specs.Platform{
60
+			OS:           platform.OS,
61
+			Architecture: platform.Architecture,
62
+			Variant:      platform.Variant,
63
+			OSVersion:    platform.OSVersion,
64
+			OSFeatures:   platform.OSFeatures,
65
+		}
66
+	}
58 67
 	if id, ok := id.(*GitIdentifier); ok {
59 68
 		for k, v := range op.Source.Attrs {
60 69
 			switch k {
... ...
@@ -136,6 +146,7 @@ func FromLLB(op *pb.Op_Source) (Identifier, error) {
136 136
 
137 137
 type ImageIdentifier struct {
138 138
 	Reference reference.Spec
139
+	Platform  *specs.Platform
139 140
 }
140 141
 
141 142
 func NewImageIdentifier(str string) (*ImageIdentifier, error) {
... ...
@@ -10,7 +10,7 @@ import (
10 10
 	"github.com/containerd/containerd/reference"
11 11
 	"github.com/containerd/containerd/remotes"
12 12
 	digest "github.com/opencontainers/go-digest"
13
-	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
13
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
14 14
 	"github.com/pkg/errors"
15 15
 )
16 16
 
... ...
@@ -19,16 +19,18 @@ type IngesterProvider interface {
19 19
 	content.Provider
20 20
 }
21 21
 
22
-func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester IngesterProvider, platform string) (digest.Digest, []byte, error) {
23
-	if platform == "" {
24
-		platform = platforms.Default()
22
+func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester IngesterProvider, platform *specs.Platform) (digest.Digest, []byte, error) {
23
+	// TODO: fix containerd to take struct instead of string
24
+	platformStr := platforms.Default()
25
+	if platform != nil {
26
+		platformStr = platforms.Format(*platform)
25 27
 	}
26 28
 	ref, err := reference.Parse(str)
27 29
 	if err != nil {
28 30
 		return "", nil, errors.WithStack(err)
29 31
 	}
30 32
 
31
-	desc := ocispec.Descriptor{
33
+	desc := specs.Descriptor{
32 34
 		Digest: ref.Digest(),
33 35
 	}
34 36
 	if desc.Digest != "" {
... ...
@@ -56,12 +58,12 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
56 56
 
57 57
 	handlers := []images.Handler{
58 58
 		remotes.FetchHandler(ingester, fetcher),
59
-		childrenConfigHandler(ingester, platform),
59
+		childrenConfigHandler(ingester, platformStr),
60 60
 	}
61 61
 	if err := images.Dispatch(ctx, images.Handlers(handlers...), desc); err != nil {
62 62
 		return "", nil, err
63 63
 	}
64
-	config, err := images.Config(ctx, ingester, desc, platform)
64
+	config, err := images.Config(ctx, ingester, desc, platformStr)
65 65
 	if err != nil {
66 66
 		return "", nil, err
67 67
 	}
... ...
@@ -75,10 +77,10 @@ func Config(ctx context.Context, str string, resolver remotes.Resolver, ingester
75 75
 }
76 76
 
77 77
 func childrenConfigHandler(provider content.Provider, platform string) images.HandlerFunc {
78
-	return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
79
-		var descs []ocispec.Descriptor
78
+	return func(ctx context.Context, desc specs.Descriptor) ([]specs.Descriptor, error) {
79
+		var descs []specs.Descriptor
80 80
 		switch desc.MediaType {
81
-		case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
81
+		case images.MediaTypeDockerSchema2Manifest, specs.MediaTypeImageManifest:
82 82
 			p, err := content.ReadBlob(ctx, provider, desc)
83 83
 			if err != nil {
84 84
 				return nil, err
... ...
@@ -86,19 +88,19 @@ func childrenConfigHandler(provider content.Provider, platform string) images.Ha
86 86
 
87 87
 			// TODO(stevvooe): We just assume oci manifest, for now. There may be
88 88
 			// subtle differences from the docker version.
89
-			var manifest ocispec.Manifest
89
+			var manifest specs.Manifest
90 90
 			if err := json.Unmarshal(p, &manifest); err != nil {
91 91
 				return nil, err
92 92
 			}
93 93
 
94 94
 			descs = append(descs, manifest.Config)
95
-		case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
95
+		case images.MediaTypeDockerSchema2ManifestList, specs.MediaTypeImageIndex:
96 96
 			p, err := content.ReadBlob(ctx, provider, desc)
97 97
 			if err != nil {
98 98
 				return nil, err
99 99
 			}
100 100
 
101
-			var index ocispec.Index
101
+			var index specs.Index
102 102
 			if err := json.Unmarshal(p, &index); err != nil {
103 103
 				return nil, err
104 104
 			}
... ...
@@ -118,7 +120,7 @@ func childrenConfigHandler(provider content.Provider, platform string) images.Ha
118 118
 			} else {
119 119
 				descs = append(descs, index.Manifests...)
120 120
 			}
121
-		case images.MediaTypeDockerSchema2Config, ocispec.MediaTypeImageConfig:
121
+		case images.MediaTypeDockerSchema2Config, specs.MediaTypeImageConfig:
122 122
 			// childless data types.
123 123
 			return nil, nil
124 124
 		default:
... ...
@@ -129,7 +131,7 @@ func childrenConfigHandler(provider content.Provider, platform string) images.Ha
129 129
 	}
130 130
 }
131 131
 
132
-// ocispec.MediaTypeImageManifest, // TODO: detect schema1/manifest-list
132
+// specs.MediaTypeImageManifest, // TODO: detect schema1/manifest-list
133 133
 func DetectManifestMediaType(ra content.ReaderAt) (string, error) {
134 134
 	// TODO: schema1
135 135
 
... ...
@@ -6,7 +6,7 @@ github.com/davecgh/go-spew v1.1.0
6 6
 github.com/pmezard/go-difflib v1.0.0
7 7
 golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993
8 8
 
9
-github.com/containerd/containerd 63522d9eaa5a0443d225642c4b6f4f5fdedf932b
9
+github.com/containerd/containerd 08f7ee9828af1783dc98cc5cc1739e915697c667
10 10
 github.com/containerd/typeurl f6943554a7e7e88b3c14aad190bf05932da84788
11 11
 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
12 12
 github.com/sirupsen/logrus v1.0.0
... ...
@@ -11,16 +11,18 @@ import (
11 11
 	"github.com/moby/buildkit/frontend"
12 12
 	"github.com/moby/buildkit/solver"
13 13
 	digest "github.com/opencontainers/go-digest"
14
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
14 15
 )
15 16
 
16 17
 type Worker interface {
17 18
 	// ID needs to be unique in the cluster
18 19
 	ID() string
19 20
 	Labels() map[string]string
21
+	Platforms() []specs.Platform
20 22
 	LoadRef(id string) (cache.ImmutableRef, error)
21 23
 	// ResolveOp resolves Vertex.Sys() to Op implementation.
22 24
 	ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error)
23
-	ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error)
25
+	ResolveImageConfig(ctx context.Context, ref string, platform *specs.Platform) (digest.Digest, []byte, error)
24 26
 	// Exec is similar to executor.Exec but without []mount.Mount
25 27
 	Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error
26 28
 	DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error)
... ...
@@ -33,8 +35,6 @@ type Worker interface {
33 33
 // Pre-defined label keys
34 34
 const (
35 35
 	labelPrefix      = "org.mobyproject.buildkit.worker."
36
-	LabelOS          = labelPrefix + "os"          // GOOS
37
-	LabelArch        = labelPrefix + "arch"        // GOARCH
38 36
 	LabelExecutor    = labelPrefix + "executor"    // "oci" or "containerd"
39 37
 	LabelSnapshotter = labelPrefix + "snapshotter" // containerd snapshotter name ("overlay", "native", ...)
40 38
 	LabelHostname    = labelPrefix + "hostname"