1package image
2
3import (
4	"runtime"
5	"testing"
6
7	"github.com/docker/docker/internal/testutil"
8	"github.com/docker/docker/layer"
9	"github.com/opencontainers/go-digest"
10	"github.com/stretchr/testify/assert"
11)
12
13func TestRestore(t *testing.T) {
14	fs, cleanup := defaultFSStoreBackend(t)
15	defer cleanup()
16
17	id1, err := fs.Set([]byte(`{"comment": "abc", "rootfs": {"type": "layers"}}`))
18	assert.NoError(t, err)
19
20	_, err = fs.Set([]byte(`invalid`))
21	assert.NoError(t, err)
22
23	id2, err := fs.Set([]byte(`{"comment": "def", "rootfs": {"type": "layers", "diff_ids": ["2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"]}}`))
24	assert.NoError(t, err)
25
26	err = fs.SetMetadata(id2, "parent", []byte(id1))
27	assert.NoError(t, err)
28
29	is, err := NewImageStore(fs, runtime.GOOS, &mockLayerGetReleaser{})
30	assert.NoError(t, err)
31
32	assert.Len(t, is.Map(), 2)
33
34	img1, err := is.Get(ID(id1))
35	assert.NoError(t, err)
36	assert.Equal(t, ID(id1), img1.computedID)
37	assert.Equal(t, string(id1), img1.computedID.String())
38
39	img2, err := is.Get(ID(id2))
40	assert.NoError(t, err)
41	assert.Equal(t, "abc", img1.Comment)
42	assert.Equal(t, "def", img2.Comment)
43
44	_, err = is.GetParent(ID(id1))
45	testutil.ErrorContains(t, err, "failed to read metadata")
46
47	p, err := is.GetParent(ID(id2))
48	assert.NoError(t, err)
49	assert.Equal(t, ID(id1), p)
50
51	children := is.Children(ID(id1))
52	assert.Len(t, children, 1)
53	assert.Equal(t, ID(id2), children[0])
54	assert.Len(t, is.Heads(), 1)
55
56	sid1, err := is.Search(string(id1)[:10])
57	assert.NoError(t, err)
58	assert.Equal(t, ID(id1), sid1)
59
60	sid1, err = is.Search(digest.Digest(id1).Hex()[:6])
61	assert.NoError(t, err)
62	assert.Equal(t, ID(id1), sid1)
63
64	invalidPattern := digest.Digest(id1).Hex()[1:6]
65	_, err = is.Search(invalidPattern)
66	testutil.ErrorContains(t, err, "No such image")
67}
68
69func TestAddDelete(t *testing.T) {
70	is, cleanup := defaultImageStore(t)
71	defer cleanup()
72
73	id1, err := is.Create([]byte(`{"comment": "abc", "rootfs": {"type": "layers", "diff_ids": ["2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"]}}`))
74	assert.NoError(t, err)
75	assert.Equal(t, ID("sha256:8d25a9c45df515f9d0fe8e4a6b1c64dd3b965a84790ddbcc7954bb9bc89eb993"), id1)
76
77	img, err := is.Get(id1)
78	assert.NoError(t, err)
79	assert.Equal(t, "abc", img.Comment)
80
81	id2, err := is.Create([]byte(`{"comment": "def", "rootfs": {"type": "layers", "diff_ids": ["2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"]}}`))
82	assert.NoError(t, err)
83
84	err = is.SetParent(id2, id1)
85	assert.NoError(t, err)
86
87	pid1, err := is.GetParent(id2)
88	assert.NoError(t, err)
89	assert.Equal(t, pid1, id1)
90
91	_, err = is.Delete(id1)
92	assert.NoError(t, err)
93
94	_, err = is.Get(id1)
95	testutil.ErrorContains(t, err, "failed to get digest")
96
97	_, err = is.Get(id2)
98	assert.NoError(t, err)
99
100	_, err = is.GetParent(id2)
101	testutil.ErrorContains(t, err, "failed to read metadata")
102}
103
104func TestSearchAfterDelete(t *testing.T) {
105	is, cleanup := defaultImageStore(t)
106	defer cleanup()
107
108	id, err := is.Create([]byte(`{"comment": "abc", "rootfs": {"type": "layers"}}`))
109	assert.NoError(t, err)
110
111	id1, err := is.Search(string(id)[:15])
112	assert.NoError(t, err)
113	assert.Equal(t, id1, id)
114
115	_, err = is.Delete(id)
116	assert.NoError(t, err)
117
118	_, err = is.Search(string(id)[:15])
119	testutil.ErrorContains(t, err, "No such image")
120}
121
122func TestParentReset(t *testing.T) {
123	is, cleanup := defaultImageStore(t)
124	defer cleanup()
125
126	id, err := is.Create([]byte(`{"comment": "abc1", "rootfs": {"type": "layers"}}`))
127	assert.NoError(t, err)
128
129	id2, err := is.Create([]byte(`{"comment": "abc2", "rootfs": {"type": "layers"}}`))
130	assert.NoError(t, err)
131
132	id3, err := is.Create([]byte(`{"comment": "abc3", "rootfs": {"type": "layers"}}`))
133	assert.NoError(t, err)
134
135	assert.NoError(t, is.SetParent(id, id2))
136	assert.Len(t, is.Children(id2), 1)
137
138	assert.NoError(t, is.SetParent(id, id3))
139	assert.Len(t, is.Children(id2), 0)
140	assert.Len(t, is.Children(id3), 1)
141}
142
143func defaultImageStore(t *testing.T) (Store, func()) {
144	fsBackend, cleanup := defaultFSStoreBackend(t)
145
146	store, err := NewImageStore(fsBackend, runtime.GOOS, &mockLayerGetReleaser{})
147	assert.NoError(t, err)
148
149	return store, cleanup
150}
151
152func TestGetAndSetLastUpdated(t *testing.T) {
153	store, cleanup := defaultImageStore(t)
154	defer cleanup()
155
156	id, err := store.Create([]byte(`{"comment": "abc1", "rootfs": {"type": "layers"}}`))
157	assert.NoError(t, err)
158
159	updated, err := store.GetLastUpdated(id)
160	assert.NoError(t, err)
161	assert.Equal(t, updated.IsZero(), true)
162
163	assert.NoError(t, store.SetLastUpdated(id))
164
165	updated, err = store.GetLastUpdated(id)
166	assert.NoError(t, err)
167	assert.Equal(t, updated.IsZero(), false)
168}
169
170type mockLayerGetReleaser struct{}
171
172func (ls *mockLayerGetReleaser) Get(layer.ChainID) (layer.Layer, error) {
173	return nil, nil
174}
175
176func (ls *mockLayerGetReleaser) Release(layer.Layer) ([]layer.Metadata, error) {
177	return nil, nil
178}
179