1
0
mirror of https://github.com/docker/cli.git synced 2025-08-01 04:26:55 +03:00

Merge pull request #6126 from ctalledo/fix-for-moby-48759

Add support for multiple platform options in image load and save
This commit is contained in:
Paweł Gronowski
2025-07-24 11:48:11 +02:00
committed by GitHub
11 changed files with 78 additions and 31 deletions

View File

@ -11,6 +11,7 @@ import (
"github.com/docker/cli/internal/jsonstream" "github.com/docker/cli/internal/jsonstream"
"github.com/moby/moby/client" "github.com/moby/moby/client"
"github.com/moby/sys/sequential" "github.com/moby/sys/sequential"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -18,7 +19,7 @@ import (
type loadOptions struct { type loadOptions struct {
input string input string
quiet bool quiet bool
platform string platform []string
} }
// NewLoadCommand creates a new `docker load` command // NewLoadCommand creates a new `docker load` command
@ -42,7 +43,7 @@ func NewLoadCommand(dockerCli command.Cli) *cobra.Command {
flags.StringVarP(&opts.input, "input", "i", "", "Read from tar archive file, instead of STDIN") flags.StringVarP(&opts.input, "input", "i", "", "Read from tar archive file, instead of STDIN")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress the load output") flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress the load output")
flags.StringVar(&opts.platform, "platform", "", `Load only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`) flags.StringSliceVar(&opts.platform, "platform", []string{}, `Load only the given platform(s). Formatted as a comma-separated list of "os[/arch[/variant]]" (e.g., "linux/amd64,linux/arm64/v8").`)
_ = flags.SetAnnotation("platform", "version", []string{"1.48"}) _ = flags.SetAnnotation("platform", "version", []string{"1.48"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms) _ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
@ -76,13 +77,16 @@ func runLoad(ctx context.Context, dockerCli command.Cli, opts loadOptions) error
options = append(options, client.ImageLoadWithQuiet(true)) options = append(options, client.ImageLoadWithQuiet(true))
} }
if opts.platform != "" { platformList := []ocispec.Platform{}
p, err := platforms.Parse(opts.platform) for _, p := range opts.platform {
pp, err := platforms.Parse(p)
if err != nil { if err != nil {
return errors.Wrap(err, "invalid platform") return errors.Wrap(err, "invalid platform")
} }
// TODO(thaJeztah): change flag-type to support multiple platforms. platformList = append(platformList, pp)
options = append(options, client.ImageLoadWithPlatforms(p)) }
if len(platformList) > 0 {
options = append(options, client.ImageLoadWithPlatforms(platformList...))
} }
response, err := dockerCli.Client().ImageLoad(ctx, input, options...) response, err := dockerCli.Client().ImageLoad(ctx, input, options...)

View File

@ -104,7 +104,7 @@ func TestNewLoadCommandSuccess(t *testing.T) {
}, },
}, },
{ {
name: "with platform", name: "with-single-platform",
args: []string{"--platform", "linux/amd64"}, args: []string{"--platform", "linux/amd64"},
imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) { imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
// FIXME(thaJeztah): need to find appropriate way to test the result of "ImageHistoryWithPlatform" being applied // FIXME(thaJeztah): need to find appropriate way to test the result of "ImageHistoryWithPlatform" being applied
@ -113,6 +113,22 @@ func TestNewLoadCommandSuccess(t *testing.T) {
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
}, },
}, },
{
name: "with-comma-separated-platforms",
args: []string{"--platform", "linux/amd64,linux/arm64/v8,linux/riscv64"},
imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
assert.Check(t, len(options) > 0) // can be 1 or two depending on whether a terminal is attached :/
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
},
},
{
name: "with-multiple-platform-options",
args: []string{"--platform", "linux/amd64", "--platform", "linux/arm64/v8", "--platform", "linux/riscv64"},
imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
assert.Check(t, len(options) > 0) // can be 1 or two depending on whether a terminal is attached :/
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
},
},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {

View File

@ -10,6 +10,7 @@ import (
"github.com/docker/cli/cli/command/completion" "github.com/docker/cli/cli/command/completion"
"github.com/moby/moby/client" "github.com/moby/moby/client"
"github.com/moby/sys/atomicwriter" "github.com/moby/sys/atomicwriter"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -17,7 +18,7 @@ import (
type saveOptions struct { type saveOptions struct {
images []string images []string
output string output string
platform string platform []string
} }
// NewSaveCommand creates a new `docker save` command // NewSaveCommand creates a new `docker save` command
@ -41,7 +42,7 @@ func NewSaveCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags() flags := cmd.Flags()
flags.StringVarP(&opts.output, "output", "o", "", "Write to a file, instead of STDOUT") flags.StringVarP(&opts.output, "output", "o", "", "Write to a file, instead of STDOUT")
flags.StringVar(&opts.platform, "platform", "", `Save only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`) flags.StringSliceVar(&opts.platform, "platform", []string{}, `Save only the given platform(s). Formatted as a comma-separated list of "os[/arch[/variant]]" (e.g., "linux/amd64,linux/arm64/v8")`)
_ = flags.SetAnnotation("platform", "version", []string{"1.48"}) _ = flags.SetAnnotation("platform", "version", []string{"1.48"})
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms) _ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
@ -51,13 +52,17 @@ func NewSaveCommand(dockerCli command.Cli) *cobra.Command {
// runSave performs a save against the engine based on the specified options // runSave performs a save against the engine based on the specified options
func runSave(ctx context.Context, dockerCLI command.Cli, opts saveOptions) error { func runSave(ctx context.Context, dockerCLI command.Cli, opts saveOptions) error {
var options []client.ImageSaveOption var options []client.ImageSaveOption
if opts.platform != "" {
p, err := platforms.Parse(opts.platform) platformList := []ocispec.Platform{}
for _, p := range opts.platform {
pp, err := platforms.Parse(p)
if err != nil { if err != nil {
return errors.Wrap(err, "invalid platform") return errors.Wrap(err, "invalid platform")
} }
// TODO(thaJeztah): change flag-type to support multiple platforms. platformList = append(platformList, pp)
options = append(options, client.ImageSaveWithPlatforms(p)) }
if len(platformList) > 0 {
options = append(options, client.ImageSaveWithPlatforms(platformList...))
} }
var output io.Writer var output io.Writer

View File

@ -111,6 +111,26 @@ func TestNewSaveCommandSuccess(t *testing.T) {
return io.NopCloser(strings.NewReader("")), nil return io.NopCloser(strings.NewReader("")), nil
}, },
}, },
{
args: []string{"--platform", "linux/amd64,linux/arm64/v8,linux/riscv64", "arg1"},
isTerminal: false,
imageSaveFunc: func(images []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
assert.Assert(t, is.Len(images, 1))
assert.Check(t, is.Equal("arg1", images[0]))
assert.Check(t, len(options) > 0) // can be 1 or 2 depending on whether a terminal is attached :/
return io.NopCloser(strings.NewReader("")), nil
},
},
{
args: []string{"--platform", "linux/amd64", "--platform", "linux/arm64/v8", "--platform", "linux/riscv64", "arg1"},
isTerminal: false,
imageSaveFunc: func(images []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
assert.Assert(t, is.Len(images, 1))
assert.Check(t, is.Equal("arg1", images[0]))
assert.Check(t, len(options) > 0) // can be 1 or 2 depending on whether a terminal is attached :/
return io.NopCloser(strings.NewReader("")), nil
},
},
} }
for _, tc := range testCases { for _, tc := range testCases {
t.Run(strings.Join(tc.args, " "), func(t *testing.T) { t.Run(strings.Join(tc.args, " "), func(t *testing.T) {

View File

@ -0,0 +1 @@
Success

View File

@ -0,0 +1 @@
Success

View File

@ -9,11 +9,11 @@ Load an image from a tar archive or STDIN
### Options ### Options
| Name | Type | Default | Description | | Name | Type | Default | Description |
|:------------------------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------| |:------------------------------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------------------------------|
| [`-i`](#input), [`--input`](#input) | `string` | | Read from tar archive file, instead of STDIN | | [`-i`](#input), [`--input`](#input) | `string` | | Read from tar archive file, instead of STDIN |
| [`--platform`](#platform) | `string` | | Load only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) | | [`--platform`](#platform) | `stringSlice` | | Load only the given platform(s). Formatted as a comma-separated list of `os[/arch[/variant]]` (e.g., `linux/amd64,linux/arm64/v8`). |
| `-q`, `--quiet` | `bool` | | Suppress the load output | | `-q`, `--quiet` | `bool` | | Suppress the load output |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->

View File

@ -9,10 +9,10 @@ Save one or more images to a tar archive (streamed to STDOUT by default)
### Options ### Options
| Name | Type | Default | Description | | Name | Type | Default | Description |
|:--------------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------| |:--------------------------|:--------------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------|
| `-o`, `--output` | `string` | | Write to a file, instead of STDOUT | | `-o`, `--output` | `string` | | Write to a file, instead of STDOUT |
| [`--platform`](#platform) | `string` | | Save only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) | | [`--platform`](#platform) | `stringSlice` | | Save only the given platform(s). Formatted as a comma-separated list of `os[/arch[/variant]]` (e.g., `linux/amd64,linux/arm64/v8`) |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->

View File

@ -9,11 +9,11 @@ Load an image from a tar archive or STDIN
### Options ### Options
| Name | Type | Default | Description | | Name | Type | Default | Description |
|:----------------|:---------|:--------|:-----------------------------------------------------------------------------------------------| |:----------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------------------------------|
| `-i`, `--input` | `string` | | Read from tar archive file, instead of STDIN | | `-i`, `--input` | `string` | | Read from tar archive file, instead of STDIN |
| `--platform` | `string` | | Load only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) | | `--platform` | `stringSlice` | | Load only the given platform(s). Formatted as a comma-separated list of `os[/arch[/variant]]` (e.g., `linux/amd64,linux/arm64/v8`). |
| `-q`, `--quiet` | `bool` | | Suppress the load output | | `-q`, `--quiet` | `bool` | | Suppress the load output |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->

View File

@ -9,10 +9,10 @@ Save one or more images to a tar archive (streamed to STDOUT by default)
### Options ### Options
| Name | Type | Default | Description | | Name | Type | Default | Description |
|:-----------------|:---------|:--------|:-----------------------------------------------------------------------------------------------| |:-----------------|:--------------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------|
| `-o`, `--output` | `string` | | Write to a file, instead of STDOUT | | `-o`, `--output` | `string` | | Write to a file, instead of STDOUT |
| `--platform` | `string` | | Save only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) | | `--platform` | `stringSlice` | | Save only the given platform(s). Formatted as a comma-separated list of `os[/arch[/variant]]` (e.g., `linux/amd64,linux/arm64/v8`) |
<!---MARKER_GEN_END--> <!---MARKER_GEN_END-->