Pull: create image record after blobs download

When pull image with unpack option, the fetch action will defer blobs
download until unpack. If create image record in ImageService before
blobs download, the following requests to use image will fail because
there is still missing blobs download.

In order to fix concurrent issue, need to create image record after
blobs download.

Fix: #3937

Signed-off-by: Wei Fu <fuweid89@gmail.com>
This commit is contained in:
Wei Fu 2020-01-20 14:11:53 +08:00
parent 592a617f3d
commit 75af17946d
2 changed files with 31 additions and 5 deletions

View File

@ -390,7 +390,11 @@ func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (imag
}
defer done(ctx)
return c.fetch(ctx, fetchCtx, ref, 0)
img, err := c.fetch(ctx, fetchCtx, ref, 0)
if err != nil {
return images.Image{}, err
}
return c.createNewImage(ctx, img)
}
// Push uploads the provided content to a remote resource

30
pull.go
View File

@ -27,6 +27,7 @@ import (
"github.com/containerd/containerd/remotes/docker/schema1"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
)
@ -62,15 +63,18 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
defer done(ctx)
var unpacks int32
var unpackEg *errgroup.Group
var unpackWrapper func(f images.Handler) images.Handler
if pullCtx.Unpack {
// unpacker only supports schema 2 image, for schema 1 this is noop.
u, err := c.newUnpacker(ctx, pullCtx)
if err != nil {
return nil, errors.Wrap(err, "create unpacker")
}
unpackWrapper, eg := u.handlerWrapper(ctx, &unpacks)
unpackWrapper, unpackEg = u.handlerWrapper(ctx, &unpacks)
defer func() {
if err := eg.Wait(); err != nil {
if err := unpackEg.Wait(); err != nil {
if retErr == nil {
retErr = errors.Wrap(err, "unpack")
}
@ -90,6 +94,22 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (_ Ima
return nil, err
}
// NOTE(fuweid): unpacker defers blobs download. before create image
// record in ImageService, should wait for unpacking(including blobs
// download).
if pullCtx.Unpack {
if unpackEg != nil {
if err := unpackEg.Wait(); err != nil {
return nil, err
}
}
}
img, err = c.createNewImage(ctx, img)
if err != nil {
return nil, err
}
i := NewImageWithPlatform(c, img, pullCtx.PlatformMatcher)
if pullCtx.Unpack {
@ -201,12 +221,14 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, lim
}
}
img := images.Image{
return images.Image{
Name: name,
Target: desc,
Labels: rCtx.Labels,
}
}, nil
}
func (c *Client) createNewImage(ctx context.Context, img images.Image) (images.Image, error) {
is := c.ImageService()
for {
if created, err := is.Create(ctx, img); err != nil {