fix(r2): return source digest/size for mounted blob HEAD responses#121
fix(r2): return source digest/size for mounted blob HEAD responses#121mushanyoung wants to merge 2 commits into
Conversation
|
While running this against the OCI distribution-spec conformance suite I hit a defect in the symlink checksum-mismatch check — I opened #139 that builds on your approach with that fix plus regression tests — full credit to you for the design. Happy for you to cherry-pick just the fix here instead if you'd rather keep ownership; whatever's easiest for the maintainers. |
Co-authored-by: Massimiliano Ferrero <m.ferrero@cognitio.it>
f175890 to
f66091a
Compare
|
Thanks @m-ferrero, good catch. I agree the previous mismatch check was comparing the R2 checksum ArrayBuffer directly against the hex digest string, which made the mismatch path fire for every digest-addressed blob. I ported your #139 fix back into this PR by normalizing both sides to full Credit added in the follow-up commit with your
|
Title
fix(r2): return source digest/size for mounted blob HEAD responses
Summary
Mounted layers in the R2 backend are stored as lightweight "symlink" objects (body like
<repo>/blobs/<digest>).HEAD /v2/:name/blobs/:digestcould return metadata from that symlink object instead of the mounted source blob.This caused invalid blob metadata in HEAD responses for mounted layers, including:
Docker-Content-DigestContent-Lengthvalues (commonly around path-string length such as86)Those headers are consumed by OCI clients during push/pull validation and can surface as unauthorized/manifest-corruption style failures in downstream workflows.
Root Cause
Two pieces combined into the bug:
mountExistingLayerstores a symlink object whose checksum represents the symlink payload bytes (the path string), not the mounted source blob digest.HEADroute read R2 object metadata directly (env.REGISTRY.head(...)), bypassing symlink resolution logic.Result: for mounted blobs, HEAD returned symlink metadata instead of source blob metadata.
Changes
1) Persist source metadata on new symlink objects
File:
src/registry/r2.tsX-Serverless-Registry-Symlink-DigestX-Serverless-Registry-Symlink-Size2) Resolve symlinks in
layerExists(including old symlinks)File:
src/registry/r2.ts<repo>/blobs/<digest>, then recursively querylayerExistson the source target.3) Route blob HEAD through
layerExistsFile:
src/router.tsHEAD /v2/:name+/blobs/:tagto useenv.REGISTRY_CLIENT.layerExists(name, tag)for local lookups.4) Add regression assertions
File:
test/index.test.tsDocker-Content-Digest == layer digestContent-Length == source blob byte lengthReproduction (before this patch)
repoA.repoBthat mounts the same layer fromrepoA.HEAD /v2/repoB/blobs/<mounted-layer-digest>.Content-Lengthcan be tiny (e.g. path-length values such as86)Behavior After Patch
For mounted blobs,
HEADnow returns source blob metadata (digest + size), including for previously-created symlink objects.Compatibility and Risk
Test Plan
Executed:
npm test -- test/index.test.ts -t "Upload manifests with recursive layer mounting"npm test -- test/index.test.tsBoth pass.