Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 11 additions & 20 deletions cli/command/image/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)
Expand Down
10 changes: 5 additions & 5 deletions docs/reference/commandline/image_rm.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`) |


<!---MARKER_GEN_END-->
Expand Down
10 changes: 5 additions & 5 deletions docs/reference/commandline/rmi.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`) |


<!---MARKER_GEN_END-->
Expand Down
53 changes: 53 additions & 0 deletions opts/platforms.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading