summaryrefslogtreecommitdiff
path: root/builder/builder-next/imagerefchecker/checker.go
blob: 7ab777a9cccb731c265a9b42aac1a73d968f91a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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())
}