diff --git a/cli/command/image/remove.go b/cli/command/image/remove.go index f0fa7d3488..7c8b49ee0a 100644 --- a/cli/command/image/remove.go +++ b/cli/command/image/remove.go @@ -6,6 +6,7 @@ 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" @@ -14,22 +15,23 @@ import ( ) type removeOptions struct { - force bool - noPrune bool + force bool + noPrune bool + platforms []string } // NewRemoveCommand creates a new `docker remove` command -func NewRemoveCommand(dockerCli command.Cli) *cobra.Command { - var opts removeOptions +func NewRemoveCommand(dockerCLI command.Cli) *cobra.Command { + var options removeOptions cmd := &cobra.Command{ Use: "rmi [OPTIONS] IMAGE [IMAGE...]", Short: "Remove one or more images", Args: cli.RequiresMinArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return runRemove(cmd.Context(), dockerCli, opts, args) + return runRemove(cmd.Context(), dockerCLI, options, args) }, - ValidArgsFunction: completion.ImageNames(dockerCli, -1), + ValidArgsFunction: completion.ImageNames(dockerCLI, -1), Annotations: map[string]string{ "aliases": "docker image rm, docker image remove, docker rmi", }, @@ -37,9 +39,14 @@ func NewRemoveCommand(dockerCli command.Cli) *cobra.Command { flags := cmd.Flags() - flags.BoolVarP(&opts.force, "force", "f", false, "Force removal of the image") - flags.BoolVar(&opts.noPrune, "no-prune", false, "Do not delete untagged parents") + 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.SetAnnotation("platform", "version", []string{"1.50"}) + + _ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms) return cmd } @@ -58,6 +65,14 @@ func runRemove(ctx context.Context, dockerCLI command.Cli, opts removeOptions, i 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 diff --git a/docs/reference/commandline/image_rm.md b/docs/reference/commandline/image_rm.md index 81ff55c6b0..2a50f74192 100644 --- a/docs/reference/commandline/image_rm.md +++ b/docs/reference/commandline/image_rm.md @@ -9,10 +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 | +| 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`) | @@ -105,3 +106,73 @@ Deleted: 4986bf8c15363d1c5d15512d5266f8777bfba4974ac56e3270e7760f6f0a8125 Deleted: ea13149945cb6b1e746bf28032f02e9b5a793523481a0a18645fc77ad53c4ea2 Deleted: df7546f9f060a2268024c8a230d8639878585defcc1bc6f79d2728a13957871b ``` + +### Remove specific platforms (`--platform`) + +The `--platform` option allows you to specify which platform variants of the +image to remove. By default, `docker image remove` removes all platform variants +that are present. Use the `--platform` option to specify which platform variant +of the image to remove. + +Removing a specific platform removes the image from all images that reference +the same content, and requires the `--force` option to be used. Omitting the +`--force` option produces a warning, and the remove is canceled: + +```console +$ docker image rm --platform=linux/amd64 alpine +Error response from daemon: Content will be removed from all images referencing this variant. Use —-force to force delete. +``` + +The platform option takes the `os[/arch[/variant]]` format; for example, +`linux/amd64` or `linux/arm64/v8`. Architecture and variant are optional, +and default to the daemon's native architecture if omitted. + +You can pass multiple platforms either by passing the `--platform` flag +multiple times, or by passing a comma-separated list of platforms to remove. +The following uses of this option are equivalent; + +```console +$ docker image rm --plaform linux/amd64 --platform linux/ppc64le myimage +$ docker image rm --plaform linux/amd64,linux/ppc64le myimage +``` + +The following example removes the `linux/amd64` and `linux/ppc64le` variants +of an `alpine` image that contains multiple platform variants in the image +cache: + +```console +$ docker image ls --tree + +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +alpine:latest a8560b36e8b8 37.8MB 11.2MB U +├─ linux/amd64 1c4eef651f65 12.1MB 3.64MB U +├─ linux/arm/v6 903bfe2ae994 0B 0B +├─ linux/arm/v7 9c2d245b3c01 0B 0B +├─ linux/arm64/v8 757d680068d7 12.8MB 3.99MB +├─ linux/386 2436f2b3b7d2 0B 0B +├─ linux/ppc64le 9ed53fd3b831 12.8MB 3.58MB +├─ linux/riscv64 1de5eb4a9a67 0B 0B +└─ linux/s390x fe0dcdd1f783 0B 0B + +$ docker image --platform=linux/amd64,linux/ppc64le --force alpine +Deleted: sha256:1c4eef651f65e2f7daee7ee785882ac164b02b78fb74503052a26dc061c90474 +Deleted: sha256:9ed53fd3b83120f78b33685d930ce9bf5aa481f6e2d165c42cbbddbeaa196f6f +``` + +After the command completes, the given variants of the `alpine` image are removed +from the image cache: + +```console +$ docker image ls --tree + +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +alpine:latest a8560b36e8b8 12.8MB 3.99MB +├─ linux/amd64 1c4eef651f65 0B 0B +├─ linux/arm/v6 903bfe2ae994 0B 0B +├─ linux/arm/v7 9c2d245b3c01 0B 0B +├─ linux/arm64/v8 757d680068d7 12.8MB 3.99MB +├─ linux/386 2436f2b3b7d2 0B 0B +├─ linux/ppc64le 9ed53fd3b831 0B 0B +├─ linux/riscv64 1de5eb4a9a67 0B 0B +└─ linux/s390x fe0dcdd1f783 0B 0B +``` diff --git a/docs/reference/commandline/rmi.md b/docs/reference/commandline/rmi.md index 6aac0b757b..d6cb23439c 100644 --- a/docs/reference/commandline/rmi.md +++ b/docs/reference/commandline/rmi.md @@ -9,10 +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 | +| 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`) |