Skip to content

image_load: 0.3.19 creates containerd image records the daemon cannot resolve (regression from #663) #716

Description

@gergelyfabian

Summary

Since v0.3.19, image_load into a Docker daemon that uses the containerd image
store
creates an image record under the raw reference instead of the
canonical one. The record shows up in docker image ls, but docker image inspect and docker run cannot resolve the name, so the load looks successful
(exit 0, the tag is printed) while nothing can use the image.

v0.3.18 is the last working release; v0.3.19 is the first broken one.

Reproduction

MODULE.bazel:

module(name = "load_name_repro", version = "0.0.0")

bazel_dep(name = "rules_img", version = "0.3.19")

pull = use_repo_rule("@rules_img//img:pull.bzl", "pull")

pull(
    name = "alpine",
    digest = "sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1",
    registry = "index.docker.io",
    repository = "library/alpine",
    tag = "3.22",
)

BUILD.bazel:

load("@rules_img//img:load.bzl", "image_load")

image_load(
    name = "load",
    image = "@alpine//:image",
    tag = "my-app:latest",
)
$ bazel run :load
...
sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1
my-app:latest
    blob transfers: 10 from disk, 0 from disk cache, 2 from container registry, ...
$ echo $?
0

$ docker image inspect my-app:latest
[]
Error response from daemon: No such image: my-app:latest

$ docker run --rm my-app:latest true
Unable to find image 'my-app:latest' locally
docker: Error response from daemon: pull access denied for my-app, ...

With only the version changed to 0.3.18, the same workspace works:

$ bazel run :load
...
docker.io/library/my-app:latest          # <- normalized name

$ docker image inspect my-app:latest >/dev/null && echo OK
OK
$ docker run --rm my-app:latest true; echo $?
0

Note the difference in the name the loader prints: my-app:latest (0.3.19) vs
docker.io/library/my-app:latest (0.3.18).

docker image ls is misleading here — it lists the unresolvable record (and can
show a duplicate row for the same tag, one of them with no ID/size in
docker image ls --tree), so the breakage only surfaces at inspect/run time.

Cause

img_tool/pkg/load/loader.go, loadContainerd, changed in #663
("fix(load): stop mangling image names that carry a registry port"):

 	for _, tag := range allTags {
-		normalizedTag := NormalizeDockerReference(tag)
 		img := containerd.Image{
-			Name:   normalizedTag,
+			Name:   tag,
 			Target: target,
 		}
 		_, err = imageService.Create(ctx, img)

Dropping the normalization is correct for the docker save tarball path, where
RepoTags should stay verbatim. For the containerd path it is not: the daemon
resolves image names as fully-qualified references, so a record named
my-app:latest is a different key from the docker.io/library/my-app:latest
that docker inspect/docker run/docker compose look up.

I suspect #663 was aimed at the localhost/ vs docker.io/ prefix question in
#563, but the containerd record needs a canonical name regardless of what the
tarball path does.

Suggested fix

Keep names verbatim for the tarball/docker save path, and normalize again for
the containerd image record — while preserving the actual fix from #663, i.e.
only qualify a reference that has no registry component, so
docker.example.com:1234/foo:latest keeps its port and is not mistaken for a
tag. NormalizeLoadReference in img_tool/pkg/api/deploy.go looks like the
natural place to distinguish "name as configured" from "name handed to the
containerd image service".

Environment

  • rules_img 0.3.19 (broken), 0.3.18 (works)
  • Bazel 9.2.0, bzlmod
  • Docker 29.7.2 with the containerd image store
    (docker infodriver-type io.containerd.snapshotter.v1)
  • linux/amd64

Only daemons using the containerd image store are affected; that is also why the
load reports success — the blobs and the record are written, just under a name
the daemon will not resolve.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions