Merge pull request #6558 from tonistiigi/fix-parent-layer-not-found

exporter: fix snapshot GC race during image unpack
This commit is contained in:
Tõnis Tiigi
2026-03-09 16:44:54 -07:00
committed by GitHub

View File

@@ -501,17 +501,33 @@ func (e *imageExporterInstance) unpackImage(ctx context.Context, img images.Imag
ctrdSnapshotter, release := snapshot.NewContainerdSnapshotter(snapshotter)
defer release()
var chain []digest.Digest
for _, layer := range layers {
if _, err := rootfs.ApplyLayer(ctx, layer, chain, ctrdSnapshotter, applier); err != nil {
return err
// Compute top chainID so we can add it to the lease before calling ApplyLayers
// as ApplyLayers may directly return after successful Stat call without applying
// layer to the lease and causing error if it gets deleted.
chainID := layersChainID(layers)
if leaseID, ok := leases.FromContext(ctx); ok {
r := leases.Resource{
ID: chainID.String(),
Type: "snapshots/" + snapshotter.Name(),
}
chain = append(chain, layer.Diff.Digest)
if err := e.opt.LeaseManager.AddResource(ctx, leases.Lease{ID: leaseID}, r); err != nil {
return errors.Wrapf(err, "failed to lease snapshot %s", chainID)
}
}
// note that calling ApplyLayer in a loop here as alternative is not safe because
// single ApplyLayer does not have a safe way to ensure parents are not removed during unpack.
appliedChainID, err := rootfs.ApplyLayers(ctx, layers, ctrdSnapshotter, applier)
if err != nil {
return err
}
if appliedChainID != chainID {
return errors.Errorf("unexpected chain ID mismatch: %s != %s", appliedChainID, chainID)
}
var (
keyGCLabel = fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", snapshotter.Name())
valueGCLabel = identity.ChainID(chain).String()
valueGCLabel = chainID.String()
)
cinfo := content.Info{
@@ -538,6 +554,14 @@ func getLayers(descs []ocispecs.Descriptor, manifest ocispecs.Manifest) ([]rootf
return layers, nil
}
func layersChainID(layers []rootfs.Layer) digest.Digest {
chain := make([]digest.Digest, len(layers))
for i, l := range layers {
chain[i] = l.Diff.Digest
}
return identity.ChainID(chain)
}
func addAnnotations(m map[digest.Digest]map[string]string, desc ocispecs.Descriptor) {
if desc.Annotations == nil {
return