diff options
Diffstat (limited to 'daemon/containerd/cache.go')
-rw-r--r-- | daemon/containerd/cache.go | 77 |
1 files changed, 76 insertions, 1 deletions
diff --git a/daemon/containerd/cache.go b/daemon/containerd/cache.go index e066e8ae87..8bd01768f8 100644 --- a/daemon/containerd/cache.go +++ b/daemon/containerd/cache.go @@ -2,11 +2,86 @@ package containerd import ( "context" + "reflect" + "strings" + "github.com/docker/docker/api/types/container" + imagetype "github.com/docker/docker/api/types/image" "github.com/docker/docker/builder" + "github.com/docker/docker/image" ) // MakeImageCache creates a stateful image cache. func (i *ImageService) MakeImageCache(ctx context.Context, cacheFrom []string) (builder.ImageCache, error) { - panic("not implemented") + images := []*image.Image{} + for _, c := range cacheFrom { + im, err := i.GetImage(ctx, c, imagetype.GetImageOpts{}) + if err != nil { + return nil, err + } + images = append(images, im) + } + return &imageCache{images: images, c: i}, nil +} + +type imageCache struct { + images []*image.Image + c *ImageService +} + +func (ic *imageCache) GetCache(parentID string, cfg *container.Config) (imageID string, err error) { + ctx := context.TODO() + parent, err := ic.c.GetImage(ctx, parentID, imagetype.GetImageOpts{}) + if err != nil { + return "", err + } + + for _, localCachedImage := range ic.images { + if isMatch(localCachedImage, parent, cfg) { + return localCachedImage.ID().String(), nil + } + } + + children, err := ic.c.Children(ctx, parent.ID()) + if err != nil { + return "", err + } + + for _, children := range children { + childImage, err := ic.c.GetImage(ctx, children.String(), imagetype.GetImageOpts{}) + if err != nil { + return "", err + } + + if isMatch(childImage, parent, cfg) { + return children.String(), nil + } + } + + return "", nil +} + +// isMatch checks whether a given target can be used as cache for the given +// parent image/config combination. +// A target can only be an immediate child of the given parent image. For +// a parent image with `n` history entries, a valid target must have `n+1` +// entries and the extra entry must match the provided config +func isMatch(target, parent *image.Image, cfg *container.Config) bool { + if target == nil || parent == nil || cfg == nil { + return false + } + + if len(target.History) != len(parent.History)+1 || + len(target.RootFS.DiffIDs) != len(parent.RootFS.DiffIDs)+1 { + return false + } + + for i := range parent.History { + if !reflect.DeepEqual(parent.History[i], target.History[i]) { + return false + } + } + + childCreatedBy := target.History[len(target.History)-1].CreatedBy + return childCreatedBy == strings.Join(cfg.Cmd, " ") } |