github.com/containerd/containerd/v2@v2.0.0-rc.2/core/snapshots/testsuite/testsuite.go (about)

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