Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -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) |