mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Builder: Plumbing through platform in FROM
statement
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
parent
7f0c2d23e1
commit
69fa84bc3d
7 changed files with 53 additions and 40 deletions
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -104,7 +103,7 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
|
|||
source = src
|
||||
}
|
||||
|
||||
os := runtime.GOOS
|
||||
os := ""
|
||||
apiPlatform := system.ParsePlatform(config.Options.Platform)
|
||||
if apiPlatform.OS != "" {
|
||||
os = apiPlatform.OS
|
||||
|
|
|
@ -145,14 +145,14 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
|
|||
imageRefOrID = stage.Image
|
||||
localOnly = true
|
||||
}
|
||||
return d.builder.imageSources.Get(imageRefOrID, localOnly)
|
||||
return d.builder.imageSources.Get(imageRefOrID, localOnly, d.state.baseImage.OperatingSystem())
|
||||
}
|
||||
|
||||
// FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
|
||||
//
|
||||
func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
|
||||
d.builder.imageProber.Reset()
|
||||
image, err := d.getFromImage(d.shlex, cmd.BaseName)
|
||||
image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.OperatingSystem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -210,20 +210,44 @@ func (d *dispatchRequest) getExpandedImageName(shlex *shell.Lex, name string) (s
|
|||
}
|
||||
return name, nil
|
||||
}
|
||||
func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
|
||||
|
||||
// getOsFromFlagsAndStage calculates the operating system if we need to pull an image.
|
||||
// stagePlatform contains the value supplied by optional `--platform=` on
|
||||
// a current FROM statement. b.builder.options.Platform contains the operating
|
||||
// system part of the optional flag passed in the API call (or CLI flag
|
||||
// through `docker build --platform=...`).
|
||||
func (d *dispatchRequest) getOsFromFlagsAndStage(stagePlatform string) string {
|
||||
osForPull := ""
|
||||
// First, take the API platform if nothing provided on FROM
|
||||
if stagePlatform == "" && d.builder.options.Platform != "" {
|
||||
osForPull = d.builder.options.Platform
|
||||
}
|
||||
// Next, use the FROM flag if that was provided
|
||||
if osForPull == "" && stagePlatform != "" {
|
||||
osForPull = stagePlatform
|
||||
}
|
||||
// Finally, assume the host OS
|
||||
if osForPull == "" {
|
||||
osForPull = runtime.GOOS
|
||||
}
|
||||
return osForPull
|
||||
}
|
||||
|
||||
func (d *dispatchRequest) getImageOrStage(name string, stagePlatform string) (builder.Image, error) {
|
||||
var localOnly bool
|
||||
if im, ok := d.stages.getByName(name); ok {
|
||||
name = im.Image
|
||||
localOnly = true
|
||||
}
|
||||
|
||||
os := d.getOsFromFlagsAndStage(stagePlatform)
|
||||
|
||||
// Windows cannot support a container with no base image unless it is LCOW.
|
||||
if name == api.NoBaseImageSpecifier {
|
||||
imageImage := &image.Image{}
|
||||
imageImage.OS = runtime.GOOS
|
||||
if runtime.GOOS == "windows" {
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
switch optionsOS {
|
||||
switch os {
|
||||
case "windows", "":
|
||||
return nil, errors.New("Windows does not support FROM scratch")
|
||||
case "linux":
|
||||
|
@ -232,23 +256,23 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
|
|||
}
|
||||
imageImage.OS = "linux"
|
||||
default:
|
||||
return nil, errors.Errorf("operating system %q is not supported", optionsOS)
|
||||
return nil, errors.Errorf("operating system %q is not supported", os)
|
||||
}
|
||||
}
|
||||
return builder.Image(imageImage), nil
|
||||
}
|
||||
imageMount, err := d.builder.imageSources.Get(name, localOnly)
|
||||
imageMount, err := d.builder.imageSources.Get(name, localOnly, os)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return imageMount.Image(), nil
|
||||
}
|
||||
func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string) (builder.Image, error) {
|
||||
func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, stagePlatform string) (builder.Image, error) {
|
||||
name, err := d.getExpandedImageName(shlex, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.getImageOrStage(name)
|
||||
return d.getImageOrStage(name, stagePlatform)
|
||||
}
|
||||
|
||||
func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
|
||||
|
@ -264,8 +288,7 @@ func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
|
|||
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
var err error
|
||||
baseImageOS := system.ParsePlatform(d.state.operatingSystem).OS
|
||||
runConfig.WorkingDir, err = normalizeWorkdir(baseImageOS, runConfig.WorkingDir, c.Path)
|
||||
runConfig.WorkingDir, err = normalizeWorkdir(d.state.baseImage.OperatingSystem(), runConfig.WorkingDir, c.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -281,7 +304,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
|||
}
|
||||
|
||||
comment := "WORKDIR " + runConfig.WorkingDir
|
||||
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, baseImageOS))
|
||||
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.state.baseImage.OperatingSystem()))
|
||||
containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd)
|
||||
if err != nil || containerID == "" {
|
||||
return err
|
||||
|
@ -316,7 +339,7 @@ func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
|||
return system.ErrNotSupportedOperatingSystem
|
||||
}
|
||||
stateRunConfig := d.state.runConfig
|
||||
cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.state.operatingSystem)
|
||||
cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.state.baseImage.OperatingSystem())
|
||||
buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env)
|
||||
|
||||
saveCmd := cmdFromArgs
|
||||
|
@ -397,8 +420,7 @@ func prependEnvOnCmd(buildArgs *buildArgs, buildArgVars []string, cmd strslice.S
|
|||
//
|
||||
func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS)
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.baseImage.OperatingSystem())
|
||||
runConfig.Cmd = cmd
|
||||
// set config as already being escaped, this prevents double escaping on windows
|
||||
runConfig.ArgsEscaped = true
|
||||
|
@ -441,8 +463,7 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
|
|||
//
|
||||
func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS)
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.state.baseImage.OperatingSystem())
|
||||
runConfig.Entrypoint = cmd
|
||||
if !d.state.cmdSet {
|
||||
runConfig.Cmd = nil
|
||||
|
|
|
@ -225,6 +225,7 @@ func TestWorkdir(t *testing.T) {
|
|||
func TestCmd(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
sb.state.baseImage = &mockImage{}
|
||||
command := "./executable"
|
||||
|
||||
cmd := &instructions.CmdCommand{
|
||||
|
@ -282,6 +283,7 @@ func TestHealthcheckCmd(t *testing.T) {
|
|||
func TestEntrypoint(t *testing.T) {
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
sb.state.baseImage = &mockImage{}
|
||||
entrypointCmd := "/usr/sbin/nginx"
|
||||
|
||||
cmd := &instructions.EntrypointCommand{
|
||||
|
@ -357,6 +359,7 @@ func TestStopSignal(t *testing.T) {
|
|||
}
|
||||
b := newBuilderWithMockBackend()
|
||||
sb := newDispatchRequest(b, '`', nil, newBuildArgs(make(map[string]*string)), newStagesBuildResults())
|
||||
sb.state.baseImage = &mockImage{}
|
||||
signal := "SIGKILL"
|
||||
|
||||
cmd := &instructions.StopSignalCommand{
|
||||
|
|
|
@ -37,8 +37,7 @@ import (
|
|||
|
||||
func dispatch(d dispatchRequest, cmd instructions.Command) (err error) {
|
||||
if c, ok := cmd.(instructions.PlatformSpecific); ok {
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
err := c.CheckPlatform(optionsOS)
|
||||
err := c.CheckPlatform(d.state.baseImage.OperatingSystem())
|
||||
if err != nil {
|
||||
return errdefs.InvalidParameter(err)
|
||||
}
|
||||
|
|
|
@ -6,13 +6,12 @@ import (
|
|||
"github.com/docker/docker/api/types/backend"
|
||||
"github.com/docker/docker/builder"
|
||||
dockerimage "github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type getAndMountFunc func(string, bool) (builder.Image, builder.ROLayer, error)
|
||||
type getAndMountFunc func(string, bool, string) (builder.Image, builder.ROLayer, error)
|
||||
|
||||
// imageSources mounts images and provides a cache for mounted images. It tracks
|
||||
// all images so they can be unmounted at the end of the build.
|
||||
|
@ -23,7 +22,7 @@ type imageSources struct {
|
|||
}
|
||||
|
||||
func newImageSources(ctx context.Context, options builderOptions) *imageSources {
|
||||
getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ROLayer, error) {
|
||||
getAndMount := func(idOrRef string, localOnly bool, osForPull string) (builder.Image, builder.ROLayer, error) {
|
||||
pullOption := backend.PullOptionNoPull
|
||||
if !localOnly {
|
||||
if options.Options.PullParent {
|
||||
|
@ -32,12 +31,11 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
|
|||
pullOption = backend.PullOptionPreferLocal
|
||||
}
|
||||
}
|
||||
optionsPlatform := system.ParsePlatform(options.Options.Platform)
|
||||
return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{
|
||||
PullOption: pullOption,
|
||||
AuthConfig: options.Options.AuthConfigs,
|
||||
Output: options.ProgressWriter.Output,
|
||||
OS: optionsPlatform.OS,
|
||||
OS: osForPull,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -47,12 +45,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
|
|||
}
|
||||
}
|
||||
|
||||
func (m *imageSources) Get(idOrRef string, localOnly bool) (*imageMount, error) {
|
||||
func (m *imageSources) Get(idOrRef string, localOnly bool, osForPull string) (*imageMount, error) {
|
||||
if im, ok := m.byImageID[idOrRef]; ok {
|
||||
return im, nil
|
||||
}
|
||||
|
||||
image, layer, err := m.getImage(idOrRef, localOnly)
|
||||
image, layer, err := m.getImage(idOrRef, localOnly, osForPull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package instructions // import "github.com/docker/docker/builder/dockerfile/inst
|
|||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -278,20 +277,16 @@ func parseFrom(req parseRequest) (*Stage, error) {
|
|||
return nil, err
|
||||
}
|
||||
specPlatform := system.ParsePlatform(flPlatform.Value)
|
||||
if specPlatform.OS == "" {
|
||||
specPlatform.OS = runtime.GOOS
|
||||
}
|
||||
if err := system.ValidatePlatform(specPlatform); err != nil {
|
||||
return nil, fmt.Errorf("invalid platform %q on FROM", flPlatform.Value)
|
||||
}
|
||||
if !system.IsOSSupported(specPlatform.OS) {
|
||||
if specPlatform.OS != "" && !system.IsOSSupported(specPlatform.OS) {
|
||||
return nil, fmt.Errorf("unsupported platform %q on FROM", flPlatform.Value)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code := strings.TrimSpace(req.original)
|
||||
fmt.Println("JJH", specPlatform.OS)
|
||||
return &Stage{
|
||||
BaseName: req.args[0],
|
||||
Name: stageName,
|
||||
|
|
|
@ -83,8 +83,7 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
|
|||
return errors.New("Please provide a source image with `from` prior to commit")
|
||||
}
|
||||
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, optionsPlatform.OS))
|
||||
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, dispatchState.baseImage.OperatingSystem()))
|
||||
hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd)
|
||||
if err != nil || hit {
|
||||
return err
|
||||
|
@ -164,16 +163,15 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
|
|||
commentStr := fmt.Sprintf("%s %s%s in %s ", inst.cmdName, chownComment, srcHash, inst.dest)
|
||||
|
||||
// TODO: should this have been using origPaths instead of srcHash in the comment?
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
runConfigWithCommentCmd := copyRunConfig(
|
||||
state.runConfig,
|
||||
withCmdCommentString(commentStr, optionsPlatform.OS))
|
||||
withCmdCommentString(commentStr, state.baseImage.OperatingSystem()))
|
||||
hit, err := b.probeCache(state, runConfigWithCommentCmd)
|
||||
if err != nil || hit {
|
||||
return err
|
||||
}
|
||||
|
||||
imageMount, err := b.imageSources.Get(state.imageID, true)
|
||||
imageMount, err := b.imageSources.Get(state.imageID, true, state.baseImage.OperatingSystem())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
|
||||
}
|
||||
|
@ -184,7 +182,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
|
|||
}
|
||||
defer rwLayer.Release()
|
||||
|
||||
destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, rwLayer, b.options.Platform)
|
||||
destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, rwLayer, state.baseImage.OperatingSystem())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue