Skip to content

Commit 1ca6c94

Browse files
authored
Merge pull request docker#6126 from ctalledo/fix-for-moby-48759
Add support for multiple platform options in image load and save
2 parents c2586d6 + 0ba4362 commit 1ca6c94

11 files changed

+78
-31
lines changed

cli/command/image/load.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ import (
1111
"github.com/docker/cli/internal/jsonstream"
1212
"github.com/moby/moby/client"
1313
"github.com/moby/sys/sequential"
14+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1415
"github.com/pkg/errors"
1516
"github.com/spf13/cobra"
1617
)
1718

1819
type loadOptions struct {
1920
input string
2021
quiet bool
21-
platform string
22+
platform []string
2223
}
2324

2425
// NewLoadCommand creates a new `docker load` command
@@ -42,7 +43,7 @@ func NewLoadCommand(dockerCli command.Cli) *cobra.Command {
4243

4344
flags.StringVarP(&opts.input, "input", "i", "", "Read from tar archive file, instead of STDIN")
4445
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress the load output")
45-
flags.StringVar(&opts.platform, "platform", "", `Load only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`)
46+
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").`)
4647
_ = flags.SetAnnotation("platform", "version", []string{"1.48"})
4748

4849
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
@@ -76,13 +77,16 @@ func runLoad(ctx context.Context, dockerCli command.Cli, opts loadOptions) error
7677
options = append(options, client.ImageLoadWithQuiet(true))
7778
}
7879

