From 679407862f2b714b2936724bfb930721ea12e8c1 Mon Sep 17 00:00:00 2001 From: Cesar Talledo Date: Mon, 21 Apr 2025 09:49:21 -0700 Subject: [PATCH] Output correct image ID when using Docker with the containerd-snapshotter. Prior to this change, the following command emits the wrong image ID when buildx uses the "docker-container" driver and Docker is configured with the containerd-snapshotter. $ docker buildx build --load --iidfile=img.txt $ docker run --rm "$(cat img.txt)" echo hello docker: Error response from daemon: No such image: sha256:4ac37e81e00f242010e42f3251094e47de6100e01d25e9bd0feac6b8906976df. See 'docker run --help'. The problem is that buildx is outputing the incorrect image ID in this scenario (it's outputing the container image config digest, instead of the container image digest used by the containerd-snapshotter). This commit fixes this. See https://github.com/moby/moby/issues/45458. Signed-off-by: Cesar Talledo --- build/build.go | 9 ++++++++- build/opt.go | 5 +++++ tests/build.go | 9 ++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/build/build.go b/build/build.go index 9c18dbba..0414b350 100644 --- a/build/build.go +++ b/build/build.go @@ -525,7 +525,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[ } } } - node := dp.Node().Driver if node.IsMobyDriver() { for _, e := range so.Exports { @@ -561,6 +560,14 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[ } } } + // if prefer-image-digest is set in the solver options, remove the image + // config digest from the exporter's response + for _, e := range so.Exports { + if e.Attrs["prefer-image-digest"] == "true" { + delete(rr.ExporterResponse, exptypes.ExporterImageConfigDigestKey) + break + } + } return nil }) } diff --git a/build/opt.go b/build/opt.go index af4986e0..74fcdb38 100644 --- a/build/opt.go +++ b/build/opt.go @@ -237,6 +237,11 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O opt.Exports[i].Output = func(_ map[string]string) (io.WriteCloser, error) { return w, nil } + // if docker is using the containerd snapshotter, prefer to export the image digest + // (rather than the image config digest). See https://github.com/moby/moby/issues/45458. + if features[dockerutil.OCIImporter] { + opt.Exports[i].Attrs["prefer-image-digest"] = "true" + } } } else if !nodeDriver.Features(ctx)[driver.DockerExporter] { return nil, nil, notSupported(driver.DockerExporter, nodeDriver, "https://docs.docker.com/go/build-exporters/") diff --git a/tests/build.go b/tests/build.go index 09a75dc4..1e40c290 100644 --- a/tests/build.go +++ b/tests/build.go @@ -399,18 +399,25 @@ func testImageIDOutput(t *testing.T, sb integration.Sandbox) { require.Equal(t, dgst.String(), strings.TrimSpace(stdout.String())) + // read the md.json file dt, err = os.ReadFile(filepath.Join(targetDir, "md.json")) require.NoError(t, err) type mdT struct { + Digest string `json:"containerimage.digest"` ConfigDigest string `json:"containerimage.config.digest"` } + var md mdT err = json.Unmarshal(dt, &md) require.NoError(t, err) require.NotEmpty(t, md.ConfigDigest) - require.Equal(t, dgst, digest.Digest(md.ConfigDigest)) + require.NotEmpty(t, md.Digest) + + // verify the image ID output is correct + // XXX: improve this by checking that it's one of the two expected digests depending on the scenario. + require.Contains(t, []digest.Digest{digest.Digest(md.ConfigDigest), digest.Digest(md.Digest)}, dgst) } func testBuildMobyFromLocalImage(t *testing.T, sb integration.Sandbox) {