Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -5,7 +5,6 @@ import ( |
| 5 | 5 |
"fmt" |
| 6 | 6 |
"io" |
| 7 | 7 |
"io/ioutil" |
| 8 |
- "runtime" |
|
| 9 | 8 |
"strings" |
| 10 | 9 |
"time" |
| 11 | 10 |
|
| ... | ... |
@@ -104,7 +103,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) ( |
| 104 | 104 |
source = src |
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 |
- os := runtime.GOOS |
|
| 107 |
+ os := "" |
|
| 108 | 108 |
apiPlatform := system.ParsePlatform(config.Options.Platform) |
| 109 | 109 |
if apiPlatform.OS != "" {
|
| 110 | 110 |
os = apiPlatform.OS |
| ... | ... |
@@ -145,14 +145,14 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error |
| 145 | 145 |
imageRefOrID = stage.Image |
| 146 | 146 |
localOnly = true |
| 147 | 147 |
} |
| 148 |
- return d.builder.imageSources.Get(imageRefOrID, localOnly) |
|
| 148 |
+ return d.builder.imageSources.Get(imageRefOrID, localOnly, d.state.baseImage.OperatingSystem()) |
|
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 | 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() |
| 155 |
- image, err := d.getFromImage(d.shlex, cmd.BaseName) |
|
| 155 |
+ image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.OperatingSystem) |
|
| 156 | 156 |
if err != nil {
|
| 157 | 157 |
return err |
| 158 | 158 |
} |
| ... | ... |
@@ -210,20 +210,44 @@ func (d *dispatchRequest) getExpandedImageName(shlex *shell.Lex, name string) (s |
| 210 | 210 |
} |
| 211 | 211 |
return name, nil |
| 212 | 212 |
} |
| 213 |
-func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
|
|
| 213 |
+ |
|
| 214 |
+// getOsFromFlagsAndStage calculates the operating system if we need to pull an image. |
|
| 215 |
+// stagePlatform contains the value supplied by optional `--platform=` on |
|
| 216 |
+// a current FROM statement. b.builder.options.Platform contains the operating |
|
| 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 |
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+func (d *dispatchRequest) getImageOrStage(name string, stagePlatform string) (builder.Image, error) {
|
|
| 214 | 237 |
var localOnly bool |
| 215 | 238 |
if im, ok := d.stages.getByName(name); ok {
|
| 216 | 239 |
name = im.Image |
| 217 | 240 |
localOnly = true |
| 218 | 241 |
} |
| 219 | 242 |
|
| 243 |
+ os := d.getOsFromFlagsAndStage(stagePlatform) |
|
| 244 |
+ |
|
| 220 | 245 |
// Windows cannot support a container with no base image unless it is LCOW. |
| 221 | 246 |
if name == api.NoBaseImageSpecifier {
|
| 222 | 247 |
imageImage := &image.Image{}
|
| 223 | 248 |
imageImage.OS = runtime.GOOS |
| 224 | 249 |
if runtime.GOOS == "windows" {
|
| 225 |
- optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
| 226 |
- switch optionsOS {
|
|
| 250 |
+ switch os {
|
|
| 227 | 251 |
case "windows", "": |
| 228 | 252 |
return nil, errors.New("Windows does not support FROM scratch")
|
| 229 | 253 |
case "linux": |
| ... | ... |
@@ -232,23 +256,23 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
|
| 232 | 232 |
} |
| 233 | 233 |
imageImage.OS = "linux" |
| 234 | 234 |
default: |
| 235 |
- return nil, errors.Errorf("operating system %q is not supported", optionsOS)
|
|
| 235 |
+ return nil, errors.Errorf("operating system %q is not supported", os)
|
|
| 236 | 236 |
} |
| 237 | 237 |
} |
| 238 | 238 |
return builder.Image(imageImage), nil |
| 239 | 239 |
} |
| 240 |
- imageMount, err := d.builder.imageSources.Get(name, localOnly) |
|
| 240 |
+ imageMount, err := d.builder.imageSources.Get(name, localOnly, os) |
|
| 241 | 241 |
if err != nil {
|
| 242 | 242 |
return nil, err |
| 243 | 243 |
} |
| 244 | 244 |
return imageMount.Image(), nil |
| 245 | 245 |
} |
| 246 |
-func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string) (builder.Image, error) {
|
|
| 246 |
+func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, stagePlatform string) (builder.Image, error) {
|
|
| 247 | 247 |
name, err := d.getExpandedImageName(shlex, name) |
| 248 | 248 |
if err != nil {
|
| 249 | 249 |
return nil, err |
| 250 | 250 |
} |
| 251 |
- return d.getImageOrStage(name) |
|
| 251 |
+ return d.getImageOrStage(name, stagePlatform) |
|
| 252 | 252 |
} |
| 253 | 253 |
|
| 254 | 254 |
func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
|
| ... | ... |
@@ -264,8 +288,7 @@ func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
|
| 264 | 264 |
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
| 265 | 265 |
runConfig := d.state.runConfig |
| 266 | 266 |
var err error |
| 267 |
- baseImageOS := system.ParsePlatform(d.state.operatingSystem).OS |
|
| 268 |
- runConfig.WorkingDir, err = normalizeWorkdir(baseImageOS, runConfig.WorkingDir, c.Path) |
|
| 267 |
+ runConfig.WorkingDir, err = normalizeWorkdir(d.state.baseImage.OperatingSystem(), runConfig.WorkingDir, c.Path) |
|
| 269 | 268 |
if err != nil {
|
| 270 | 269 |
return err |
| 271 | 270 |
} |
| ... | ... |
@@ -281,7 +304,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
| 281 | 281 |
} |
| 282 | 282 |
|
| 283 | 283 |
comment := "WORKDIR " + runConfig.WorkingDir |
| 284 |
- runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, baseImageOS)) |
|
| 284 |
+ runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.state.baseImage.OperatingSystem())) |
|
| 285 | 285 |
containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd) |
| 286 | 286 |
if err != nil || containerID == "" {
|
| 287 | 287 |
return err |
| ... | ... |
@@ -316,7 +339,7 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
| 316 | 316 |
return system.ErrNotSupportedOperatingSystem |
| 317 | 317 |
} |
| 318 | 318 |
stateRunConfig := d.state.runConfig |
| 319 |
- cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.state.operatingSystem) |
|
| 319 |
+ cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.state.baseImage.OperatingSystem()) |
|
| 320 | 320 |
buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env) |
| 321 | 321 |
|
| 322 | 322 |
saveCmd := cmdFromArgs |
| ... | ... |
@@ -397,8 +420,7 @@ func prependEnvOnCmd(buildArgs *buildArgs, buildArgVars []string, cmd strslice.S |
| 397 | 397 |
// |
| 398 | 398 |
func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
|
| 399 | 399 |
runConfig := d.state.runConfig |
| 400 |
- optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
| 401 |
- cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS) |
|
| 400 |
+ cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.baseImage.OperatingSystem()) |
|
| 402 | 401 |
runConfig.Cmd = cmd |
| 403 | 402 |
// set config as already being escaped, this prevents double escaping on windows |
| 404 | 403 |
runConfig.ArgsEscaped = true |
| ... | ... |
@@ -441,8 +463,7 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand) |
| 441 | 441 |
// |
| 442 | 442 |
func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error {
|
| 443 | 443 |
runConfig := d.state.runConfig |
| 444 |
- optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
| 445 |
- cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS) |
|
| 444 |
+ cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.baseImage.OperatingSystem()) |
|
| 446 | 445 |
runConfig.Entrypoint = cmd |
| 447 | 446 |
if !d.state.cmdSet {
|
| 448 | 447 |
runConfig.Cmd = nil |
| ... | ... |
@@ -225,6 +225,7 @@ func TestWorkdir(t *testing.T) {
|
| 225 | 225 |
func TestCmd(t *testing.T) {
|
| 226 | 226 |
b := newBuilderWithMockBackend() |
| 227 | 227 |
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults()) |
| 228 |
+ sb.state.baseImage = &mockImage{}
|
|
| 228 | 229 |
command := "./executable" |
| 229 | 230 |
|
| 230 | 231 |
cmd := &instructions.CmdCommand{
|
| ... | ... |
@@ -282,6 +283,7 @@ func TestHealthcheckCmd(t *testing.T) {
|
| 282 | 282 |
func TestEntrypoint(t *testing.T) {
|
| 283 | 283 |
b := newBuilderWithMockBackend() |
| 284 | 284 |
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults()) |
| 285 |
+ sb.state.baseImage = &mockImage{}
|
|
| 285 | 286 |
entrypointCmd := "/usr/sbin/nginx" |
| 286 | 287 |
|
| 287 | 288 |
cmd := &instructions.EntrypointCommand{
|
| ... | ... |
@@ -357,6 +359,7 @@ func TestStopSignal(t *testing.T) {
|
| 357 | 357 |
} |
| 358 | 358 |
b := newBuilderWithMockBackend() |
| 359 | 359 |
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults()) |
| 360 |
+ sb.state.baseImage = &mockImage{}
|
|
| 360 | 361 |
signal := "SIGKILL" |
| 361 | 362 |
|
| 362 | 363 |
cmd := &instructions.StopSignalCommand{
|
| ... | ... |
@@ -37,8 +37,7 @@ import ( |
| 37 | 37 |
|
| 38 | 38 |
func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
|
| 39 | 39 |
if c, ok := cmd.(instructions.PlatformSpecific); ok {
|
| 40 |
- optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
| 41 |
- err := c.CheckPlatform(optionsOS) |
|
| 40 |
+ err := c.CheckPlatform(d.state.baseImage.OperatingSystem()) |
|
| 42 | 41 |
if err != nil {
|
| 43 | 42 |
return errdefs.InvalidParameter(err) |
| 44 | 43 |
} |
| ... | ... |
@@ -6,13 +6,12 @@ import ( |
| 6 | 6 |
"github.com/docker/docker/api/types/backend" |
| 7 | 7 |
"github.com/docker/docker/builder" |
| 8 | 8 |
dockerimage "github.com/docker/docker/image" |
| 9 |
- "github.com/docker/docker/pkg/system" |
|
| 10 | 9 |
"github.com/pkg/errors" |
| 11 | 10 |
"github.com/sirupsen/logrus" |
| 12 | 11 |
"golang.org/x/net/context" |
| 13 | 12 |
) |
| 14 | 13 |
|
| 15 |
-type getAndMountFunc func(string, bool) (builder.Image, builder.ROLayer, error) |
|
| 14 |
+type getAndMountFunc func(string, bool, string) (builder.Image, builder.ROLayer, error) |
|
| 16 | 15 |
|
| 17 | 16 |
// imageSources mounts images and provides a cache for mounted images. It tracks |
| 18 | 17 |
// all images so they can be unmounted at the end of the build. |
| ... | ... |
@@ -23,7 +22,7 @@ type imageSources struct {
|
| 23 | 23 |
} |
| 24 | 24 |
|
| 25 | 25 |
func newImageSources(ctx context.Context, options builderOptions) *imageSources {
|
| 26 |
- getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ROLayer, error) {
|
|
| 26 |
+ getAndMount := func(idOrRef string, localOnly bool, osForPull string) (builder.Image, builder.ROLayer, error) {
|
|
| 27 | 27 |
pullOption := backend.PullOptionNoPull |
| 28 | 28 |
if !localOnly {
|
| 29 | 29 |
if options.Options.PullParent {
|
| ... | ... |
@@ -32,12 +31,11 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources |
| 32 | 32 |
pullOption = backend.PullOptionPreferLocal |
| 33 | 33 |
} |
| 34 | 34 |
} |
| 35 |
- optionsPlatform := system.ParsePlatform(options.Options.Platform) |
|
| 36 | 35 |
return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{
|
| 37 | 36 |
PullOption: pullOption, |
| 38 | 37 |
AuthConfig: options.Options.AuthConfigs, |
| 39 | 38 |
Output: options.ProgressWriter.Output, |
| 40 |
- OS: optionsPlatform.OS, |
|
| 39 |
+ OS: osForPull, |
|
| 41 | 40 |
}) |
| 42 | 41 |
} |
| 43 | 42 |
|
| ... | ... |
@@ -47,12 +45,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources |
| 47 | 47 |
} |
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 |
-func (m *imageSources) Get(idOrRef string, localOnly bool) (*imageMount, error) {
|
|
| 50 |
+func (m *imageSources) Get(idOrRef string, localOnly bool, osForPull string) (*imageMount, error) {
|
|
| 51 | 51 |
if im, ok := m.byImageID[idOrRef]; ok {
|
| 52 | 52 |
return im, nil |
| 53 | 53 |
} |
| 54 | 54 |
|
| 55 |
- image, layer, err := m.getImage(idOrRef, localOnly) |
|
| 55 |
+ image, layer, err := m.getImage(idOrRef, localOnly, osForPull) |
|
| 56 | 56 |
if err != nil {
|
| 57 | 57 |
return nil, err |
| 58 | 58 |
} |
| ... | ... |
@@ -3,7 +3,6 @@ package instructions // import "github.com/docker/docker/builder/dockerfile/inst |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"regexp" |
| 6 |
- "runtime" |
|
| 7 | 6 |
"sort" |
| 8 | 7 |
"strconv" |
| 9 | 8 |
"strings" |
| ... | ... |
@@ -278,20 +277,16 @@ func parseFrom(req parseRequest) (*Stage, error) {
|
| 278 | 278 |
return nil, err |
| 279 | 279 |
} |
| 280 | 280 |
specPlatform := system.ParsePlatform(flPlatform.Value) |
| 281 |
- if specPlatform.OS == "" {
|
|
| 282 |
- specPlatform.OS = runtime.GOOS |
|
| 283 |
- } |
|
| 284 | 281 |
if err := system.ValidatePlatform(specPlatform); err != nil {
|
| 285 | 282 |
return nil, fmt.Errorf("invalid platform %q on FROM", flPlatform.Value)
|
| 286 | 283 |
} |
| 287 |
- if !system.IsOSSupported(specPlatform.OS) {
|
|
| 284 |
+ if specPlatform.OS != "" && !system.IsOSSupported(specPlatform.OS) {
|
|
| 288 | 285 |
return nil, fmt.Errorf("unsupported platform %q on FROM", flPlatform.Value)
|
| 289 | 286 |
} |
| 290 | 287 |
if err != nil {
|
| 291 | 288 |
return nil, err |
| 292 | 289 |
} |
| 293 | 290 |
code := strings.TrimSpace(req.original) |
| 294 |
- fmt.Println("JJH", specPlatform.OS)
|
|
| 295 | 291 |
return &Stage{
|
| 296 | 292 |
BaseName: req.args[0], |
| 297 | 293 |
Name: stageName, |
| ... | ... |
@@ -83,8 +83,7 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
|
| 83 | 83 |
return errors.New("Please provide a source image with `from` prior to commit")
|
| 84 | 84 |
} |
| 85 | 85 |
|
| 86 |
- optionsPlatform := system.ParsePlatform(b.options.Platform) |
|
| 87 |
- runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, optionsPlatform.OS)) |
|
| 86 |
+ runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, dispatchState.baseImage.OperatingSystem())) |
|
| 88 | 87 |
hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd) |
| 89 | 88 |
if err != nil || hit {
|
| 90 | 89 |
return err |
| ... | ... |
@@ -164,16 +163,15 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error |
| 164 | 164 |
commentStr := fmt.Sprintf("%s %s%s in %s ", inst.cmdName, chownComment, srcHash, inst.dest)
|
| 165 | 165 |
|
| 166 | 166 |
// TODO: should this have been using origPaths instead of srcHash in the comment? |
| 167 |
- optionsPlatform := system.ParsePlatform(b.options.Platform) |
|
| 168 | 167 |
runConfigWithCommentCmd := copyRunConfig( |
| 169 | 168 |
state.runConfig, |
| 170 |
- withCmdCommentString(commentStr, optionsPlatform.OS)) |
|
| 169 |
+ withCmdCommentString(commentStr, state.baseImage.OperatingSystem())) |
|
| 171 | 170 |
hit, err := b.probeCache(state, runConfigWithCommentCmd) |
| 172 | 171 |
if err != nil || hit {
|
| 173 | 172 |
return err |
| 174 | 173 |
} |
| 175 | 174 |
|
| 176 |
- imageMount, err := b.imageSources.Get(state.imageID, true) |
|
| 175 |
+ imageMount, err := b.imageSources.Get(state.imageID, true, state.baseImage.OperatingSystem()) |
|
| 177 | 176 |
if err != nil {
|
| 178 | 177 |
return errors.Wrapf(err, "failed to get destination image %q", state.imageID) |
| 179 | 178 |
} |
| ... | ... |
@@ -184,7 +182,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error |
| 184 | 184 |
} |
| 185 | 185 |
defer rwLayer.Release() |
| 186 | 186 |
|
| 187 |
- destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, rwLayer, b.options.Platform) |
|
| 187 |
+ destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, rwLayer, state.baseImage.OperatingSystem()) |
|
| 188 | 188 |
if err != nil {
|
| 189 | 189 |
return err |
| 190 | 190 |
} |