image_pull: check image already pulled

This is an optimization of our image pull code path. It's basically
how docker handles pulls as well. Let's be smart and check the image in
pull code path as well.
This also matches docker behavior which first checks whether we're
allowed to actually pull an image before looking into local storage.

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
Antonio Murdaca 2017-06-07 19:54:02 +02:00
parent cb4c6004fc
commit 3b545abf12
No known key found for this signature in database
GPG key ID: B2BEAD150DE936B9
2 changed files with 52 additions and 3 deletions

View file

@ -3,6 +3,7 @@ package storage
import ( import (
"github.com/containers/image/copy" "github.com/containers/image/copy"
"github.com/containers/image/docker/reference" "github.com/containers/image/docker/reference"
"github.com/containers/image/image"
"github.com/containers/image/signature" "github.com/containers/image/signature"
istorage "github.com/containers/image/storage" istorage "github.com/containers/image/storage"
"github.com/containers/image/transports/alltransports" "github.com/containers/image/transports/alltransports"
@ -38,6 +39,8 @@ type ImageServer interface {
// the image server uses to hold images, and is the destination used // the image server uses to hold images, and is the destination used
// when it's asked to pull an image. // when it's asked to pull an image.
GetStore() storage.Store GetStore() storage.Store
// CanPull preliminary checks whether we're allowed to pull an image
CanPull(imageName string, sourceCtx *types.SystemContext) (bool, error)
} }
func (svc *imageService) ListImages(filter string) ([]ImageResult, error) { func (svc *imageService) ListImages(filter string) ([]ImageResult, error) {
@ -116,6 +119,35 @@ func imageSize(img types.Image) *uint64 {
return nil return nil
} }
func (svc *imageService) CanPull(imageName string, sourceCtx *types.SystemContext) (bool, error) {
if imageName == "" {
return false, storage.ErrNotAnImage
}
srcRef, err := alltransports.ParseImageName(imageName)
if err != nil {
if svc.defaultTransport == "" {
return false, err
}
srcRef2, err2 := alltransports.ParseImageName(svc.defaultTransport + imageName)
if err2 != nil {
return false, err
}
srcRef = srcRef2
}
rawSource, err := srcRef.NewImageSource(sourceCtx, nil)
if err != nil {
return false, err
}
unparsedImage := image.UnparsedFromSource(rawSource)
defer unparsedImage.Close()
src, err := image.FromUnparsedImage(unparsedImage)
if err != nil {
return false, err
}
src.Close()
return true, nil
}
func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error) { func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error) {
policy, err := signature.DefaultPolicy(systemContext) policy, err := signature.DefaultPolicy(systemContext)
if err != nil { if err != nil {

View file

@ -21,6 +21,7 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P
if img != nil { if img != nil {
image = img.Image image = img.Image
} }
var ( var (
username string username string
password string password string
@ -36,7 +37,11 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P
} }
} }
} }
options := &copy.Options{} options := &copy.Options{
// TODO: we need a way to specify insecure registries like docker
//DockerInsecureSkipTLSVerify: true,
SourceCtx: &types.SystemContext{},
}
// a not empty username should be sufficient to decide whether to send auth // a not empty username should be sufficient to decide whether to send auth
// or not I guess // or not I guess
if username != "" { if username != "" {
@ -47,8 +52,20 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P
}, },
} }
} }
_, err := s.storageImageServer.PullImage(s.imageContext, image, options)
if err != nil { canPull, err := s.storageImageServer.CanPull(image, options.SourceCtx)
if err != nil && !canPull {
return nil, err
}
// let's be smart, docker doesn't repull if image already exists.
if _, err := s.storageImageServer.ImageStatus(s.imageContext, image); err == nil {
return &pb.PullImageResponse{
ImageRef: image,
}, nil
}
if _, err := s.storageImageServer.PullImage(s.imageContext, image, options); err != nil {
return nil, err return nil, err
} }
resp := &pb.PullImageResponse{ resp := &pb.PullImageResponse{