1package layer // import "github.com/docker/docker/layer"
2
3import (
4	"io/ioutil"
5	"runtime"
6	"sort"
7	"testing"
8
9	"github.com/containerd/continuity/driver"
10	"github.com/docker/docker/pkg/archive"
11	"github.com/docker/docker/pkg/containerfs"
12)
13
14func TestMountInit(t *testing.T) {
15	// TODO Windows: Figure out why this is failing
16	if runtime.GOOS == "windows" {
17		t.Skip("Failing on Windows")
18	}
19	ls, _, cleanup := newTestStore(t)
20	defer cleanup()
21
22	basefile := newTestFile("testfile.txt", []byte("base data!"), 0644)
23	initfile := newTestFile("testfile.txt", []byte("init data!"), 0777)
24
25	li := initWithFiles(basefile)
26	layer, err := createLayer(ls, "", li)
27	if err != nil {
28		t.Fatal(err)
29	}
30
31	mountInit := func(root containerfs.ContainerFS) error {
32		return initfile.ApplyFile(root)
33	}
34
35	rwLayerOpts := &CreateRWLayerOpts{
36		InitFunc: mountInit,
37	}
38	m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), rwLayerOpts)
39	if err != nil {
40		t.Fatal(err)
41	}
42
43	pathFS, err := m.Mount("")
44	if err != nil {
45		t.Fatal(err)
46	}
47
48	fi, err := pathFS.Stat(pathFS.Join(pathFS.Path(), "testfile.txt"))
49	if err != nil {
50		t.Fatal(err)
51	}
52
53	f, err := pathFS.Open(pathFS.Join(pathFS.Path(), "testfile.txt"))
54	if err != nil {
55		t.Fatal(err)
56	}
57	defer f.Close()
58
59	b, err := ioutil.ReadAll(f)
60	if err != nil {
61		t.Fatal(err)
62	}
63
64	if expected := "init data!"; string(b) != expected {
65		t.Fatalf("Unexpected test file contents %q, expected %q", string(b), expected)
66	}
67
68	if fi.Mode().Perm() != 0777 {
69		t.Fatalf("Unexpected filemode %o, expecting %o", fi.Mode().Perm(), 0777)
70	}
71}
72
73func TestMountSize(t *testing.T) {
74	// TODO Windows: Figure out why this is failing
75	if runtime.GOOS == "windows" {
76		t.Skip("Failing on Windows")
77	}
78	ls, _, cleanup := newTestStore(t)
79	defer cleanup()
80
81	content1 := []byte("Base contents")
82	content2 := []byte("Mutable contents")
83	contentInit := []byte("why am I excluded from the size ☹")
84
85	li := initWithFiles(newTestFile("file1", content1, 0644))
86	layer, err := createLayer(ls, "", li)
87	if err != nil {
88		t.Fatal(err)
89	}
90
91	mountInit := func(root containerfs.ContainerFS) error {
92		return newTestFile("file-init", contentInit, 0777).ApplyFile(root)
93	}
94	rwLayerOpts := &CreateRWLayerOpts{
95		InitFunc: mountInit,
96	}
97
98	m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), rwLayerOpts)
99	if err != nil {
100		t.Fatal(err)
101	}
102
103	pathFS, err := m.Mount("")
104	if err != nil {
105		t.Fatal(err)
106	}
107
108	if err := driver.WriteFile(pathFS, pathFS.Join(pathFS.Path(), "file2"), content2, 0755); err != nil {
109		t.Fatal(err)
110	}
111
112	mountSize, err := m.Size()
113	if err != nil {
114		t.Fatal(err)
115	}
116
117	if expected := len(content2); int(mountSize) != expected {
118		t.Fatalf("Unexpected mount size %d, expected %d", int(mountSize), expected)
119	}
120}
121
122func TestMountChanges(t *testing.T) {
123	// TODO Windows: Figure out why this is failing
124	if runtime.GOOS == "windows" {
125		t.Skip("Failing on Windows")
126	}
127	ls, _, cleanup := newTestStore(t)
128	defer cleanup()
129
130	basefiles := []FileApplier{
131		newTestFile("testfile1.txt", []byte("base data!"), 0644),
132		newTestFile("testfile2.txt", []byte("base data!"), 0644),
133		newTestFile("testfile3.txt", []byte("base data!"), 0644),
134	}
135	initfile := newTestFile("testfile1.txt", []byte("init data!"), 0777)
136
137	li := initWithFiles(basefiles...)
138	layer, err := createLayer(ls, "", li)
139	if err != nil {
140		t.Fatal(err)
141	}
142
143	mountInit := func(root containerfs.ContainerFS) error {
144		return initfile.ApplyFile(root)
145	}
146	rwLayerOpts := &CreateRWLayerOpts{
147		InitFunc: mountInit,
148	}
149
150	m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), rwLayerOpts)
151	if err != nil {
152		t.Fatal(err)
153	}
154
155	pathFS, err := m.Mount("")
156	if err != nil {
157		t.Fatal(err)
158	}
159
160	if err := pathFS.Lchmod(pathFS.Join(pathFS.Path(), "testfile1.txt"), 0755); err != nil {
161		t.Fatal(err)
162	}
163
164	if err := driver.WriteFile(pathFS, pathFS.Join(pathFS.Path(), "testfile1.txt"), []byte("mount data!"), 0755); err != nil {
165		t.Fatal(err)
166	}
167
168	if err := pathFS.Remove(pathFS.Join(pathFS.Path(), "testfile2.txt")); err != nil {
169		t.Fatal(err)
170	}
171
172	if err := pathFS.Lchmod(pathFS.Join(pathFS.Path(), "testfile3.txt"), 0755); err != nil {
173		t.Fatal(err)
174	}
175
176	if err := driver.WriteFile(pathFS, pathFS.Join(pathFS.Path(), "testfile4.txt"), []byte("mount data!"), 0644); err != nil {
177		t.Fatal(err)
178	}
179
180	changes, err := m.Changes()
181	if err != nil {
182		t.Fatal(err)
183	}
184
185	if expected := 4; len(changes) != expected {
186		t.Fatalf("Wrong number of changes %d, expected %d", len(changes), expected)
187	}
188
189	sortChanges(changes)
190
191	assertChange(t, changes[0], archive.Change{
192		Path: "/testfile1.txt",
193		Kind: archive.ChangeModify,
194	})
195	assertChange(t, changes[1], archive.Change{
196		Path: "/testfile2.txt",
197		Kind: archive.ChangeDelete,
198	})
199	assertChange(t, changes[2], archive.Change{
200		Path: "/testfile3.txt",
201		Kind: archive.ChangeModify,
202	})
203	assertChange(t, changes[3], archive.Change{
204		Path: "/testfile4.txt",
205		Kind: archive.ChangeAdd,
206	})
207}
208
209func TestMountApply(t *testing.T) {
210	// TODO Windows: Figure out why this is failing
211	if runtime.GOOS == "windows" {
212		t.Skip("Failing on Windows")
213	}
214	ls, _, cleanup := newTestStore(t)
215	defer cleanup()
216
217	basefile := newTestFile("testfile.txt", []byte("base data!"), 0644)
218	newfile := newTestFile("newfile.txt", []byte("new data!"), 0755)
219
220	li := initWithFiles(basefile)
221	layer, err := createLayer(ls, "", li)
222	if err != nil {
223		t.Fatal(err)
224	}
225
226	di := initWithFiles(newfile)
227	diffLayer, err := createLayer(ls, "", di)
228	if err != nil {
229		t.Fatal(err)
230	}
231
232	m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), nil)
233	if err != nil {
234		t.Fatal(err)
235	}
236
237	r, err := diffLayer.TarStream()
238	if err != nil {
239		t.Fatal(err)
240	}
241
242	if _, err := m.ApplyDiff(r); err != nil {
243		t.Fatal(err)
244	}
245
246	pathFS, err := m.Mount("")
247	if err != nil {
248		t.Fatal(err)
249	}
250
251	f, err := pathFS.Open(pathFS.Join(pathFS.Path(), "newfile.txt"))
252	if err != nil {
253		t.Fatal(err)
254	}
255	defer f.Close()
256
257	b, err := ioutil.ReadAll(f)
258	if err != nil {
259		t.Fatal(err)
260	}
261
262	if expected := "new data!"; string(b) != expected {
263		t.Fatalf("Unexpected test file contents %q, expected %q", string(b), expected)
264	}
265}
266
267func assertChange(t *testing.T, actual, expected archive.Change) {
268	if actual.Path != expected.Path {
269		t.Fatalf("Unexpected change path %s, expected %s", actual.Path, expected.Path)
270	}
271	if actual.Kind != expected.Kind {
272		t.Fatalf("Unexpected change type %s, expected %s", actual.Kind, expected.Kind)
273	}
274}
275
276func sortChanges(changes []archive.Change) {
277	cs := &changeSorter{
278		changes: changes,
279	}
280	sort.Sort(cs)
281}
282
283type changeSorter struct {
284	changes []archive.Change
285}
286
287func (cs *changeSorter) Len() int {
288	return len(cs.changes)
289}
290
291func (cs *changeSorter) Swap(i, j int) {
292	cs.changes[i], cs.changes[j] = cs.changes[j], cs.changes[i]
293}
294
295func (cs *changeSorter) Less(i, j int) bool {
296	return cs.changes[i].Path < cs.changes[j].Path
297}
298