Browse code

builder: implement ref checker

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
Signed-off-by: Tibor Vass <tibor@docker.com>

Tonis Tiigi authored on 2018/09/01 10:28:25
Showing 3 changed files
... ...
@@ -110,6 +110,10 @@ func (s *snapshotter) chainID(key string) (layer.ChainID, bool) {
110 110
 	return "", false
111 111
 }
112 112
 
113
+func (s *snapshotter) GetLayer(key string) (layer.Layer, error) {
114
+	return s.getLayer(key, true)
115
+}
116
+
113 117
 func (s *snapshotter) getLayer(key string, withCommitted bool) (layer.Layer, error) {
114 118
 	s.mu.Lock()
115 119
 	l, ok := s.refs[key]
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/docker/docker/builder/builder-next/adapters/containerimage"
10 10
 	"github.com/docker/docker/builder/builder-next/adapters/snapshot"
11 11
 	containerimageexp "github.com/docker/docker/builder/builder-next/exporter"
12
+	"github.com/docker/docker/builder/builder-next/imagerefchecker"
12 13
 	mobyworker "github.com/docker/docker/builder/builder-next/worker"
13 14
 	"github.com/docker/docker/daemon/graphdriver"
14 15
 	"github.com/moby/buildkit/cache"
... ...
@@ -69,11 +70,20 @@ func newController(rt http.RoundTripper, opt Opt) (*control.Controller, error) {
69 69
 		MetadataStore: md,
70 70
 	})
71 71
 
72
+	layerGetter, ok := sbase.(imagerefchecker.LayerGetter)
73
+	if !ok {
74
+		return nil, errors.Errorf("snapshotter does not implement layergetter")
75
+	}
76
+
77
+	refChecker := imagerefchecker.New(imagerefchecker.Opt{
78
+		ImageStore:  dist.ImageStore,
79
+		LayerGetter: layerGetter,
80
+	})
81
+
72 82
 	cm, err := cache.NewManager(cache.ManagerOpt{
73
-		Snapshotter:   snapshotter,
74
-		MetadataStore: md,
75
-		// TODO: implement PruneRefChecker to correctly mark cache objects as "Shared"
76
-		PruneRefChecker: nil,
83
+		Snapshotter:     snapshotter,
84
+		MetadataStore:   md,
85
+		PruneRefChecker: refChecker,
77 86
 	})
78 87
 	if err != nil {
79 88
 		return nil, err
80 89
new file mode 100644
... ...
@@ -0,0 +1,96 @@
0
+package imagerefchecker
1
+
2
+import (
3
+	"sync"
4
+
5
+	"github.com/docker/docker/image"
6
+	"github.com/docker/docker/layer"
7
+	"github.com/moby/buildkit/cache"
8
+)
9
+
10
+// LayerGetter abstracts away the snapshotter
11
+type LayerGetter interface {
12
+	GetLayer(string) (layer.Layer, error)
13
+}
14
+
15
+// Opt represents the options needed to create a refchecker
16
+type Opt struct {
17
+	LayerGetter LayerGetter
18
+	ImageStore  image.Store
19
+}
20
+
21
+// New creates new image reference checker that can be used to see if a reference
22
+// is being used by any of the images in the image store
23
+func New(opt Opt) cache.ExternalRefCheckerFunc {
24
+	return func() (cache.ExternalRefChecker, error) {
25
+		return &checker{opt: opt, layers: lchain{}, cache: map[string]bool{}}, nil
26
+	}
27
+}
28
+
29
+type lchain map[layer.DiffID]lchain
30
+
31
+func (c lchain) add(ids []layer.DiffID) {
32
+	if len(ids) == 0 {
33
+		return
34
+	}
35
+	id := ids[0]
36
+	ch, ok := c[id]
37
+	if !ok {
38
+		ch = lchain{}
39
+		c[id] = ch
40
+	}
41
+	ch.add(ids[1:])
42
+}
43
+
44
+func (c lchain) has(ids []layer.DiffID) bool {
45
+	if len(ids) == 0 {
46
+		return true
47
+	}
48
+	ch, ok := c[ids[0]]
49
+	return ok && ch.has(ids[1:])
50
+}
51
+
52
+type checker struct {
53
+	opt    Opt
54
+	once   sync.Once
55
+	layers lchain
56
+	cache  map[string]bool
57
+}
58
+
59
+func (c *checker) Exists(key string) bool {
60
+	if c.opt.ImageStore == nil {
61
+		return false
62
+	}
63
+
64
+	c.once.Do(c.init)
65
+
66
+	if b, ok := c.cache[key]; ok {
67
+		return b
68
+	}
69
+
70
+	l, err := c.opt.LayerGetter.GetLayer(key)
71
+	if err != nil || l == nil {
72
+		c.cache[key] = false
73
+		return false
74
+	}
75
+
76
+	ok := c.layers.has(diffIDs(l))
77
+	c.cache[key] = ok
78
+	return ok
79
+}
80
+
81
+func (c *checker) init() {
82
+	imgs := c.opt.ImageStore.Map()
83
+
84
+	for _, img := range imgs {
85
+		c.layers.add(img.RootFS.DiffIDs)
86
+	}
87
+}
88
+
89
+func diffIDs(l layer.Layer) []layer.DiffID {
90
+	p := l.Parent()
91
+	if p == nil {
92
+		return []layer.DiffID{l.DiffID()}
93
+	}
94
+	return append(diffIDs(p), l.DiffID())
95
+}