Add snapshot test cases for former issues

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan 2017-07-28 13:15:42 -07:00
parent 20fa6aee2e
commit a7c44f0038
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB
3 changed files with 298 additions and 0 deletions

View File

@ -0,0 +1,120 @@
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/snapshot"
"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.MountAll(m, td); err != nil {
return err
}
defer func() {
if err1 := mount.UnmountAll(td, 0); err == nil {
err = err1
}
}()
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 snapshot.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)
if err != nil {
return "", err
}
if err := applyToMounts(m, work, a); err != nil {
return "", err
}
if err := sn.Commit(ctx, n, prepare); err != nil {
return "", err
}
return n, nil
}
func checkSnapshot(ctx context.Context, sn snapshot.Snapshotter, work, name, check string) error {
td, err := ioutil.TempDir(work, "check")
if err != nil {
return errors.Wrap(err, "failed to create temp dir")
}
defer os.RemoveAll(td)
view := fmt.Sprintf("%s-view", name)
m, err := sn.View(ctx, view, name)
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.MountAll(m, td); err != nil {
return errors.Wrap(err, "failed to unmount")
}
defer func() {
if err1 := mount.UnmountAll(td, 0); 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 snapshot.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
}

View File

@ -0,0 +1,172 @@
package testsuite
import (
"context"
"testing"
"time"
"github.com/containerd/containerd/fs/fstest"
"github.com/containerd/containerd/snapshot"
)
// 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 snapshot.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 snapshot.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 snapshot.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 snapshot.Snapshotter, work string) {
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 snapshot.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)
}
}
// 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

View File

@ -23,6 +23,12 @@ func SnapshotterSuite(t *testing.T, name string, snapshotterFn func(ctx context.
t.Run("StatComitted", makeTest(t, name, snapshotterFn, checkSnapshotterStatCommitted))
t.Run("TransitivityTest", makeTest(t, name, snapshotterFn, checkSnapshotterTransitivity))
t.Run("PreareViewFailingtest", makeTest(t, name, snapshotterFn, checkSnapshotterPrepareView))
t.Run("LayerFileupdate", makeTest(t, name, snapshotterFn, checkLayerFileUpdate))
t.Run("RemoveDirectoryInLowerLayer", makeTest(t, name, snapshotterFn, checkRemoveDirectoryInLowerLayer))
t.Run("Chown", makeTest(t, name, snapshotterFn, checkChown))
t.Run("Rename", makeTest(t, name, snapshotterFn, checkRename))
t.Run("DirectoryPermissionOnCommit", makeTest(t, name, snapshotterFn, checkDirectoryPermissionOnCommit))
}
func makeTest(t *testing.T, name string, snapshotterFn func(ctx context.Context, root string) (snapshot.Snapshotter, func(), error), fn func(ctx context.Context, t *testing.T, snapshotter snapshot.Snapshotter, work string)) func(t *testing.T) {