Skip to content

rmi resolves only fully-qualified refs — deletes the wrong image on a bare/qualified name collision, cannot remove mocker-built images #76

Description

@KennethWKZ

mocker version: 0.7.2 (Homebrew) — macOS, Darwin 25.5.0, arm64

Summary

mocker rmi resolves image references only in their fully-qualified form. Consequences, worst first:

  1. When a bare tag and a qualified tag share a name, rmi <name>:<tag> deletes the qualified image instead of the bare one and reports success — the wrong image is destroyed silently.
  2. Images that mocker itself builds (tagged with a bare name, see compose build tags image with bare service name — FROM <base> then resolves to mocker's own previous build #73) cannot be removed through mocker at all.
  3. Short references such as busybox:1.37 — ordinary docker rmi usage — fail with No such image.

The backend supports all of these; the gap is in mocker's reference resolution.

Reproduction 1 — rmi deletes the wrong image

Starting state, two distinct images both named caddy:latest (the bare one produced by compose build per #73, the qualified one pulled from Docker Hub):

$ mocker images | grep -i caddy
caddy                     latest   sha256:4b9ba     # mocker's build
docker.io/library/caddy   latest   sha256:844f6     # official base

Attempt to remove the build:

$ mocker rmi caddy:latest
Untagged: docker.io/library/caddy:latest
Deleted: sha256:844f60b64e4724a5aa8245e019dace0d3f199f7433ce6c57676cb30a920dbad9

The official base was deleted. The build survived. The output reads as success, and nothing indicates a different image was targeted than the one named.

Repeating the command now fails, since the only remaining caddy:latest is the bare-name build:

$ mocker rmi caddy:latest
Error: Error response from daemon: No such image: caddy:latest

Reproduction 2 — mocker lists an image it then denies exists

/tmp/mocker-rmi/
  Dockerfile          FROM docker.io/library/alpine:3.20
                      RUN echo hi > /hi
  docker-compose.yml  services:
                        tinysvc:
                          build: .
$ mocker compose build tinysvc
Successfully built tinysvc

$ mocker images | grep tiny
tinysvc   latest   sha256:269b1              # listed

$ mocker rmi tinysvc:latest
Error: Error response from daemon: No such image: tinysvc:latest

$ mocker rmi docker.io/library/tinysvc:latest
Error: Error response from daemon: No such image: docker.io/library/tinysvc:latest

Both forms fail — the image is unreachable via rmi in any spelling, so there is no way to remove a mocker-built image with mocker. mocker images and mocker rmi disagree about the same reference string.

Reproduction 3 — short references fail for pulled images

$ mocker pull docker.io/library/busybox:1.37
Status: Downloaded newer image for docker.io/library/busybox:1.37

$ mocker rmi docker.io/library/busybox:1.37
Untagged: docker.io/library/busybox:1.37
Deleted: sha256:9db7b59979c38555a39def84a31fb98b5296952f9e3afd4f6f11f05b07adfab0   # qualified: works

$ mocker rmi busybox:1.37
Error: Error response from daemon: No such image: busybox:1.37                     # short form: fails

Backend handles what mocker rejects

container image rm removes a bare-name image without trouble:

$ container image rm caddy:latest
Reclaimed 28.54 GB in disk space
caddy:latest

$ container image ls | grep -i caddy
                    # gone

So the capability is present underneath. container image rm is the workaround.

Expected

  • Resolve the reference as given first, then fall back to the normalized docker.io/library/... form — not the other way round. An exact match on the literal string the user typed should always win.
  • Accept short references for pulled images, matching docker rmi behavior.
  • When a reference is ambiguous across namespaces, either error and ask for a digest, or state which image was selected. Deleting a different image than the one named, with Deleted: <other digest> as the only hint, is the dangerous part.

Relationship to #73

#73 (bare service name as build tag) is what creates the ambiguous pair. This issue is what mis-resolves it destructively. Fixing #73 shrinks the blast radius, since builds would land on <project>-<service>:latest and stop colliding with base image names — but the resolution order here is worth fixing independently, as reproductions 2 and 3 do not depend on it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions