From fecbc3db89bea74ff0a4cb0916a02c9d1ac127cf Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 26 May 2025 20:19:44 +0200 Subject: [PATCH 1/2] opts: implement PlatformSlice option Signed-off-by: Sebastiaan van Stijn --- opts/platforms.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 opts/platforms.go diff --git a/opts/platforms.go b/opts/platforms.go new file mode 100644 index 000000000000..3bbade52a5ac --- /dev/null +++ b/opts/platforms.go @@ -0,0 +1,53 @@ +package opts + +import ( + "strings" + + "github.com/containerd/platforms" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func NewPlatformSlice(val *[]ocispec.Platform) *PlatformSlice { + return &PlatformSlice{values: val} +} + +// PlatformSlice is a Value type for passing multiple platforms. +type PlatformSlice struct { + values *[]ocispec.Platform +} + +func (m *PlatformSlice) Set(value string) error { + vals := strings.Split(value, ",") + for _, val := range vals { + p, err := platforms.Parse(val) + if err != nil { + return err + } + *m.values = append(*m.values, p) + } + return nil +} + +// Type returns the type of this option +func (*PlatformSlice) Type() string { + return "platforms" +} + +// String returns a string representation of this option. +func (m *PlatformSlice) String() string { + return strings.Join(m.GetSlice(), ", ") +} + +// GetSlice returns the platforms as a string-slice. +func (m *PlatformSlice) GetSlice() []string { + values := make([]string, 0, len(*m.values)) + for _, v := range *m.values { + values = append(values, platforms.FormatAll(v)) + } + return values +} + +// Value returns the platforms +func (m *PlatformSlice) Value() []ocispec.Platform { + return *m.values +} From 7c8160f709cc1a3c3c917e187a7d600eb7fad9c1 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 8 Jul 2025 10:59:12 +0200 Subject: [PATCH 2/2] cli/command/image: use opts.PlatformSlice for image remove Signed-off-by: Sebastiaan van Stijn --- cli/command/image/remove.go | 31 +++++++++----------------- docs/reference/commandline/image_rm.md | 10 ++++----- docs/reference/commandline/rmi.md | 10 ++++----- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/cli/command/image/remove.go b/cli/command/image/remove.go index 7c8b49ee0a99..e686a4a6291b 100644 --- a/cli/command/image/remove.go +++ b/cli/command/image/remove.go @@ -6,18 +6,19 @@ import ( "fmt" cerrdefs "github.com/containerd/errdefs" - "github.com/containerd/platforms" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/completion" + "github.com/docker/cli/opts" "github.com/docker/docker/api/types/image" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" ) type removeOptions struct { force bool noPrune bool - platforms []string + platforms []ocispec.Platform } // NewRemoveCommand creates a new `docker remove` command @@ -42,8 +43,7 @@ func NewRemoveCommand(dockerCLI command.Cli) *cobra.Command { flags.BoolVarP(&options.force, "force", "f", false, "Force removal of the image") flags.BoolVar(&options.noPrune, "no-prune", false, "Do not delete untagged parents") - // TODO(thaJeztah): create a "platforms" option for this (including validation / parsing). - flags.StringSliceVar(&options.platforms, "platform", nil, `Remove only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`) + flags.Var(opts.NewPlatformSlice(&options.platforms), "platform", `Remove only the given platform variant. Formatted as "os[/arch[/variant]]" (e.g., "linux/amd64")`) _ = flags.SetAnnotation("platform", "version", []string{"1.50"}) _ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms) @@ -57,27 +57,18 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command { return &cmd } -func runRemove(ctx context.Context, dockerCLI command.Cli, opts removeOptions, images []string) error { +func runRemove(ctx context.Context, dockerCLI command.Cli, options removeOptions, images []string) error { apiClient := dockerCLI.Client() - options := image.RemoveOptions{ - Force: opts.force, - PruneChildren: !opts.noPrune, - } - - for _, v := range opts.platforms { - p, err := platforms.Parse(v) - if err != nil { - return err - } - options.Platforms = append(options.Platforms, p) - } - // TODO(thaJeztah): this logic can likely be simplified: do we want to print "not found" errors at all when using "force"? fatalErr := false var errs []error for _, img := range images { - dels, err := apiClient.ImageRemove(ctx, img, options) + dels, err := apiClient.ImageRemove(ctx, img, image.RemoveOptions{ + Force: options.force, + PruneChildren: !options.noPrune, + Platforms: options.platforms, + }) if err != nil { if !cerrdefs.IsNotFound(err) { fatalErr = true @@ -95,7 +86,7 @@ func runRemove(ctx context.Context, dockerCLI command.Cli, opts removeOptions, i } if err := errors.Join(errs...); err != nil { - if !opts.force || fatalErr { + if !options.force || fatalErr { return err } _, _ = fmt.Fprintln(dockerCLI.Err(), err) diff --git a/docs/reference/commandline/image_rm.md b/docs/reference/commandline/image_rm.md index 2a50f74192b0..e104ed06e61a 100644 --- a/docs/reference/commandline/image_rm.md +++ b/docs/reference/commandline/image_rm.md @@ -9,11 +9,11 @@ Remove one or more images ### Options -| Name | Type | Default | Description | -|:--------------------------|:--------------|:--------|:-------------------------------------------------------------------------------------------------| -| `-f`, `--force` | `bool` | | Force removal of the image | -| `--no-prune` | `bool` | | Do not delete untagged parents | -| [`--platform`](#platform) | `stringSlice` | | Remove only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) | +| Name | Type | Default | Description | +|:--------------------------|:------------|:--------|:-------------------------------------------------------------------------------------------------| +| `-f`, `--force` | `bool` | | Force removal of the image | +| `--no-prune` | `bool` | | Do not delete untagged parents | +| [`--platform`](#platform) | `platforms` | | Remove only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) | diff --git a/docs/reference/commandline/rmi.md b/docs/reference/commandline/rmi.md index d6cb23439c91..7d1744ec1dd0 100644 --- a/docs/reference/commandline/rmi.md +++ b/docs/reference/commandline/rmi.md @@ -9,11 +9,11 @@ Remove one or more images ### Options -| Name | Type | Default | Description | -|:----------------|:--------------|:--------|:-------------------------------------------------------------------------------------------------| -| `-f`, `--force` | `bool` | | Force removal of the image | -| `--no-prune` | `bool` | | Do not delete untagged parents | -| `--platform` | `stringSlice` | | Remove only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) | +| Name | Type | Default | Description | +|:----------------|:------------|:--------|:-------------------------------------------------------------------------------------------------| +| `-f`, `--force` | `bool` | | Force removal of the image | +| `--no-prune` | `bool` | | Do not delete untagged parents | +| `--platform` | `platforms` | | Remove only the given platform variant. Formatted as `os[/arch[/variant]]` (e.g., `linux/amd64`) |