summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerek McGowan <derek@mcgstyle.net>2016-04-14 18:14:42 -0700
committerDerek McGowan <derek@mcgstyle.net>2016-06-08 00:16:01 -0700
commit8b0441d42cca4f89cf871c1b64172cc06f9c98e6 (patch)
treea1cae5b809908beaf26dfcbfc09e55c35f0d34a4
parent8222c86360b3f63174e1dbd9b35e27a3d01298bc (diff)
downloaddocker-8b0441d42cca4f89cf871c1b64172cc06f9c98e6.tar.gz
Expand graphtest package
Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
-rw-r--r--daemon/graphdriver/graphtest/graphbench_unix.go264
-rw-r--r--daemon/graphdriver/graphtest/graphtest_unix.go287
-rw-r--r--daemon/graphdriver/graphtest/testutil.go301
-rw-r--r--daemon/graphdriver/graphtest/testutil_unix.go143
4 files changed, 842 insertions, 153 deletions
diff --git a/daemon/graphdriver/graphtest/graphbench_unix.go b/daemon/graphdriver/graphtest/graphbench_unix.go
new file mode 100644
index 0000000000..a32df02e16
--- /dev/null
+++ b/daemon/graphdriver/graphtest/graphbench_unix.go
@@ -0,0 +1,264 @@
+// +build linux freebsd
+
+package graphtest
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "path/filepath"
+ "testing"
+
+ "github.com/docker/docker/pkg/stringid"
+)
+
+// DriverBenchExists benchmarks calls to exist
+func DriverBenchExists(b *testing.B, drivername string, driveroptions ...string) {
+ driver := GetDriver(b, drivername, driveroptions...)
+ defer PutDriver(b)
+
+ base := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if !driver.Exists(base) {
+ b.Fatal("Newly created image doesn't exist")
+ }
+ }
+}
+
+// DriverBenchGetEmpty benchmarks calls to get on an empty layer
+func DriverBenchGetEmpty(b *testing.B, drivername string, driveroptions ...string) {
+ driver := GetDriver(b, drivername, driveroptions...)
+ defer PutDriver(b)
+
+ base := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ _, err := driver.Get(base, "")
+ b.StopTimer()
+ if err != nil {
+ b.Fatalf("Error getting mount: %s", err)
+ }
+ if err := driver.Put(base); err != nil {
+ b.Fatalf("Error putting mount: %s", err)
+ }
+ b.StartTimer()
+ }
+}
+
+// DriverBenchDiffBase benchmarks calls to diff on a root layer
+func DriverBenchDiffBase(b *testing.B, drivername string, driveroptions ...string) {
+ driver := GetDriver(b, drivername, driveroptions...)
+ defer PutDriver(b)
+
+ base := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := addFiles(driver, base, 3); err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ arch, err := driver.Diff(base, "")
+ if err != nil {
+ b.Fatal(err)
+ }
+ _, err = io.Copy(ioutil.Discard, arch)
+ if err != nil {
+ b.Fatalf("Error copying archive: %s", err)
+ }
+ arch.Close()
+ }
+}
+
+// DriverBenchDiffN benchmarks calls to diff on two layers with
+// a provided number of files on the lower and upper layers.
+func DriverBenchDiffN(b *testing.B, bottom, top int, drivername string, driveroptions ...string) {
+ driver := GetDriver(b, drivername, driveroptions...)
+ defer PutDriver(b)
+ base := stringid.GenerateRandomID()
+ upper := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := addManyFiles(driver, base, bottom, 3); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := driver.Create(upper, base, "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := addManyFiles(driver, upper, top, 6); err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ arch, err := driver.Diff(upper, "")
+ if err != nil {
+ b.Fatal(err)
+ }
+ _, err = io.Copy(ioutil.Discard, arch)
+ if err != nil {
+ b.Fatalf("Error copying archive: %s", err)
+ }
+ arch.Close()
+ }
+}
+
+// DriverBenchDiffApplyN benchmarks calls to diff and apply together
+func DriverBenchDiffApplyN(b *testing.B, fileCount int, drivername string, driveroptions ...string) {
+ driver := GetDriver(b, drivername, driveroptions...)
+ defer PutDriver(b)
+ base := stringid.GenerateRandomID()
+ upper := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := addManyFiles(driver, base, fileCount, 3); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := driver.Create(upper, base, "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := addManyFiles(driver, upper, fileCount, 6); err != nil {
+ b.Fatal(err)
+ }
+ diffSize, err := driver.DiffSize(upper, "")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.ResetTimer()
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ diff := stringid.GenerateRandomID()
+ if err := driver.Create(diff, base, "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := checkManyFiles(driver, diff, fileCount, 3); err != nil {
+ b.Fatal(err)
+ }
+
+ b.StartTimer()
+
+ arch, err := driver.Diff(upper, "")
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ applyDiffSize, err := driver.ApplyDiff(diff, "", arch)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.StopTimer()
+ arch.Close()
+
+ if applyDiffSize != diffSize {
+ // TODO: enforce this
+ //b.Fatalf("Apply diff size different, got %d, expected %s", applyDiffSize, diffSize)
+ }
+ if err := checkManyFiles(driver, diff, fileCount, 6); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+// DriverBenchDeepLayerDiff benchmarks calls to diff on top of a given number of layers.
+func DriverBenchDeepLayerDiff(b *testing.B, layerCount int, drivername string, driveroptions ...string) {
+ driver := GetDriver(b, drivername, driveroptions...)
+ defer PutDriver(b)
+
+ base := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ if err := addFiles(driver, base, 50); err != nil {
+ b.Fatal(err)
+ }
+
+ topLayer, err := addManyLayers(driver, base, layerCount)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ arch, err := driver.Diff(topLayer, "")
+ if err != nil {
+ b.Fatal(err)
+ }
+ _, err = io.Copy(ioutil.Discard, arch)
+ if err != nil {
+ b.Fatalf("Error copying archive: %s", err)
+ }
+ arch.Close()
+ }
+}
+
+// DriverBenchDeepLayerRead benchmarks calls to read a file under a given number of layers.
+func DriverBenchDeepLayerRead(b *testing.B, layerCount int, drivername string, driveroptions ...string) {
+ driver := GetDriver(b, drivername, driveroptions...)
+ defer PutDriver(b)
+
+ base := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
+ b.Fatal(err)
+ }
+
+ content := []byte("test content")
+ if err := addFile(driver, base, "testfile.txt", content); err != nil {
+ b.Fatal(err)
+ }
+
+ topLayer, err := addManyLayers(driver, base, layerCount)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ root, err := driver.Get(topLayer, "")
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer driver.Put(topLayer)
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+
+ // Read content
+ c, err := ioutil.ReadFile(filepath.Join(root, "testfile.txt"))
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.StopTimer()
+ if bytes.Compare(c, content) != 0 {
+ b.Fatalf("Wrong content in file %v, expected %v", c, content)
+ }
+ b.StartTimer()
+ }
+}
diff --git a/daemon/graphdriver/graphtest/graphtest_unix.go b/daemon/graphdriver/graphtest/graphtest_unix.go
index 85342abba1..4762c85556 100644
--- a/daemon/graphdriver/graphtest/graphtest_unix.go
+++ b/daemon/graphdriver/graphtest/graphtest_unix.go
@@ -3,7 +3,7 @@
package graphtest
import (
- "fmt"
+ "bytes"
"io/ioutil"
"math/rand"
"os"
@@ -14,6 +14,7 @@ import (
"unsafe"
"github.com/docker/docker/daemon/graphdriver"
+ "github.com/docker/docker/pkg/stringid"
"github.com/docker/go-units"
)
@@ -30,47 +31,7 @@ type Driver struct {
refCount int
}
-// InitLoopbacks ensures that the loopback devices are properly created within
-// the system running the device mapper tests.
-func InitLoopbacks() error {
- statT, err := getBaseLoopStats()
- if err != nil {
- return err
- }
- // create at least 8 loopback files, ya, that is a good number
- for i := 0; i < 8; i++ {
- loopPath := fmt.Sprintf("/dev/loop%d", i)
- // only create new loopback files if they don't exist
- if _, err := os.Stat(loopPath); err != nil {
- if mkerr := syscall.Mknod(loopPath,
- uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil {
- return mkerr
- }
- os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
- }
- }
- return nil
-}
-
-// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
-// loop0 device on the system. If it does not exist we assume 0,0,0660 for the
-// stat data
-func getBaseLoopStats() (*syscall.Stat_t, error) {
- loop0, err := os.Stat("/dev/loop0")
- if err != nil {
- if os.IsNotExist(err) {
- return &syscall.Stat_t{
- Uid: 0,
- Gid: 0,
- Mode: 0660,
- }, nil
- }
- return nil, err
- }
- return loop0.Sys().(*syscall.Stat_t), nil
-}
-
-func newDriver(t *testing.T, name string) *Driver {
+func newDriver(t testing.TB, name string, options []string) *Driver {
root, err := ioutil.TempDir("", "docker-graphtest-")
if err != nil {
t.Fatal(err)
@@ -80,7 +41,7 @@ func newDriver(t *testing.T, name string) *Driver {
t.Fatal(err)
}
- d, err := graphdriver.GetDriver(name, root, nil, nil, nil)
+ d, err := graphdriver.GetDriver(name, root, options, nil, nil)
if err != nil {
t.Logf("graphdriver: %v\n", err)
if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {
@@ -91,7 +52,7 @@ func newDriver(t *testing.T, name string) *Driver {
return &Driver{d, root, 1}
}
-func cleanup(t *testing.T, d *Driver) {
+func cleanup(t testing.TB, d *Driver) {
if err := drv.Cleanup(); err != nil {
t.Fatal(err)
}
@@ -99,9 +60,9 @@ func cleanup(t *testing.T, d *Driver) {
}
// GetDriver create a new driver with given name or return an existing driver with the name updating the reference count.
-func GetDriver(t *testing.T, name string) graphdriver.Driver {
+func GetDriver(t testing.TB, name string, options ...string) graphdriver.Driver {
if drv == nil {
- drv = newDriver(t, name)
+ drv = newDriver(t, name, options)
} else {
drv.refCount++
}
@@ -109,7 +70,7 @@ func GetDriver(t *testing.T, name string) graphdriver.Driver {
}
// PutDriver removes the driver if it is no longer used and updates the reference count.
-func PutDriver(t *testing.T) {
+func PutDriver(t testing.TB) {
if drv == nil {
t.Skip("No driver to put!")
}
@@ -120,190 +81,210 @@ func PutDriver(t *testing.T) {
}
}
-func verifyFile(t *testing.T, path string, mode os.FileMode, uid, gid uint32) {
- fi, err := os.Stat(path)
- if err != nil {
+// DriverTestCreateEmpty creates a new image and verifies it is empty and the right metadata
+func DriverTestCreateEmpty(t testing.TB, drivername string, driverOptions ...string) {
+ driver := GetDriver(t, drivername, driverOptions...)
+ defer PutDriver(t)
+
+ if err := driver.Create("empty", "", "", nil); err != nil {
t.Fatal(err)
}
- if fi.Mode()&os.ModeType != mode&os.ModeType {
- t.Fatalf("Expected %s type 0x%x, got 0x%x", path, mode&os.ModeType, fi.Mode()&os.ModeType)
- }
+ defer func() {
+ if err := driver.Remove("empty"); err != nil {
+ t.Fatal(err)
+ }
+ }()
- if fi.Mode()&os.ModePerm != mode&os.ModePerm {
- t.Fatalf("Expected %s mode %o, got %o", path, mode&os.ModePerm, fi.Mode()&os.ModePerm)
+ if !driver.Exists("empty") {
+ t.Fatal("Newly created image doesn't exist")
}
- if fi.Mode()&os.ModeSticky != mode&os.ModeSticky {
- t.Fatalf("Expected %s sticky 0x%x, got 0x%x", path, mode&os.ModeSticky, fi.Mode()&os.ModeSticky)
+ dir, err := driver.Get("empty", "")
+ if err != nil {
+ t.Fatal(err)
}
- if fi.Mode()&os.ModeSetuid != mode&os.ModeSetuid {
- t.Fatalf("Expected %s setuid 0x%x, got 0x%x", path, mode&os.ModeSetuid, fi.Mode()&os.ModeSetuid)
- }
+ verifyFile(t, dir, 0755|os.ModeDir, 0, 0)
- if fi.Mode()&os.ModeSetgid != mode&os.ModeSetgid {
- t.Fatalf("Expected %s setgid 0x%x, got 0x%x", path, mode&os.ModeSetgid, fi.Mode()&os.ModeSetgid)
+ // Verify that the directory is empty
+ fis, err := readDir(dir)
+ if err != nil {
+ t.Fatal(err)
}
- if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
- if stat.Uid != uid {
- t.Fatalf("%s no owned by uid %d", path, uid)
- }
- if stat.Gid != gid {
- t.Fatalf("%s not owned by gid %d", path, gid)
- }
+ if len(fis) != 0 {
+ t.Fatal("New directory not empty")
}
+ driver.Put("empty")
}
-// readDir reads a directory just like ioutil.ReadDir()
-// then hides specific files (currently "lost+found")
-// so the tests don't "see" it
-func readDir(dir string) ([]os.FileInfo, error) {
- a, err := ioutil.ReadDir(dir)
- if err != nil {
- return nil, err
- }
+// DriverTestCreateBase create a base driver and verify.
+func DriverTestCreateBase(t testing.TB, drivername string, driverOptions ...string) {
+ driver := GetDriver(t, drivername, driverOptions...)
+ defer PutDriver(t)
- b := a[:0]
- for _, x := range a {
- if x.Name() != "lost+found" { // ext4 always have this dir
- b = append(b, x)
+ createBase(t, driver, "Base")
+ defer func() {
+ if err := driver.Remove("Base"); err != nil {
+ t.Fatal(err)
}
- }
-
- return b, nil
+ }()
+ verifyBase(t, driver, "Base")
}
-// DriverTestCreateEmpty creates a new image and verifies it is empty and the right metadata
-func DriverTestCreateEmpty(t *testing.T, drivername string) {
- driver := GetDriver(t, drivername)
+// DriverTestCreateSnap Create a driver and snap and verify.
+func DriverTestCreateSnap(t testing.TB, drivername string, driverOptions ...string) {
+ driver := GetDriver(t, drivername, driverOptions...)
defer PutDriver(t)
- if err := driver.Create("empty", "", "", nil); err != nil {
+ createBase(t, driver, "Base")
+
+ defer func() {
+ if err := driver.Remove("Base"); err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ if err := driver.Create("Snap", "Base", "", nil); err != nil {
t.Fatal(err)
}
defer func() {
- if err := driver.Remove("empty"); err != nil {
+ if err := driver.Remove("Snap"); err != nil {
t.Fatal(err)
}
}()
- if !driver.Exists("empty") {
- t.Fatal("Newly created image doesn't exist")
- }
+ verifyBase(t, driver, "Snap")
+}
- dir, err := driver.Get("empty", "")
- if err != nil {
+// DriverTestDeepLayerRead reads a file from a lower layer under a given number of layers
+func DriverTestDeepLayerRead(t testing.TB, layerCount int, drivername string, driverOptions ...string) {
+ driver := GetDriver(t, drivername, driverOptions...)
+ defer PutDriver(t)
+
+ base := stringid.GenerateRandomID()
+
+ if err := driver.Create(base, "", "", nil); err != nil {
t.Fatal(err)
}
- verifyFile(t, dir, 0755|os.ModeDir, 0, 0)
+ content := []byte("test content")
+ if err := addFile(driver, base, "testfile.txt", content); err != nil {
+ t.Fatal(err)
+ }
- // Verify that the directory is empty
- fis, err := readDir(dir)
+ topLayer, err := addManyLayers(driver, base, layerCount)
if err != nil {
t.Fatal(err)
}
- if len(fis) != 0 {
- t.Fatal("New directory not empty")
+ err = checkManyLayers(driver, topLayer, layerCount)
+ if err != nil {
+ t.Fatal(err)
}
- driver.Put("empty")
+ if err := checkFile(driver, topLayer, "testfile.txt", content); err != nil {
+ t.Fatal(err)
+ }
}
-func createBase(t *testing.T, driver graphdriver.Driver, name string) {
- // We need to be able to set any perms
- oldmask := syscall.Umask(0)
- defer syscall.Umask(oldmask)
+// DriverTestDiffApply tests diffing and applying produces the same layer
+func DriverTestDiffApply(t testing.TB, fileCount int, drivername string, driverOptions ...string) {
+ driver := GetDriver(t, drivername, driverOptions...)
+ defer PutDriver(t)
+ base := stringid.GenerateRandomID()
+ upper := stringid.GenerateRandomID()
- if err := driver.CreateReadWrite(name, "", "", nil); err != nil {
+ if err := driver.Create(base, "", "", nil); err != nil {
t.Fatal(err)
}
- dir, err := driver.Get(name, "")
- if err != nil {
+ if err := addManyFiles(driver, base, fileCount, 3); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := driver.Create(upper, base, "", nil); err != nil {
t.Fatal(err)
}
- defer driver.Put(name)
- subdir := path.Join(dir, "a subdir")
- if err := os.Mkdir(subdir, 0705|os.ModeSticky); err != nil {
+ if err := addManyFiles(driver, upper, fileCount, 6); err != nil {
t.Fatal(err)
}
- if err := os.Chown(subdir, 1, 2); err != nil {
+ diffSize, err := driver.DiffSize(upper, "")
+ if err != nil {
t.Fatal(err)
}
- file := path.Join(dir, "a file")
- if err := ioutil.WriteFile(file, []byte("Some data"), 0222|os.ModeSetuid); err != nil {
+ diff := stringid.GenerateRandomID()
+ if err := driver.Create(diff, base, "", nil); err != nil {
t.Fatal(err)
}
-}
-func verifyBase(t *testing.T, driver graphdriver.Driver, name string) {
- dir, err := driver.Get(name, "")
- if err != nil {
+ if err := checkManyFiles(driver, diff, fileCount, 3); err != nil {
t.Fatal(err)
}
- defer driver.Put(name)
- subdir := path.Join(dir, "a subdir")
- verifyFile(t, subdir, 0705|os.ModeDir|os.ModeSticky, 1, 2)
+ arch, err := driver.Diff(upper, base)
+ if err != nil {
+ t.Fatal(err)
+ }
- file := path.Join(dir, "a file")
- verifyFile(t, file, 0222|os.ModeSetuid, 0, 0)
+ buf := bytes.NewBuffer(nil)
+ if _, err := buf.ReadFrom(arch); err != nil {
+ t.Fatal(err)
+ }
+ if err := arch.Close(); err != nil {
+ t.Fatal(err)
+ }
- fis, err := readDir(dir)
+ applyDiffSize, err := driver.ApplyDiff(diff, base, bytes.NewReader(buf.Bytes()))
if err != nil {
t.Fatal(err)
}
- if len(fis) != 2 {
- t.Fatal("Unexpected files in base image")
+ if applyDiffSize != diffSize {
+ t.Fatalf("Apply diff size different, got %d, expected %d", applyDiffSize, diffSize)
+ }
+ if err := checkManyFiles(driver, diff, fileCount, 6); err != nil {
+ t.Fatal(err)
}
}
-// DriverTestCreateBase create a base driver and verify.
-func DriverTestCreateBase(t *testing.T, drivername string) {
- driver := GetDriver(t, drivername)
+// DriverTestChanges tests computed changes on a layer matches changes made
+func DriverTestChanges(t testing.TB, drivername string, driverOptions ...string) {
+ driver := GetDriver(t, drivername, driverOptions...)
defer PutDriver(t)
+ base := stringid.GenerateRandomID()
+ upper := stringid.GenerateRandomID()
- createBase(t, driver, "Base")
- defer func() {
- if err := driver.Remove("Base"); err != nil {
- t.Fatal(err)
- }
- }()
- verifyBase(t, driver, "Base")
-}
+ if err := driver.Create(base, "", "", nil); err != nil {
+ t.Fatal(err)
+ }
-// DriverTestCreateSnap Create a driver and snap and verify.
-func DriverTestCreateSnap(t *testing.T, drivername string) {
- driver := GetDriver(t, drivername)
- defer PutDriver(t)
+ if err := addManyFiles(driver, base, 20, 3); err != nil {
+ t.Fatal(err)
+ }
- createBase(t, driver, "Base")
+ if err := driver.Create(upper, base, "", nil); err != nil {
+ t.Fatal(err)
+ }
- defer func() {
- if err := driver.Remove("Base"); err != nil {
- t.Fatal(err)
- }
- }()
+ expectedChanges, err := changeManyFiles(driver, upper, 20, 6)
+ if err != nil {
+ t.Fatal(err)
+ }
- if err := driver.Create("Snap", "Base", "", nil); err != nil {
+ changes, err := driver.Changes(upper, base)
+ if err != nil {
t.Fatal(err)
}
- defer func() {
- if err := driver.Remove("Snap"); err != nil {
- t.Fatal(err)
- }
- }()
- verifyBase(t, driver, "Snap")
+ if err = checkChanges(expectedChanges, changes); err != nil {
+ t.Fatal(err)
+ }
}
func writeRandomFile(path string, size uint64) error {
diff --git a/daemon/graphdriver/graphtest/testutil.go b/daemon/graphdriver/graphtest/testutil.go
new file mode 100644
index 0000000000..5917d0dac4
--- /dev/null
+++ b/daemon/graphdriver/graphtest/testutil.go
@@ -0,0 +1,301 @@
+package graphtest
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "path"
+ "sort"
+
+ "github.com/docker/docker/daemon/graphdriver"
+ "github.com/docker/docker/pkg/archive"
+ "github.com/docker/docker/pkg/stringid"
+)
+
+func randomContent(size int, seed int64) []byte {
+ s := rand.NewSource(seed)
+ content := make([]byte, size)
+
+ for i := 0; i < len(content); i += 7 {
+ val := s.Int63()
+ for j := 0; i+j < len(content) && j < 7; j++ {
+ content[i+j] = byte(val)
+ val >>= 8
+ }
+ }
+
+ return content
+}
+
+func addFiles(drv graphdriver.Driver, layer string, seed int64) error {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return err
+ }
+ defer drv.Put(layer)
+
+ if err := ioutil.WriteFile(path.Join(root, "file-a"), randomContent(64, seed), 0755); err != nil {
+ return err
+ }
+ if err := os.MkdirAll(path.Join(root, "dir-b"), 0755); err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(path.Join(root, "dir-b", "file-b"), randomContent(128, seed+1), 0755); err != nil {
+ return err
+ }
+
+ return ioutil.WriteFile(path.Join(root, "file-c"), randomContent(128*128, seed+2), 0755)
+}
+
+func checkFile(drv graphdriver.Driver, layer, filename string, content []byte) error {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return err
+ }
+ defer drv.Put(layer)
+
+ fileContent, err := ioutil.ReadFile(path.Join(root, filename))
+ if err != nil {
+ return err
+ }
+
+ if bytes.Compare(fileContent, content) != 0 {
+ return fmt.Errorf("mismatched file content %v, expecting %v", fileContent, content)
+ }
+
+ return nil
+}
+
+func addFile(drv graphdriver.Driver, layer, filename string, content []byte) error {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return err
+ }
+ defer drv.Put(layer)
+
+ return ioutil.WriteFile(path.Join(root, filename), content, 0755)
+}
+
+func addManyFiles(drv graphdriver.Driver, layer string, count int, seed int64) error {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return err
+ }
+ defer drv.Put(layer)
+
+ for i := 0; i < count; i += 100 {
+ dir := path.Join(root, fmt.Sprintf("directory-%d", i))
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ return err
+ }
+ for j := 0; i+j < count && j < 100; j++ {
+ file := path.Join(dir, fmt.Sprintf("file-%d", i+j))
+ if err := ioutil.WriteFile(file, randomContent(64, seed+int64(i+j)), 0755); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func changeManyFiles(drv graphdriver.Driver, layer string, count int, seed int64) ([]archive.Change, error) {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return nil, err
+ }
+ defer drv.Put(layer)
+
+ changes := []archive.Change{}
+ for i := 0; i < count; i += 100 {
+ archiveRoot := fmt.Sprintf("/directory-%d", i)
+ if err := os.MkdirAll(path.Join(root, archiveRoot), 0755); err != nil {
+ return nil, err
+ }
+ for j := 0; i+j < count && j < 100; j++ {
+ if j == 0 {
+ changes = append(changes, archive.Change{
+ Path: archiveRoot,
+ Kind: archive.ChangeModify,
+ })
+ }
+ var change archive.Change
+ switch j % 3 {
+ // Update file
+ case 0:
+ change.Path = path.Join(archiveRoot, fmt.Sprintf("file-%d", i+j))
+ change.Kind = archive.ChangeModify
+ if err := ioutil.WriteFile(path.Join(root, change.Path), randomContent(64, seed+int64(i+j)), 0755); err != nil {
+ return nil, err
+ }
+ // Add file
+ case 1:
+ change.Path = path.Join(archiveRoot, fmt.Sprintf("file-%d-%d", seed, i+j))
+ change.Kind = archive.ChangeAdd
+ if err := ioutil.WriteFile(path.Join(root, change.Path), randomContent(64, seed+int64(i+j)), 0755); err != nil {
+ return nil, err
+ }
+ // Remove file
+ case 2:
+ change.Path = path.Join(archiveRoot, fmt.Sprintf("file-%d", i+j))
+ change.Kind = archive.ChangeDelete
+ if err := os.Remove(path.Join(root, change.Path)); err != nil {
+ return nil, err
+ }
+ }
+ changes = append(changes, change)
+ }
+ }
+
+ return changes, nil
+}
+
+func checkManyFiles(drv graphdriver.Driver, layer string, count int, seed int64) error {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return err
+ }
+ defer drv.Put(layer)
+
+ for i := 0; i < count; i += 100 {
+ dir := path.Join(root, fmt.Sprintf("directory-%d", i))
+ for j := 0; i+j < count && j < 100; j++ {
+ file := path.Join(dir, fmt.Sprintf("file-%d", i+j))
+ fileContent, err := ioutil.ReadFile(file)
+ if err != nil {
+ return err
+ }
+
+ content := randomContent(64, seed+int64(i+j))
+
+ if bytes.Compare(fileContent, content) != 0 {
+ return fmt.Errorf("mismatched file content %v, expecting %v", fileContent, content)
+ }
+ }
+ }
+
+ return nil
+}
+
+type changeList []archive.Change
+
+func (c changeList) Less(i, j int) bool {
+ if c[i].Path == c[j].Path {
+ return c[i].Kind < c[j].Kind
+ }
+ return c[i].Path < c[j].Path
+}
+func (c changeList) Len() int { return len(c) }
+func (c changeList) Swap(i, j int) { c[j], c[i] = c[i], c[j] }
+
+func checkChanges(expected, actual []archive.Change) error {
+ if len(expected) != len(actual) {
+ return fmt.Errorf("unexpected number of changes, expected %d, got %d", len(expected), len(actual))
+ }
+ sort.Sort(changeList(expected))
+ sort.Sort(changeList(actual))
+
+ for i := range expected {
+ if expected[i] != actual[i] {
+ return fmt.Errorf("unexpected change, expecting %v, got %v", expected[i], actual[i])
+ }
+ }
+
+ return nil
+}
+
+func addLayerFiles(drv graphdriver.Driver, layer, parent string, i int) error {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return err
+ }
+ defer drv.Put(layer)
+
+ if err := ioutil.WriteFile(path.Join(root, "top-id"), []byte(layer), 0755); err != nil {
+ return err
+ }
+ layerDir := path.Join(root, fmt.Sprintf("layer-%d", i))
+ if err := os.MkdirAll(layerDir, 0755); err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(path.Join(layerDir, "layer-id"), []byte(layer), 0755); err != nil {
+ return err
+ }
+ if err := ioutil.WriteFile(path.Join(layerDir, "parent-id"), []byte(parent), 0755); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func addManyLayers(drv graphdriver.Driver, baseLayer string, count int) (string, error) {
+ lastLayer := baseLayer
+ for i := 1; i <= count; i++ {
+ nextLayer := stringid.GenerateRandomID()
+ if err := drv.Create(nextLayer, lastLayer, "", nil); err != nil {
+ return "", err
+ }
+ if err := addLayerFiles(drv, nextLayer, lastLayer, i); err != nil {
+ return "", err
+ }
+
+ lastLayer = nextLayer
+
+ }
+ return lastLayer, nil
+}
+
+func checkManyLayers(drv graphdriver.Driver, layer string, count int) error {
+ root, err := drv.Get(layer, "")
+ if err != nil {
+ return err
+ }
+ defer drv.Put(layer)
+
+ layerIDBytes, err := ioutil.ReadFile(path.Join(root, "top-id"))
+ if err != nil {
+ return err
+ }
+
+ if bytes.Compare(layerIDBytes, []byte(layer)) != 0 {
+ return fmt.Errorf("mismatched file content %v, expecting %v", layerIDBytes, []byte(layer))
+ }
+
+ for i := count; i > 0; i-- {
+ layerDir := path.Join(root, fmt.Sprintf("layer-%d", i))
+
+ thisLayerIDBytes, err := ioutil.ReadFile(path.Join(layerDir, "layer-id"))
+ if err != nil {
+ return err
+ }
+ if bytes.Compare(thisLayerIDBytes, layerIDBytes) != 0 {
+ return fmt.Errorf("mismatched file content %v, expecting %v", thisLayerIDBytes, layerIDBytes)
+ }
+ layerIDBytes, err = ioutil.ReadFile(path.Join(layerDir, "parent-id"))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// readDir reads a directory just like ioutil.ReadDir()
+// then hides specific files (currently "lost+found")
+// so the tests don't "see" it
+func readDir(dir string) ([]os.FileInfo, error) {
+ a, err := ioutil.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+
+ b := a[:0]
+ for _, x := range a {
+ if x.Name() != "lost+found" { // ext4 always have this dir
+ b = append(b, x)
+ }
+ }
+
+ return b, nil
+}
diff --git a/daemon/graphdriver/graphtest/testutil_unix.go b/daemon/graphdriver/graphtest/testutil_unix.go
new file mode 100644
index 0000000000..07d038c3b2
--- /dev/null
+++ b/daemon/graphdriver/graphtest/testutil_unix.go
@@ -0,0 +1,143 @@
+// +build linux freebsd
+
+package graphtest
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "syscall"
+ "testing"
+
+ "github.com/docker/docker/daemon/graphdriver"
+)
+
+// InitLoopbacks ensures that the loopback devices are properly created within
+// the system running the device mapper tests.
+func InitLoopbacks() error {
+ statT, err := getBaseLoopStats()
+ if err != nil {
+ return err
+ }
+ // create at least 8 loopback files, ya, that is a good number
+ for i := 0; i < 8; i++ {
+ loopPath := fmt.Sprintf("/dev/loop%d", i)
+ // only create new loopback files if they don't exist
+ if _, err := os.Stat(loopPath); err != nil {
+ if mkerr := syscall.Mknod(loopPath,
+ uint32(statT.Mode|syscall.S_IFBLK), int((7<<8)|(i&0xff)|((i&0xfff00)<<12))); mkerr != nil {
+ return mkerr
+ }
+ os.Chown(loopPath, int(statT.Uid), int(statT.Gid))
+ }
+ }
+ return nil
+}
+
+// getBaseLoopStats inspects /dev/loop0 to collect uid,gid, and mode for the
+// loop0 device on the system. If it does not exist we assume 0,0,0660 for the
+// stat data
+func getBaseLoopStats() (*syscall.Stat_t, error) {
+ loop0, err := os.Stat("/dev/loop0")
+ if err != nil {
+ if os.IsNotExist(err) {
+ return &syscall.Stat_t{
+ Uid: 0,
+ Gid: 0,
+ Mode: 0660,
+ }, nil
+ }
+ return nil, err
+ }
+ return loop0.Sys().(*syscall.Stat_t), nil
+}
+
+func verifyFile(t testing.TB, path string, mode os.FileMode, uid, gid uint32) {
+ fi, err := os.Stat(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if fi.Mode()&os.ModeType != mode&os.ModeType {
+ t.Fatalf("Expected %s type 0x%x, got 0x%x", path, mode&os.ModeType, fi.Mode()&os.ModeType)
+ }
+
+ if fi.Mode()&os.ModePerm != mode&os.ModePerm {
+ t.Fatalf("Expected %s mode %o, got %o", path, mode&os.ModePerm, fi.Mode()&os.ModePerm)
+ }
+
+ if fi.Mode()&os.ModeSticky != mode&os.ModeSticky {
+ t.Fatalf("Expected %s sticky 0x%x, got 0x%x", path, mode&os.ModeSticky, fi.Mode()&os.ModeSticky)
+ }
+
+ if fi.Mode()&os.ModeSetuid != mode&os.ModeSetuid {
+ t.Fatalf("Expected %s setuid 0x%x, got 0x%x", path, mode&os.ModeSetuid, fi.Mode()&os.ModeSetuid)
+ }
+
+ if fi.Mode()&os.ModeSetgid != mode&os.ModeSetgid {
+ t.Fatalf("Expected %s setgid 0x%x, got 0x%x", path, mode&os.ModeSetgid, fi.Mode()&os.ModeSetgid)
+ }
+
+ if stat, ok := fi.Sys().(*syscall.Stat_t); ok {
+ if stat.Uid != uid {
+ t.Fatalf("%s no owned by uid %d", path, uid)
+ }
+ if stat.Gid != gid {
+ t.Fatalf("%s not owned by gid %d", path, gid)
+ }
+ }
+}
+
+func createBase(t testing.TB, driver graphdriver.Driver, name string) {
+ // We need to be able to set any perms
+ oldmask := syscall.Umask(0)
+ defer syscall.Umask(oldmask)
+
+ if err := driver.CreateReadWrite(name, "", "", nil); err != nil {
+ t.Fatal(err)
+ }
+
+ dir, err := driver.Get(name, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer driver.Put(name)
+
+ subdir := path.Join(dir, "a subdir")
+ if err := os.Mkdir(subdir, 0705|os.ModeSticky); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.Chown(subdir, 1, 2); err != nil {
+ t.Fatal(err)
+ }
+
+ file := path.Join(dir, "a file")
+ if err := ioutil.WriteFile(file, []byte("Some data"), 0222|os.ModeSetuid); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func verifyBase(t testing.TB, driver graphdriver.Driver, name string) {
+ dir, err := driver.Get(name, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer driver.Put(name)
+
+ subdir := path.Join(dir, "a subdir")
+ verifyFile(t, subdir, 0705|os.ModeDir|os.ModeSticky, 1, 2)
+
+ file := path.Join(dir, "a file")
+ verifyFile(t, file, 0222|os.ModeSetuid, 0, 0)
+
+ fis, err := readDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(fis) != 2 {
+ t.Fatal("Unexpected files in base image")
+ }
+
+}