Repository navigation
Protect every repository of a referenced digest from image retention - #520
Conversation
Converted image content is shared by digest across repositories, but the retention sweep only protected the repository named by an instance or snapshot. When a build's image was pushed to another registry and an instance was created from the pushed name, the original builds/<id> image looked unused, and deleting it removed the shared content the instance needed to restore from standby.
chruffins
left a comment
There was a problem hiding this comment.
approving.
follow-up (pre-existing, not introduced by this PR, so not blocking): reference collection in lib/imageretention/controller.go (collectReferencedImageNames, ~lines 378-401) reads only StoredMetadata.Image. that field holds the caller's tag. boot, start, and restore use ResolvedImage, the digest pinned at create time (bootImageRef in lib/instances/create_image.go). if a tag moves to a new digest, the old digest and its build alias can still age out, and the instance then fails to restore. the README's "every repository whose image resolves to a referenced digest is kept" holds only for references that are collected.
suggested fix: collect ResolvedImage with a fallback to Image for both instances and snapshots, matching bootImageRef. that needs bootImageRef exported or an equivalent helper.
Reference collection read only the image name the caller passed, but boot, start, and restore use the digest pinned at create time. If the caller's tag later moved, the pinned digest aged out and the instance could no longer restore. Collect both names.
|
Took the follow-up here in 2a1e29e rather than leaving it: both |
Summary
Converted image content lives once per digest and is shared by every repository that resolves to it. The retention sweep built its protected set from the repository plus digest named by each instance and snapshot, so an image of the same digest under a different repository looked unused.
That bites when a build's image is pushed to another registry and an instance is created from the pushed name. The instance protects
<registry>/<repo>@sha256:…, but the originalbuilds/<id>@sha256:…ages out of the allow window and gets deleted, which removes the shared content. The instance can then no longer restore from standby:failed to resolve image for restore: image not found.The sweep now also keys protection by digest. Any ready image whose digest is referenced is skipped, and its retention state is cleared so the unused timer restarts if the reference later disappears. Images are otherwise treated as before.
Validation
go test -race ./lib/imageretentionTestSweepProtectsAliasOfReferencedDigestseeds the same digest underbuilds/abcand a digest-form registry name, references only the registry name from an instance, and expires thebuilds/abcstate. It fails on main and passes with the change.Note
Medium Risk
Changes when cached images are deleted can affect restore/standby if protection logic is wrong; scope is limited to retention sweep and only expands the protected set.
Overview
Image retention now protects shared digest content across all repository aliases, fixing cases where a build image under
builds/<id>was deleted while an instance still referenced the same digest under a pushed registry name.The sweep builds a digest-level protected set from instance and snapshot references: any ready cached image whose digest is referenced is skipped for deletion, and stale unused state on alias repos (e.g.
builds/abcwhen only ECR@sha256:…is referenced) is cleared.Reference collection now includes both
StoredMetadata.ImageandResolvedImage, so the digest pinned at instance create time stays protected when a mutable tag later points at different content.README documents digest-wide protection. New tests cover alias protection and tag-move scenarios.
Reviewed by Cursor Bugbot for commit 2a1e29e. Bugbot is set up for automated code reviews on this repo. Configure here.
Follow-up in the second commit
Reference collection read only
StoredMetadata.Image, the name the caller passed. Boot, start, and restore useResolvedImage, the digest pinned at create time, so a tag that later moved left the pinned digest unprotected and the instance unable to restore. Both names are now collected for instances and snapshots. This only adds to the protected set.TestSweepProtectsResolvedDigestAfterTagMovescovers it and fails without the change.