Skip to content

Commit

Permalink
Merge pull request moby#37350 from tonistiigi/platform-support
Browse files Browse the repository at this point in the history
Fix platform struct passing
  • Loading branch information
tiborvass committed Jun 28, 2018
2 parents 68e25cf + 951faae commit 1436dc8
Show file tree
Hide file tree
Showing 67 changed files with 1,952 additions and 821 deletions.
14 changes: 10 additions & 4 deletions api/server/router/build/build_routes.go
Expand Up @@ -14,6 +14,7 @@ import (
"strings"
"sync"

"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
Expand Down Expand Up @@ -72,11 +73,16 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
options.RemoteContext = r.FormValue("remote")
if versions.GreaterThanOrEqualTo(version, "1.32") {
apiPlatform := r.FormValue("platform")
p := system.ParsePlatform(apiPlatform)
if err := system.ValidatePlatform(p); err != nil {
return nil, errdefs.InvalidParameter(errors.Errorf("invalid platform: %s", err))
if apiPlatform != "" {
sp, err := platforms.Parse(apiPlatform)
if err != nil {
return nil, err
}
if err := system.ValidatePlatform(sp); err != nil {
return nil, err
}
options.Platform = &sp
}
options.Platform = p.OS
}

if r.Form.Get("shmsize") != "" {
Expand Down
3 changes: 2 additions & 1 deletion api/server/router/image/backend.go
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)

// Backend is all the methods that need to be implemented
Expand All @@ -34,7 +35,7 @@ type importExportBackend interface {
}

type registryBackend interface {
PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
PullImage(ctx context.Context, image, tag string, platform *specs.Platform, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error
SearchRegistryForImages(ctx context.Context, filtersArgs string, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registry.SearchResults, error)
}
24 changes: 17 additions & 7 deletions api/server/router/image/image_routes.go
Expand Up @@ -4,11 +4,11 @@ import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"

"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/server/httputils"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
Expand Down Expand Up @@ -36,7 +36,7 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
message = r.Form.Get("message")
err error
output = ioutils.NewWriteFlusher(w)
platform = &specs.Platform{}
platform *specs.Platform
)
defer output.Close()

Expand All @@ -45,9 +45,15 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
version := httputils.VersionFromContext(ctx)
if versions.GreaterThanOrEqualTo(version, "1.32") {
apiPlatform := r.FormValue("platform")
platform = system.ParsePlatform(apiPlatform)
if err = system.ValidatePlatform(platform); err != nil {
err = fmt.Errorf("invalid platform: %s", err)
if apiPlatform != "" {
sp, err := platforms.Parse(apiPlatform)
if err != nil {
return err
}
if err := system.ValidatePlatform(sp); err != nil {
return err
}
platform = &sp
}
}

Expand All @@ -70,13 +76,17 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
authConfig = &types.AuthConfig{}
}
}
err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output)
err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output)
} else { //import
src := r.Form.Get("fromSrc")
// 'err' MUST NOT be defined within this block, we need any error
// generated from the download to be available to the output
// stream processing below
err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"])
os := ""
if platform != nil {
os = platform.OS
}
err = s.backend.ImportImage(src, repo, os, tag, message, r.Body, output, r.Form["changes"])
}
}
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion api/types/backend/build.go
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/streamformatter"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)

// PullOption defines different modes for accessing images
Expand Down Expand Up @@ -40,5 +41,5 @@ type GetImageAndLayerOptions struct {
PullOption PullOption
AuthConfig map[string]types.AuthConfig
Output io.Writer
OS string
Platform *specs.Platform
}
3 changes: 2 additions & 1 deletion api/types/client.go
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/go-units"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)

