summaryrefslogtreecommitdiff
path: root/daemon/containerd/cache.go
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/containerd/cache.go')
-rw-r--r--daemon/containerd/cache.go77
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, " ")
}