2017-01-13 23:31:21 +00:00
|
|
|
package overlay
|
2016-12-09 19:27:30 +00:00
|
|
|
|
|
|
|
import (
|
2017-02-24 23:23:23 +00:00
|
|
|
"context"
|
2017-02-18 00:34:21 +00:00
|
|
|
"fmt"
|
2016-12-09 19:27:30 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"syscall"
|
|
|
|
"testing"
|
|
|
|
|
2017-04-03 20:14:15 +00:00
|
|
|
"github.com/containerd/containerd"
|
|
|
|
"github.com/containerd/containerd/snapshot"
|
2017-03-25 00:16:41 +00:00
|
|
|
"github.com/containerd/containerd/snapshot/storage"
|
2017-04-03 20:14:15 +00:00
|
|
|
"github.com/containerd/containerd/snapshot/testsuite"
|
|
|
|
"github.com/containerd/containerd/testutil"
|
2016-12-09 19:27:30 +00:00
|
|
|
)
|
|
|
|
|
2017-03-25 00:16:41 +00:00
|
|
|
func newSnapshotter(ctx context.Context, root string) (snapshot.Snapshotter, func(), error) {
|
|
|
|
snapshotter, err := NewSnapshotter(root)
|
2017-03-16 06:25:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return snapshotter, func() {}, nil
|
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
func TestOverlay(t *testing.T) {
|
2017-02-07 23:51:37 +00:00
|
|
|
testutil.RequiresRoot(t)
|
2017-03-25 00:16:41 +00:00
|
|
|
testsuite.SnapshotterSuite(t, "Overlay", newSnapshotter)
|
2017-02-07 23:51:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestOverlayMounts(t *testing.T) {
|
2017-02-24 23:23:23 +00:00
|
|
|
ctx := context.TODO()
|
2016-12-09 19:27:30 +00:00
|
|
|
root, err := ioutil.TempDir("", "overlay")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(root)
|
2017-03-25 00:16:41 +00:00
|
|
|
o, _, err := newSnapshotter(ctx, root)
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2017-02-24 23:23:23 +00:00
|
|
|
mounts, err := o.Prepare(ctx, "/tmp/test", "")
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(mounts) != 1 {
|
|
|
|
t.Errorf("should only have 1 mount but received %d", len(mounts))
|
|
|
|
}
|
|
|
|
m := mounts[0]
|
|
|
|
if m.Type != "bind" {
|
|
|
|
t.Errorf("mount type should be bind but received %q", m.Type)
|
|
|
|
}
|
2017-03-16 06:25:53 +00:00
|
|
|
expected := filepath.Join(root, "snapshots", "1", "fs")
|
2016-12-09 19:27:30 +00:00
|
|
|
if m.Source != expected {
|
|
|
|
t.Errorf("expected source %q but received %q", expected, m.Source)
|
|
|
|
}
|
|
|
|
if m.Options[0] != "rw" {
|
|
|
|
t.Errorf("expected mount option rw but received %q", m.Options[0])
|
|
|
|
}
|
|
|
|
if m.Options[1] != "rbind" {
|
|
|
|
t.Errorf("expected mount option rbind but received %q", m.Options[1])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
func TestOverlayCommit(t *testing.T) {
|
2017-02-24 23:23:23 +00:00
|
|
|
ctx := context.TODO()
|
2016-12-09 19:27:30 +00:00
|
|
|
root, err := ioutil.TempDir("", "overlay")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(root)
|
2017-03-25 00:16:41 +00:00
|
|
|
o, _, err := newSnapshotter(ctx, root)
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
key := "/tmp/test"
|
2017-02-24 23:23:23 +00:00
|
|
|
mounts, err := o.Prepare(ctx, key, "")
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m := mounts[0]
|
|
|
|
if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2017-02-24 23:23:23 +00:00
|
|
|
if err := o.Commit(ctx, "base", key); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
func TestOverlayOverlayMount(t *testing.T) {
|
2017-02-24 23:23:23 +00:00
|
|
|
ctx := context.TODO()
|
2016-12-09 19:27:30 +00:00
|
|
|
root, err := ioutil.TempDir("", "overlay")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(root)
|
2017-03-25 00:16:41 +00:00
|
|
|
o, _, err := newSnapshotter(ctx, root)
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
key := "/tmp/test"
|
2017-02-24 23:23:23 +00:00
|
|
|
mounts, err := o.Prepare(ctx, key, "")
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2017-02-24 23:23:23 +00:00
|
|
|
if err := o.Commit(ctx, "base", key); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2017-02-24 23:23:23 +00:00
|
|
|
if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(mounts) != 1 {
|
|
|
|
t.Errorf("should only have 1 mount but received %d", len(mounts))
|
|
|
|
}
|
|
|
|
m := mounts[0]
|
|
|
|
if m.Type != "overlay" {
|
|
|
|
t.Errorf("mount type should be overlay but received %q", m.Type)
|
|
|
|
}
|
|
|
|
if m.Source != "overlay" {
|
|
|
|
t.Errorf("expected source %q but received %q", "overlay", m.Source)
|
|
|
|
}
|
|
|
|
var (
|
2017-03-16 06:25:53 +00:00
|
|
|
bp = getBasePath(ctx, o, root, "/tmp/layer2")
|
|
|
|
work = "workdir=" + filepath.Join(bp, "work")
|
|
|
|
upper = "upperdir=" + filepath.Join(bp, "fs")
|
|
|
|
lower = "lowerdir=" + getParents(ctx, o, root, "/tmp/layer2")[0]
|
2016-12-09 19:27:30 +00:00
|
|
|
)
|
|
|
|
for i, v := range []string{
|
|
|
|
work,
|
|
|
|
upper,
|
|
|
|
lower,
|
|
|
|
} {
|
|
|
|
if m.Options[i] != v {
|
|
|
|
t.Errorf("expected %q but received %q", v, m.Options[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-16 06:25:53 +00:00
|
|
|
func getBasePath(ctx context.Context, sn snapshot.Snapshotter, root, key string) string {
|
2017-03-25 00:21:03 +00:00
|
|
|
o := sn.(*snapshotter)
|
2017-03-16 06:25:53 +00:00
|
|
|
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
defer t.Rollback()
|
|
|
|
|
2017-03-25 00:16:41 +00:00
|
|
|
active, err := storage.GetActive(ctx, key)
|
2017-03-16 06:25:53 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return filepath.Join(root, "snapshots", active.ID)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getParents(ctx context.Context, sn snapshot.Snapshotter, root, key string) []string {
|
2017-03-25 00:21:03 +00:00
|
|
|
o := sn.(*snapshotter)
|
2017-03-16 06:25:53 +00:00
|
|
|
ctx, t, err := o.ms.TransactionContext(ctx, false)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
defer t.Rollback()
|
2017-03-25 00:16:41 +00:00
|
|
|
active, err := storage.GetActive(ctx, key)
|
2017-03-16 06:25:53 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
parents := make([]string, len(active.ParentIDs))
|
|
|
|
for i := range active.ParentIDs {
|
|
|
|
parents[i] = filepath.Join(root, "snapshots", active.ParentIDs[i], "fs")
|
|
|
|
}
|
|
|
|
return parents
|
|
|
|
}
|
|
|
|
|
2017-01-17 08:42:27 +00:00
|
|
|
func TestOverlayOverlayRead(t *testing.T) {
|
2017-01-25 00:10:48 +00:00
|
|
|
testutil.RequiresRoot(t)
|
2017-02-24 23:23:23 +00:00
|
|
|
ctx := context.TODO()
|
2016-12-09 19:27:30 +00:00
|
|
|
root, err := ioutil.TempDir("", "overlay")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(root)
|
2017-03-25 00:16:41 +00:00
|
|
|
o, _, err := newSnapshotter(ctx, root)
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
key := "/tmp/test"
|
2017-02-24 23:23:23 +00:00
|
|
|
mounts, err := o.Prepare(ctx, key, "")
|
2016-12-09 19:27:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
m := mounts[0]
|
|
|
|
if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2017-02-24 23:23:23 +00:00
|
|
|
if err := o.Commit(ctx, "base", key); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2017-02-24 23:23:23 +00:00
|
|
|
if mounts, err = o.Prepare(ctx, "/tmp/layer2", "base"); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
dest := filepath.Join(root, "dest")
|
|
|
|
if err := os.Mkdir(dest, 0700); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2017-01-17 21:26:56 +00:00
|
|
|
if err := containerd.MountAll(mounts, dest); err != nil {
|
2016-12-09 19:27:30 +00:00
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer syscall.Unmount(dest, 0)
|
|
|
|
data, err := ioutil.ReadFile(filepath.Join(dest, "foo"))
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if e := string(data); e != "hi" {
|
|
|
|
t.Errorf("expected file contents hi but got %q", e)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2017-02-18 00:34:21 +00:00
|
|
|
|
|
|
|
func TestOverlayView(t *testing.T) {
|
|
|
|
ctx := context.TODO()
|
|
|
|
root, err := ioutil.TempDir("", "overlay")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(root)
|
2017-03-25 00:16:41 +00:00
|
|
|
o, _, err := newSnapshotter(ctx, root)
|
2017-02-18 00:34:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-02-27 21:21:20 +00:00
|
|
|
key := "/tmp/base"
|
2017-02-18 00:34:21 +00:00
|
|
|
mounts, err := o.Prepare(ctx, key, "")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
m := mounts[0]
|
|
|
|
if err := ioutil.WriteFile(filepath.Join(m.Source, "foo"), []byte("hi"), 0660); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if err := o.Commit(ctx, "base", key); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-02-27 21:21:20 +00:00
|
|
|
|
|
|
|
key = "/tmp/top"
|
|
|
|
_, err = o.Prepare(ctx, key, "base")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-03-16 06:25:53 +00:00
|
|
|
if err := ioutil.WriteFile(filepath.Join(getParents(ctx, o, root, "/tmp/top")[0], "foo"), []byte("hi, again"), 0660); err != nil {
|
2017-02-27 21:21:20 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if err := o.Commit(ctx, "top", key); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mounts, err = o.View(ctx, "/tmp/view1", "base")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(mounts) != 1 {
|
|
|
|
t.Fatalf("should only have 1 mount but received %d", len(mounts))
|
|
|
|
}
|
|
|
|
m = mounts[0]
|
|
|
|
if m.Type != "bind" {
|
|
|
|
t.Errorf("mount type should be bind but received %q", m.Type)
|
|
|
|
}
|
2017-03-16 06:25:53 +00:00
|
|
|
expected := getParents(ctx, o, root, "/tmp/view1")[0]
|
2017-02-27 21:21:20 +00:00
|
|
|
if m.Source != expected {
|
|
|
|
t.Errorf("expected source %q but received %q", expected, m.Source)
|
|
|
|
}
|
|
|
|
if m.Options[0] != "ro" {
|
|
|
|
t.Errorf("expected mount option ro but received %q", m.Options[0])
|
|
|
|
}
|
|
|
|
if m.Options[1] != "rbind" {
|
|
|
|
t.Errorf("expected mount option rbind but received %q", m.Options[1])
|
|
|
|
}
|
|
|
|
|
|
|
|
mounts, err = o.View(ctx, "/tmp/view2", "top")
|
2017-02-18 00:34:21 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(mounts) != 1 {
|
|
|
|
t.Fatalf("should only have 1 mount but received %d", len(mounts))
|
|
|
|
}
|
|
|
|
m = mounts[0]
|
|
|
|
if m.Type != "overlay" {
|
|
|
|
t.Errorf("mount type should be overlay but received %q", m.Type)
|
|
|
|
}
|
|
|
|
if m.Source != "overlay" {
|
|
|
|
t.Errorf("mount source should be overlay but received %q", m.Source)
|
|
|
|
}
|
|
|
|
if len(m.Options) != 1 {
|
|
|
|
t.Errorf("expected 1 mount option but got %d", len(m.Options))
|
|
|
|
}
|
2017-03-16 06:25:53 +00:00
|
|
|
lowers := getParents(ctx, o, root, "/tmp/view2")
|
|
|
|
expected = fmt.Sprintf("lowerdir=%s:%s", lowers[0], lowers[1])
|
2017-02-18 00:34:21 +00:00
|
|
|
if m.Options[0] != expected {
|
|
|
|
t.Errorf("expected option %q but received %q", expected, m.Options[0])
|
|
|
|
}
|
|
|
|
}
|