1 package image
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "sync"
8 "time"
9
10 "github.com/containerd/log"
11 "github.com/docker/docker/errdefs"
12 "github.com/docker/docker/layer"
13 "github.com/opencontainers/go-digest"
14 "github.com/opencontainers/go-digest/digestset"
15 "github.com/pkg/errors"
16 )
17
18
19 type Store interface {
20 Create(config []byte) (ID, error)
21 Get(id ID) (*Image, error)
22 Delete(id ID) ([]layer.Metadata, error)
23 Search(partialID string) (ID, error)
24 SetParent(id ID, parent ID) error
25 GetParent(id ID) (ID, error)
26 SetLastUpdated(id ID) error
27 GetLastUpdated(id ID) (time.Time, error)
28 SetBuiltLocally(id ID) error
29 IsBuiltLocally(id ID) (bool, error)
30 Children(id ID) []ID
31 Map() map[ID]*Image
32 Heads() map[ID]*Image
33 Len() int
34 }
35
36
37 type LayerGetReleaser interface {
38 Get(layer.ChainID) (layer.Layer, error)
39 Release(layer.Layer) ([]layer.Metadata, error)
40 }
41
42 type imageMeta struct {
43 layer layer.Layer
44 children map[ID]struct{}
45 }
46
47 type store struct {
48 sync.RWMutex
49 lss LayerGetReleaser
50 images map[ID]*imageMeta
51 fs StoreBackend
52 digestSet *digestset.Set
53 }
54
55
56 func NewImageStore(fs StoreBackend, lss LayerGetReleaser) (Store, error) {
57 is := &store{
58 lss: lss,
59 images: make(map[ID]*imageMeta),
60 fs: fs,
61 digestSet: digestset.NewSet(),
62 }
63
64
65 if err := is.restore(); err != nil {
66 return nil, err
67 }
68
69 return is, nil
70 }
71
72 func (is *store) restore() error {
73
74
75
76
77
78
79 type f = log.Fields
80 err := is.fs.Walk(func(dgst digest.Digest) error {
81 img, err := is.Get(ID(dgst))
82 if err != nil {
83 log.G(context.TODO()).WithFields(f{"digest": dgst, "err": err}).Error("invalid image")
84 return nil
85 }
86 var l layer.Layer
87 if chainID := img.RootFS.ChainID(); chainID != "" {
88 if err := CheckOS(img.OperatingSystem()); err != nil {
89 log.G(context.TODO()).WithFields(f{"chainID": chainID, "os": img.OperatingSystem()}).Error("not restoring image with unsupported operating system")
90 return nil
91 }
92 l, err = is.lss.Get(chainID)
93 if err != nil {
94 if errors.Is(err, layer.ErrLayerDoesNotExist) {
95 log.G(context.TODO()).WithFields(f{"chainID": chainID, "os": img.OperatingSystem(), "err": err}).Error("not restoring image")
96 return nil
97 }
98 return err
99 }
100 }
101 if err := is.digestSet.Add(dgst); err != nil {
102 return err
103 }
104
105 is.images[ID(dgst)] = &imageMeta{
106 layer: l,
107 children: make(map[ID]struct{}),
108 }
109
110 return nil
111 })
112 if err != nil {
113 return err
114 }
115
116
117 for id := range is.images {
118 if parent, err := is.GetParent(id); err == nil {
119 if parentMeta := is.images[parent]; parentMeta != nil {
120 parentMeta.children[id] = struct{}{}
121 }
122 }
123 }
124
125 return nil
126 }
127
128 func (is *store) Create(config []byte) (ID, error) {
129 var img *Image
130 img, err := NewFromJSON(config)
131 if err != nil {
132 return "", err
133 }
134
135
136
137 rootFSLayers := make(map[layer.DiffID]struct{})
138 for _, diffID := range img.RootFS.DiffIDs {
139 rootFSLayers[diffID] = struct{}{}
140 }
141
142 layerCounter := 0
143 for _, h := range img.History {
144 if !h.EmptyLayer {
145 layerCounter++
146 }
147 }
148 if layerCounter > len(img.RootFS.DiffIDs) {
149 return "", errdefs.InvalidParameter(errors.New("too many non-empty layers in History section"))
150 }
151
152 imageDigest, err := is.fs.Set(config)
153 if err != nil {
154 return "", errdefs.InvalidParameter(err)
155 }
156
157 is.Lock()
158 defer is.Unlock()
159
160 imageID := ID(imageDigest)
161 if _, exists := is.images[imageID]; exists {
162 return imageID, nil
163 }
164
165 layerID := img.RootFS.ChainID()
166
167 var l layer.Layer
168 if layerID != "" {
169 if err := CheckOS(img.OperatingSystem()); err != nil {
170 return "", err
171 }
172 l, err = is.lss.Get(layerID)
173 if err != nil {
174 return "", errdefs.InvalidParameter(errors.Wrapf(err, "failed to get layer %s", layerID))
175 }
176 }
177
178 is.images[imageID] = &imageMeta{
179 layer: l,
180 children: make(map[ID]struct{}),
181 }
182
183 if err = is.digestSet.Add(imageDigest); err != nil {
184 delete(is.images, imageID)
185 return "", errdefs.InvalidParameter(err)
186 }
187
188 return imageID, nil
189 }
190
191 type imageNotFoundError string
192
193 func (e imageNotFoundError) Error() string {
194 return "No such image: " + string(e)
195 }
196
197 func (imageNotFoundError) NotFound() {}
198
199 func (is *store) Search(term string) (ID, error) {
200 dgst, err := is.digestSet.Lookup(term)
201 if err != nil {
202 if err == digestset.ErrDigestNotFound {
203 err = imageNotFoundError(term)
204 }
205 return "", errors.WithStack(err)
206 }
207 return ID(dgst), nil
208 }
209
210 func (is *store) Get(id ID) (*Image, error) {
211
212
213 config, err := is.fs.Get(id.Digest())
214 if err != nil {
215 return nil, errdefs.NotFound(err)
216 }
217
218 img, err := NewFromJSON(config)
219 if err != nil {
220 return nil, errdefs.InvalidParameter(err)
221 }
222 img.computedID = id
223
224 img.Parent, err = is.GetParent(id)
225 if err != nil {
226 img.Parent = ""
227 }
228
229 return img, nil
230 }
231
232 func (is *store) Delete(id ID) ([]layer.Metadata, error) {
233 is.Lock()
234 defer is.Unlock()
235
236 imgMeta := is.images[id]
237 if imgMeta == nil {
238 return nil, errdefs.NotFound(fmt.Errorf("unrecognized image ID %s", id.String()))
239 }
240 _, err := is.Get(id)
241 if err != nil {
242 return nil, errdefs.NotFound(fmt.Errorf("unrecognized image %s, %v", id.String(), err))
243 }
244 for cID := range imgMeta.children {
245 is.fs.DeleteMetadata(cID.Digest(), "parent")
246 }
247 if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
248 delete(is.images[parent].children, id)
249 }
250
251 if err := is.digestSet.Remove(id.Digest()); err != nil {
252 log.G(context.TODO()).Errorf("error removing %s from digest set: %q", id, err)
253 }
254 delete(is.images, id)
255 is.fs.Delete(id.Digest())
256
257 if imgMeta.layer != nil {
258 return is.lss.Release(imgMeta.layer)
259 }
260 return nil, nil
261 }
262
263 func (is *store) SetParent(id, parentID ID) error {
264 is.Lock()
265 defer is.Unlock()
266 parentMeta := is.images[parentID]
267 if parentMeta == nil {
268 return errdefs.NotFound(fmt.Errorf("unknown parent image ID %s", parentID.String()))
269 }
270 if parent, err := is.GetParent(id); err == nil && is.images[parent] != nil {
271 delete(is.images[parent].children, id)
272 }
273 parentMeta.children[id] = struct{}{}
274 return is.fs.SetMetadata(id.Digest(), "parent", []byte(parentID))
275 }
276
277 func (is *store) GetParent(id ID) (ID, error) {
278 d, err := is.fs.GetMetadata(id.Digest(), "parent")
279 if err != nil {
280 return "", errdefs.NotFound(err)
281 }
282 return ID(d), nil
283 }
284
285
286 func (is *store) SetLastUpdated(id ID) error {
287 lastUpdated := []byte(time.Now().Format(time.RFC3339Nano))
288 return is.fs.SetMetadata(id.Digest(), "lastUpdated", lastUpdated)
289 }
290
291
292 func (is *store) GetLastUpdated(id ID) (time.Time, error) {
293 bytes, err := is.fs.GetMetadata(id.Digest(), "lastUpdated")
294 if err != nil || len(bytes) == 0 {
295
296 return time.Time{}, nil
297 }
298 return time.Parse(time.RFC3339Nano, string(bytes))
299 }
300
301
302 func (is *store) SetBuiltLocally(id ID) error {
303 return is.fs.SetMetadata(id.Digest(), "builtLocally", []byte{1})
304 }
305
306
307 func (is *store) IsBuiltLocally(id ID) (bool, error) {
308 bytes, err := is.fs.GetMetadata(id.Digest(), "builtLocally")
309 if err != nil || len(bytes) == 0 {
310 if errors.Is(err, os.ErrNotExist) {
311 err = nil
312 }
313 return false, err
314 }
315 return bytes[0] == 1, nil
316 }
317
318 func (is *store) Children(id ID) []ID {
319 is.RLock()
320 defer is.RUnlock()
321
322 return is.children(id)
323 }
324
325 func (is *store) children(id ID) []ID {
326 var ids []ID
327 if is.images[id] != nil {
328 for id := range is.images[id].children {
329 ids = append(ids, id)
330 }
331 }
332 return ids
333 }
334
335 func (is *store) Heads() map[ID]*Image {
336 return is.imagesMap(false)
337 }
338
339 func (is *store) Map() map[ID]*Image {
340 return is.imagesMap(true)
341 }
342
343 func (is *store) imagesMap(all bool) map[ID]*Image {
344 is.RLock()
345 defer is.RUnlock()
346
347 images := make(map[ID]*Image)
348
349 for id := range is.images {
350 if !all && len(is.children(id)) > 0 {
351 continue
352 }
353 img, err := is.Get(id)
354 if err != nil {
355 log.G(context.TODO()).Errorf("invalid image access: %q, error: %q", id, err)
356 continue
357 }
358 images[id] = img
359 }
360 return images
361 }
362
363 func (is *store) Len() int {
364 is.RLock()
365 defer is.RUnlock()
366 return len(is.images)
367 }
368
View as plain text