distribution: fix passing platform struct to puller

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Upstream-commit: 337ba71fc1124603302e28d94e2f08674e31a756
Component: engine
This commit is contained in:
Tonis Tiigi
2018-06-27 14:59:31 -07:00
parent 28ca1b95b2
commit b67a507422
19 changed files with 208 additions and 128 deletions
+8 -2
View File
@@ -96,8 +96,14 @@ func (o *copier) createCopyInstruction(args []string, cmdName string) (copyInstr
last := len(args) - 1
// Work in platform-specific filepath semantics
inst.dest = fromSlash(args[last], o.platform.OS)
separator := string(separator(o.platform.OS))
// TODO: This OS switch for paths is NOT correct and should not be supported.
// Maintained for backwards compatibility
pathOS := runtime.GOOS
if o.platform != nil {
pathOS = o.platform.OS
}
inst.dest = fromSlash(args[last], pathOS)
separator := string(separator(pathOS))
infos, err := o.getCopyInfosForSourcePaths(args[0:last], inst.dest)
if err != nil {
return inst, errors.Wrapf(err, "%s failed", cmdName)
@@ -28,6 +28,7 @@ import (
"github.com/moby/buildkit/frontend/dockerfile/instructions"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"github.com/moby/buildkit/frontend/dockerfile/shell"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
@@ -103,7 +104,7 @@ func dispatchAdd(d dispatchRequest, c *instructions.AddCommand) error {
copyInstruction.chownStr = c.Chown
copyInstruction.allowLocalDecompression = true
return d.builder.performCopy(d.state, copyInstruction)
return d.builder.performCopy(d, copyInstruction)
}
// COPY foo /path
@@ -127,7 +128,7 @@ func dispatchCopy(d dispatchRequest, c *instructions.CopyCommand) error {
}
copyInstruction.chownStr = c.Chown
return d.builder.performCopy(d.state, copyInstruction)
return d.builder.performCopy(d, copyInstruction)
}
func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error) {
@@ -145,7 +146,7 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
imageRefOrID = stage.Image
localOnly = true
}
return d.builder.imageSources.Get(imageRefOrID, localOnly, d.state.operatingSystem)
return d.builder.imageSources.Get(imageRefOrID, localOnly, d.builder.options.Platform)
}
// FROM [--platform=platform] imagename[:tag | @digest] [AS build-stage-name]
@@ -153,22 +154,21 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
d.builder.imageProber.Reset()
// TODO: pass *platform instead, allow autodetect
platform := platforms.DefaultSpec()
var platform *specs.Platform
if v := cmd.Platform; v != "" {
// TODO:
// v, err := shlex.ProcessWord(v, toEnvList(metaArgs, nil))
// if err != nil {
// return nil, nil, errors.Wrapf(err, "failed to process arguments for platform %s", v)
// }
v, err := d.getExpandedString(d.shlex, v)
if err != nil {
return errors.Wrapf(err, "failed to process arguments for platform %s", v)
}
p, err := platforms.Parse(v)
if err != nil {
return errors.Wrapf(err, "failed to parse platform %s", v)
}
platform = p
platform = &p
}
image, err := d.getFromImage(d.shlex, cmd.BaseName, platform.OS)
image, err := d.getFromImage(d.shlex, cmd.BaseName, platform)
if err != nil {
return err
}
@@ -214,82 +214,72 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
return nil
}
func (d *dispatchRequest) getExpandedImageName(shlex *shell.Lex, name string) (string, error) {
func (d *dispatchRequest) getExpandedString(shlex *shell.Lex, str string) (string, error) {
substitutionArgs := []string{}
for key, value := range d.state.buildArgs.GetAllMeta() {
substitutionArgs = append(substitutionArgs, key+"="+value)
}
name, err := shlex.ProcessWord(name, substitutionArgs)
name, err := shlex.ProcessWord(str, substitutionArgs)
if err != nil {
return "", err
}
return name, nil
}
// 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=...`). Precedence is for an explicit
// platform indication in the FROM statement.
func (d *dispatchRequest) getOsFromFlagsAndStage(stageOS string) string {
switch {
case stageOS != "":
return stageOS
case d.builder.options.Platform.OS != "":
// Note this is API "platform", but by this point, as the daemon is not
// multi-arch aware yet, it is guaranteed to only hold the OS part here.
return d.builder.options.Platform.OS
default:
return "" // Auto-select
}
}
func (d *dispatchRequest) getImageOrStage(name string, stageOS string) (builder.Image, error) {
func (d *dispatchRequest) getImageOrStage(name string, platform *specs.Platform) (builder.Image, error) {
var localOnly bool
if im, ok := d.stages.getByName(name); ok {
name = im.Image
localOnly = true
}
os := d.getOsFromFlagsAndStage(stageOS)
if platform == nil {
platform = d.builder.options.Platform
}
// Windows cannot support a container with no base image unless it is LCOW.
if name == api.NoBaseImageSpecifier {
p := platforms.DefaultSpec()
if platform != nil {
p = *platform
}
imageImage := &image.Image{}
imageImage.OS = runtime.GOOS
imageImage.OS = p.OS
// old windows scratch handling
// TODO: scratch should not have an os. It should be nil image.
// Windows supports scratch. What is not supported is running containers
// from it.
if runtime.GOOS == "windows" {
switch os {
case "windows":
return nil, errors.New("Windows does not support FROM scratch")
case "linux", "":
if platform == nil || platform.OS == "linux" {
if !system.LCOWSupported() {
return nil, errors.New("Linux containers are not supported on this system")
}
imageImage.OS = "linux"
default:
return nil, errors.Errorf("operating system %q is not supported", os)
} else if platform.OS == "windows" {
return nil, errors.New("Windows does not support FROM scratch")
} else {
return nil, errors.Errorf("platform %s is not supported", platforms.Format(p))
}
}
return builder.Image(imageImage), nil
}
imageMount, err := d.builder.imageSources.Get(name, localOnly, os)
imageMount, err := d.builder.imageSources.Get(name, localOnly, platform)
if err != nil {
return nil, err
}
return imageMount.Image(), nil
}
func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, stageOS string) (builder.Image, error) {
name, err := d.getExpandedImageName(shlex, name)
func (d *dispatchRequest) getFromImage(shlex *shell.Lex, name string, platform *specs.Platform) (builder.Image, error) {
name, err := d.getExpandedString(shlex, name)
if err != nil {
return nil, err
}
return d.getImageOrStage(name, stageOS)
return d.getImageOrStage(name, platform)
}
func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
d.state.runConfig.OnBuild = append(d.state.runConfig.OnBuild, c.Expression)
return d.builder.commit(d.state, "ONBUILD "+c.Expression)
}
@@ -6,6 +6,7 @@ import (
"runtime"
"testing"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
@@ -22,15 +23,17 @@ import (
func newBuilderWithMockBackend() *Builder {
mockBackend := &MockBackend{}
defaultPlatform := platforms.DefaultSpec()
opts := &types.ImageBuildOptions{Platform: &defaultPlatform}
ctx := context.Background()
b := &Builder{
options: &types.ImageBuildOptions{Platform: runtime.GOOS},
options: opts,
docker: mockBackend,
Stdout: new(bytes.Buffer),
clientCtx: ctx,
disableCommit: true,
imageSources: newImageSources(ctx, builderOptions{
Options: &types.ImageBuildOptions{Platform: runtime.GOOS},
Options: opts,
Backend: mockBackend,
}),
imageProber: newImageProber(mockBackend, nil, false),
@@ -7,11 +7,12 @@ import (
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/builder"
dockerimage "github.com/docker/docker/image"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
type getAndMountFunc func(string, bool, string) (builder.Image, builder.ROLayer, error)
type getAndMountFunc func(string, bool, *specs.Platform) (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.
@@ -22,7 +23,7 @@ type imageSources struct {
}
func newImageSources(ctx context.Context, options builderOptions) *imageSources {
getAndMount := func(idOrRef string, localOnly bool, osForPull string) (builder.Image, builder.ROLayer, error) {
getAndMount := func(idOrRef string, localOnly bool, platform *specs.Platform) (builder.Image, builder.ROLayer, error) {
pullOption := backend.PullOptionNoPull
if !localOnly {
if options.Options.PullParent {
@@ -35,7 +36,7 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
PullOption: pullOption,
AuthConfig: options.Options.AuthConfigs,
Output: options.ProgressWriter.Output,
OS: osForPull,
Platform: platform,
})
}
@@ -45,12 +46,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
}
}
func (m *imageSources) Get(idOrRef string, localOnly bool, osForPull string) (*imageMount, error) {
func (m *imageSources) Get(idOrRef string, localOnly bool, platform *specs.Platform) (*imageMount, error) {
if im, ok := m.byImageID[idOrRef]; ok {
return im, nil
}
image, layer, err := m.getImage(idOrRef, localOnly, osForPull)
image, layer, err := m.getImage(idOrRef, localOnly, platform)
if err != nil {
return nil, err
}
@@ -150,7 +150,8 @@ func (b *Builder) exportImage(state *dispatchState, layer builder.RWLayer, paren
return nil
}
func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error {
func (b *Builder) performCopy(req dispatchRequest, inst copyInstruction) error {
state := req.state
srcHash := getSourceHashFromInfos(inst.infos)
var chownComment string
@@ -168,7 +169,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
return err
}
imageMount, err := b.imageSources.Get(state.imageID, true, state.operatingSystem)
imageMount, err := b.imageSources.Get(state.imageID, true, req.builder.options.Platform)
if err != nil {
return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
}
@@ -456,7 +457,7 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf
// is too small for builder scenarios where many users are
// using RUN statements to install large amounts of data.
// Use 127GB as that's the default size of a VHD in Hyper-V.
if runtime.GOOS == "windows" && options.Platform.OS == "windows" {
if runtime.GOOS == "windows" && options.Platform != nil && options.Platform.OS == "windows" {
hc.StorageOpt = make(map[string]string)
hc.StorageOpt["size"] = "127GB"
}