Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1262 from dmcgowan/more-snapshot-tests
Add snapshot test cases for former issues
- Loading branch information
Showing
4 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters