1package layer
2
3import (
4	"bytes"
5	"compress/gzip"
6	"fmt"
7	"io"
8	"io/ioutil"
9	"os"
10	"path/filepath"
11	"runtime"
12	"testing"
13
14	"github.com/docker/docker/daemon/graphdriver"
15	"github.com/docker/docker/pkg/archive"
16	"github.com/docker/docker/pkg/stringid"
17	"github.com/vbatts/tar-split/tar/asm"
18	"github.com/vbatts/tar-split/tar/storage"
19)
20
21func writeTarSplitFile(name string, tarContent []byte) error {
22	f, err := os.OpenFile(name, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
23	if err != nil {
24		return err
25	}
26	defer f.Close()
27
28	fz := gzip.NewWriter(f)
29
30	metaPacker := storage.NewJSONPacker(fz)
31	defer fz.Close()
32
33	rdr, err := asm.NewInputTarStream(bytes.NewReader(tarContent), metaPacker, nil)
34	if err != nil {
35		return err
36	}
37
38	if _, err := io.Copy(ioutil.Discard, rdr); err != nil {
39		return err
40	}
41
42	return nil
43}
44
45func TestLayerMigration(t *testing.T) {
46	// TODO Windows: Figure out why this is failing
47	if runtime.GOOS == "windows" {
48		t.Skip("Failing on Windows")
49	}
50	td, err := ioutil.TempDir("", "migration-test-")
51	if err != nil {
52		t.Fatal(err)
53	}
54	defer os.RemoveAll(td)
55
56	layer1Files := []FileApplier{
57		newTestFile("/root/.bashrc", []byte("# Boring configuration"), 0644),
58		newTestFile("/etc/profile", []byte("# Base configuration"), 0644),
59	}
60
61	layer2Files := []FileApplier{
62		newTestFile("/root/.bashrc", []byte("# Updated configuration"), 0644),
63	}
64
65	tar1, err := tarFromFiles(layer1Files...)
66	if err != nil {
67		t.Fatal(err)
68	}
69
70	tar2, err := tarFromFiles(layer2Files...)
71	if err != nil {
72		t.Fatal(err)
73	}
74
75	graph, err := newVFSGraphDriver(filepath.Join(td, "graphdriver-"))
76	if err != nil {
77		t.Fatal(err)
78	}
79
80	graphID1 := stringid.GenerateRandomID()
81	if err := graph.Create(graphID1, "", nil); err != nil {
82		t.Fatal(err)
83	}
84	if _, err := graph.ApplyDiff(graphID1, "", bytes.NewReader(tar1)); err != nil {
85		t.Fatal(err)
86	}
87
88	tf1 := filepath.Join(td, "tar1.json.gz")
89	if err := writeTarSplitFile(tf1, tar1); err != nil {
90		t.Fatal(err)
91	}
92
93	fms, err := NewFSMetadataStore(filepath.Join(td, "layers"))
94	if err != nil {
95		t.Fatal(err)
96	}
97	ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS)
98	if err != nil {
99		t.Fatal(err)
100	}
101
102	newTarDataPath := filepath.Join(td, ".migration-tardata")
103	diffID, size, err := ls.(*layerStore).ChecksumForGraphID(graphID1, "", tf1, newTarDataPath)
104	if err != nil {
105		t.Fatal(err)
106	}
107
108	layer1a, err := ls.(*layerStore).RegisterByGraphID(graphID1, "", diffID, newTarDataPath, size)
109	if err != nil {
110		t.Fatal(err)
111	}
112
113	layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
114	if err != nil {
115		t.Fatal(err)
116	}
117
118	assertReferences(t, layer1a, layer1b)
119	// Attempt register, should be same
120	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS))
121	if err != nil {
122		t.Fatal(err)
123	}
124
125	graphID2 := stringid.GenerateRandomID()
126	if err := graph.Create(graphID2, graphID1, nil); err != nil {
127		t.Fatal(err)
128	}
129	if _, err := graph.ApplyDiff(graphID2, graphID1, bytes.NewReader(tar2)); err != nil {
130		t.Fatal(err)
131	}
132
133	tf2 := filepath.Join(td, "tar2.json.gz")
134	if err := writeTarSplitFile(tf2, tar2); err != nil {
135		t.Fatal(err)
136	}
137	diffID, size, err = ls.(*layerStore).ChecksumForGraphID(graphID2, graphID1, tf2, newTarDataPath)
138	if err != nil {
139		t.Fatal(err)
140	}
141
142	layer2b, err := ls.(*layerStore).RegisterByGraphID(graphID2, layer1a.ChainID(), diffID, tf2, size)
143	if err != nil {
144		t.Fatal(err)
145	}
146	assertReferences(t, layer2a, layer2b)
147
148	if metadata, err := ls.Release(layer2a); err != nil {
149		t.Fatal(err)
150	} else if len(metadata) > 0 {
151		t.Fatalf("Unexpected layer removal after first release: %#v", metadata)
152	}
153
154	metadata, err := ls.Release(layer2b)
155	if err != nil {
156		t.Fatal(err)
157	}
158
159	assertMetadata(t, metadata, createMetadata(layer2a))
160}
161
162func tarFromFilesInGraph(graph graphdriver.Driver, graphID, parentID string, files ...FileApplier) ([]byte, error) {
163	t, err := tarFromFiles(files...)
164	if err != nil {
165		return nil, err
166	}
167
168	if err := graph.Create(graphID, parentID, nil); err != nil {
169		return nil, err
170	}
171	if _, err := graph.ApplyDiff(graphID, parentID, bytes.NewReader(t)); err != nil {
172		return nil, err
173	}
174
175	ar, err := graph.Diff(graphID, parentID)
176	if err != nil {
177		return nil, err
178	}
179	defer ar.Close()
180
181	return ioutil.ReadAll(ar)
182}
183
184func TestLayerMigrationNoTarsplit(t *testing.T) {
185	// TODO Windows: Figure out why this is failing
186	if runtime.GOOS == "windows" {
187		t.Skip("Failing on Windows")
188	}
189	td, err := ioutil.TempDir("", "migration-test-")
190	if err != nil {
191		t.Fatal(err)
192	}
193	defer os.RemoveAll(td)
194
195	layer1Files := []FileApplier{
196		newTestFile("/root/.bashrc", []byte("# Boring configuration"), 0644),
197		newTestFile("/etc/profile", []byte("# Base configuration"), 0644),
198	}
199
200	layer2Files := []FileApplier{
201		newTestFile("/root/.bashrc", []byte("# Updated configuration"), 0644),
202	}
203
204	graph, err := newVFSGraphDriver(filepath.Join(td, "graphdriver-"))
205	if err != nil {
206		t.Fatal(err)
207	}
208	graphID1 := stringid.GenerateRandomID()
209	graphID2 := stringid.GenerateRandomID()
210
211	tar1, err := tarFromFilesInGraph(graph, graphID1, "", layer1Files...)
212	if err != nil {
213		t.Fatal(err)
214	}
215
216	tar2, err := tarFromFilesInGraph(graph, graphID2, graphID1, layer2Files...)
217	if err != nil {
218		t.Fatal(err)
219	}
220
221	fms, err := NewFSMetadataStore(filepath.Join(td, "layers"))
222	if err != nil {
223		t.Fatal(err)
224	}
225	ls, err := NewStoreFromGraphDriver(fms, graph, runtime.GOOS)
226	if err != nil {
227		t.Fatal(err)
228	}
229
230	newTarDataPath := filepath.Join(td, ".migration-tardata")
231	diffID, size, err := ls.(*layerStore).ChecksumForGraphID(graphID1, "", "", newTarDataPath)
232	if err != nil {
233		t.Fatal(err)
234	}
235
236	layer1a, err := ls.(*layerStore).RegisterByGraphID(graphID1, "", diffID, newTarDataPath, size)
237	if err != nil {
238		t.Fatal(err)
239	}
240
241	layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
242	if err != nil {
243		t.Fatal(err)
244	}
245
246	assertReferences(t, layer1a, layer1b)
247
248	// Attempt register, should be same
249	layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS))
250	if err != nil {
251		t.Fatal(err)
252	}
253
254	diffID, size, err = ls.(*layerStore).ChecksumForGraphID(graphID2, graphID1, "", newTarDataPath)
255	if err != nil {
256		t.Fatal(err)
257	}
258
259	layer2b, err := ls.(*layerStore).RegisterByGraphID(graphID2, layer1a.ChainID(), diffID, newTarDataPath, size)
260	if err != nil {
261		t.Fatal(err)
262	}
263	assertReferences(t, layer2a, layer2b)
264
265	if metadata, err := ls.Release(layer2a); err != nil {
266		t.Fatal(err)
267	} else if len(metadata) > 0 {
268		t.Fatalf("Unexpected layer removal after first release: %#v", metadata)
269	}
270
271	metadata, err := ls.Release(layer2b)
272	if err != nil {
273		t.Fatal(err)
274	}
275
276	assertMetadata(t, metadata, createMetadata(layer2a))
277}
278
279func TestMountMigration(t *testing.T) {
280	// TODO Windows: Figure out why this is failing (obvious - paths... needs porting)
281	if runtime.GOOS == "windows" {
282		t.Skip("Failing on Windows")
283	}
284	ls, _, cleanup := newTestStore(t)
285	defer cleanup()
286
287	baseFiles := []FileApplier{
288		newTestFile("/root/.bashrc", []byte("# Boring configuration"), 0644),
289		newTestFile("/etc/profile", []byte("# Base configuration"), 0644),
290	}
291	initFiles := []FileApplier{
292		newTestFile("/etc/hosts", []byte{}, 0644),
293		newTestFile("/etc/resolv.conf", []byte{}, 0644),
294	}
295	mountFiles := []FileApplier{
296		newTestFile("/etc/hosts", []byte("localhost 127.0.0.1"), 0644),
297		newTestFile("/root/.bashrc", []byte("# Updated configuration"), 0644),
298		newTestFile("/root/testfile1.txt", []byte("nothing valuable"), 0644),
299	}
300
301	initTar, err := tarFromFiles(initFiles...)
302	if err != nil {
303		t.Fatal(err)
304	}
305
306	mountTar, err := tarFromFiles(mountFiles...)
307	if err != nil {
308		t.Fatal(err)
309	}
310
311	graph := ls.(*layerStore).driver
312
313	layer1, err := createLayer(ls, "", initWithFiles(baseFiles...))
314	if err != nil {
315		t.Fatal(err)
316	}
317
318	graphID1 := layer1.(*referencedCacheLayer).cacheID
319
320	containerID := stringid.GenerateRandomID()
321	containerInit := fmt.Sprintf("%s-init", containerID)
322
323	if err := graph.Create(containerInit, graphID1, nil); err != nil {
324		t.Fatal(err)
325	}
326	if _, err := graph.ApplyDiff(containerInit, graphID1, bytes.NewReader(initTar)); err != nil {
327		t.Fatal(err)
328	}
329
330	if err := graph.Create(containerID, containerInit, nil); err != nil {
331		t.Fatal(err)
332	}
333	if _, err := graph.ApplyDiff(containerID, containerInit, bytes.NewReader(mountTar)); err != nil {
334		t.Fatal(err)
335	}
336
337	if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, layer1.ChainID()); err != nil {
338		t.Fatal(err)
339	}
340
341	rwLayer1, err := ls.GetRWLayer("migration-mount")
342	if err != nil {
343		t.Fatal(err)
344	}
345
346	if _, err := rwLayer1.Mount(""); err != nil {
347		t.Fatal(err)
348	}
349
350	changes, err := rwLayer1.Changes()
351	if err != nil {
352		t.Fatal(err)
353	}
354
355	if expected := 5; len(changes) != expected {
356		t.Logf("Changes %#v", changes)
357		t.Fatalf("Wrong number of changes %d, expected %d", len(changes), expected)
358	}
359
360	sortChanges(changes)
361
362	assertChange(t, changes[0], archive.Change{
363		Path: "/etc",
364		Kind: archive.ChangeModify,
365	})
366	assertChange(t, changes[1], archive.Change{
367		Path: "/etc/hosts",
368		Kind: archive.ChangeModify,
369	})
370	assertChange(t, changes[2], archive.Change{
371		Path: "/root",
372		Kind: archive.ChangeModify,
373	})
374	assertChange(t, changes[3], archive.Change{
375		Path: "/root/.bashrc",
376		Kind: archive.ChangeModify,
377	})
378	assertChange(t, changes[4], archive.Change{
379		Path: "/root/testfile1.txt",
380		Kind: archive.ChangeAdd,
381	})
382
383	if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), nil); err == nil {
384		t.Fatal("Expected error creating mount with same name")
385	} else if err != ErrMountNameConflict {
386		t.Fatal(err)
387	}
388
389	rwLayer2, err := ls.GetRWLayer("migration-mount")
390	if err != nil {
391		t.Fatal(err)
392	}
393
394	if getMountLayer(rwLayer1) != getMountLayer(rwLayer2) {
395		t.Fatal("Expected same layer from get with same name as from migrate")
396	}
397
398	if _, err := rwLayer2.Mount(""); err != nil {
399		t.Fatal(err)
400	}
401
402	if _, err := rwLayer2.Mount(""); err != nil {
403		t.Fatal(err)
404	}
405
406	if metadata, err := ls.Release(layer1); err != nil {
407		t.Fatal(err)
408	} else if len(metadata) > 0 {
409		t.Fatalf("Expected no layers to be deleted, deleted %#v", metadata)
410	}
411
412	if err := rwLayer1.Unmount(); err != nil {
413		t.Fatal(err)
414	}
415
416	if _, err := ls.ReleaseRWLayer(rwLayer1); err != nil {
417		t.Fatal(err)
418	}
419
420	if err := rwLayer2.Unmount(); err != nil {
421		t.Fatal(err)
422	}
423	if err := rwLayer2.Unmount(); err != nil {
424		t.Fatal(err)
425	}
426	metadata, err := ls.ReleaseRWLayer(rwLayer2)
427	if err != nil {
428		t.Fatal(err)
429	}
430	if len(metadata) == 0 {
431		t.Fatal("Expected base layer to be deleted when deleting mount")
432	}
433
434	assertMetadata(t, metadata, createMetadata(layer1))
435}
436