mutate: close layer readers during export by gnix0 · Pull Request #2277 · google/go-containerregistry (original) (raw)
crane export uses mutate.Extract for normal layered images. mutate.Extract opened each layer reader in sequence, but deferred each Close until the whole extraction returned. After google#2271, remote layer readers hold a pull job slot until their response body is closed, so extraction could fill all available slots and then block opening the next layer.
The single-layer crane export path also copied from Uncompressed without closing the reader.
Close each extraction layer reader as soon as that layer is processed, and close the single-layer export reader after copying.
Fixes google#2276
Reproduced: Added a test image whose layer readers model the remote pull limiter: opening a reader consumes one of four slots, and Close releases it. Before the fix, extracting five layers failed when the fifth reader opened before the previous readers were closed. After the fix, extraction completes and all slots are released.
Tested: go test ./pkg/v1/mutate go test ./pkg/crane go test ./pkg/v1/remote ./pkg/v1/mutate ./pkg/crane ./cmd/crane/rebase_test.sh
approved these changes Apr 28, 2026
Subserial pushed a commit to Subserial/go-containerregistry that referenced this pull request
crane export uses mutate.Extract for normal layered images. mutate.Extract opened each layer reader in sequence, but deferred each Close until the whole extraction returned. After google#2271, remote layer readers hold a pull job slot until their response body is closed, so extraction could fill all available slots and then block opening the next layer.
The single-layer crane export path also copied from Uncompressed without closing the reader.
Close each extraction layer reader as soon as that layer is processed, and close the single-layer export reader after copying.
Fixes google#2276
Reproduced: Added a test image whose layer readers model the remote pull limiter: opening a reader consumes one of four slots, and Close releases it. Before the fix, extracting five layers failed when the fifth reader opened before the previous readers were closed. After the fix, extraction completes and all slots are released.
Tested: go test ./pkg/v1/mutate go test ./pkg/crane go test ./pkg/v1/remote ./pkg/v1/mutate ./pkg/crane ./cmd/crane/rebase_test.sh
This was referenced
May 19, 2026
nandbhat added a commit to nandbhat/go-containerregistry that referenced this pull request
tarball.MultiRefWrite (and crane pull, which uses it) opens each layer's
Compressed() reader and copies it into the tar without closing it.
After google#2271, remote layer readers hold a pull-job slot until their
response body is closed, so writing more than defaultJobs layers
fills all available slots and deadlocks the next layer open.
This is the symmetric leak to the one google#2277 fixed in mutate.Extract / crane export. Close each layer reader as soon as that layer is written.
Reproduced: crane pull postgres:16 /tmp/pg.tar # v0.21.6 hangs; v0.21.5 works
Added TestWriteClosesLayerBeforeOpeningNext, which wraps a random.Image with a token-limited Compressed() that fails if a reader is opened before previous readers were closed. Before this change the test fails with "layer reader opened before previous readers were closed". After this change the test passes and the limiter is fully drained.
Tested: go test ./pkg/v1/tarball go test ./pkg/v1/tarball ./pkg/v1/mutate ./pkg/crane Signed-off-by: Nandan Bhat nandanbhatd@gmail.com
Subserial pushed a commit that referenced this pull request
- tarball: close layer readers during Write
tarball.MultiRefWrite (and crane pull, which uses it) opens each layer's
Compressed() reader and copies it into the tar without closing it.
After #2271, remote layer readers hold a pull-job slot until their
response body is closed, so writing more than defaultJobs layers
fills all available slots and deadlocks the next layer open.
This is the symmetric leak to the one #2277 fixed in mutate.Extract / crane export. Close each layer reader as soon as that layer is written.
Reproduced: crane pull postgres:16 /tmp/pg.tar # v0.21.6 hangs; v0.21.5 works
Added TestWriteClosesLayerBeforeOpeningNext, which wraps a random.Image with a token-limited Compressed() that fails if a reader is opened before previous readers were closed. Before this change the test fails with "layer reader opened before previous readers were closed". After this change the test passes and the limiter is fully drained.
Tested: go test ./pkg/v1/tarball go test ./pkg/v1/tarball ./pkg/v1/mutate ./pkg/crane Signed-off-by: Nandan Bhat nandanbhatd@gmail.com
- tarball: place writeLayer next to writeTarEntry
Address review feedback on #2308: the previous patch inserted writeLayer between writeTarEntry's leading doc-comment and its declaration, orphaning the comment. Move writeLayer immediately after writeTarEntry so the existing function block is untouched, and tighten writeLayer's own doc-comment to the non-obvious WHY (pull-limiter slot release).
No behavioral change.
Signed-off-by: Nandan Bhat nandanbhatd@gmail.com
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
[ Show hidden characters]({{ revealButtonHref }})