mirror of
https://github.com/moby/moby.git
synced 2026-01-11 18:51:37 +00:00
client: support multiple platforms on save and load
We don't yet support this at the API level, so for now it returns an error when trying to set multiple, but this makes sure that the client types are already ready for this. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@@ -248,6 +248,10 @@ func (ir *imageRouter) getImagesGet(ctx context.Context, w http.ResponseWriter,
|
||||
|
||||
var platform *ocispec.Platform
|
||||
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.48") {
|
||||
if formPlatforms := r.Form["platform"]; len(formPlatforms) > 1 {
|
||||
// TODO(thaJeztah): remove once we support multiple platforms: see https://github.com/moby/moby/issues/48759
|
||||
return errdefs.InvalidParameter(errors.New("multiple platform parameters not supported"))
|
||||
}
|
||||
if formPlatform := r.Form.Get("platform"); formPlatform != "" {
|
||||
p, err := httputils.DecodePlatform(formPlatform)
|
||||
if err != nil {
|
||||
@@ -273,6 +277,10 @@ func (ir *imageRouter) postImagesLoad(ctx context.Context, w http.ResponseWriter
|
||||
|
||||
var platform *ocispec.Platform
|
||||
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.48") {
|
||||
if formPlatforms := r.Form["platform"]; len(formPlatforms) > 1 {
|
||||
// TODO(thaJeztah): remove once we support multiple platforms: see https://github.com/moby/moby/issues/48759
|
||||
return errdefs.InvalidParameter(errors.New("multiple platform parameters not supported"))
|
||||
}
|
||||
if formPlatform := r.Form.Get("platform"); formPlatform != "" {
|
||||
p, err := httputils.DecodePlatform(formPlatform)
|
||||
if err != nil {
|
||||
|
||||
@@ -98,12 +98,14 @@ type LoadOptions struct {
|
||||
// Quiet suppresses progress output
|
||||
Quiet bool
|
||||
|
||||
// Platform is a specific platform to load when the image is a multi-platform
|
||||
Platform *ocispec.Platform
|
||||
// Platforms selects the platforms to load if the image is a
|
||||
// multi-platform image and has multiple variants.
|
||||
Platforms []ocispec.Platform
|
||||
}
|
||||
|
||||
// SaveOptions holds parameters to save images.
|
||||
type SaveOptions struct {
|
||||
// Platform is a specific platform to save if the image is a multi-platform image.
|
||||
Platform *ocispec.Platform
|
||||
// Platforms selects the platforms to save if the image is a
|
||||
// multi-platform image and has multiple variants.
|
||||
Platforms []ocispec.Platform
|
||||
}
|
||||
|
||||
@@ -22,16 +22,16 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.Lo
|
||||
if opts.Quiet {
|
||||
query.Set("quiet", "1")
|
||||
}
|
||||
if opts.Platform != nil {
|
||||
if len(opts.Platforms) > 0 {
|
||||
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
|
||||
return image.LoadResponse{}, err
|
||||
}
|
||||
|
||||
p, err := encodePlatform(opts.Platform)
|
||||
p, err := encodePlatforms(opts.Platforms...)
|
||||
if err != nil {
|
||||
return image.LoadResponse{}, err
|
||||
}
|
||||
query.Set("platform", p)
|
||||
query["platform"] = p
|
||||
}
|
||||
|
||||
resp, err := cli.postRaw(ctx, "/images/load", query, input, http.Header{
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestImageLoad(t *testing.T) {
|
||||
tests := []struct {
|
||||
doc string
|
||||
quiet bool
|
||||
platform *ocispec.Platform
|
||||
platforms []ocispec.Platform
|
||||
responseContentType string
|
||||
expectedResponseJSON bool
|
||||
expectedQueryParams url.Values
|
||||
@@ -59,7 +59,7 @@ func TestImageLoad(t *testing.T) {
|
||||
},
|
||||
{
|
||||
doc: "json with platform",
|
||||
platform: &ocispec.Platform{Architecture: "arm64", OS: "linux", Variant: "v8"},
|
||||
platforms: []ocispec.Platform{{Architecture: "arm64", OS: "linux", Variant: "v8"}},
|
||||
responseContentType: "application/json",
|
||||
expectedResponseJSON: true,
|
||||
expectedQueryParams: url.Values{
|
||||
@@ -67,6 +67,19 @@ func TestImageLoad(t *testing.T) {
|
||||
"quiet": {"0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "json with multiple platforms",
|
||||
platforms: []ocispec.Platform{
|
||||
{Architecture: "arm64", OS: "linux", Variant: "v8"},
|
||||
{Architecture: "amd64", OS: "linux"},
|
||||
},
|
||||
responseContentType: "application/json",
|
||||
expectedResponseJSON: true,
|
||||
expectedQueryParams: url.Values{
|
||||
"platform": {`{"architecture":"arm64","os":"linux","variant":"v8"}`, `{"architecture":"amd64","os":"linux"}`},
|
||||
"quiet": {"0"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
@@ -85,8 +98,8 @@ func TestImageLoad(t *testing.T) {
|
||||
|
||||
input := bytes.NewReader([]byte(expectedInput))
|
||||
imageLoadResponse, err := client.ImageLoad(context.Background(), input, image.LoadOptions{
|
||||
Quiet: tc.quiet,
|
||||
Platform: tc.platform,
|
||||
Quiet: tc.quiet,
|
||||
Platforms: tc.platforms,
|
||||
})
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(imageLoadResponse.JSON, tc.expectedResponseJSON))
|
||||
|
||||
@@ -15,16 +15,15 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, opts image.
|
||||
"names": imageIDs,
|
||||
}
|
||||
|
||||
if opts.Platform != nil {
|
||||
if len(opts.Platforms) > 0 {
|
||||
if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := encodePlatform(opts.Platform)
|
||||
p, err := encodePlatforms(opts.Platforms...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query.Set("platform", p)
|
||||
query["platform"] = p
|
||||
}
|
||||
|
||||
resp, err := cli.get(ctx, "/images/get", query, nil)
|
||||
|
||||
@@ -42,13 +42,26 @@ func TestImageSave(t *testing.T) {
|
||||
{
|
||||
doc: "platform",
|
||||
options: image.SaveOptions{
|
||||
Platform: &ocispec.Platform{Architecture: "arm64", OS: "linux", Variant: "v8"},
|
||||
Platforms: []ocispec.Platform{{Architecture: "arm64", OS: "linux", Variant: "v8"}},
|
||||
},
|
||||
expectedQueryParams: url.Values{
|
||||
"names": {"image_id1", "image_id2"},
|
||||
"platform": {`{"architecture":"arm64","os":"linux","variant":"v8"}`},
|
||||
},
|
||||
},
|
||||
{
|
||||
doc: "multiple platforms",
|
||||
options: image.SaveOptions{
|
||||
Platforms: []ocispec.Platform{
|
||||
{Architecture: "arm64", OS: "linux", Variant: "v8"},
|
||||
{Architecture: "amd64", OS: "linux"},
|
||||
},
|
||||
},
|
||||
expectedQueryParams: url.Values{
|
||||
"names": {"image_id1", "image_id2"},
|
||||
"platform": {`{"architecture":"arm64","os":"linux","variant":"v8"}`, `{"architecture":"amd64","os":"linux"}`},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.doc, func(t *testing.T) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/integration/internal/build"
|
||||
"github.com/docker/docker/integration/internal/container"
|
||||
"github.com/docker/docker/internal/testutils"
|
||||
@@ -212,6 +213,27 @@ func TestSaveOCI(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(thaJeztah): this test currently only checks invalid cases; update this test to use a table-test and test both valid and invalid platform options.
|
||||
func TestSavePlatform(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
|
||||
t.Parallel()
|
||||
client := testEnv.APIClient()
|
||||
|
||||
const repoName = "busybox:latest"
|
||||
_, _, err := client.ImageInspectWithRaw(ctx, repoName)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = client.ImageSave(ctx, []string{repoName}, image.SaveOptions{
|
||||
Platforms: []ocispec.Platform{
|
||||
{Architecture: "amd64", OS: "linux"},
|
||||
{Architecture: "arm64", OS: "linux", Variant: "v8"},
|
||||
},
|
||||
})
|
||||
assert.Check(t, is.ErrorType(err, errdefs.IsInvalidParameter))
|
||||
assert.Check(t, is.Error(err, "Error response from daemon: multiple platform parameters not supported"))
|
||||
}
|
||||
|
||||
func TestSaveRepoWithMultipleImages(t *testing.T) {
|
||||
ctx := setupTest(t)
|
||||
client := testEnv.APIClient()
|
||||
|
||||
Reference in New Issue
Block a user