c8d: Make sure the content isn't removed while we export

This change add leases for all the content that will be exported, once
the image(s) are exported the lease is removed, thus letting
containerd's GC to do its job if needed. This fixes the case where
someone would remove an image that is still being exported.

This fixes the TestAPIImagesSaveAndLoad cli integration test.

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
This commit is contained in:
Djordje Lukic 2023-07-13 18:50:03 +02:00
parent ee79423124
commit f3a6b0fd08
No known key found for this signature in database

View file

@ -6,9 +6,11 @@ import (
"io"
"github.com/containerd/containerd"
"github.com/containerd/containerd/content"
cerrdefs "github.com/containerd/containerd/errdefs"
containerdimages "github.com/containerd/containerd/images"
"github.com/containerd/containerd/images/archive"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
cplatforms "github.com/containerd/containerd/platforms"
@ -58,11 +60,17 @@ func (i *ImageService) ExportImage(ctx context.Context, names []string, outStrea
archive.WithPlatform(platform),
}
ctx, release, err := i.client.WithLease(ctx)
contentStore := i.client.ContentStore()
leasesManager := i.client.LeasesService()
lease, err := leasesManager.Create(ctx, leases.WithRandomID())
if err != nil {
return errdefs.System(err)
}
defer release(ctx)
defer func() {
if err := leasesManager.Delete(ctx, lease); err != nil {
log.G(ctx).WithError(err).Warn("cleaning up lease")
}
}()
for _, name := range names {
target, err := i.resolveDescriptor(ctx, name)
@ -70,6 +78,10 @@ func (i *ImageService) ExportImage(ctx context.Context, names []string, outStrea
return err
}
if err = leaseContent(ctx, contentStore, leasesManager, lease, target); err != nil {
return err
}
// We may not have locally all the platforms that are specified in the index.
// Export only those manifests that we have.
// TODO(vvoland): Reconsider this when `--platform` is added.
@ -101,6 +113,30 @@ func (i *ImageService) ExportImage(ctx context.Context, names []string, outStrea
return i.client.Export(ctx, outStream, opts...)
}
// leaseContent will add a resource to the lease for each child of the descriptor making sure that it and
// its children won't be deleted while the lease exists
func leaseContent(ctx context.Context, store content.Store, leasesManager leases.Manager, lease leases.Lease, desc ocispec.Descriptor) error {
return containerdimages.Walk(ctx, containerdimages.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
_, err := store.Info(ctx, desc.Digest)
if err != nil {
if errors.Is(err, cerrdefs.ErrNotFound) {
return nil, nil
}
return nil, errdefs.System(err)
}
r := leases.Resource{
ID: desc.Digest.String(),
Type: "content",
}
if err := leasesManager.AddResource(ctx, lease, r); err != nil {
return nil, errdefs.System(err)
}
return containerdimages.Children(ctx, store, desc)
}), desc)
}
// LoadImage uploads a set of images into the repository. This is the
// complement of ExportImage. The input stream is an uncompressed tar
// ball containing images and metadata.