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