// CheckpointCreateOptions holds parameters to create a checkpoint from a container
Expand Down Expand Up @@ -180,7 +181,7 @@ type ImageBuildOptions struct {
ExtraHosts []string // List of extra hosts
Target string
SessionID string
Platform string
Platform *specs.Platform
// Version specifies the version of the unerlying builder to use
Version BuilderVersion
// BuildID is an optional identifier that can be passed together with the
Expand Down
33 changes: 21 additions & 12 deletions builder/builder-next/adapters/containerimage/pull.go
Expand Up @@ -113,7 +113,7 @@ func (is *imageSource) resolveLocal(refStr string) ([]byte, error) {
return img.RawJSON(), nil
}

func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error) {
func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) {
if preferLocal {
dt, err := is.resolveLocal(ref)
if err == nil {
Expand All @@ -126,7 +126,7 @@ func (is *imageSource) ResolveImageConfig(ctx context.Context, ref string) (dige
dt []byte
}
res, err := is.g.Do(ctx, ref, func(ctx context.Context) (interface{}, error) {
dgst, dt, err := imageutil.Config(ctx, ref, is.getResolver(ctx), is.ContentStore, "")
dgst, dt, err := imageutil.Config(ctx, ref, is.getResolver(ctx), is.ContentStore, platform)
if err != nil {
return nil, err
}
Expand All @@ -145,10 +145,16 @@ func (is *imageSource) Resolve(ctx context.Context, id source.Identifier) (sourc
return nil, errors.Errorf("invalid image identifier %v", id)
}

platform := platforms.DefaultSpec()
if imageIdentifier.Platform != nil {
platform = *imageIdentifier.Platform
}

p := &puller{
src: imageIdentifier,
is: is,
resolver: is.getResolver(ctx),
platform: platform,
}
return p, nil
}
Expand All @@ -163,17 +169,20 @@ type puller struct {
resolveErr error
resolver remotes.Resolver
config []byte
platform ocispec.Platform
}

func (p *puller) mainManifestKey(dgst digest.Digest) (digest.Digest, error) {
func (p *puller) mainManifestKey(dgst digest.Digest, platform ocispec.Platform) (digest.Digest, error) {
dt, err := json.Marshal(struct {
Digest digest.Digest
OS string
Arch string
Digest digest.Digest
OS string
Arch string
Variant string `json:",omitempty"`
}{
Digest: p.desc.Digest,
OS: runtime.GOOS,
Arch: runtime.GOARCH,
Digest: p.desc.Digest,
OS: platform.OS,
Arch: platform.Architecture,
Variant: platform.Variant,
})
if err != nil {
return "", err
Expand Down Expand Up @@ -248,7 +257,7 @@ func (p *puller) resolve(ctx context.Context) error {
return
}

_, dt, err := p.is.ResolveImageConfig(ctx, ref.String())
_, dt, err := p.is.ResolveImageConfig(ctx, ref.String(), &p.platform)
if err != nil {
p.resolveErr = err
resolveProgressDone(err)
Expand All @@ -266,7 +275,7 @@ func (p *puller) CacheKey(ctx context.Context, index int) (string, bool, error)
p.resolveLocal()

if p.desc.Digest != "" && index == 0 {
dgst, err := p.mainManifestKey(p.desc.Digest)
dgst, err := p.mainManifestKey(p.desc.Digest, p.platform)
if err != nil {
return "", false, err
}
Expand All @@ -282,7 +291,7 @@ func (p *puller) CacheKey(ctx context.Context, index int) (string, bool, error)
}

if p.desc.Digest != "" && index == 0 {
dgst, err := p.mainManifestKey(p.desc.Digest)
dgst, err := p.mainManifestKey(p.desc.Digest, p.platform)
if err != nil {
return "", false, err
}
Expand Down
5 changes: 5 additions & 0 deletions builder/builder-next/builder.go
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/containerd/containerd/content"
"github.com/containerd/containerd/platforms"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/builder"
Expand Down Expand Up @@ -208,6 +209,10 @@ func (b *Builder) Build(ctx context.Context, opt backend.BuildConfig) (*builder.
frontendAttrs["no-cache"] = ""
}

if opt.Options.Platform != nil {
frontendAttrs["platform"] = platforms.Format(*opt.Options.Platform)
}

exporterAttrs := map[string]string{}

if len(opt.Options.Tags) > 0 {
Expand Down
32 changes: 20 additions & 12 deletions builder/builder-next/worker/worker.go
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/containerd/containerd/content"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/rootfs"
"github.com/docker/docker/distribution"
distmetadata "github.com/docker/docker/distribution/metadata"
Expand Down Expand Up @@ -122,33 +123,40 @@ func (w *Worker) Labels() map[string]string {
return w.Opt.Labels
}

// Platforms returns one or more platforms supported by the image.
func (w *Worker) Platforms() []ocispec.Platform {
// does not handle lcow
return []ocispec.Platform{platforms.DefaultSpec()}
}

// LoadRef loads a reference by ID
func (w *Worker) LoadRef(id string) (cache.ImmutableRef, error) {
return w.CacheManager.Get(context.TODO(), id)
}

// ResolveOp converts a LLB vertex into a LLB operation
func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error) {
switch op := v.Sys().(type) {
case *pb.Op_Source:
return ops.NewSourceOp(v, op, w.SourceManager, w)
case *pb.Op_Exec:
return ops.NewExecOp(v, op, w.CacheManager, w.MetadataStore, w.Executor, w)
case *pb.Op_Build:
return ops.NewBuildOp(v, op, s, w)
default:
return nil, errors.Errorf("could not resolve %v", v)
if baseOp, ok := v.Sys().(*pb.Op); ok {
switch op := baseOp.Op.(type) {
case *pb.Op_Source:
return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, w)
case *pb.Op_Exec:
return ops.NewExecOp(v, op, w.CacheManager, w.MetadataStore, w.Executor, w)
case *pb.Op_Build:
return ops.NewBuildOp(v, op, s, w)
}
}
return nil, errors.Errorf("could not resolve %v", v)
}

// ResolveImageConfig returns image config for an image
func (w *Worker) ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error) {
func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error) {
// ImageSource is typically source/containerimage
resolveImageConfig, ok := w.ImageSource.(resolveImageConfig)
if !ok {
return "", nil, errors.Errorf("worker %q does not implement ResolveImageConfig", w.ID())
}
return resolveImageConfig.ResolveImageConfig(ctx, ref)
return resolveImageConfig.ResolveImageConfig(ctx, ref, platform)
}

// Exec executes a process directly on a worker
Expand Down Expand Up @@ -319,5 +327,5 @@ func oneOffProgress(ctx context.Context, id string) func(err error) error {
}

type resolveImageConfig interface {
ResolveImageConfig(ctx context.Context, ref string) (digest.Digest, []byte, error)
ResolveImageConfig(ctx context.Context, ref string, platform *ocispec.Platform) (digest.Digest, []byte, error)
}
7 changes: 0 additions & 7 deletions builder/dockerfile/builder.go
Expand Up @@ -104,13 +104,6 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
source = src
}

os := ""
apiPlatform := system.ParsePlatform(config.Options.Platform)
if apiPlatform.OS != "" {
os = apiPlatform.OS
}
config.Options.Platform = os

builderOptions := builderOptions{
Options: config.Options,
ProgressWriter: config.ProgressWriter,
Expand Down
13 changes: 10 additions & 3 deletions builder/dockerfile/copy.go
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/pkg/urlutil"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -72,7 +73,7 @@ type copier struct {
source builder.Source
pathCache pathCache
download sourceDownloader
platform string
platform *specs.Platform
// for cleanup. TODO: having copier.cleanup() is error prone and hard to
// follow. Code calling performCopy should manage the lifecycle of its params.
// Copier should take override source as input, not imageMount.
Expand All @@ -95,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)
separator := string(separator(o.platform))
// 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)
Expand Down

0 comments on commit 1436dc8

Please sign in to comment.