1/*
2   Copyright The containerd Authors.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17package testsuite
18
19import (
20	"context"
21	//nolint:golint
22	_ "crypto/sha256"
23	"fmt"
24	"io/ioutil"
25	"math/rand"
26	"os"
27	"path/filepath"
28	"sort"
29	"testing"
30	"time"
31
32	"github.com/containerd/containerd/errdefs"
33	"github.com/containerd/containerd/log/logtest"
34	"github.com/containerd/containerd/mount"
35	"github.com/containerd/containerd/namespaces"
36	"github.com/containerd/containerd/pkg/testutil"
37	"github.com/containerd/containerd/snapshots"
38	"github.com/containerd/continuity/fs/fstest"
39	"gotest.tools/v3/assert"
40	is "gotest.tools/v3/assert/cmp"
41)
42
43// SnapshotterFunc is used in SnapshotterSuite
44type SnapshotterFunc func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error)
45
46// SnapshotterSuite runs a test suite on the snapshotter given a factory function.
47func SnapshotterSuite(t *testing.T, name string, snapshotterFn SnapshotterFunc) {
48	restoreMask := clearMask()
49	defer restoreMask()
50
51	t.Run("Basic", makeTest(name, snapshotterFn, checkSnapshotterBasic))
52	t.Run("StatActive", makeTest(name, snapshotterFn, checkSnapshotterStatActive))
53	t.Run("StatComitted", makeTest(name, snapshotterFn, checkSnapshotterStatCommitted))
54	t.Run("TransitivityTest", makeTest(name, snapshotterFn, checkSnapshotterTransitivity))
55	t.Run("PreareViewFailingtest", makeTest(name, snapshotterFn, checkSnapshotterPrepareView))
56	t.Run("Update", makeTest(name, snapshotterFn, checkUpdate))
57	t.Run("Remove", makeTest(name, snapshotterFn, checkRemove))
58	t.Run("Walk", makeTest(name, snapshotterFn, checkWalk))
59
60	t.Run("LayerFileupdate", makeTest(name, snapshotterFn, checkLayerFileUpdate))
61	t.Run("RemoveDirectoryInLowerLayer", makeTest(name, snapshotterFn, checkRemoveDirectoryInLowerLayer))
62	t.Run("Chown", makeTest(name, snapshotterFn, checkChown))
63	t.Run("DirectoryPermissionOnCommit", makeTest(name, snapshotterFn, checkDirectoryPermissionOnCommit))
64	t.Run("RemoveIntermediateSnapshot", makeTest(name, snapshotterFn, checkRemoveIntermediateSnapshot))
65	t.Run("DeletedFilesInChildSnapshot", makeTest(name, snapshotterFn, checkDeletedFilesInChildSnapshot))
66	t.Run("MoveFileFromLowerLayer", makeTest(name, snapshotterFn, checkFileFromLowerLayer))
67	t.Run("Rename", makeTest(name, snapshotterFn, checkRename))
68
69	t.Run("ViewReadonly", makeTest(name, snapshotterFn, checkSnapshotterViewReadonly))
70
71	t.Run("StatInWalk", makeTest(name, snapshotterFn, checkStatInWalk))
72	t.Run("CloseTwice", makeTest(name, snapshotterFn, closeTwice))
73	t.Run("RootPermission", makeTest(name, snapshotterFn, checkRootPermission))
74
75	t.Run("128LayersMount", makeTest(name, snapshotterFn, check128LayersMount(name)))
76}
77
78func makeTest(name string, snapshotterFn func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error), fn func(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string)) func(t *testing.T) {
79	return func(t *testing.T) {
80		t.Parallel()
81
82		ctx := logtest.WithT(context.Background(), t)
83		ctx = namespaces.WithNamespace(ctx, "testsuite")
84		// Make two directories: a snapshotter root and a play area for the tests:
85		//
86		// 	/tmp
87		// 		work/ -> passed to test functions
88		// 		root/ -> passed to snapshotter
89		//
90		tmpDir, err := ioutil.TempDir("", "snapshot-suite-"+name+"-")
91		if err != nil {
92			t.Fatal(err)
93		}
94		defer os.RemoveAll(tmpDir)
95
96		root := filepath.Join(tmpDir, "root")
97		if err := os.MkdirAll(root, 0777); err != nil {
98			t.Fatal(err)
99		}
100
101		snapshotter, cleanup, err := snapshotterFn(ctx, root)
102		if err != nil {
103			t.Fatalf("Failed to initialize snapshotter: %+v", err)
104		}
105		defer func() {
106			if cleanup != nil {
107				if err := cleanup(); err != nil {
108					t.Errorf("Cleanup failed: %v", err)
109				}
110			}
111		}()
112
113		work := filepath.Join(tmpDir, "work")
114		if err := os.MkdirAll(work, 0777); err != nil {
115			t.Fatal(err)
116		}
117
118		defer testutil.DumpDirOnFailure(t, tmpDir)
119		fn(ctx, t, snapshotter, work)
120	}
121}
122
123var opt = snapshots.WithLabels(map[string]string{
124	"containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339),
125})
126
127// checkSnapshotterBasic tests the basic workflow of a snapshot snapshotter.
128func checkSnapshotterBasic(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
129	initialApplier := fstest.Apply(
130		fstest.CreateFile("/foo", []byte("foo\n"), 0777),
131		fstest.CreateDir("/a", 0755),
132		fstest.CreateDir("/a/b", 0755),
133		fstest.CreateDir("/a/b/c", 0755),
134	)
135
136	diffApplier := fstest.Apply(
137		fstest.CreateFile("/bar", []byte("bar\n"), 0777),
138		// also, change content of foo to bar
139		fstest.CreateFile("/foo", []byte("bar\n"), 0777),
140		fstest.RemoveAll("/a/b"),
141	)
142
143	preparing := filepath.Join(work, "preparing")
144	if err := os.MkdirAll(preparing, 0777); err != nil {
145		t.Fatalf("failure reason: %+v", err)
146	}
147
148	mounts, err := snapshotter.Prepare(ctx, preparing, "", opt)
149	if err != nil {
150		t.Fatalf("failure reason: %+v", err)
151	}
152
153	if len(mounts) < 1 {
154		t.Fatal("expected mounts to have entries")
155	}
156
157	if err := mount.All(mounts, preparing); err != nil {
158		t.Fatalf("failure reason: %+v", err)
159	}
160
161	if err := initialApplier.Apply(preparing); err != nil {
162		t.Fatalf("failure reason: %+v", err)
163	}
164	// unmount before commit
165	testutil.Unmount(t, preparing)
166
167	committed := filepath.Join(work, "committed")
168	if err := snapshotter.Commit(ctx, committed, preparing, opt); err != nil {
169		t.Fatalf("failure reason: %+v", err)
170	}
171
172	si, err := snapshotter.Stat(ctx, committed)
173	if err != nil {
174		t.Fatalf("failure reason: %+v", err)
175	}
176
177	assert.Check(t, is.Equal("", si.Parent))
178	assert.Check(t, is.Equal(snapshots.KindCommitted, si.Kind))
179
180	_, err = snapshotter.Stat(ctx, preparing)
181	if err == nil {
182		t.Fatalf("%s should no longer be available after Commit", preparing)
183	}
184
185	next := filepath.Join(work, "nextlayer")
186	if err := os.MkdirAll(next, 0777); err != nil {
187		t.Fatalf("failure reason: %+v", err)
188	}
189
190	mounts, err = snapshotter.Prepare(ctx, next, committed, opt)
191	if err != nil {
192		t.Fatalf("failure reason: %+v", err)
193	}
194	if err := mount.All(mounts, next); err != nil {
195		t.Fatalf("failure reason: %+v", err)
196	}
197
198	if err := fstest.CheckDirectoryEqualWithApplier(next, initialApplier); err != nil {
199		t.Fatalf("failure reason: %+v", err)
200	}
201
202	if err := diffApplier.Apply(next); err != nil {
203		t.Fatalf("failure reason: %+v", err)
204	}
205	// unmount before commit
206	testutil.Unmount(t, next)
207
208	ni, err := snapshotter.Stat(ctx, next)
209	if err != nil {
210		t.Fatal(err)
211	}
212
213	assert.Check(t, is.Equal(committed, ni.Parent))
214	assert.Check(t, is.Equal(snapshots.KindActive, ni.Kind))
215
216	nextCommitted := filepath.Join(work, "committed-next")
217	if err := snapshotter.Commit(ctx, nextCommitted, next, opt); err != nil {
218		t.Fatalf("failure reason: %+v", err)
219	}
220
221	si2, err := snapshotter.Stat(ctx, nextCommitted)
222	if err != nil {
223		t.Fatalf("failure reason: %+v", err)
224	}
225
226	assert.Check(t, is.Equal(committed, si2.Parent))
227	assert.Check(t, is.Equal(snapshots.KindCommitted, si2.Kind))
228
229	_, err = snapshotter.Stat(ctx, next)
230	if err == nil {
231		t.Fatalf("%s should no longer be available after Commit", next)
232	}
233
234	expected := map[string]snapshots.Info{
235		si.Name:  si,
236		si2.Name: si2,
237	}
238	walked := map[string]snapshots.Info{} // walk is not ordered
239	assert.NilError(t, snapshotter.Walk(ctx, func(ctx context.Context, si snapshots.Info) error {
240		walked[si.Name] = si
241		return nil
242	}))
243
244	for ek, ev := range expected {
245		av, ok := walked[ek]
246		if !ok {
247			t.Errorf("Missing stat for %v", ek)
248			continue
249		}
250		assert.Check(t, is.DeepEqual(ev, av))
251	}
252
253	nextnext := filepath.Join(work, "nextnextlayer")
254	if err := os.MkdirAll(nextnext, 0777); err != nil {
255		t.Fatalf("failure reason: %+v", err)
256	}
257
258	mounts, err = snapshotter.View(ctx, nextnext, nextCommitted, opt)
259	if err != nil {
260		t.Fatalf("failure reason: %+v", err)
261	}
262	if err := mount.All(mounts, nextnext); err != nil {
263		t.Fatalf("failure reason: %+v", err)
264	}
265
266	if err := fstest.CheckDirectoryEqualWithApplier(nextnext,
267		fstest.Apply(initialApplier, diffApplier)); err != nil {
268		testutil.Unmount(t, nextnext)
269		t.Fatalf("failure reason: %+v", err)
270	}
271
272	testutil.Unmount(t, nextnext)
273	assert.NilError(t, snapshotter.Remove(ctx, nextnext))
274	assert.Assert(t, is.ErrorContains(snapshotter.Remove(ctx, committed), "remove"))
275	assert.NilError(t, snapshotter.Remove(ctx, nextCommitted))
276	assert.NilError(t, snapshotter.Remove(ctx, committed))
277}
278
279// Create a New Layer on top of base layer with Prepare, Stat on new layer, should return Active layer.
280func checkSnapshotterStatActive(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
281	preparing := filepath.Join(work, "preparing")
282	if err := os.MkdirAll(preparing, 0777); err != nil {
283		t.Fatal(err)
284	}
285
286	mounts, err := snapshotter.Prepare(ctx, preparing, "", opt)
287	if err != nil {
288		t.Fatal(err)
289	}
290
291	if len(mounts) < 1 {
292		t.Fatal("expected mounts to have entries")
293	}
294
295	if err = mount.All(mounts, preparing); err != nil {
296		t.Fatal(err)
297	}
298	defer testutil.Unmount(t, preparing)
299
300	if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil {
301		t.Fatal(err)
302	}
303
304	si, err := snapshotter.Stat(ctx, preparing)
305	if err != nil {
306		t.Fatal(err)
307	}
308	assert.Check(t, is.Equal(si.Name, preparing))
309	assert.Check(t, is.Equal(snapshots.KindActive, si.Kind))
310	assert.Check(t, is.Equal("", si.Parent))
311}
312
313// Commit a New Layer on top of base layer with Prepare & Commit , Stat on new layer, should return Committed layer.
314func checkSnapshotterStatCommitted(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
315	preparing := filepath.Join(work, "preparing")
316	if err := os.MkdirAll(preparing, 0777); err != nil {
317		t.Fatal(err)
318	}
319
320	mounts, err := snapshotter.Prepare(ctx, preparing, "", opt)
321	if err != nil {
322		t.Fatal(err)
323	}
324
325	if len(mounts) < 1 {
326		t.Fatal("expected mounts to have entries")
327	}
328
329	if err = mount.All(mounts, preparing); err != nil {
330		t.Fatal(err)
331	}
332	defer testutil.Unmount(t, preparing)
333
334	if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil {
335		t.Fatal(err)
336	}
337
338	committed := filepath.Join(work, "committed")
339	if err = snapshotter.Commit(ctx, committed, preparing, opt); err != nil {
340		t.Fatal(err)
341	}
342
343	si, err := snapshotter.Stat(ctx, committed)
344	if err != nil {
345		t.Fatal(err)
346	}
347	assert.Check(t, is.Equal(si.Name, committed))
348	assert.Check(t, is.Equal(snapshots.KindCommitted, si.Kind))
349	assert.Check(t, is.Equal("", si.Parent))
350
351}
352
353func snapshotterPrepareMount(ctx context.Context, snapshotter snapshots.Snapshotter, diffPathName string, parent string, work string) (string, error) {
354	preparing := filepath.Join(work, diffPathName)
355	if err := os.MkdirAll(preparing, 0777); err != nil {
356		return "", err
357	}
358
359	mounts, err := snapshotter.Prepare(ctx, preparing, parent, opt)
360	if err != nil {
361		return "", err
362	}
363
364	if len(mounts) < 1 {
365		return "", fmt.Errorf("expected mounts to have entries")
366	}
367
368	if err = mount.All(mounts, preparing); err != nil {
369		return "", err
370	}
371	return preparing, nil
372}
373
374// Given A <- B <- C, B is the parent of C and A is a transitive parent of C (in this case, a "grandparent")
375func checkSnapshotterTransitivity(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
376	preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work)
377	if err != nil {
378		t.Fatal(err)
379	}
380	defer testutil.Unmount(t, preparing)
381
382	if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil {
383		t.Fatal(err)
384	}
385
386	snapA := filepath.Join(work, "snapA")
387	if err = snapshotter.Commit(ctx, snapA, preparing, opt); err != nil {
388		t.Fatal(err)
389	}
390
391	next, err := snapshotterPrepareMount(ctx, snapshotter, "next", snapA, work)
392	if err != nil {
393		t.Fatal(err)
394	}
395	defer testutil.Unmount(t, next)
396
397	if err = ioutil.WriteFile(filepath.Join(next, "foo"), []byte("foo bar\n"), 0777); err != nil {
398		t.Fatal(err)
399	}
400
401	snapB := filepath.Join(work, "snapB")
402	if err = snapshotter.Commit(ctx, snapB, next, opt); err != nil {
403		t.Fatal(err)
404	}
405
406	siA, err := snapshotter.Stat(ctx, snapA)
407	if err != nil {
408		t.Fatal(err)
409	}
410
411	siB, err := snapshotter.Stat(ctx, snapB)
412	if err != nil {
413		t.Fatal(err)
414	}
415
416	siParentB, err := snapshotter.Stat(ctx, siB.Parent)
417	if err != nil {
418		t.Fatal(err)
419	}
420
421	// Test the transivity
422	assert.Check(t, is.Equal("", siA.Parent))
423	assert.Check(t, is.Equal(snapA, siB.Parent))
424	assert.Check(t, is.Equal("", siParentB.Parent))
425
426}
427
428// Creating two layers with Prepare or View with same key must fail.
429func checkSnapshotterPrepareView(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
430	preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work)
431	if err != nil {
432		t.Fatal(err)
433	}
434	defer testutil.Unmount(t, preparing)
435
436	snapA := filepath.Join(work, "snapA")
437	if err = snapshotter.Commit(ctx, snapA, preparing, opt); err != nil {
438		t.Fatal(err)
439	}
440
441	// Prepare & View with same key
442	newLayer := filepath.Join(work, "newlayer")
443	if err = os.MkdirAll(preparing, 0777); err != nil {
444		t.Fatal(err)
445	}
446
447	// Prepare & View with same key
448	_, err = snapshotter.Prepare(ctx, newLayer, snapA, opt)
449	if err != nil {
450		t.Fatal(err)
451	}
452
453	_, err = snapshotter.View(ctx, newLayer, snapA, opt)
454	assert.Check(t, err != nil)
455
456	// Two Prepare with same key
457	prepLayer := filepath.Join(work, "prepLayer")
458	if err = os.MkdirAll(preparing, 0777); err != nil {
459		t.Fatal(err)
460	}
461
462	_, err = snapshotter.Prepare(ctx, prepLayer, snapA, opt)
463	if err != nil {
464		t.Fatal(err)
465	}
466
467	_, err = snapshotter.Prepare(ctx, prepLayer, snapA, opt)
468	assert.Check(t, err != nil)
469
470	// Two View with same key
471	viewLayer := filepath.Join(work, "viewLayer")
472	if err = os.MkdirAll(preparing, 0777); err != nil {
473		t.Fatal(err)
474	}
475
476	_, err = snapshotter.View(ctx, viewLayer, snapA, opt)
477	if err != nil {
478		t.Fatal(err)
479	}
480
481	_, err = snapshotter.View(ctx, viewLayer, snapA, opt)
482	assert.Check(t, err != nil)
483
484}
485
486// Deletion of files/folder of base layer in new layer, On Commit, those files should not be visible.
487func checkDeletedFilesInChildSnapshot(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
488
489	l1Init := fstest.Apply(
490		fstest.CreateFile("/foo", []byte("foo\n"), 0777),
491		fstest.CreateFile("/foobar", []byte("foobar\n"), 0777),
492	)
493	l2Init := fstest.Apply(
494		fstest.RemoveAll("/foobar"),
495	)
496	l3Init := fstest.Apply()
497
498	if err := checkSnapshots(ctx, snapshotter, work, l1Init, l2Init, l3Init); err != nil {
499		t.Fatalf("Check snapshots failed: %+v", err)
500	}
501
502}
503
504//Create three layers. Deleting intermediate layer must fail.
505func checkRemoveIntermediateSnapshot(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
506
507	base, err := snapshotterPrepareMount(ctx, snapshotter, "base", "", work)
508	if err != nil {
509		t.Fatal(err)
510	}
511
512	committedBase := filepath.Join(work, "committed-base")
513	if err = snapshotter.Commit(ctx, committedBase, base, opt); err != nil {
514		t.Fatal(err)
515	}
516
517	// Create intermediate layer
518	intermediate := filepath.Join(work, "intermediate")
519	if _, err = snapshotter.Prepare(ctx, intermediate, committedBase, opt); err != nil {
520		t.Fatal(err)
521	}
522
523	committedInter := filepath.Join(work, "committed-inter")
524	if err = snapshotter.Commit(ctx, committedInter, intermediate, opt); err != nil {
525		t.Fatal(err)
526	}
527
528	// Create top layer
529	topLayer := filepath.Join(work, "toplayer")
530	if _, err = snapshotter.Prepare(ctx, topLayer, committedInter, opt); err != nil {
531		t.Fatal(err)
532	}
533
534	// Deletion of intermediate layer must fail.
535	err = snapshotter.Remove(ctx, committedInter)
536	if err == nil {
537		t.Fatal("intermediate layer removal should fail.")
538	}
539
540	//Removal from toplayer to base should not fail.
541	err = snapshotter.Remove(ctx, topLayer)
542	if err != nil {
543		t.Fatal(err)
544	}
545	err = snapshotter.Remove(ctx, committedInter)
546	if err != nil {
547		t.Fatal(err)
548	}
549	testutil.Unmount(t, base)
550	err = snapshotter.Remove(ctx, committedBase)
551	if err != nil {
552		t.Fatal(err)
553	}
554}
555
556// baseTestSnapshots creates a base set of snapshots for tests, each snapshot is empty
557// Tests snapshots:
558//  c1 - committed snapshot, no parent
559//  c2 - committed snapshot, c1 is parent
560//  a1 - active snapshot, c2 is parent
561//  a1 - active snapshot, no parent
562//  v1 - view snapshot, v1 is parent
563//  v2 - view snapshot, no parent
564func baseTestSnapshots(ctx context.Context, snapshotter snapshots.Snapshotter) error {
565	if _, err := snapshotter.Prepare(ctx, "c1-a", "", opt); err != nil {
566		return err
567	}
568	if err := snapshotter.Commit(ctx, "c1", "c1-a", opt); err != nil {
569		return err
570	}
571	if _, err := snapshotter.Prepare(ctx, "c2-a", "c1", opt); err != nil {
572		return err
573	}
574	if err := snapshotter.Commit(ctx, "c2", "c2-a", opt); err != nil {
575		return err
576	}
577	if _, err := snapshotter.Prepare(ctx, "a1", "c2", opt); err != nil {
578		return err
579	}
580	if _, err := snapshotter.Prepare(ctx, "a2", "", opt); err != nil {
581		return err
582	}
583	if _, err := snapshotter.View(ctx, "v1", "c2", opt); err != nil {
584		return err
585	}
586	if _, err := snapshotter.View(ctx, "v2", "", opt); err != nil {
587		return err
588	}
589	return nil
590}
591
592func checkUpdate(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
593	t1 := time.Now().UTC()
594	if err := baseTestSnapshots(ctx, snapshotter); err != nil {
595		t.Fatalf("Failed to create base snapshots: %v", err)
596	}
597	t2 := time.Now().UTC()
598	testcases := []struct {
599		name   string
600		kind   snapshots.Kind
601		parent string
602	}{
603		{
604			name: "c1",
605			kind: snapshots.KindCommitted,
606		},
607		{
608			name:   "c2",
609			kind:   snapshots.KindCommitted,
610			parent: "c1",
611		},
612		{
613			name:   "a1",
614			kind:   snapshots.KindActive,
615			parent: "c2",
616		},
617		{
618			name: "a2",
619			kind: snapshots.KindActive,
620		},
621		{
622			name:   "v1",
623			kind:   snapshots.KindView,
624			parent: "c2",
625		},
626		{
627			name: "v2",
628			kind: snapshots.KindView,
629		},
630	}
631	for _, tc := range testcases {
632		st, err := snapshotter.Stat(ctx, tc.name)
633		if err != nil {
634			t.Fatalf("Failed to stat %s: %v", tc.name, err)
635		}
636		if st.Created.Before(t1) || st.Created.After(t2) {
637			t.Errorf("(%s) wrong created time %s: expected between %s and %s", tc.name, st.Created, t1, t2)
638			continue
639		}
640		if st.Created != st.Updated {
641			t.Errorf("(%s) unexpected updated time %s: expected %s", tc.name, st.Updated, st.Created)
642			continue
643		}
644		if st.Kind != tc.kind {
645			t.Errorf("(%s) unexpected kind %s, expected %s", tc.name, st.Kind, tc.kind)
646			continue
647		}
648		if st.Parent != tc.parent {
649			t.Errorf("(%s) unexpected parent %q, expected %q", tc.name, st.Parent, tc.parent)
650			continue
651		}
652		if st.Name != tc.name {
653			t.Errorf("(%s) unexpected name %q, expected %q", tc.name, st.Name, tc.name)
654			continue
655		}
656
657		createdAt := st.Created
658		rootTime := time.Now().UTC().Format(time.RFC3339)
659		expected := map[string]string{
660			"l1": "v1",
661			"l2": "v2",
662			"l3": "v3",
663			// Keep root label
664			"containerd.io/gc.root": rootTime,
665		}
666		st.Parent = "doesnotexist"
667		st.Labels = expected
668		u1 := time.Now().UTC()
669		st, err = snapshotter.Update(ctx, st)
670		if err != nil {
671			t.Fatalf("Failed to update %s: %v", tc.name, err)
672		}
673		u2 := time.Now().UTC()
674
675		if st.Created != createdAt {
676			t.Errorf("(%s) wrong created time %s: expected %s", tc.name, st.Created, createdAt)
677			continue
678		}
679		if st.Updated.Before(u1) || st.Updated.After(u2) {
680			t.Errorf("(%s) wrong updated time %s: expected between %s and %s", tc.name, st.Updated, u1, u2)
681			continue
682		}
683		if st.Kind != tc.kind {
684			t.Errorf("(%s) unexpected kind %s, expected %s", tc.name, st.Kind, tc.kind)
685			continue
686		}
687		if st.Parent != tc.parent {
688			t.Errorf("(%s) unexpected parent %q, expected %q", tc.name, st.Parent, tc.parent)
689			continue
690		}
691		if st.Name != tc.name {
692			t.Errorf("(%s) unexpected name %q, expected %q", tc.name, st.Name, tc.name)
693			continue
694		}
695		assertLabels(t, st.Labels, expected)
696
697		expected = map[string]string{
698			"l1": "updated",
699			"l3": "v3",
700
701			"containerd.io/gc.root": rootTime,
702		}
703		st.Labels = map[string]string{
704			"l1": "updated",
705			"l4": "v4",
706		}
707		st, err = snapshotter.Update(ctx, st, "labels.l1", "labels.l2")
708		if err != nil {
709			t.Fatalf("Failed to update %s: %v", tc.name, err)
710		}
711		assertLabels(t, st.Labels, expected)
712
713		expected = map[string]string{
714			"l4": "v4",
715
716			"containerd.io/gc.root": rootTime,
717		}
718		st.Labels = expected
719		st, err = snapshotter.Update(ctx, st, "labels")
720		if err != nil {
721			t.Fatalf("Failed to update %s: %v", tc.name, err)
722		}
723		assertLabels(t, st.Labels, expected)
724
725		// Test failure received when providing immutable field path
726		st.Parent = "doesnotexist"
727		st, err = snapshotter.Update(ctx, st, "parent")
728		if err == nil {
729			t.Errorf("Expected error updating with immutable field path")
730		} else if !errdefs.IsInvalidArgument(err) {
731			t.Fatalf("Unexpected error updating %s: %+v", tc.name, err)
732		}
733	}
734}
735
736func assertLabels(t *testing.T, actual, expected map[string]string) {
737	if len(actual) != len(expected) {
738		t.Fatalf("Label size mismatch: %d vs %d\n\tActual: %#v\n\tExpected: %#v", len(actual), len(expected), actual, expected)
739	}
740	for k, v := range expected {
741		if a := actual[k]; v != a {
742			t.Errorf("Wrong label value for %s, got %q, expected %q", k, a, v)
743		}
744	}
745	if t.Failed() {
746		t.FailNow()
747	}
748}
749
750func checkRemove(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
751	if _, err := snapshotter.Prepare(ctx, "committed-a", "", opt); err != nil {
752		t.Fatal(err)
753	}
754	if err := snapshotter.Commit(ctx, "committed-1", "committed-a", opt); err != nil {
755		t.Fatal(err)
756	}
757	if _, err := snapshotter.Prepare(ctx, "reuse-1", "committed-1", opt); err != nil {
758		t.Fatal(err)
759	}
760	if err := snapshotter.Remove(ctx, "reuse-1"); err != nil {
761		t.Fatal(err)
762	}
763	if _, err := snapshotter.View(ctx, "reuse-1", "committed-1", opt); err != nil {
764		t.Fatal(err)
765	}
766	if err := snapshotter.Remove(ctx, "reuse-1"); err != nil {
767		t.Fatal(err)
768	}
769	if _, err := snapshotter.Prepare(ctx, "reuse-1", "", opt); err != nil {
770		t.Fatal(err)
771	}
772	if err := snapshotter.Remove(ctx, "committed-1"); err != nil {
773		t.Fatal(err)
774	}
775	if err := snapshotter.Commit(ctx, "committed-1", "reuse-1", opt); err != nil {
776		t.Fatal(err)
777	}
778}
779
780// checkSnapshotterViewReadonly ensures a KindView snapshot to be mounted as a read-only filesystem.
781// This function is called only when WithTestViewReadonly is true.
782func checkSnapshotterViewReadonly(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
783	preparing := filepath.Join(work, "preparing")
784	if _, err := snapshotter.Prepare(ctx, preparing, "", opt); err != nil {
785		t.Fatal(err)
786	}
787	committed := filepath.Join(work, "committed")
788	if err := snapshotter.Commit(ctx, committed, preparing, opt); err != nil {
789		t.Fatal(err)
790	}
791	view := filepath.Join(work, "view")
792	m, err := snapshotter.View(ctx, view, committed, opt)
793	if err != nil {
794		t.Fatal(err)
795	}
796	viewMountPoint := filepath.Join(work, "view-mount")
797	if err := os.MkdirAll(viewMountPoint, 0777); err != nil {
798		t.Fatal(err)
799	}
800
801	// Just checking the option string of m is not enough, we need to test real mount. (#1368)
802	if err := mount.All(m, viewMountPoint); err != nil {
803		t.Fatal(err)
804	}
805
806	testfile := filepath.Join(viewMountPoint, "testfile")
807	if err := ioutil.WriteFile(testfile, []byte("testcontent"), 0777); err != nil {
808		t.Logf("write to %q failed with %v (EROFS is expected but can be other error code)", testfile, err)
809	} else {
810		t.Fatalf("write to %q should fail (EROFS) but did not fail", testfile)
811	}
812	testutil.Unmount(t, viewMountPoint)
813	assert.NilError(t, snapshotter.Remove(ctx, view))
814	assert.NilError(t, snapshotter.Remove(ctx, committed))
815}
816
817// Move files from base layer to new location in intermediate layer.
818// Verify if the file at source is deleted and copied to new location.
819func checkFileFromLowerLayer(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
820	l1Init := fstest.Apply(
821		fstest.CreateDir("/dir1", 0700),
822		fstest.CreateFile("/dir1/f1", []byte("Hello"), 0644),
823		fstest.CreateDir("dir2", 0700),
824		fstest.CreateFile("dir2/f2", []byte("..."), 0644),
825	)
826	l2Init := fstest.Apply(
827		fstest.CreateDir("/dir3", 0700),
828		fstest.CreateFile("/dir3/f1", []byte("Hello"), 0644),
829		fstest.RemoveAll("/dir1"),
830		fstest.Link("dir2/f2", "dir3/f2"),
831		fstest.RemoveAll("dir2/f2"),
832	)
833
834	if err := checkSnapshots(ctx, snapshotter, work, l1Init, l2Init); err != nil {
835		t.Fatalf("Check snapshots failed: %+v", err)
836	}
837}
838
839func closeTwice(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
840	n := fmt.Sprintf("closeTwice-%d", rand.Int())
841	prepare := fmt.Sprintf("%s-prepare", n)
842
843	// do some dummy ops to modify the snapshotter internal state
844	if _, err := snapshotter.Prepare(ctx, prepare, "", opt); err != nil {
845		t.Fatal(err)
846	}
847	if err := snapshotter.Commit(ctx, n, prepare, opt); err != nil {
848		t.Fatal(err)
849	}
850	if err := snapshotter.Remove(ctx, n); err != nil {
851		t.Fatal(err)
852	}
853	if err := snapshotter.Close(); err != nil {
854		t.Fatalf("The first close failed: %+v", err)
855	}
856	if err := snapshotter.Close(); err != nil {
857		t.Fatalf("The second close failed: %+v", err)
858	}
859}
860
861func checkRootPermission(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
862	preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work)
863	if err != nil {
864		t.Fatal(err)
865	}
866	defer testutil.Unmount(t, preparing)
867	st, err := os.Stat(preparing)
868	if err != nil {
869		t.Fatal(err)
870	}
871	if mode := st.Mode() & 0777; mode != 0755 {
872		t.Fatalf("expected 0755, got 0%o", mode)
873	}
874}
875
876func check128LayersMount(name string) func(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
877	return func(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
878		if name == "Aufs" {
879			t.Skip("aufs tests have issues with whiteouts here on some CI kernels")
880		}
881		lowestApply := fstest.Apply(
882			fstest.CreateFile("/bottom", []byte("way at the bottom\n"), 0777),
883			fstest.CreateFile("/overwriteme", []byte("FIRST!\n"), 0777),
884			fstest.CreateDir("/ADDHERE", 0755),
885			fstest.CreateDir("/ONLYME", 0755),
886			fstest.CreateFile("/ONLYME/bottom", []byte("bye!\n"), 0777),
887		)
888
889		appliers := []fstest.Applier{lowestApply}
890		for i := 1; i <= 127; i++ {
891			appliers = append(appliers, fstest.Apply(
892				fstest.CreateFile("/overwriteme", []byte(fmt.Sprintf("%d WAS HERE!\n", i)), 0777),
893				fstest.CreateFile(fmt.Sprintf("/ADDHERE/file-%d", i), []byte("same\n"), 0755),
894				fstest.RemoveAll("/ONLYME"),
895				fstest.CreateDir("/ONLYME", 0755),
896				fstest.CreateFile(fmt.Sprintf("/ONLYME/file-%d", i), []byte("only me!\n"), 0777),
897			))
898		}
899
900		flat := filepath.Join(work, "flat")
901		if err := os.MkdirAll(flat, 0777); err != nil {
902			t.Fatalf("failed to create flat dir(%s): %+v", flat, err)
903		}
904
905		// NOTE: add gc labels to avoid snapshots get removed by gc...
906		parent := ""
907		for i, applier := range appliers {
908			preparing := filepath.Join(work, fmt.Sprintf("prepare-layer-%d", i))
909			if err := os.MkdirAll(preparing, 0777); err != nil {
910				t.Fatalf("[layer %d] failed to create preparing dir(%s): %+v", i, preparing, err)
911			}
912
913			mounts, err := snapshotter.Prepare(ctx, preparing, parent, opt)
914			if err != nil {
915				t.Fatalf("[layer %d] failed to get mount info: %+v", i, err)
916			}
917
918			if err := mount.All(mounts, preparing); err != nil {
919				t.Fatalf("[layer %d] failed to mount on the target(%s): %+v", i, preparing, err)
920			}
921
922			if err := fstest.CheckDirectoryEqual(preparing, flat); err != nil {
923				testutil.Unmount(t, preparing)
924				t.Fatalf("[layer %d] preparing doesn't equal to flat before apply: %+v", i, err)
925			}
926
927			if err := applier.Apply(flat); err != nil {
928				testutil.Unmount(t, preparing)
929				t.Fatalf("[layer %d] failed to apply on flat dir: %+v", i, err)
930			}
931
932			if err = applier.Apply(preparing); err != nil {
933				testutil.Unmount(t, preparing)
934				t.Fatalf("[layer %d] failed to apply on preparing dir: %+v", i, err)
935			}
936
937			if err := fstest.CheckDirectoryEqual(preparing, flat); err != nil {
938				testutil.Unmount(t, preparing)
939				t.Fatalf("[layer %d] preparing doesn't equal to flat after apply: %+v", i, err)
940			}
941
942			testutil.Unmount(t, preparing)
943
944			parent = filepath.Join(work, fmt.Sprintf("committed-%d", i))
945			if err := snapshotter.Commit(ctx, parent, preparing, opt); err != nil {
946				t.Fatalf("[layer %d] failed to commit the preparing: %+v", i, err)
947			}
948
949		}
950
951		view := filepath.Join(work, "fullview")
952		if err := os.MkdirAll(view, 0777); err != nil {
953			t.Fatalf("failed to create fullview dir(%s): %+v", view, err)
954		}
955
956		mounts, err := snapshotter.View(ctx, view, parent, opt)
957		if err != nil {
958			t.Fatalf("failed to get view's mount info: %+v", err)
959		}
960
961		if err := mount.All(mounts, view); err != nil {
962			t.Fatalf("failed to mount on the target(%s): %+v", view, err)
963		}
964		defer testutil.Unmount(t, view)
965
966		if err := fstest.CheckDirectoryEqual(view, flat); err != nil {
967			t.Fatalf("fullview should equal to flat: %+v", err)
968		}
969	}
970}
971
972func checkWalk(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
973	opt := snapshots.WithLabels(map[string]string{
974		"containerd.io/gc.root": "check-walk",
975	})
976
977	// No parent active
978	if _, err := snapshotter.Prepare(ctx, "a-np", "", opt); err != nil {
979		t.Fatal(err)
980	}
981
982	// Base parent
983	if _, err := snapshotter.Prepare(ctx, "p-tmp", "", opt); err != nil {
984		t.Fatal(err)
985	}
986	if err := snapshotter.Commit(ctx, "p", "p-tmp", opt); err != nil {
987		t.Fatal(err)
988	}
989
990	// Active
991	if _, err := snapshotter.Prepare(ctx, "a", "p", opt); err != nil {
992		t.Fatal(err)
993	}
994
995	// View
996	if _, err := snapshotter.View(ctx, "v", "p", opt); err != nil {
997		t.Fatal(err)
998	}
999
1000	// Base parent with label=1
1001	if _, err := snapshotter.Prepare(ctx, "p-wl-tmp", "", opt); err != nil {
1002		t.Fatal(err)
1003	}
1004	if err := snapshotter.Commit(ctx, "p-wl", "p-wl-tmp", snapshots.WithLabels(map[string]string{
1005		"l":                     "1",
1006		"containerd.io/gc.root": "check-walk",
1007	})); err != nil {
1008		t.Fatal(err)
1009	}
1010
1011	// active with label=2
1012	if _, err := snapshotter.Prepare(ctx, "a-wl", "p-wl", snapshots.WithLabels(map[string]string{
1013		"l":                     "2",
1014		"containerd.io/gc.root": "check-walk",
1015	})); err != nil {
1016		t.Fatal(err)
1017	}
1018
1019	// view with label=3
1020	if _, err := snapshotter.View(ctx, "v-wl", "p-wl", snapshots.WithLabels(map[string]string{
1021		"l":                     "3",
1022		"containerd.io/gc.root": "check-walk",
1023	})); err != nil {
1024		t.Fatal(err)
1025	}
1026
1027	// no parent active with label=2
1028	if _, err := snapshotter.Prepare(ctx, "a-np-wl", "", snapshots.WithLabels(map[string]string{
1029		"l":                     "2",
1030		"containerd.io/gc.root": "check-walk",
1031	})); err != nil {
1032		t.Fatal(err)
1033	}
1034
1035	for i, tc := range []struct {
1036		matches []string
1037		filters []string
1038	}{
1039		{
1040			matches: []string{"a-np", "p", "a", "v", "p-wl", "a-wl", "v-wl", "a-np-wl"},
1041			filters: []string{"labels.\"containerd.io/gc.root\"==check-walk"},
1042		},
1043		{
1044			matches: []string{"a-np", "a", "a-wl", "a-np-wl"},
1045			filters: []string{"kind==active,labels.\"containerd.io/gc.root\"==check-walk"},
1046		},
1047		{
1048			matches: []string{"v", "v-wl"},
1049			filters: []string{"kind==view,labels.\"containerd.io/gc.root\"==check-walk"},
1050		},
1051		{
1052			matches: []string{"p", "p-wl"},
1053			filters: []string{"kind==committed,labels.\"containerd.io/gc.root\"==check-walk"},
1054		},
1055		{
1056			matches: []string{"p", "a-np-wl"},
1057			filters: []string{"name==p", "name==a-np-wl"},
1058		},
1059		{
1060			matches: []string{"a-wl"},
1061			filters: []string{"name==a-wl,labels.l"},
1062		},
1063		{
1064			matches: []string{"a", "v"},
1065			filters: []string{"parent==p"},
1066		},
1067		{
1068			matches: []string{"a", "v", "a-wl", "v-wl"},
1069			filters: []string{"parent!=\"\",labels.\"containerd.io/gc.root\"==check-walk"},
1070		},
1071		{
1072			matches: []string{"p-wl", "a-wl", "v-wl", "a-np-wl"},
1073			filters: []string{"labels.l"},
1074		},
1075		{
1076			matches: []string{"a-wl", "a-np-wl"},
1077			filters: []string{"labels.l==2"},
1078		},
1079	} {
1080		actual := []string{}
1081		err := snapshotter.Walk(ctx, func(ctx context.Context, si snapshots.Info) error {
1082			actual = append(actual, si.Name)
1083			return nil
1084		}, tc.filters...)
1085		if err != nil {
1086			t.Fatal(err)
1087		}
1088
1089		sort.Strings(tc.matches)
1090		sort.Strings(actual)
1091		if len(actual) != len(tc.matches) {
1092			t.Errorf("[%d] Unexpected result (size):\nActual:\n\t%#v\nExpected:\n\t%#v", i, actual, tc.matches)
1093			continue
1094		}
1095		for j := range actual {
1096			if actual[j] != tc.matches[j] {
1097				t.Errorf("[%d] Unexpected result @%d:\nActual:\n\t%#vExpected:\n\t%#v", i, j, actual, tc.matches)
1098				break
1099			}
1100		}
1101	}
1102}
1103