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:
@ -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...)
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
1
cli/command/image/testdata/load-command-success.with-multiple-platform-options.golden
vendored
Normal file
1
cli/command/image/testdata/load-command-success.with-multiple-platform-options.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Success
|
1
cli/command/image/testdata/load-command-success.with-single-platform.golden
vendored
Normal file
1
cli/command/image/testdata/load-command-success.with-single-platform.golden
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Success
|
@ -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-->
|
||||||
|
@ -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-->
|
||||||
|
@ -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-->
|
||||||
|
@ -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-->
|
||||||
|
Reference in New Issue
Block a user