From 95e15632e8faef24e3ad51053ea2cc1d72ada99f Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 22 Jan 2026 14:54:12 -0800 Subject: [PATCH 1/3] imagetools: fix excessive copies on create command Currently needed manifests were filtered out and then copied in a loop, but for each copy still the full unfiltered descriptor was copied instead of single manifest, resulting multiple push attempts for same descriptor. Signed-off-by: Tonis Tiigi (cherry picked from commit 32e650f3df5884a602ecd52b649f9258a76aad42) --- commands/imagetools/create.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/commands/imagetools/create.go b/commands/imagetools/create.go index 95c59f0d8304..58d68db0bc2e 100644 --- a/commands/imagetools/create.go +++ b/commands/imagetools/create.go @@ -209,7 +209,10 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg eg2.Go(func() error { ctx = withMediaTypeKeyPrefix(baseCtx) sub.Log(1, fmt.Appendf(nil, "copying %s from %s to %s\n", desc.Digest.String(), desc.Source.Ref.String(), t.String())) - return r.Copy(ctx, desc.Source, t) + return r.Copy(ctx, &imagetools.Source{ + Ref: desc.Source.Ref, + Desc: desc.Descriptor, + }, t) }) } if err := eg2.Wait(); err != nil { From d8413302bff32b73140d0b183560796d1458cec1 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 22 Jan 2026 15:46:15 -0800 Subject: [PATCH 2/3] imagetools: avoid trying to load attestations inline references When creating resulting image index it is wasteful to check for attestations for the descriptors in original index what were already attestation for subject manifest. Signed-off-by: Tonis Tiigi (cherry picked from commit 631e822886c403f1495733f0d61421ca6e688341) --- util/imagetools/create.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/imagetools/create.go b/util/imagetools/create.go index 60db747a47fd..979b3f7793b4 100644 --- a/util/imagetools/create.go +++ b/util/imagetools/create.go @@ -429,6 +429,9 @@ func (r *Resolver) filterPlatforms(ctx context.Context, dt []byte, desc ocispecs // try to pull in attestation manifest via referrer if one exists addedRef := false for d := range matchedManifests { + if _, ok := references[d]; ok { // manifest itself is already attestation + continue + } hasRef := false for _, subject := range references { if subject.Digest == d { From 3fba85632f0b3b6a6da2e1b0eeb0e0774d337469 Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Thu, 22 Jan 2026 16:47:40 -0800 Subject: [PATCH 3/3] imagetools: avoid pushing to same repo in parallel If multiple tags are specified for the same repo, it is wasteful to use them as separate targets and better to make them share the blob upload phase of the push. Signed-off-by: Tonis Tiigi (cherry picked from commit 917d7f252f75ecd830dbb5d372686831fd817104) --- commands/imagetools/create.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/commands/imagetools/create.go b/commands/imagetools/create.go index 58d68db0bc2e..c02fc2ce686d 100644 --- a/commands/imagetools/create.go +++ b/commands/imagetools/create.go @@ -200,27 +200,39 @@ func runCreate(ctx context.Context, dockerCli command.Cli, in createOptions, arg eg, _ := errgroup.WithContext(ctx) pw := progress.WithPrefix(printer, "internal", true) + tagsByRepo := map[string][]reference.Named{} for _, t := range tags { + repo := t.Name() + tagsByRepo[repo] = append(tagsByRepo[repo], t) + } + + for repo, repoTags := range tagsByRepo { eg.Go(func() error { - return progress.Wrap(fmt.Sprintf("pushing %s", t.String()), pw.Write, func(sub progress.SubLogger) error { + seed := repoTags[0] + return progress.Wrap(fmt.Sprintf("pushing %s", repo), pw.Write, func(sub progress.SubLogger) error { baseCtx := ctx eg2, _ := errgroup.WithContext(ctx) for _, desc := range manifests { eg2.Go(func() error { ctx = withMediaTypeKeyPrefix(baseCtx) - sub.Log(1, fmt.Appendf(nil, "copying %s from %s to %s\n", desc.Digest.String(), desc.Source.Ref.String(), t.String())) + sub.Log(1, fmt.Appendf(nil, "copying %s from %s to %s\n", desc.Digest.String(), desc.Source.Ref.String(), repo)) return r.Copy(ctx, &imagetools.Source{ Ref: desc.Source.Ref, Desc: desc.Descriptor, - }, t) + }, seed) }) } if err := eg2.Wait(); err != nil { return err } ctx = withMediaTypeKeyPrefix(ctx) // because of containerd bug this needs to be called separately for each ctx/goroutine pair to avoid concurrent map write - sub.Log(1, fmt.Appendf(nil, "pushing %s to %s\n", desc.Digest.String(), t.String())) - return r.Push(ctx, t, desc, dt) + for _, t := range repoTags { + sub.Log(1, fmt.Appendf(nil, "pushing %s to %s\n", desc.Digest.String(), t.String())) + if err := r.Push(ctx, t, desc, dt); err != nil { + return err + } + } + return nil }) }) }