From 21de7d207874bb848c943f68cf1bd90a5899dc7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Mon, 13 Jul 2026 17:16:48 +0200 Subject: [PATCH] build: Fix iidfile for containerd-backed Docker driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Images loaded into Docker's containerd image store are identified by their manifest or index digest, while the legacy graphdriver store uses the config digest. buildx prefers containerimage.config.digest when the Moby exporter returns both digests. Some exporter responses still produce a usable iidfile, but affected builds, such as those with attestations disabled, write a config digest that Docker image commands cannot resolve. Expose the Docker driver's image-store mode as a feature and set prefer-image-digest for containerd-backed Moby exports. Signed-off-by: Paweł Gronowski --- build/opt.go | 4 ++++ commands/build.go | 2 +- driver/docker/driver.go | 13 +++++++------ driver/features.go | 1 + tests/build.go | 11 +++++++++-- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/build/opt.go b/build/opt.go index 78aa7c533e69..c32eb0ceea68 100644 --- a/build/opt.go +++ b/build/opt.go @@ -491,6 +491,10 @@ func toSolveOpt(ctx context.Context, np *noderesolver.ResolvedNode, multiDriver } if e.Type == "image" && nodeDriver.IsMobyDriver() { opt.Exports[i].Type = "moby" + // The containerd image store resolves images by manifest or index digest. + if nodeDriver.Features(ctx)[driver.PreferImageDigest] { + opt.Exports[i].Attrs["prefer-image-digest"] = "true" + } if e.Attrs["push"] != "" { if ok, _ := strconv.ParseBool(e.Attrs["push"]); ok { if ok, _ := strconv.ParseBool(e.Attrs["push-by-digest"]); ok { diff --git a/commands/build.go b/commands/build.go index 6c310cd6f190..04d20bea6e41 100644 --- a/commands/build.go +++ b/commands/build.go @@ -463,7 +463,7 @@ func runBuild(ctx context.Context, dockerCli command.Cli, debugOpts debuggerOpti return nil } -// getImageID returns the image ID - the digest of the image config +// getImageID returns the image identifier selected for the export destination. func getImageID(resp map[string]string) string { dgst := resp[exptypes.ExporterImageDigestKey] if v, ok := resp[exptypes.ExporterImageConfigDigestKey]; ok { diff --git a/driver/docker/driver.go b/driver/docker/driver.go index 787f323d5175..402509b613b7 100644 --- a/driver/docker/driver.go +++ b/driver/docker/driver.go @@ -92,12 +92,13 @@ func (d *Driver) Features(ctx context.Context) map[driver.Feature]bool { c.Close() } d.features.list = map[driver.Feature]bool{ - driver.OCIExporter: useContainerdSnapshotter, - driver.DockerExporter: useContainerdSnapshotter, - driver.CacheExport: useContainerdSnapshotter, - driver.MultiPlatform: useContainerdSnapshotter, - driver.DirectPush: useContainerdSnapshotter, - driver.DefaultLoad: true, + driver.OCIExporter: useContainerdSnapshotter, + driver.DockerExporter: useContainerdSnapshotter, + driver.CacheExport: useContainerdSnapshotter, + driver.MultiPlatform: useContainerdSnapshotter, + driver.DirectPush: useContainerdSnapshotter, + driver.PreferImageDigest: useContainerdSnapshotter, + driver.DefaultLoad: true, } }) return d.features.list diff --git a/driver/features.go b/driver/features.go index c12d9c7490f0..dec8623ce4e7 100644 --- a/driver/features.go +++ b/driver/features.go @@ -8,5 +8,6 @@ const DockerExporter Feature = "Docker exporter" const CacheExport Feature = "Cache export" const MultiPlatform Feature = "Multi-platform build" const DirectPush Feature = "Direct push" +const PreferImageDigest Feature = "Prefer image digest" const DefaultLoad Feature = "Automatically load images to the Docker Engine image store" diff --git a/tests/build.go b/tests/build.go index d3a69500e086..1ed34642e193 100644 --- a/tests/build.go +++ b/tests/build.go @@ -682,12 +682,19 @@ func testImageIDOutput(t *testing.T, sb integration.Sandbox) { err = json.Unmarshal(dt, &md) require.NoError(t, err) - require.NotEmpty(t, md.ConfigDigest) require.NotEmpty(t, md.Digest) + if !isMobyContainerdSnapWorker(sb) { + require.NotEmpty(t, md.ConfigDigest) + } // 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) + + if sb.DockerAddress() != "" { + cmd = dockerCmd(sb, withArgs("image", "inspect", imageID)) + out, err := cmd.CombinedOutput() + require.NoError(t, err, string(out)) + } } func testBuildMobyFromLocalImage(t *testing.T, sb integration.Sandbox) {