Sfoglia il codice sorgente

Initial support for OCI multi-platform image

Add the OCI spec compatible image support in client side.

Signed-off-by: Dennis Chen <dennis.chen@arm.com>
Dennis Chen 7 anni fa
parent
commit
7f334d3acf

+ 7 - 5
api/server/router/build/build_routes.go

@@ -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"
@@ -23,7 +24,6 @@ import (
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/progress"
 	"github.com/docker/docker/pkg/streamformatter"
-	"github.com/docker/docker/pkg/system"
 	"github.com/docker/go-units"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -72,11 +72,13 @@ 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 len(strings.TrimSpace(apiPlatform)) != 0 {
+			sp, err := platforms.Parse(apiPlatform)
+			if err != nil {
+				return nil, err
+			}
+			options.Platform = sp
 		}
-		options.Platform = p.OS
 	}
 
 	if r.Form.Get("shmsize") != "" {

+ 7 - 5
api/server/router/image/image_routes.go

@@ -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"
@@ -16,7 +16,6 @@ import (
 	"github.com/docker/docker/errdefs"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/streamformatter"
-	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/registry"
 	specs "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
@@ -45,9 +44,12 @@ 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 len(strings.TrimSpace(apiPlatform)) != 0 {
+			sp, err := platforms.Parse(apiPlatform)
+			if err != nil {
+				return err
+			}
+			platform = &sp
 		}
 	}
 

+ 2 - 1
api/types/client.go

@@ -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
@@ -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

+ 0 - 7
builder/dockerfile/builder.go

@@ -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,

+ 4 - 3
builder/dockerfile/copy.go

@@ -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"
 )
 
@@ -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.
@@ -95,8 +96,8 @@ 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))
+	inst.dest = fromSlash(args[last], o.platform.OS)
+	separator := string(separator(o.platform.OS))
 	infos, err := o.getCopyInfosForSourcePaths(args[0:last], inst.dest)
 	if err != nil {
 		return inst, errors.Wrapf(err, "%s failed", cmdName)

+ 6 - 3
builder/dockerfile/dispatchers.go

@@ -14,6 +14,7 @@ import (
 	"sort"
 	"strings"
 
+	"github.com/containerd/containerd/platforms"
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types/container"
 	"github.com/docker/docker/api/types/strslice"
@@ -151,9 +152,11 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error
 //
 func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
 	d.builder.imageProber.Reset()
-	if err := system.ValidatePlatform(&cmd.Platform); err != nil {
+	//TODO(@arm64b): Leave the sanity check of the spec platform to the containerd code
+	if err := platforms.ValidatePlatform(&cmd.Platform); err != nil {
 		return err
 	}
+
 	image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.Platform.OS)
 	if err != nil {
 		return err
@@ -223,10 +226,10 @@ func (d *dispatchRequest) getOsFromFlagsAndStage(stageOS string) string {
 	switch {
 	case stageOS != "":
 		return stageOS
-	case d.builder.options.Platform != "":
+	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
+		return d.builder.options.Platform.OS
 	default:
 		return "" // Auto-select
 	}

+ 1 - 1
builder/dockerfile/internals.go

@@ -456,7 +456,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 == "windows" {
+	if runtime.GOOS == "windows" && options.Platform.OS == "windows" {
 		hc.StorageOpt = make(map[string]string)
 		hc.StorageOpt["size"] = "127GB"
 	}

+ 1 - 3
image/tarexport/load.go

@@ -422,8 +422,6 @@ func checkCompatibleOS(imageOS string) error {
 		return fmt.Errorf("cannot load %s image on %s", imageOS, runtime.GOOS)
 	}
 	// Finally, check the image OS is supported for the platform.
-	if err := system.ValidatePlatform(system.ParsePlatform(imageOS)); err != nil {
-		return fmt.Errorf("cannot load %s image on %s: %s", imageOS, runtime.GOOS, err)
-	}
+	// TODO(@arm64b): Leave this sanity check to the containerd code in the future
 	return nil
 }

+ 0 - 53
pkg/system/lcow.go

@@ -1,62 +1,9 @@
 package system // import "github.com/docker/docker/pkg/system"
 
 import (
-	"fmt"
 	"runtime"
-	"strings"
-
-	specs "github.com/opencontainers/image-spec/specs-go/v1"
 )
 
-// ValidatePlatform determines if a platform structure is valid.
-// TODO This is a temporary function - can be replaced by parsing from
-// https://github.com/containerd/containerd/pull/1403/files at a later date.
-// @jhowardmsft
-func ValidatePlatform(platform *specs.Platform) error {
-	platform.Architecture = strings.ToLower(platform.Architecture)
-	platform.OS = strings.ToLower(platform.OS)
-	// Based on https://github.com/moby/moby/pull/34642#issuecomment-330375350, do
-	// not support anything except operating system.
-	if platform.Architecture != "" {
-		return fmt.Errorf("invalid platform architecture %q", platform.Architecture)
-	}
-	if platform.OS != "" {
-		if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) {
-			return fmt.Errorf("invalid platform os %q", platform.OS)
-		}
-	}
-	if len(platform.OSFeatures) != 0 {
-		return fmt.Errorf("invalid platform osfeatures %q", platform.OSFeatures)
-	}
-	if platform.OSVersion != "" {
-		return fmt.Errorf("invalid platform osversion %q", platform.OSVersion)
-	}
-	if platform.Variant != "" {
-		return fmt.Errorf("invalid platform variant %q", platform.Variant)
-	}
-	return nil
-}
-
-// ParsePlatform parses a platform string in the format os[/arch[/variant]
-// into an OCI image-spec platform structure.
-// TODO This is a temporary function - can be replaced by parsing from
-// https://github.com/containerd/containerd/pull/1403/files at a later date.
-// @jhowardmsft
-func ParsePlatform(in string) *specs.Platform {
-	p := &specs.Platform{}
-	elements := strings.SplitN(strings.ToLower(in), "/", 3)
-	if len(elements) == 3 {
-		p.Variant = elements[2]
-	}
-	if len(elements) >= 2 {
-		p.Architecture = elements[1]
-	}
-	if len(elements) >= 1 {
-		p.OS = elements[0]
-	}
-	return p
-}
-
 // IsOSSupported determines if an operating system is supported by the host
 func IsOSSupported(os string) bool {
 	if strings.EqualFold(runtime.GOOS, os) {