| 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) { |
| 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, " ") |
| } |