fix(image): force-pull when digest pinned but missing locally#39
Merged
Conversation
EnsureImage already proves at the first Inspect call that the requested ImageDigest isn't in the local cache. The subsequent Pull, however, passed force=false and so hit the URL-level short-circuit in images/cloudimg/pull.go that skips re-downloading when "any blob is already cached under this URL." For mutable tags (e.g. epoch's /dl/ endpoint pointing at the latest push of a repo), the cached blob can be a stale digest — exactly the scenario that left clones failing downstream in vm.restore with a "Backing file I/O error" against a qcow2 backing file whose digest doesn't exist on disk. Pass force=true to Pull when ImageDigest is set. Cheap path (no digest pinned) stays cheap. Slow path triggers only when the caller wants a specific digest and we've shown it isn't local — same semantics users already get from `cocoon image pull --force`. Fixes #37.
There was a problem hiding this comment.
Pull request overview
This PR fixes a clone/restore failure mode for cloud image URLs when the VM config pins a specific ImageDigest but the local URL→blob cache points at stale content. It makes EnsureImage force a re-pull when a digest is pinned, aligning EnsureImage behavior with cocoon image pull --force.
Changes:
- Update
cmd/core/helpers.go:EnsureImageto callPull(..., force=true, ...)whenvmCfg.ImageDigest != "". - Add unit tests to assert the
forcedecision and thatPullis skipped when the digest is already local.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| cmd/core/helpers.go | Forces pulls when ImageDigest is pinned to bypass cloudimg URL-cache short-circuiting. |
| cmd/core/ensure_image_test.go | Adds regression tests verifying EnsureImage passes the expected force flag and skips pulling when already local. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Collapse 6-line rationale to one line in EnsureImage force-pull guard. - Drop unused pullErr/postPullInspect from the test fake; tighten test docstrings.
This was referenced May 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
cmd/core/helpers.go:EnsureImagealready proves at the firstInspect(lookupRef=ImageDigest)that the requested digest isn't in the local cache. The subsequentPullthen passedforce=false, which letsimages/cloudimg/pull.go:62's URL-level short-circuit win:That short-circuit's policy is "any cached blob for this URL is fine" — perfect for non-digest-pinned reuse, wrong when the caller wants a specific digest. The URL→blob mapping in the local index can point at a stale digest (cached from a previous version of the upstream tag); the post-pull
Inspect(ImageDigest)then warns and returns, and the failure surfaces deep invm.restoreas aBacking file I/O erroragainst a qcow2 backing file that doesn't exist locally.This PR flips force=true exactly when
vmCfg.ImageDigest != "". Same semantics users get manually fromcocoon image pull --force.Why is this fine
Inspect(ImageDigest)validatesdigestPullRefalready convertsimage@sha256:...for OCI refs, which are digest-immutable; the OCI Pull happily handles the redundant force flagTest plan
cmd/core/ensure_image_test.go— fake backend recordsPull(force), asserts:ImageDigestpinned + digest missing locally →force=trueImageDigestset →force=falseImageDigestpinned + digest already local →Pullnot called at allgo test ./cmd/core/... -vpassesgo build ./...cleanVerifyBaseFilesRelated
Closes #37.
Companion work:
/dl/{name}/{ref}route adds tag- and digest-aware URL forms, so issue 38'scocoonstack.snapshot.baseimageannotations can use immutable digest URLs that pin perfectly via this fix