rename snapshot->snapshots pkg
Signed-off-by: Jess Valarezo <valarezo.jessica@gmail.com>
This commit is contained in:
154
snapshots/testsuite/helpers.go
Normal file
154
snapshots/testsuite/helpers.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package testsuite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
"github.com/containerd/containerd/fs/fstest"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func applyToMounts(m []mount.Mount, work string, a fstest.Applier) (err error) {
|
||||
td, err := ioutil.TempDir(work, "prepare")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
if err := mount.All(m, td); err != nil {
|
||||
return errors.Wrap(err, "failed to mount")
|
||||
}
|
||||
defer func() {
|
||||
if err1 := mount.UnmountAll(td, umountflags); err == nil {
|
||||
err = errors.Wrap(err1, "failed to unmount")
|
||||
}
|
||||
}()
|
||||
|
||||
return a.Apply(td)
|
||||
}
|
||||
|
||||
// createSnapshot creates a new snapshot in the snapshotter
|
||||
// given an applier to run on top of the given parent.
|
||||
func createSnapshot(ctx context.Context, sn snapshots.Snapshotter, parent, work string, a fstest.Applier) (string, error) {
|
||||
n := fmt.Sprintf("%p-%d", a, rand.Int())
|
||||
prepare := fmt.Sprintf("%s-prepare", n)
|
||||
|
||||
m, err := sn.Prepare(ctx, prepare, parent, opt)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to prepare snapshot")
|
||||
}
|
||||
|
||||
if err := applyToMounts(m, work, a); err != nil {
|
||||
return "", errors.Wrap(err, "failed to apply")
|
||||
}
|
||||
|
||||
if err := sn.Commit(ctx, n, prepare, opt); err != nil {
|
||||
return "", errors.Wrap(err, "failed to commit")
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func checkSnapshot(ctx context.Context, sn snapshots.Snapshotter, work, name, check string) (err error) {
|
||||
td, err := ioutil.TempDir(work, "check")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer func() {
|
||||
if err1 := os.RemoveAll(td); err == nil {
|
||||
err = errors.Wrapf(err1, "failed to remove temporary directory %s", td)
|
||||
}
|
||||
}()
|
||||
|
||||
view := fmt.Sprintf("%s-view", name)
|
||||
m, err := sn.View(ctx, view, name, opt)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create view")
|
||||
}
|
||||
defer func() {
|
||||
if err1 := sn.Remove(ctx, view); err == nil {
|
||||
err = errors.Wrap(err1, "failed to remove view")
|
||||
}
|
||||
}()
|
||||
|
||||
if err := mount.All(m, td); err != nil {
|
||||
return errors.Wrap(err, "failed to mount")
|
||||
}
|
||||
defer func() {
|
||||
if err1 := mount.UnmountAll(td, umountflags); err == nil {
|
||||
err = errors.Wrap(err1, "failed to unmount view")
|
||||
}
|
||||
}()
|
||||
|
||||
if err := fstest.CheckDirectoryEqual(check, td); err != nil {
|
||||
return errors.Wrap(err, "check directory failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkSnapshots creates a new chain of snapshots in the given snapshotter
|
||||
// using the provided appliers, checking each snapshot created in a view
|
||||
// against the changes applied to a single directory.
|
||||
func checkSnapshots(ctx context.Context, sn snapshots.Snapshotter, work string, as ...fstest.Applier) error {
|
||||
td, err := ioutil.TempDir(work, "flat")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
var parentID string
|
||||
for i, a := range as {
|
||||
s, err := createSnapshot(ctx, sn, parentID, work, a)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to create snapshot %d", i+1)
|
||||
}
|
||||
|
||||
if err := a.Apply(td); err != nil {
|
||||
return errors.Wrapf(err, "failed to apply to check directory on %d", i+1)
|
||||
}
|
||||
|
||||
if err := checkSnapshot(ctx, sn, work, s, td); err != nil {
|
||||
return errors.Wrapf(err, "snapshot check failed on snapshot %d", i+1)
|
||||
}
|
||||
|
||||
parentID = s
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// checkInfo checks that the infos are the same
|
||||
func checkInfo(si1, si2 snapshots.Info) error {
|
||||
if si1.Kind != si2.Kind {
|
||||
return errors.Errorf("Expected kind %v, got %v", si1.Kind, si2.Kind)
|
||||
}
|
||||
if si1.Name != si2.Name {
|
||||
return errors.Errorf("Expected name %v, got %v", si1.Name, si2.Name)
|
||||
}
|
||||
if si1.Parent != si2.Parent {
|
||||
return errors.Errorf("Expected Parent %v, got %v", si1.Parent, si2.Parent)
|
||||
}
|
||||
if len(si1.Labels) != len(si2.Labels) {
|
||||
return errors.Errorf("Expected %d labels, got %d", len(si1.Labels), len(si2.Labels))
|
||||
}
|
||||
for k, l1 := range si1.Labels {
|
||||
l2 := si2.Labels[k]
|
||||
if l1 != l2 {
|
||||
return errors.Errorf("Expected label %v, got %v", l1, l2)
|
||||
}
|
||||
}
|
||||
if si1.Created != si2.Created {
|
||||
return errors.Errorf("Expected Created %v, got %v", si1.Created, si2.Created)
|
||||
}
|
||||
if si1.Updated != si2.Updated {
|
||||
return errors.Errorf("Expected Updated %v, got %v", si1.Updated, si2.Updated)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
5
snapshots/testsuite/helpers_linux.go
Normal file
5
snapshots/testsuite/helpers_linux.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package testsuite
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const umountflags int = unix.MNT_DETACH
|
||||
5
snapshots/testsuite/helpers_other.go
Normal file
5
snapshots/testsuite/helpers_other.go
Normal file
@@ -0,0 +1,5 @@
|
||||
// +build !linux
|
||||
|
||||
package testsuite
|
||||
|
||||
const umountflags int = 0
|
||||
226
snapshots/testsuite/issues.go
Normal file
226
snapshots/testsuite/issues.go
Normal file
@@ -0,0 +1,226 @@
|
||||
package testsuite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/fs/fstest"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
)
|
||||
|
||||
// Checks which cover former issues found in older layering models.
|
||||
//
|
||||
// NOTE: In older models, applying with tar was used to create read only layers,
|
||||
// however with the snapshot model read only layers are created just using
|
||||
// mounts and commits. Read write layers are a separate type of snapshot which
|
||||
// is not committed, avoiding any confusion in the snapshotter about whether
|
||||
// a snapshot will be mutated in the future.
|
||||
|
||||
// checkLayerFileUpdate tests the update of a single file in an upper layer
|
||||
// Cause of issue was originally related to tar, snapshot should be able to
|
||||
// avoid such issues by not relying on tar to create layers.
|
||||
// See https://github.com/docker/docker/issues/21555
|
||||
func checkLayerFileUpdate(ctx context.Context, t *testing.T, sn snapshots.Snapshotter, work string) {
|
||||
l1Init := fstest.Apply(
|
||||
fstest.CreateDir("/etc", 0700),
|
||||
fstest.CreateFile("/etc/hosts", []byte("mydomain 10.0.0.1"), 0644),
|
||||
fstest.CreateFile("/etc/profile", []byte("PATH=/usr/bin"), 0644),
|
||||
)
|
||||
l2Init := fstest.Apply(
|
||||
fstest.CreateFile("/etc/hosts", []byte("mydomain 10.0.0.2"), 0644),
|
||||
fstest.CreateFile("/etc/profile", []byte("PATH=/usr/bin"), 0666),
|
||||
fstest.CreateDir("/root", 0700),
|
||||
fstest.CreateFile("/root/.bashrc", []byte("PATH=/usr/sbin:/usr/bin"), 0644),
|
||||
)
|
||||
|
||||
var sleepTime time.Duration
|
||||
|
||||
// run 5 times to account for sporadic failure
|
||||
for i := 0; i < 5; i++ {
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
if err := checkSnapshots(ctx, sn, work, l1Init, l2Init); err != nil {
|
||||
t.Fatalf("Check snapshots failed: %+v", err)
|
||||
}
|
||||
|
||||
// Sleep until next second boundary before running again
|
||||
nextTime := time.Now()
|
||||
sleepTime = time.Unix(nextTime.Unix()+1, 0).Sub(nextTime)
|
||||
}
|
||||
}
|
||||
|
||||
// checkRemoveDirectoryInLowerLayer
|
||||
// See https://github.com/docker/docker/issues/25244
|
||||
func checkRemoveDirectoryInLowerLayer(ctx context.Context, t *testing.T, sn snapshots.Snapshotter, work string) {
|
||||
l1Init := fstest.Apply(
|
||||
fstest.CreateDir("/lib", 0700),
|
||||
fstest.CreateFile("/lib/hidden", []byte{}, 0644),
|
||||
)
|
||||
l2Init := fstest.Apply(
|
||||
fstest.RemoveAll("/lib"),
|
||||
fstest.CreateDir("/lib", 0700),
|
||||
fstest.CreateFile("/lib/not-hidden", []byte{}, 0644),
|
||||
)
|
||||
l3Init := fstest.Apply(
|
||||
fstest.CreateFile("/lib/newfile", []byte{}, 0644),
|
||||
)
|
||||
|
||||
if err := checkSnapshots(ctx, sn, work, l1Init, l2Init, l3Init); err != nil {
|
||||
t.Fatalf("Check snapshots failed: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// checkChown
|
||||
// See https://github.com/docker/docker/issues/20240 aufs
|
||||
// See https://github.com/docker/docker/issues/24913 overlay
|
||||
// see https://github.com/docker/docker/issues/28391 overlay2
|
||||
func checkChown(ctx context.Context, t *testing.T, sn snapshots.Snapshotter, work string) {
|
||||
l1Init := fstest.Apply(
|
||||
fstest.CreateDir("/opt", 0700),
|
||||
fstest.CreateDir("/opt/a", 0700),
|
||||
fstest.CreateDir("/opt/a/b", 0700),
|
||||
fstest.CreateFile("/opt/a/b/file.txt", []byte("hello"), 0644),
|
||||
)
|
||||
l2Init := fstest.Apply(
|
||||
fstest.Chown("/opt", 1, 1),
|
||||
fstest.Chown("/opt/a", 1, 1),
|
||||
fstest.Chown("/opt/a/b", 1, 1),
|
||||
fstest.Chown("/opt/a/b/file.txt", 1, 1),
|
||||
)
|
||||
|
||||
if err := checkSnapshots(ctx, sn, work, l1Init, l2Init); err != nil {
|
||||
t.Fatalf("Check snapshots failed: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// checkRename
|
||||
// https://github.com/docker/docker/issues/25409
|
||||
func checkRename(ctx context.Context, t *testing.T, sn snapshots.Snapshotter, work string) {
|
||||
t.Skip("rename test still fails on some kernels with overlay")
|
||||
l1Init := fstest.Apply(
|
||||
fstest.CreateDir("/dir1", 0700),
|
||||
fstest.CreateDir("/somefiles", 0700),
|
||||
fstest.CreateFile("/somefiles/f1", []byte("was here first!"), 0644),
|
||||
fstest.CreateFile("/somefiles/f2", []byte("nothing interesting"), 0644),
|
||||
)
|
||||
l2Init := fstest.Apply(
|
||||
fstest.Rename("/dir1", "/dir2"),
|
||||
fstest.CreateFile("/somefiles/f1-overwrite", []byte("new content 1"), 0644),
|
||||
fstest.Rename("/somefiles/f1-overwrite", "/somefiles/f1"),
|
||||
fstest.Rename("/somefiles/f2", "/somefiles/f3"),
|
||||
)
|
||||
|
||||
if err := checkSnapshots(ctx, sn, work, l1Init, l2Init); err != nil {
|
||||
t.Fatalf("Check snapshots failed: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// checkDirectoryPermissionOnCommit
|
||||
// https://github.com/docker/docker/issues/27298
|
||||
func checkDirectoryPermissionOnCommit(ctx context.Context, t *testing.T, sn snapshots.Snapshotter, work string) {
|
||||
l1Init := fstest.Apply(
|
||||
fstest.CreateDir("/dir1", 0700),
|
||||
fstest.CreateDir("/dir2", 0700),
|
||||
fstest.CreateDir("/dir3", 0700),
|
||||
fstest.CreateDir("/dir4", 0700),
|
||||
fstest.CreateFile("/dir4/f1", []byte("..."), 0644),
|
||||
fstest.CreateDir("/dir5", 0700),
|
||||
fstest.CreateFile("/dir5/f1", []byte("..."), 0644),
|
||||
fstest.Chown("/dir1", 1, 1),
|
||||
fstest.Chown("/dir2", 1, 1),
|
||||
fstest.Chown("/dir3", 1, 1),
|
||||
fstest.Chown("/dir5", 1, 1),
|
||||
fstest.Chown("/dir5/f1", 1, 1),
|
||||
)
|
||||
l2Init := fstest.Apply(
|
||||
fstest.Chown("/dir2", 0, 0),
|
||||
fstest.RemoveAll("/dir3"),
|
||||
fstest.Chown("/dir4", 1, 1),
|
||||
fstest.Chown("/dir4/f1", 1, 1),
|
||||
)
|
||||
l3Init := fstest.Apply(
|
||||
fstest.CreateDir("/dir3", 0700),
|
||||
fstest.Chown("/dir3", 1, 1),
|
||||
fstest.RemoveAll("/dir5"),
|
||||
fstest.CreateDir("/dir5", 0700),
|
||||
fstest.Chown("/dir5", 1, 1),
|
||||
)
|
||||
|
||||
if err := checkSnapshots(ctx, sn, work, l1Init, l2Init, l3Init); err != nil {
|
||||
t.Fatalf("Check snapshots failed: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// checkStatInWalk ensures that a stat can be called during a walk
|
||||
func checkStatInWalk(ctx context.Context, t *testing.T, sn snapshots.Snapshotter, work string) {
|
||||
prefix := "stats-in-walk-"
|
||||
if err := createNamedSnapshots(ctx, sn, prefix); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := sn.Walk(ctx, func(ctx context.Context, si snapshots.Info) error {
|
||||
if !strings.HasPrefix(si.Name, prefix) {
|
||||
// Only stat snapshots from this test
|
||||
return nil
|
||||
}
|
||||
si2, err := sn.Stat(ctx, si.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return checkInfo(si, si2)
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func createNamedSnapshots(ctx context.Context, snapshotter snapshots.Snapshotter, ns string) error {
|
||||
c1 := fmt.Sprintf("%sc1", ns)
|
||||
c2 := fmt.Sprintf("%sc2", ns)
|
||||
if _, err := snapshotter.Prepare(ctx, c1+"-a", "", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := snapshotter.Commit(ctx, c1, c1+"-a", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.Prepare(ctx, c2+"-a", c1, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := snapshotter.Commit(ctx, c2, c2+"-a", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.Prepare(ctx, fmt.Sprintf("%sa1", ns), c2, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.View(ctx, fmt.Sprintf("%sv1", ns), c2, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// More issues to test
|
||||
//
|
||||
// checkRemoveAfterCommit
|
||||
// See https://github.com/docker/docker/issues/24309
|
||||
//
|
||||
// checkUnixDomainSockets
|
||||
// See https://github.com/docker/docker/issues/12080
|
||||
//
|
||||
// checkDirectoryInodeStability
|
||||
// See https://github.com/docker/docker/issues/19647
|
||||
//
|
||||
// checkOpenFileInodeStability
|
||||
// See https://github.com/docker/docker/issues/12327
|
||||
//
|
||||
// checkGetCWD
|
||||
// See https://github.com/docker/docker/issues/19082
|
||||
//
|
||||
// checkChmod
|
||||
// See https://github.com/docker/machine/issues/3327
|
||||
//
|
||||
// checkRemoveInWalk
|
||||
// Allow mutations during walk without deadlocking
|
||||
826
snapshots/testsuite/testsuite.go
Normal file
826
snapshots/testsuite/testsuite.go
Normal file
@@ -0,0 +1,826 @@
|
||||
package testsuite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/fs/fstest"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
"github.com/containerd/containerd/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// SnapshotterSuite runs a test suite on the snapshotter given a factory function.
|
||||
func SnapshotterSuite(t *testing.T, name string, snapshotterFn func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error)) {
|
||||
restoreMask := clearMask()
|
||||
defer restoreMask()
|
||||
|
||||
t.Run("Basic", makeTest(name, snapshotterFn, checkSnapshotterBasic))
|
||||
t.Run("StatActive", makeTest(name, snapshotterFn, checkSnapshotterStatActive))
|
||||
t.Run("StatComitted", makeTest(name, snapshotterFn, checkSnapshotterStatCommitted))
|
||||
t.Run("TransitivityTest", makeTest(name, snapshotterFn, checkSnapshotterTransitivity))
|
||||
t.Run("PreareViewFailingtest", makeTest(name, snapshotterFn, checkSnapshotterPrepareView))
|
||||
t.Run("Update", makeTest(name, snapshotterFn, checkUpdate))
|
||||
t.Run("Remove", makeTest(name, snapshotterFn, checkRemove))
|
||||
|
||||
t.Run("LayerFileupdate", makeTest(name, snapshotterFn, checkLayerFileUpdate))
|
||||
t.Run("RemoveDirectoryInLowerLayer", makeTest(name, snapshotterFn, checkRemoveDirectoryInLowerLayer))
|
||||
t.Run("Chown", makeTest(name, snapshotterFn, checkChown))
|
||||
t.Run("DirectoryPermissionOnCommit", makeTest(name, snapshotterFn, checkDirectoryPermissionOnCommit))
|
||||
t.Run("RemoveIntermediateSnapshot", makeTest(name, snapshotterFn, checkRemoveIntermediateSnapshot))
|
||||
t.Run("DeletedFilesInChildSnapshot", makeTest(name, snapshotterFn, checkDeletedFilesInChildSnapshot))
|
||||
t.Run("MoveFileFromLowerLayer", makeTest(name, snapshotterFn, checkFileFromLowerLayer))
|
||||
t.Run("Rename", makeTest(name, snapshotterFn, checkRename))
|
||||
|
||||
t.Run("ViewReadonly", makeTest(name, snapshotterFn, checkSnapshotterViewReadonly))
|
||||
|
||||
t.Run("StatInWalk", makeTest(name, snapshotterFn, checkStatInWalk))
|
||||
t.Run("CloseTwice", makeTest(name, snapshotterFn, closeTwice))
|
||||
}
|
||||
|
||||
func 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) {
|
||||
return func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = namespaces.WithNamespace(ctx, "testsuite")
|
||||
// Make two directories: a snapshotter root and a play area for the tests:
|
||||
//
|
||||
// /tmp
|
||||
// work/ -> passed to test functions
|
||||
// root/ -> passed to snapshotter
|
||||
//
|
||||
tmpDir, err := ioutil.TempDir("", "snapshot-suite-"+name+"-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
root := filepath.Join(tmpDir, "root")
|
||||
if err := os.MkdirAll(root, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
snapshotter, cleanup, err := snapshotterFn(ctx, root)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to initialize snapshotter: %+v", err)
|
||||
}
|
||||
defer func() {
|
||||
if cleanup != nil {
|
||||
if err := cleanup(); err != nil {
|
||||
t.Errorf("Cleanup failed: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
work := filepath.Join(tmpDir, "work")
|
||||
if err := os.MkdirAll(work, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer testutil.DumpDir(t, tmpDir)
|
||||
fn(ctx, t, snapshotter, work)
|
||||
}
|
||||
}
|
||||
|
||||
var opt = snapshots.WithLabels(map[string]string{
|
||||
"containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
|
||||
// checkSnapshotterBasic tests the basic workflow of a snapshot snapshotter.
|
||||
func checkSnapshotterBasic(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
initialApplier := fstest.Apply(
|
||||
fstest.CreateFile("/foo", []byte("foo\n"), 0777),
|
||||
fstest.CreateDir("/a", 0755),
|
||||
fstest.CreateDir("/a/b", 0755),
|
||||
fstest.CreateDir("/a/b/c", 0755),
|
||||
)
|
||||
|
||||
diffApplier := fstest.Apply(
|
||||
fstest.CreateFile("/bar", []byte("bar\n"), 0777),
|
||||
// also, change content of foo to bar
|
||||
fstest.CreateFile("/foo", []byte("bar\n"), 0777),
|
||||
fstest.RemoveAll("/a/b"),
|
||||
)
|
||||
|
||||
preparing := filepath.Join(work, "preparing")
|
||||
if err := os.MkdirAll(preparing, 0777); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
mounts, err := snapshotter.Prepare(ctx, preparing, "", opt)
|
||||
if err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
if len(mounts) < 1 {
|
||||
t.Fatal("expected mounts to have entries")
|
||||
}
|
||||
|
||||
if err := mount.All(mounts, preparing); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
defer testutil.Unmount(t, preparing)
|
||||
|
||||
if err := initialApplier.Apply(preparing); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
committed := filepath.Join(work, "committed")
|
||||
if err := snapshotter.Commit(ctx, committed, preparing, opt); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
si, err := snapshotter.Stat(ctx, committed)
|
||||
if err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "", si.Parent)
|
||||
assert.Equal(t, snapshots.KindCommitted, si.Kind)
|
||||
|
||||
_, err = snapshotter.Stat(ctx, preparing)
|
||||
if err == nil {
|
||||
t.Fatalf("%s should no longer be available after Commit", preparing)
|
||||
}
|
||||
|
||||
next := filepath.Join(work, "nextlayer")
|
||||
if err := os.MkdirAll(next, 0777); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
mounts, err = snapshotter.Prepare(ctx, next, committed, opt)
|
||||
if err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
if err := mount.All(mounts, next); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
defer testutil.Unmount(t, next)
|
||||
|
||||
if err := fstest.CheckDirectoryEqualWithApplier(next, initialApplier); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
if err := diffApplier.Apply(next); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
ni, err := snapshotter.Stat(ctx, next)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, committed, ni.Parent)
|
||||
assert.Equal(t, snapshots.KindActive, ni.Kind)
|
||||
|
||||
nextCommitted := filepath.Join(work, "committed-next")
|
||||
if err := snapshotter.Commit(ctx, nextCommitted, next, opt); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
si2, err := snapshotter.Stat(ctx, nextCommitted)
|
||||
if err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, committed, si2.Parent)
|
||||
assert.Equal(t, snapshots.KindCommitted, si2.Kind)
|
||||
|
||||
_, err = snapshotter.Stat(ctx, next)
|
||||
if err == nil {
|
||||
t.Fatalf("%s should no longer be available after Commit", next)
|
||||
}
|
||||
|
||||
expected := map[string]snapshots.Info{
|
||||
si.Name: si,
|
||||
si2.Name: si2,
|
||||
}
|
||||
walked := map[string]snapshots.Info{} // walk is not ordered
|
||||
assert.NoError(t, snapshotter.Walk(ctx, func(ctx context.Context, si snapshots.Info) error {
|
||||
walked[si.Name] = si
|
||||
return nil
|
||||
}))
|
||||
|
||||
for ek, ev := range expected {
|
||||
av, ok := walked[ek]
|
||||
if !ok {
|
||||
t.Errorf("Missing stat for %v", ek)
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, ev, av)
|
||||
}
|
||||
|
||||
nextnext := filepath.Join(work, "nextnextlayer")
|
||||
if err := os.MkdirAll(nextnext, 0777); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
mounts, err = snapshotter.View(ctx, nextnext, nextCommitted, opt)
|
||||
if err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
if err := mount.All(mounts, nextnext); err != nil {
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
if err := fstest.CheckDirectoryEqualWithApplier(nextnext,
|
||||
fstest.Apply(initialApplier, diffApplier)); err != nil {
|
||||
testutil.Unmount(t, nextnext)
|
||||
t.Fatalf("failure reason: %+v", err)
|
||||
}
|
||||
|
||||
testutil.Unmount(t, nextnext)
|
||||
assert.NoError(t, snapshotter.Remove(ctx, nextnext))
|
||||
assert.Error(t, snapshotter.Remove(ctx, committed))
|
||||
assert.NoError(t, snapshotter.Remove(ctx, nextCommitted))
|
||||
assert.NoError(t, snapshotter.Remove(ctx, committed))
|
||||
}
|
||||
|
||||
// Create a New Layer on top of base layer with Prepare, Stat on new layer, should return Active layer.
|
||||
func checkSnapshotterStatActive(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
preparing := filepath.Join(work, "preparing")
|
||||
if err := os.MkdirAll(preparing, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mounts, err := snapshotter.Prepare(ctx, preparing, "", opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(mounts) < 1 {
|
||||
t.Fatal("expected mounts to have entries")
|
||||
}
|
||||
|
||||
if err = mount.All(mounts, preparing); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testutil.Unmount(t, preparing)
|
||||
|
||||
if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
si, err := snapshotter.Stat(ctx, preparing)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, si.Name, preparing)
|
||||
assert.Equal(t, snapshots.KindActive, si.Kind)
|
||||
assert.Equal(t, "", si.Parent)
|
||||
}
|
||||
|
||||
// Commit a New Layer on top of base layer with Prepare & Commit , Stat on new layer, should return Committed layer.
|
||||
func checkSnapshotterStatCommitted(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
preparing := filepath.Join(work, "preparing")
|
||||
if err := os.MkdirAll(preparing, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mounts, err := snapshotter.Prepare(ctx, preparing, "", opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(mounts) < 1 {
|
||||
t.Fatal("expected mounts to have entries")
|
||||
}
|
||||
|
||||
if err = mount.All(mounts, preparing); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testutil.Unmount(t, preparing)
|
||||
|
||||
if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
committed := filepath.Join(work, "committed")
|
||||
if err = snapshotter.Commit(ctx, committed, preparing, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
si, err := snapshotter.Stat(ctx, committed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, si.Name, committed)
|
||||
assert.Equal(t, snapshots.KindCommitted, si.Kind)
|
||||
assert.Equal(t, "", si.Parent)
|
||||
|
||||
}
|
||||
|
||||
func snapshotterPrepareMount(ctx context.Context, snapshotter snapshots.Snapshotter, diffPathName string, parent string, work string) (string, error) {
|
||||
preparing := filepath.Join(work, diffPathName)
|
||||
if err := os.MkdirAll(preparing, 0777); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
mounts, err := snapshotter.Prepare(ctx, preparing, parent, opt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(mounts) < 1 {
|
||||
return "", fmt.Errorf("expected mounts to have entries")
|
||||
}
|
||||
|
||||
if err = mount.All(mounts, preparing); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return preparing, nil
|
||||
}
|
||||
|
||||
// Given A <- B <- C, B is the parent of C and A is a transitive parent of C (in this case, a "grandparent")
|
||||
func checkSnapshotterTransitivity(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testutil.Unmount(t, preparing)
|
||||
|
||||
if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
snapA := filepath.Join(work, "snapA")
|
||||
if err = snapshotter.Commit(ctx, snapA, preparing, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
next, err := snapshotterPrepareMount(ctx, snapshotter, "next", snapA, work)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testutil.Unmount(t, next)
|
||||
|
||||
if err = ioutil.WriteFile(filepath.Join(next, "foo"), []byte("foo bar\n"), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
snapB := filepath.Join(work, "snapB")
|
||||
if err = snapshotter.Commit(ctx, snapB, next, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
siA, err := snapshotter.Stat(ctx, snapA)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
siB, err := snapshotter.Stat(ctx, snapB)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
siParentB, err := snapshotter.Stat(ctx, siB.Parent)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test the transivity
|
||||
assert.Equal(t, "", siA.Parent)
|
||||
assert.Equal(t, snapA, siB.Parent)
|
||||
assert.Equal(t, "", siParentB.Parent)
|
||||
|
||||
}
|
||||
|
||||
// Creating two layers with Prepare or View with same key must fail.
|
||||
func checkSnapshotterPrepareView(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testutil.Unmount(t, preparing)
|
||||
|
||||
snapA := filepath.Join(work, "snapA")
|
||||
if err = snapshotter.Commit(ctx, snapA, preparing, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Prepare & View with same key
|
||||
newLayer := filepath.Join(work, "newlayer")
|
||||
if err = os.MkdirAll(preparing, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Prepare & View with same key
|
||||
_, err = snapshotter.Prepare(ctx, newLayer, snapA, opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = snapshotter.View(ctx, newLayer, snapA, opt)
|
||||
//must be err != nil
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// Two Prepare with same key
|
||||
prepLayer := filepath.Join(work, "prepLayer")
|
||||
if err = os.MkdirAll(preparing, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = snapshotter.Prepare(ctx, prepLayer, snapA, opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = snapshotter.Prepare(ctx, prepLayer, snapA, opt)
|
||||
//must be err != nil
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// Two View with same key
|
||||
viewLayer := filepath.Join(work, "viewLayer")
|
||||
if err = os.MkdirAll(preparing, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = snapshotter.View(ctx, viewLayer, snapA, opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = snapshotter.View(ctx, viewLayer, snapA, opt)
|
||||
//must be err != nil
|
||||
assert.NotNil(t, err)
|
||||
|
||||
}
|
||||
|
||||
// Deletion of files/folder of base layer in new layer, On Commit, those files should not be visible.
|
||||
func checkDeletedFilesInChildSnapshot(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
|
||||
l1Init := fstest.Apply(
|
||||
fstest.CreateFile("/foo", []byte("foo\n"), 0777),
|
||||
fstest.CreateFile("/foobar", []byte("foobar\n"), 0777),
|
||||
)
|
||||
l2Init := fstest.Apply(
|
||||
fstest.RemoveAll("/foobar"),
|
||||
)
|
||||
l3Init := fstest.Apply()
|
||||
|
||||
if err := checkSnapshots(ctx, snapshotter, work, l1Init, l2Init, l3Init); err != nil {
|
||||
t.Fatalf("Check snapshots failed: %+v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Create three layers. Deleting intermediate layer must fail.
|
||||
func checkRemoveIntermediateSnapshot(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
|
||||
base, err := snapshotterPrepareMount(ctx, snapshotter, "base", "", work)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer testutil.Unmount(t, base)
|
||||
|
||||
committedBase := filepath.Join(work, "committed-base")
|
||||
if err = snapshotter.Commit(ctx, committedBase, base, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create intermediate layer
|
||||
intermediate := filepath.Join(work, "intermediate")
|
||||
if _, err = snapshotter.Prepare(ctx, intermediate, committedBase, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
committedInter := filepath.Join(work, "committed-inter")
|
||||
if err = snapshotter.Commit(ctx, committedInter, intermediate, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create top layer
|
||||
topLayer := filepath.Join(work, "toplayer")
|
||||
if _, err = snapshotter.Prepare(ctx, topLayer, committedInter, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Deletion of intermediate layer must fail.
|
||||
err = snapshotter.Remove(ctx, committedInter)
|
||||
if err == nil {
|
||||
t.Fatal("intermediate layer removal should fail.")
|
||||
}
|
||||
|
||||
//Removal from toplayer to base should not fail.
|
||||
err = snapshotter.Remove(ctx, topLayer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = snapshotter.Remove(ctx, committedInter)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = snapshotter.Remove(ctx, committedBase)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// baseTestSnapshots creates a base set of snapshots for tests, each snapshot is empty
|
||||
// Tests snapshots:
|
||||
// c1 - committed snapshot, no parent
|
||||
// c2 - commited snapshot, c1 is parent
|
||||
// a1 - active snapshot, c2 is parent
|
||||
// a1 - active snapshot, no parent
|
||||
// v1 - view snapshot, v1 is parent
|
||||
// v2 - view snapshot, no parent
|
||||
func baseTestSnapshots(ctx context.Context, snapshotter snapshots.Snapshotter) error {
|
||||
if _, err := snapshotter.Prepare(ctx, "c1-a", "", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := snapshotter.Commit(ctx, "c1", "c1-a", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.Prepare(ctx, "c2-a", "c1", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := snapshotter.Commit(ctx, "c2", "c2-a", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.Prepare(ctx, "a1", "c2", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.Prepare(ctx, "a2", "", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.View(ctx, "v1", "c2", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := snapshotter.View(ctx, "v2", "", opt); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkUpdate(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
t1 := time.Now().UTC()
|
||||
if err := baseTestSnapshots(ctx, snapshotter); err != nil {
|
||||
t.Fatalf("Failed to create base snapshots: %v", err)
|
||||
}
|
||||
t2 := time.Now().UTC()
|
||||
testcases := []struct {
|
||||
name string
|
||||
kind snapshots.Kind
|
||||
parent string
|
||||
}{
|
||||
{
|
||||
name: "c1",
|
||||
kind: snapshots.KindCommitted,
|
||||
},
|
||||
{
|
||||
name: "c2",
|
||||
kind: snapshots.KindCommitted,
|
||||
parent: "c1",
|
||||
},
|
||||
{
|
||||
name: "a1",
|
||||
kind: snapshots.KindActive,
|
||||
parent: "c2",
|
||||
},
|
||||
{
|
||||
name: "a2",
|
||||
kind: snapshots.KindActive,
|
||||
},
|
||||
{
|
||||
name: "v1",
|
||||
kind: snapshots.KindView,
|
||||
parent: "c2",
|
||||
},
|
||||
{
|
||||
name: "v2",
|
||||
kind: snapshots.KindView,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
st, err := snapshotter.Stat(ctx, tc.name)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to stat %s: %v", tc.name, err)
|
||||
}
|
||||
if st.Created.Before(t1) || st.Created.After(t2) {
|
||||
t.Errorf("(%s) wrong created time %s: expected between %s and %s", tc.name, st.Created, t1, t2)
|
||||
continue
|
||||
}
|
||||
if st.Created != st.Updated {
|
||||
t.Errorf("(%s) unexpected updated time %s: expected %s", tc.name, st.Updated, st.Created)
|
||||
continue
|
||||
}
|
||||
if st.Kind != tc.kind {
|
||||
t.Errorf("(%s) unexpected kind %s, expected %s", tc.name, st.Kind, tc.kind)
|
||||
continue
|
||||
}
|
||||
if st.Parent != tc.parent {
|
||||
t.Errorf("(%s) unexpected parent %q, expected %q", tc.name, st.Parent, tc.parent)
|
||||
continue
|
||||
}
|
||||
if st.Name != tc.name {
|
||||
t.Errorf("(%s) unexpected name %q, expected %q", tc.name, st.Name, tc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
createdAt := st.Created
|
||||
rootTime := time.Now().UTC().Format(time.RFC3339)
|
||||
expected := map[string]string{
|
||||
"l1": "v1",
|
||||
"l2": "v2",
|
||||
"l3": "v3",
|
||||
// Keep root label
|
||||
"containerd.io/gc.root": rootTime,
|
||||
}
|
||||
st.Parent = "doesnotexist"
|
||||
st.Labels = expected
|
||||
u1 := time.Now().UTC()
|
||||
st, err = snapshotter.Update(ctx, st)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update %s: %v", tc.name, err)
|
||||
}
|
||||
u2 := time.Now().UTC()
|
||||
|
||||
if st.Created != createdAt {
|
||||
t.Errorf("(%s) wrong created time %s: expected %s", tc.name, st.Created, createdAt)
|
||||
continue
|
||||
}
|
||||
if st.Updated.Before(u1) || st.Updated.After(u2) {
|
||||
t.Errorf("(%s) wrong updated time %s: expected between %s and %s", tc.name, st.Updated, u1, u2)
|
||||
continue
|
||||
}
|
||||
if st.Kind != tc.kind {
|
||||
t.Errorf("(%s) unexpected kind %s, expected %s", tc.name, st.Kind, tc.kind)
|
||||
continue
|
||||
}
|
||||
if st.Parent != tc.parent {
|
||||
t.Errorf("(%s) unexpected parent %q, expected %q", tc.name, st.Parent, tc.parent)
|
||||
continue
|
||||
}
|
||||
if st.Name != tc.name {
|
||||
t.Errorf("(%s) unexpected name %q, expected %q", tc.name, st.Name, tc.name)
|
||||
continue
|
||||
}
|
||||
assertLabels(t, st.Labels, expected)
|
||||
|
||||
expected = map[string]string{
|
||||
"l1": "updated",
|
||||
"l3": "v3",
|
||||
"containerd.io/gc.root": rootTime,
|
||||
}
|
||||
st.Labels = map[string]string{
|
||||
"l1": "updated",
|
||||
"l4": "v4",
|
||||
}
|
||||
st, err = snapshotter.Update(ctx, st, "labels.l1", "labels.l2")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update %s: %v", tc.name, err)
|
||||
}
|
||||
assertLabels(t, st.Labels, expected)
|
||||
|
||||
expected = map[string]string{
|
||||
"l4": "v4",
|
||||
"containerd.io/gc.root": rootTime,
|
||||
}
|
||||
st.Labels = expected
|
||||
st, err = snapshotter.Update(ctx, st, "labels")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to update %s: %v", tc.name, err)
|
||||
}
|
||||
assertLabels(t, st.Labels, expected)
|
||||
|
||||
// Test failure received when providing immutable field path
|
||||
st.Parent = "doesnotexist"
|
||||
st, err = snapshotter.Update(ctx, st, "parent")
|
||||
if err == nil {
|
||||
t.Errorf("Expected error updating with immutable field path")
|
||||
} else if !errdefs.IsInvalidArgument(err) {
|
||||
t.Fatalf("Unexpected error updating %s: %+v", tc.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertLabels(t *testing.T, actual, expected map[string]string) {
|
||||
if len(actual) != len(expected) {
|
||||
t.Fatalf("Label size mismatch: %d vs %d\n\tActual: %#v\n\tExpected: %#v", len(actual), len(expected), actual, expected)
|
||||
}
|
||||
for k, v := range expected {
|
||||
if a := actual[k]; v != a {
|
||||
t.Errorf("Wrong label value for %s, got %q, expected %q", k, a, v)
|
||||
}
|
||||
}
|
||||
if t.Failed() {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func checkRemove(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
if _, err := snapshotter.Prepare(ctx, "committed-a", "", opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Commit(ctx, "committed-1", "committed-a", opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := snapshotter.Prepare(ctx, "reuse-1", "committed-1", opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Remove(ctx, "reuse-1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := snapshotter.View(ctx, "reuse-1", "committed-1", opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Remove(ctx, "reuse-1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := snapshotter.Prepare(ctx, "reuse-1", "", opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Remove(ctx, "committed-1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Commit(ctx, "commited-1", "reuse-1", opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// checkSnapshotterViewReadonly ensures a KindView snapshot to be mounted as a read-only filesystem.
|
||||
// This function is called only when WithTestViewReadonly is true.
|
||||
func checkSnapshotterViewReadonly(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
preparing := filepath.Join(work, "preparing")
|
||||
if _, err := snapshotter.Prepare(ctx, preparing, "", opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
committed := filepath.Join(work, "commited")
|
||||
if err := snapshotter.Commit(ctx, committed, preparing, opt); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
view := filepath.Join(work, "view")
|
||||
m, err := snapshotter.View(ctx, view, committed, opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
viewMountPoint := filepath.Join(work, "view-mount")
|
||||
if err := os.MkdirAll(viewMountPoint, 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Just checking the option string of m is not enough, we need to test real mount. (#1368)
|
||||
if err := mount.All(m, viewMountPoint); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testfile := filepath.Join(viewMountPoint, "testfile")
|
||||
if err := ioutil.WriteFile(testfile, []byte("testcontent"), 0777); err != nil {
|
||||
t.Logf("write to %q failed with %v (EROFS is expected but can be other error code)", testfile, err)
|
||||
} else {
|
||||
t.Fatalf("write to %q should fail (EROFS) but did not fail", testfile)
|
||||
}
|
||||
testutil.Unmount(t, viewMountPoint)
|
||||
assert.NoError(t, snapshotter.Remove(ctx, view))
|
||||
assert.NoError(t, snapshotter.Remove(ctx, committed))
|
||||
}
|
||||
|
||||
// Move files from base layer to new location in intermediate layer.
|
||||
// Verify if the file at source is deleted and copied to new location.
|
||||
func checkFileFromLowerLayer(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
l1Init := fstest.Apply(
|
||||
fstest.CreateDir("/dir1", 0700),
|
||||
fstest.CreateFile("/dir1/f1", []byte("Hello"), 0644),
|
||||
fstest.CreateDir("dir2", 0700),
|
||||
fstest.CreateFile("dir2/f2", []byte("..."), 0644),
|
||||
)
|
||||
l2Init := fstest.Apply(
|
||||
fstest.CreateDir("/dir3", 0700),
|
||||
fstest.CreateFile("/dir3/f1", []byte("Hello"), 0644),
|
||||
fstest.RemoveAll("/dir1"),
|
||||
fstest.Link("dir2/f2", "dir3/f2"),
|
||||
fstest.RemoveAll("dir2/f2"),
|
||||
)
|
||||
|
||||
if err := checkSnapshots(ctx, snapshotter, work, l1Init, l2Init); err != nil {
|
||||
t.Fatalf("Check snapshots failed: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func closeTwice(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) {
|
||||
// do some dummy ops to modify the snapshotter internal state
|
||||
if _, err := snapshotter.Prepare(ctx, "dummy", ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Commit(ctx, "dummy-1", "dummy"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Remove(ctx, "dummy-1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := snapshotter.Close(); err != nil {
|
||||
t.Fatalf("The first close failed: %+v", err)
|
||||
}
|
||||
if err := snapshotter.Close(); err != nil {
|
||||
t.Fatalf("The second close failed: %+v", err)
|
||||
}
|
||||
}
|
||||
12
snapshots/testsuite/testsuite_unix.go
Normal file
12
snapshots/testsuite/testsuite_unix.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// +build !windows
|
||||
|
||||
package testsuite
|
||||
|
||||
import "syscall"
|
||||
|
||||
func clearMask() func() {
|
||||
oldumask := syscall.Umask(0)
|
||||
return func() {
|
||||
syscall.Umask(oldumask)
|
||||
}
|
||||
}
|
||||
5
snapshots/testsuite/testsuite_windows.go
Normal file
5
snapshots/testsuite/testsuite_windows.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package testsuite
|
||||
|
||||
func clearMask() func() {
|
||||
return func() {}
|
||||
}
|
||||
Reference in New Issue
Block a user