123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- 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()
- if parentID == "" {
- // TODO handle "parentless" image cache lookups ("FROM scratch")
- return "", nil
- }
- 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, " ")
- }
|