Browse code

Builder - add --platform to FROM statement

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

John Howard authored on 2017/10/04 07:34:33
Showing 4 changed files
... ...
@@ -148,7 +148,7 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
148 148
 	return d.builder.imageSources.Get(imageRefOrID, localOnly)
149 149
 }
150 150
 
151
-// FROM imagename[:tag | @digest] [AS build-stage-name]
151
+// FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
152 152
 //
153 153
 func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
154 154
 	d.builder.imageProber.Reset()
... ...
@@ -357,10 +357,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
360
+	Name            string
361
+	Commands        []Command
362
+	BaseName        string
363
+	SourceCode      string
364
+	OperatingSystem string
364 365
 }
365 366
 
366 367
 // AddCommand to the stage
... ...
@@ -3,6 +3,7 @@ package instructions // import "github.com/docker/docker/builder/dockerfile/inst
3 3
 import (
4 4
 	"fmt"
5 5
 	"regexp"
6
+	"runtime"
6 7
 	"sort"
7 8
 	"strconv"
8 9
 	"strings"
... ...
@@ -12,6 +13,7 @@ import (
12 12
 	"github.com/docker/docker/api/types/strslice"
13 13
 	"github.com/docker/docker/builder/dockerfile/command"
14 14
 	"github.com/docker/docker/builder/dockerfile/parser"
15
+	"github.com/docker/docker/pkg/system"
15 16
 	"github.com/pkg/errors"
16 17
 )
17 18
 
... ...
@@ -271,16 +273,31 @@ func parseFrom(req parseRequest) (*Stage, error) {
271 271
 		return nil, err
272 272
 	}
273 273
 
274
+	flPlatform := req.flags.AddString("platform", "")
274 275
 	if err := req.flags.Parse(); err != nil {
275 276
 		return nil, err
276 277
 	}
278
+	specPlatform := system.ParsePlatform(flPlatform.Value)
279
+	if specPlatform.OS == "" {
280
+		specPlatform.OS = runtime.GOOS
281
+	}
282
+	if err := system.ValidatePlatform(specPlatform); err != nil {
283
+		return nil, fmt.Errorf("invalid platform %q on FROM", flPlatform.Value)
284
+	}
285
+	if !system.IsOSSupported(specPlatform.OS) {
286
+		return nil, fmt.Errorf("unsupported platform %q on FROM", flPlatform.Value)
287
+	}
288
+	if err != nil {
289
+		return nil, err
290
+	}
277 291
 	code := strings.TrimSpace(req.original)
278
-
292
+	fmt.Println("JJH", specPlatform.OS)
279 293
 	return &Stage{
280
-		BaseName:   req.args[0],
281
-		Name:       stageName,
282
-		SourceCode: code,
283
-		Commands:   []Command{},
294
+		BaseName:        req.args[0],
295
+		Name:            stageName,
296
+		SourceCode:      code,
297
+		Commands:        []Command{},
298
+		OperatingSystem: specPlatform.OS,
284 299
 	}, nil
285 300
 
286 301
 }
... ...
@@ -1,6 +1,8 @@
1 1
 package instructions // import "github.com/docker/docker/builder/dockerfile/instructions"
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"runtime"
4 6
 	"strings"
5 7
 	"testing"
6 8
 
... ...
@@ -184,6 +186,16 @@ func TestErrorCases(t *testing.T) {
184 184
 			dockerfile:    `foo bar`,
185 185
 			expectedError: "unknown instruction: FOO",
186 186
 		},
187
+		{
188
+			name:          "Invalid platform",
189
+			dockerfile:    `FROM --platform=invalid busybox`,
190
+			expectedError: `invalid platform "invalid"`,
191
+		},
192
+		{
193
+			name:          "Only OS",
194
+			dockerfile:    fmt.Sprintf(`FROM --platform=%s/%s busybox`, runtime.GOOS, runtime.GOARCH),
195
+			expectedError: `invalid platform`,
196
+		},
187 197
 	}
188 198
 	for _, c := range cases {
189 199
 		r := strings.NewReader(c.dockerfile)
... ...
@@ -196,5 +208,4 @@ func TestErrorCases(t *testing.T) {
196 196
 		_, err = ParseInstruction(n)
197 197
 		testutil.ErrorContains(t, err, c.expectedError)
198 198
 	}
199
-
200 199
 }