Add the OCI spec compatible image support in client side.
Signed-off-by: Dennis Chen <dennis.chen@arm.com>
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
"strings" |
| 15 | 15 |
"sync" |
| 16 | 16 |
|
| 17 |
+ "github.com/containerd/containerd/platforms" |
|
| 17 | 18 |
"github.com/docker/docker/api/server/httputils" |
| 18 | 19 |
"github.com/docker/docker/api/types" |
| 19 | 20 |
"github.com/docker/docker/api/types/backend" |
| ... | ... |
@@ -23,7 +24,6 @@ import ( |
| 23 | 23 |
"github.com/docker/docker/pkg/ioutils" |
| 24 | 24 |
"github.com/docker/docker/pkg/progress" |
| 25 | 25 |
"github.com/docker/docker/pkg/streamformatter" |
| 26 |
- "github.com/docker/docker/pkg/system" |
|
| 27 | 26 |
"github.com/docker/go-units" |
| 28 | 27 |
"github.com/pkg/errors" |
| 29 | 28 |
"github.com/sirupsen/logrus" |
| ... | ... |
@@ -72,11 +72,13 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui |
| 72 | 72 |
options.RemoteContext = r.FormValue("remote")
|
| 73 | 73 |
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
| 74 | 74 |
apiPlatform := r.FormValue("platform")
|
| 75 |
- p := system.ParsePlatform(apiPlatform) |
|
| 76 |
- if err := system.ValidatePlatform(p); err != nil {
|
|
| 77 |
- return nil, errdefs.InvalidParameter(errors.Errorf("invalid platform: %s", err))
|
|
| 75 |
+ if len(strings.TrimSpace(apiPlatform)) != 0 {
|
|
| 76 |
+ sp, err := platforms.Parse(apiPlatform) |
|
| 77 |
+ if err != nil {
|
|
| 78 |
+ return nil, err |
|
| 79 |
+ } |
|
| 80 |
+ options.Platform = sp |
|
| 78 | 81 |
} |
| 79 |
- options.Platform = p.OS |
|
| 80 | 82 |
} |
| 81 | 83 |
|
| 82 | 84 |
if r.Form.Get("shmsize") != "" {
|
| ... | ... |
@@ -4,11 +4,11 @@ import ( |
| 4 | 4 |
"context" |
| 5 | 5 |
"encoding/base64" |
| 6 | 6 |
"encoding/json" |
| 7 |
- "fmt" |
|
| 8 | 7 |
"net/http" |
| 9 | 8 |
"strconv" |
| 10 | 9 |
"strings" |
| 11 | 10 |
|
| 11 |
+ "github.com/containerd/containerd/platforms" |
|
| 12 | 12 |
"github.com/docker/docker/api/server/httputils" |
| 13 | 13 |
"github.com/docker/docker/api/types" |
| 14 | 14 |
"github.com/docker/docker/api/types/filters" |
| ... | ... |
@@ -16,7 +16,6 @@ import ( |
| 16 | 16 |
"github.com/docker/docker/errdefs" |
| 17 | 17 |
"github.com/docker/docker/pkg/ioutils" |
| 18 | 18 |
"github.com/docker/docker/pkg/streamformatter" |
| 19 |
- "github.com/docker/docker/pkg/system" |
|
| 20 | 19 |
"github.com/docker/docker/registry" |
| 21 | 20 |
specs "github.com/opencontainers/image-spec/specs-go/v1" |
| 22 | 21 |
"github.com/pkg/errors" |
| ... | ... |
@@ -45,9 +44,12 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite |
| 45 | 45 |
version := httputils.VersionFromContext(ctx) |
| 46 | 46 |
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
| 47 | 47 |
apiPlatform := r.FormValue("platform")
|
| 48 |
- platform = system.ParsePlatform(apiPlatform) |
|
| 49 |
- if err = system.ValidatePlatform(platform); err != nil {
|
|
| 50 |
- err = fmt.Errorf("invalid platform: %s", err)
|
|
| 48 |
+ if len(strings.TrimSpace(apiPlatform)) != 0 {
|
|
| 49 |
+ sp, err := platforms.Parse(apiPlatform) |
|
| 50 |
+ if err != nil {
|
|
| 51 |
+ return err |
|
| 52 |
+ } |
|
| 53 |
+ platform = &sp |
|
| 51 | 54 |
} |
| 52 | 55 |
} |
| 53 | 56 |
|
| ... | ... |
@@ -8,6 +8,7 @@ import ( |
| 8 | 8 |
"github.com/docker/docker/api/types/container" |
| 9 | 9 |
"github.com/docker/docker/api/types/filters" |
| 10 | 10 |
"github.com/docker/go-units" |
| 11 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 11 | 12 |
) |
| 12 | 13 |
|
| 13 | 14 |
// CheckpointCreateOptions holds parameters to create a checkpoint from a container |
| ... | ... |
@@ -180,7 +181,7 @@ type ImageBuildOptions struct {
|
| 180 | 180 |
ExtraHosts []string // List of extra hosts |
| 181 | 181 |
Target string |
| 182 | 182 |
SessionID string |
| 183 |
- Platform string |
|
| 183 |
+ Platform specs.Platform |
|
| 184 | 184 |
// Version specifies the version of the unerlying builder to use |
| 185 | 185 |
Version BuilderVersion |
| 186 | 186 |
// 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) ( |
| 104 | 104 |
source = src |
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 |
- os := "" |
|
| 108 |
- apiPlatform := system.ParsePlatform(config.Options.Platform) |
|
| 109 |
- if apiPlatform.OS != "" {
|
|
| 110 |
- os = apiPlatform.OS |
|
| 111 |
- } |
|
| 112 |
- config.Options.Platform = os |
|
| 113 |
- |
|
| 114 | 107 |
builderOptions := builderOptions{
|
| 115 | 108 |
Options: config.Options, |
| 116 | 109 |
ProgressWriter: config.ProgressWriter, |
| ... | ... |
@@ -24,6 +24,7 @@ import ( |
| 24 | 24 |
"github.com/docker/docker/pkg/streamformatter" |
| 25 | 25 |
"github.com/docker/docker/pkg/system" |
| 26 | 26 |
"github.com/docker/docker/pkg/urlutil" |
| 27 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 27 | 28 |
"github.com/pkg/errors" |
| 28 | 29 |
) |
| 29 | 30 |
|
| ... | ... |
@@ -72,7 +73,7 @@ type copier struct {
|
| 72 | 72 |
source builder.Source |
| 73 | 73 |
pathCache pathCache |
| 74 | 74 |
download sourceDownloader |
| 75 |
- platform string |
|
| 75 |
+ platform specs.Platform |
|
| 76 | 76 |
// for cleanup. TODO: having copier.cleanup() is error prone and hard to |
| 77 | 77 |
// follow. Code calling performCopy should manage the lifecycle of its params. |
| 78 | 78 |
// Copier should take override source as input, not imageMount. |
| ... | ... |
@@ -95,8 +96,8 @@ func (o *copier) createCopyInstruction(args []string, cmdName string) (copyInstr |
| 95 | 95 |
last := len(args) - 1 |
| 96 | 96 |
|
| 97 | 97 |
// Work in platform-specific filepath semantics |
| 98 |
- inst.dest = fromSlash(args[last], o.platform) |
|
| 99 |
- separator := string(separator(o.platform)) |
|
| 98 |
+ inst.dest = fromSlash(args[last], o.platform.OS) |
|
| 99 |
+ separator := string(separator(o.platform.OS)) |
|
| 100 | 100 |
infos, err := o.getCopyInfosForSourcePaths(args[0:last], inst.dest) |
| 101 | 101 |
if err != nil {
|
| 102 | 102 |
return inst, errors.Wrapf(err, "%s failed", cmdName) |
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
"sort" |
| 15 | 15 |
"strings" |
| 16 | 16 |
|
| 17 |
+ "github.com/containerd/containerd/platforms" |
|
| 17 | 18 |
"github.com/docker/docker/api" |
| 18 | 19 |
"github.com/docker/docker/api/types/container" |
| 19 | 20 |
"github.com/docker/docker/api/types/strslice" |
| ... | ... |
@@ -151,9 +152,11 @@ func (d *dispatchRequest) getImageMount(imageRefOrID string) (*imageMount, error |
| 151 | 151 |
// |
| 152 | 152 |
func initializeStage(d dispatchRequest, cmd *instructions.Stage) error {
|
| 153 | 153 |
d.builder.imageProber.Reset() |
| 154 |
- if err := system.ValidatePlatform(&cmd.Platform); err != nil {
|
|
| 154 |
+ //TODO(@arm64b): Leave the sanity check of the spec platform to the containerd code |
|
| 155 |
+ if err := platforms.ValidatePlatform(&cmd.Platform); err != nil {
|
|
| 155 | 156 |
return err |
| 156 | 157 |
} |
| 158 |
+ |
|
| 157 | 159 |
image, err := d.getFromImage(d.shlex, cmd.BaseName, cmd.Platform.OS) |
| 158 | 160 |
if err != nil {
|
| 159 | 161 |
return err |
| ... | ... |
@@ -223,10 +226,10 @@ func (d *dispatchRequest) getOsFromFlagsAndStage(stageOS string) string {
|
| 223 | 223 |
switch {
|
| 224 | 224 |
case stageOS != "": |
| 225 | 225 |
return stageOS |
| 226 |
- case d.builder.options.Platform != "": |
|
| 226 |
+ case d.builder.options.Platform.OS != "": |
|
| 227 | 227 |
// Note this is API "platform", but by this point, as the daemon is not |
| 228 | 228 |
// multi-arch aware yet, it is guaranteed to only hold the OS part here. |
| 229 |
- return d.builder.options.Platform |
|
| 229 |
+ return d.builder.options.Platform.OS |
|
| 230 | 230 |
default: |
| 231 | 231 |
return "" // Auto-select |
| 232 | 232 |
} |
| ... | ... |
@@ -456,7 +456,7 @@ func hostConfigFromOptions(options *types.ImageBuildOptions) *container.HostConf |
| 456 | 456 |
// is too small for builder scenarios where many users are |
| 457 | 457 |
// using RUN statements to install large amounts of data. |
| 458 | 458 |
// Use 127GB as that's the default size of a VHD in Hyper-V. |
| 459 |
- if runtime.GOOS == "windows" && options.Platform == "windows" {
|
|
| 459 |
+ if runtime.GOOS == "windows" && options.Platform.OS == "windows" {
|
|
| 460 | 460 |
hc.StorageOpt = make(map[string]string) |
| 461 | 461 |
hc.StorageOpt["size"] = "127GB" |
| 462 | 462 |
} |
| ... | ... |
@@ -422,8 +422,6 @@ func checkCompatibleOS(imageOS string) error {
|
| 422 | 422 |
return fmt.Errorf("cannot load %s image on %s", imageOS, runtime.GOOS)
|
| 423 | 423 |
} |
| 424 | 424 |
// Finally, check the image OS is supported for the platform. |
| 425 |
- if err := system.ValidatePlatform(system.ParsePlatform(imageOS)); err != nil {
|
|
| 426 |
- return fmt.Errorf("cannot load %s image on %s: %s", imageOS, runtime.GOOS, err)
|
|
| 427 |
- } |
|
| 425 |
+ // TODO(@arm64b): Leave this sanity check to the containerd code in the future |
|
| 428 | 426 |
return nil |
| 429 | 427 |
} |
| ... | ... |
@@ -1,62 +1,9 @@ |
| 1 | 1 |
package system // import "github.com/docker/docker/pkg/system" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "fmt" |
|
| 5 | 4 |
"runtime" |
| 6 |
- "strings" |
|
| 7 |
- |
|
| 8 |
- specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 9 | 5 |
) |
| 10 | 6 |
|
| 11 |
-// ValidatePlatform determines if a platform structure is valid. |
|
| 12 |
-// TODO This is a temporary function - can be replaced by parsing from |
|
| 13 |
-// https://github.com/containerd/containerd/pull/1403/files at a later date. |
|
| 14 |
-// @jhowardmsft |
|
| 15 |
-func ValidatePlatform(platform *specs.Platform) error {
|
|
| 16 |
- platform.Architecture = strings.ToLower(platform.Architecture) |
|
| 17 |
- platform.OS = strings.ToLower(platform.OS) |
|
| 18 |
- // Based on https://github.com/moby/moby/pull/34642#issuecomment-330375350, do |
|
| 19 |
- // not support anything except operating system. |
|
| 20 |
- if platform.Architecture != "" {
|
|
| 21 |
- return fmt.Errorf("invalid platform architecture %q", platform.Architecture)
|
|
| 22 |
- } |
|
| 23 |
- if platform.OS != "" {
|
|
| 24 |
- if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) {
|
|
| 25 |
- return fmt.Errorf("invalid platform os %q", platform.OS)
|
|
| 26 |
- } |
|
| 27 |
- } |
|
| 28 |
- if len(platform.OSFeatures) != 0 {
|
|
| 29 |
- return fmt.Errorf("invalid platform osfeatures %q", platform.OSFeatures)
|
|
| 30 |
- } |
|
| 31 |
- if platform.OSVersion != "" {
|
|
| 32 |
- return fmt.Errorf("invalid platform osversion %q", platform.OSVersion)
|
|
| 33 |
- } |
|
| 34 |
- if platform.Variant != "" {
|
|
| 35 |
- return fmt.Errorf("invalid platform variant %q", platform.Variant)
|
|
| 36 |
- } |
|
| 37 |
- return nil |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-// ParsePlatform parses a platform string in the format os[/arch[/variant] |
|
| 41 |
-// into an OCI image-spec platform structure. |
|
| 42 |
-// TODO This is a temporary function - can be replaced by parsing from |
|
| 43 |
-// https://github.com/containerd/containerd/pull/1403/files at a later date. |
|
| 44 |
-// @jhowardmsft |
|
| 45 |
-func ParsePlatform(in string) *specs.Platform {
|
|
| 46 |
- p := &specs.Platform{}
|
|
| 47 |
- elements := strings.SplitN(strings.ToLower(in), "/", 3) |
|
| 48 |
- if len(elements) == 3 {
|
|
| 49 |
- p.Variant = elements[2] |
|
| 50 |
- } |
|
| 51 |
- if len(elements) >= 2 {
|
|
| 52 |
- p.Architecture = elements[1] |
|
| 53 |
- } |
|
| 54 |
- if len(elements) >= 1 {
|
|
| 55 |
- p.OS = elements[0] |
|
| 56 |
- } |
|
| 57 |
- return p |
|
| 58 |
-} |
|
| 59 |
- |
|
| 60 | 7 |
// IsOSSupported determines if an operating system is supported by the host |
| 61 | 8 |
func IsOSSupported(os string) bool {
|
| 62 | 9 |
if strings.EqualFold(runtime.GOOS, os) {
|