github.com/rawahars/moby@v24.0.4+incompatible/daemon/containerd/cache.go (about) 1 package containerd 2 3 import ( 4 "context" 5 "reflect" 6 "strings" 7 8 "github.com/docker/docker/api/types/container" 9 imagetype "github.com/docker/docker/api/types/image" 10 "github.com/docker/docker/builder" 11 "github.com/docker/docker/image" 12 ) 13 14 // MakeImageCache creates a stateful image cache. 15 func (i *ImageService) MakeImageCache(ctx context.Context, cacheFrom []string) (builder.ImageCache, error) { 16 images := []*image.Image{} 17 for _, c := range cacheFrom { 18 im, err := i.GetImage(ctx, c, imagetype.GetImageOpts{}) 19 if err != nil { 20 return nil, err 21 } 22 images = append(images, im) 23 } 24 return &imageCache{images: images, c: i}, nil 25 } 26 27 type imageCache struct { 28 images []*image.Image 29 c *ImageService 30 } 31 32 func (ic *imageCache) GetCache(parentID string, cfg *container.Config) (imageID string, err error) { 33 ctx := context.TODO() 34 35 if parentID == "" { 36 // TODO handle "parentless" image cache lookups ("FROM scratch") 37 return "", nil 38 } 39 40 parent, err := ic.c.GetImage(ctx, parentID, imagetype.GetImageOpts{}) 41 if err != nil { 42 return "", err 43 } 44 45 for _, localCachedImage := range ic.images { 46 if isMatch(localCachedImage, parent, cfg) { 47 return localCachedImage.ID().String(), nil 48 } 49 } 50 51 children, err := ic.c.Children(ctx, parent.ID()) 52 if err != nil { 53 return "", err 54 } 55 56 for _, children := range children { 57 childImage, err := ic.c.GetImage(ctx, children.String(), imagetype.GetImageOpts{}) 58 if err != nil { 59 return "", err 60 } 61 62 if isMatch(childImage, parent, cfg) { 63 return children.String(), nil 64 } 65 } 66 67 return "", nil 68 } 69 70 // isMatch checks whether a given target can be used as cache for the given 71 // parent image/config combination. 72 // A target can only be an immediate child of the given parent image. For 73 // a parent image with `n` history entries, a valid target must have `n+1` 74 // entries and the extra entry must match the provided config 75 func isMatch(target, parent *image.Image, cfg *container.Config) bool { 76 if target == nil || parent == nil || cfg == nil { 77 return false 78 } 79 80 if len(target.History) != len(parent.History)+1 || 81 len(target.RootFS.DiffIDs) != len(parent.RootFS.DiffIDs)+1 { 82 return false 83 } 84 85 for i := range parent.History { 86 if !reflect.DeepEqual(parent.History[i], target.History[i]) { 87 return false 88 } 89 } 90 91 childCreatedBy := target.History[len(target.History)-1].CreatedBy 92 return childCreatedBy == strings.Join(cfg.Cmd, " ") 93 }