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>
This commit is contained in:
parent
35193c0e7d
commit
7f334d3acf
9 changed files with 28 additions and 81 deletions
|
@ -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") != "" {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue