dotcloud/docker

View on GitHub
builder/builder-next/imagerefchecker/checker.go

Summary

Maintainability
A
0 mins
Test Coverage
package imagerefchecker

import (
    "sync"

    "github.com/docker/docker/image"
    "github.com/docker/docker/layer"
    "github.com/moby/buildkit/cache"
    "github.com/opencontainers/go-digest"
)

// LayerGetter abstracts away the snapshotter
type LayerGetter interface {
    GetLayer(string) (layer.Layer, error)
}

// Opt represents the options needed to create a refchecker
type Opt struct {
    LayerGetter LayerGetter
    ImageStore  image.Store
}

// New creates new image reference checker that can be used to see if a reference
// is being used by any of the images in the image store
func New(opt Opt) cache.ExternalRefCheckerFunc {
    return func() (cache.ExternalRefChecker, error) {
        return &checker{opt: opt, layers: lchain{}, cache: map[string]bool{}}, nil
    }
}

type lchain map[layer.DiffID]lchain

func (c lchain) add(ids []layer.DiffID) {
    if len(ids) == 0 {
        return
    }
    id := ids[0]
    ch, ok := c[id]
    if !ok {
        ch = lchain{}
        c[id] = ch
    }
    ch.add(ids[1:])
}

func (c lchain) has(ids []layer.DiffID) bool {
    if len(ids) == 0 {
        return true
    }
    ch, ok := c[ids[0]]
    return ok && ch.has(ids[1:])
}

type checker struct {
    opt    Opt
    once   sync.Once
    layers lchain
    cache  map[string]bool
}

func (c *checker) Exists(key string, chain []digest.Digest) bool {
    if c.opt.ImageStore == nil {
        return false
    }

    c.once.Do(c.init)

    if b, ok := c.cache[key]; ok {
        return b
    }

    l, err := c.opt.LayerGetter.GetLayer(key)
    if err != nil || l == nil {
        c.cache[key] = false
        return false
    }

    ok := c.layers.has(diffIDs(l))
    c.cache[key] = ok
    return ok
}

func (c *checker) init() {
    imgs := c.opt.ImageStore.Map()

    for _, img := range imgs {
        c.layers.add(img.RootFS.DiffIDs)
    }
}

func diffIDs(l layer.Layer) []layer.DiffID {
    p := l.Parent()
    if p == nil {
        return []layer.DiffID{l.DiffID()}
    }
    return append(diffIDs(p), l.DiffID())
}