79-
if opts.platform != "" {
80-
p, err := platforms.Parse(opts.platform)
80+
platformList := []ocispec.Platform{}
81+
for _, p := range opts.platform {
82+
pp, err := platforms.Parse(p)
8183
if err != nil {
8284
return errors.Wrap(err, "invalid platform")
8385
}
84-
// TODO(thaJeztah): change flag-type to support multiple platforms.
85-
options = append(options, client.ImageLoadWithPlatforms(p))
86+
platformList = append(platformList, pp)
87+
}
88+
if len(platformList) > 0 {
89+
options = append(options, client.ImageLoadWithPlatforms(platformList...))
8690
}
8791

8892
response, err := dockerCli.Client().ImageLoad(ctx, input, options...)

cli/command/image/load_test.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func TestNewLoadCommandSuccess(t *testing.T) {
104104
},
105105
},
106106
{
107-
name: "with platform",
107+
name: "with-single-platform",
108108
args: []string{"--platform", "linux/amd64"},
109109
imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
110110
// FIXME(thaJeztah): need to find appropriate way to test the result of "ImageHistoryWithPlatform" being applied
@@ -113,6 +113,22 @@ func TestNewLoadCommandSuccess(t *testing.T) {
113113
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
114114
},
115115
},
116+
{
117+
name: "with-comma-separated-platforms",
118+
args: []string{"--platform", "linux/amd64,linux/arm64/v8,linux/riscv64"},
119+
imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
120+
assert.Check(t, len(options) > 0) // can be 1 or two depending on whether a terminal is attached :/
121+
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
122+
},
123+
},
124+
{
125+
name: "with-multiple-platform-options",
126+
args: []string{"--platform", "linux/amd64", "--platform", "linux/arm64/v8", "--platform", "linux/riscv64"},
127+
imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (image.LoadResponse, error) {
128+
assert.Check(t, len(options) > 0) // can be 1 or two depending on whether a terminal is attached :/
129+
return image.LoadResponse{Body: io.NopCloser(strings.NewReader("Success"))}, nil
130+
},
131+
},
116132
}
117133
for _, tc := range testCases {
118134
t.Run(tc.name, func(t *testing.T) {

cli/command/image/save.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import (
1010
"github.com/docker/cli/cli/command/completion"
1111
"github.com/moby/moby/client"
1212
"github.com/moby/sys/atomicwriter"
13+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1314
"github.com/pkg/errors"
1415
"github.com/spf13/cobra"
1516
)
1617

1718
type saveOptions struct {
1819
images []string
1920
output string
20-
platform string
21+
platform []string
2122
}
2223

2324
// NewSaveCommand creates a new `docker save` command
@@ -41,7 +42,7 @@ func NewSaveCommand(dockerCli command.Cli) *cobra.Command {
4142
flags := cmd.Flags()
4243

4344
flags.StringVarP(&opts.output, "output", "o", "", "Write to a file, instead of STDOUT")
44-
flags.StringVar(&opts.platform, "platform", "", `Save only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`)
45+
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")`)
4546
_ = flags.SetAnnotation("platform", "version", []string{"1.48"})
4647

4748
_ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
@@ -51,13 +52,17 @@ func NewSaveCommand(dockerCli command.Cli) *cobra.Command {
5152
// runSave performs a save against the engine based on the specified options
5253
func runSave(ctx context.Context, dockerCLI command.Cli, opts saveOptions) error {
5354
var options []client.ImageSaveOption
54-
if opts.platform != "" {
55-
p, err := platforms.Parse(opts.platform)
55+
56+
platformList := []ocispec.Platform{}
57+
for _, p := range opts.platform {
58+
pp, err := platforms.Parse(p)
5659
if err != nil {
5760
return errors.Wrap(err, "invalid platform")
5861
}
59-
// TODO(thaJeztah): change flag-type to support multiple platforms.
60-
options = append(options, client.ImageSaveWithPlatforms(p))
62+
platformList = append(platformList, pp)
63+
}
64+
if len(platformList) > 0 {
65+
options = append(options, client.ImageSaveWithPlatforms(platformList...))
6166
}
6267

6368
var output io.Writer

cli/command/image/save_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,26 @@ func TestNewSaveCommandSuccess(t *testing.T) {
111111
return io.NopCloser(strings.NewReader("")), nil
112112
},
113113
},
114+
{
115+
args: []string{"--platform", "linux/amd64,linux/arm64/v8,linux/riscv64", "arg1"},
116+
isTerminal: false,
117+
imageSaveFunc: func(images []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
118+
assert.Assert(t, is.Len(images, 1))
119+
assert.Check(t, is.Equal("arg1", images[0]))
120+
assert.Check(t, len(options) > 0) // can be 1 or 2 depending on whether a terminal is attached :/
121+
return io.NopCloser(strings.NewReader("")), nil
122+
},
123+
},
124+
{
125+
args: []string{"--platform", "linux/amd64", "--platform", "linux/arm64/v8", "--platform", "linux/riscv64", "arg1"},
126+
isTerminal: false,
127+
imageSaveFunc: func(images []string, options ...client.ImageSaveOption) (io.ReadCloser, error) {
128+
assert.Assert(t, is.Len(images, 1))
129+
assert.Check(t, is.Equal("arg1", images[0]))
130+
assert.Check(t, len(options) > 0) // can be 1 or 2 depending on whether a terminal is attached :/
131+
return io.NopCloser(strings.NewReader("")), nil
132+
},
133+
},
114134
}
115135
for _, tc := range testCases {
116136
t.Run(strings.Join(tc.args, " "), func(t *testing.T) {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Success
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Success

docs/reference/commandline/image_load.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ Load an image from a tar archive or STDIN
99

1010
### Options
1111

12-
| Name | Type | Default | Description |
13-
|:------------------------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------|
14-
| [`-i`](#input), [`--input`](#input) | `string` | | Read from tar archive file, instead of STDIN |
15-
| [`--platform`](#platform) | `string` | | Load only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) |
16-
| `-q`, `--quiet` | `bool` | | Suppress the load output |
12+
| Name | Type | Default | Description |
13+
|:------------------------------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------------------------------|
14+
| [`-i`](#input), [`--input`](#input) | `string` | | Read from tar archive file, instead of STDIN |
15+
| [`--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`). |
16+
| `-q`, `--quiet` | `bool` | | Suppress the load output |
1717

1818

1919
<!---MARKER_GEN_END-->

docs/reference/commandline/image_save.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ Save one or more images to a tar archive (streamed to STDOUT by default)
99

1010
### Options
1111

12-
| Name | Type | Default | Description |
13-
|:--------------------------|:---------|:--------|:-----------------------------------------------------------------------------------------------|
14-
| `-o`, `--output` | `string` | | Write to a file, instead of STDOUT |
15-
| [`--platform`](#platform) | `string` | | Save only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) |
12+
| Name | Type | Default | Description |
13+
|:--------------------------|:--------------|:--------|:-----------------------------------------------------------------------------------------------------------------------------------|
14+
| `-o`, `--output` | `string` | | Write to a file, instead of STDOUT |
15+
| [`--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`) |
1616

1717

1818
<!---MARKER_GEN_END-->

docs/reference/commandline/load.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ Load an image from a tar archive or STDIN
99

1010
### Options
1111

12-
| Name | Type | Default | Description |
13-
|:----------------|:---------|:--------|:-----------------------------------------------------------------------------------------------|
14-
| `-i`, `--input` | `string` | | Read from tar archive file, instead of STDIN |
15-
| `--platform` | `string` | | Load only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) |
16-
| `-q`, `--quiet` | `bool` | | Suppress the load output |
12+
| Name | Type | Default | Description |
13+
|:----------------|:--------------|:--------|:------------------------------------------------------------------------------------------------------------------------------------|
14+
| `-i`, `--input` | `string` | | Read from tar archive file, instead of STDIN |
15+
| `--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`). |
16+
| `-q`, `--quiet` | `bool` | | Suppress the load output |
1717

1818

1919
<!---MARKER_GEN_END-->

0 commit comments

Comments
 (0)