Browse code

Builder: Review feedback

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2018/02/24 06:03:49
Showing 4 changed files
... ...
@@ -152,7 +152,10 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
152 152
 //
153 153
 func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
154 154
 	d.builder.imageProber.Reset()
155
-	image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.OperatingSystem)
155
+	if err := system.ValidatePlatform(&cmd.Platform); err != nil {
156
+		return err
157
+	}
158
+	image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.Platform.OS)
156 159
 	if err != nil {
157 160
 		return err
158 161
 	}
... ...
@@ -215,32 +218,29 @@ func (d *dispatchRequest) getExpandedImageName(shlex *shell.Lex, name string) (s
215 215
 // stagePlatform contains the value supplied by optional `--platform=` on
216 216
 // a current FROM statement. b.builder.options.Platform contains the operating
217 217
 // system part of the optional flag passed in the API call (or CLI flag
218
-// through `docker build --platform=...`).
219
-func (d *dispatchRequest) getOsFromFlagsAndStage(stagePlatform string) string {
220
-	osForPull := ""
221
-	// First, take the API platform if nothing provided on FROM
222
-	if stagePlatform == "" && d.builder.options.Platform != "" {
223
-		osForPull = d.builder.options.Platform
224
-	}
225
-	// Next, use the FROM flag if that was provided
226
-	if osForPull == "" && stagePlatform != "" {
227
-		osForPull = stagePlatform
228
-	}
229
-	// Finally, assume the host OS
230
-	if osForPull == "" {
231
-		osForPull = runtime.GOOS
232
-	}
233
-	return osForPull
218
+// through `docker build --platform=...`). Precedence is for an explicit
219
+// platform indication in the FROM statement.
220
+func (d *dispatchRequest) getOsFromFlagsAndStage(stageOS string) string {
221
+	switch {
222
+	case stageOS != "":
223
+		return stageOS
224
+	case d.builder.options.Platform != "":
225
+		// Note this is API "platform", but by this point, as the daemon is not
226
+		// multi-arch aware yet, it is guaranteed to only hold the OS part here.
227
+		return d.builder.options.Platform
228
+	default:
229
+		return runtime.GOOS
230
+	}
234 231
 }
235 232
 
236
-func (d *dispatchRequest) getImageOrStage(name string, stagePlatform string) (builder.Image, error) {
233
+func (d *dispatchRequest) getImageOrStage(name string, stageOS string) (builder.Image, error) {
237 234
 	var localOnly bool
238 235
 	if im, ok := d.stages.getByName(name); ok {
239 236
 		name = im.Image
240 237
 		localOnly = true
241 238
 	}
242 239
 
243
-	os := d.getOsFromFlagsAndStage(stagePlatform)
240
+	os := d.getOsFromFlagsAndStage(stageOS)
244 241
 
245 242
 	// Windows cannot support a container with no base image unless it is LCOW.
246 243
 	if name == api.NoBaseImageSpecifier {
... ...
@@ -267,12 +267,12 @@ func (d *dispatchRequest) getImageOrStage(name string, stagePlatform string) (bu
267 267
 	}
268 268
 	return imageMount.Image(), nil
269 269
 }
270
-func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, stagePlatform string) (builder.Image, error) {
270
+func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, stageOS string) (builder.Image, error) {
271 271
 	name, err := d.getExpandedImageName(shlex, name)
272 272
 	if err != nil {
273 273
 		return nil, err
274 274
 	}
275
-	return d.getImageOrStage(name, stagePlatform)
275
+	return d.getImageOrStage(name, stageOS)
276 276
 }
277 277
 
278 278
 func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
... ...
@@ -7,6 +7,7 @@ import (
7 7
 
8 8
 	"github.com/docker/docker/api/types/container"
9 9
 	"github.com/docker/docker/api/types/strslice"
10
+	specs "github.com/opencontainers/image-spec/specs-go/v1"
10 11
 )
11 12
 
12 13
 // KeyValuePair represent an arbitrary named value (useful in slice instead of map[string] string to preserve ordering)
... ...
@@ -357,11 +358,11 @@ type ShellCommand struct {
357 357
 
358 358
 // Stage represents a single stage in a multi-stage build
359 359
 type Stage struct {
360
-	Name            string
361
-	Commands        []Command
362
-	BaseName        string
363
-	SourceCode      string
364
-	OperatingSystem string
360
+	Name       string
361
+	Commands   []Command
362
+	BaseName   string
363
+	SourceCode string
364
+	Platform   specs.Platform
365 365
 }
366 366
 
367 367
 // AddCommand to the stage
... ...
@@ -276,23 +276,13 @@ func parseFrom(req parseRequest) (*Stage, error) {
276 276
 	if err := req.flags.Parse(); err != nil {
277 277
 		return nil, err
278 278
 	}
279
-	specPlatform := system.ParsePlatform(flPlatform.Value)
280
-	if err := system.ValidatePlatform(specPlatform); err != nil {
281
-		return nil, fmt.Errorf("invalid platform %q on FROM", flPlatform.Value)
282
-	}
283
-	if specPlatform.OS != "" && !system.IsOSSupported(specPlatform.OS) {
284
-		return nil, fmt.Errorf("unsupported platform %q on FROM", flPlatform.Value)
285
-	}
286
-	if err != nil {
287
-		return nil, err
288
-	}
289 279
 	code := strings.TrimSpace(req.original)
290 280
 	return &Stage{
291
-		BaseName:        req.args[0],
292
-		Name:            stageName,
293
-		SourceCode:      code,
294
-		Commands:        []Command{},
295
-		OperatingSystem: specPlatform.OS,
281
+		BaseName:   req.args[0],
282
+		Name:       stageName,
283
+		SourceCode: code,
284
+		Commands:   []Command{},
285
+		Platform:   *system.ParsePlatform(flPlatform.Value),
296 286
 	}, nil
297 287
 
298 288
 }
... ...
@@ -1,8 +1,6 @@
1 1
 package instructions // import "github.com/docker/docker/builder/dockerfile/instructions"
2 2
 
3 3
 import (
4
-	"fmt"
5
-	"runtime"
6 4
 	"strings"
7 5
 	"testing"
8 6
 
... ...
@@ -186,16 +184,6 @@ func TestErrorCases(t *testing.T) {
186 186
 			dockerfile:    `foo bar`,
187 187
 			expectedError: "unknown instruction: FOO",
188 188
 		},
189
-		{
190
-			name:          "Invalid platform",
191
-			dockerfile:    `FROM --platform=invalid busybox`,
192
-			expectedError: `invalid platform "invalid"`,
193
-		},
194
-		{
195
-			name:          "Only OS",
196
-			dockerfile:    fmt.Sprintf(`FROM --platform=%s/%s busybox`, runtime.GOOS, runtime.GOARCH),
197
-			expectedError: `invalid platform`,
198
-		},
199 189
 	}
200 190
 	for _, c := range cases {
201 191
 		r := strings.NewReader(c.dockerfile)