Skip to content

PE-8905: leaves-first push in WriteSignedEntity to heal dangling index children#35

Open
kirankn8 wants to merge 1 commit into
mainfrom
PE-8905
Open

PE-8905: leaves-first push in WriteSignedEntity to heal dangling index children#35
kirankn8 wants to merge 1 commit into
mainfrom
PE-8905

Conversation

@kirankn8

Copy link
Copy Markdown

Summary

When WriteSignedEntity writes a SignedImageIndex, it calls go-containerregistry's remote.WriteIndex, which short-circuits if the index manifest is already present at the destination tag. It does not verify the children exist. As a result, a re-push cannot heal a partially-broken index whose top-level digest is unchanged but one of its per-arch children was previously deleted (e.g. by registry garbage collection).

The fix walks the index's children first and pushes each one by digest (recursing into nested indexes) before publishing the index itself. goremote.Write/WriteIndex on each child still short-circuits when content is already present, so the steady-state cost is unchanged; missing children are restored.

Code change

pkg/oci/remote/write.goWriteSignedEntity's SignedImageIndex branch now calls a new writeIndexLeavesFirst helper instead of goremote.WriteIndex directly:

case oci.SignedImageIndex:
    fmt.Println("writing signed image index to", dst.Name())
-   if err := goremote.WriteIndex(dst, si, o.ROpt...); err != nil {
+   if err := writeIndexLeavesFirst(dst, si, o.ROpt...); err != nil {
        return fmt.Errorf("writing index: %w", err)
    }

writeIndexLeavesFirst walks IndexManifest().Manifests, pushes each leaf image by digest with goremote.Write, recurses into nested indexes, and finally calls goremote.WriteIndex to bind the tag.

Test plan

  • New unit test TestWriteSignedEntity_IndexHealsMissingChild (in pkg/oci/remote/write_test.go) uses github.com/google/go-containerregistry/pkg/registry to spin up an in-memory OCI registry, builds a real multi-arch index, deletes one per-arch child manifest to simulate a GC pass, re-pushes, and asserts the deleted child is restored.
  • All pre-existing pkg/oci/remote/ tests continue to pass.

Why now

This surfaced via PE-8905 — a downstream registry-side bug in Stylus where local-sync pushes single-arch manifests and spc-sync later overwrites the tag with a multi-arch index. The orphaned single-arch manifest is reaped by Zot GC, and on the next K8s upgrade containerd 404s on its cached old digest. Centralizing the fix here in cosign avoids per-caller workarounds in Stylus, Palette, and the bundle library.

Downstream propagation

After merge:

  • bump github.com/spectrocloud/cosign/v3 pseudo-version in spectrocloud/bundle's go.mod, cut v1.3.7.
  • bump github.com/spectrocloud/bundle in spectrocloud/stylus's go.mod to v1.3.7.

Made with Cursor

@kirankn8 kirankn8 changed the title fix: leaves-first push in WriteSignedEntity to heal dangling index children PE-8905: leaves-first push in WriteSignedEntity to heal dangling index children Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant