1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/daemon/graphdriver/aufs/aufs_test.go
Brian Goff 55c91f2ab9 Fix some issues with concurrency in aufs.
Adds a benchmark to measure performance under concurrent actions.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
2016-02-25 14:32:13 -05:00

801 lines
14 KiB
Go

// +build linux
package aufs
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"path"
"sync"
"testing"
"github.com/docker/docker/daemon/graphdriver"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/docker/pkg/stringid"
)
var (
tmpOuter = path.Join(os.TempDir(), "aufs-tests")
tmp = path.Join(tmpOuter, "aufs")
)
func init() {
reexec.Init()
}
func testInit(dir string, t testing.TB) graphdriver.Driver {
d, err := Init(dir, nil, nil, nil)
if err != nil {
if err == graphdriver.ErrNotSupported {
t.Skip(err)
} else {
t.Fatal(err)
}
}
return d
}
func newDriver(t testing.TB) *Driver {
if err := os.MkdirAll(tmp, 0755); err != nil {
t.Fatal(err)
}
d := testInit(tmp, t)
return d.(*Driver)
}
func TestNewDriver(t *testing.T) {
if err := os.MkdirAll(tmp, 0755); err != nil {
t.Fatal(err)
}
d := testInit(tmp, t)
defer os.RemoveAll(tmp)
if d == nil {
t.Fatalf("Driver should not be nil")
}
}
func TestAufsString(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if d.String() != "aufs" {
t.Fatalf("Expected aufs got %s", d.String())
}
}
func TestCreateDirStructure(t *testing.T) {
newDriver(t)
defer os.RemoveAll(tmp)
paths := []string{
"mnt",
"layers",
"diff",
}
for _, p := range paths {
if _, err := os.Stat(path.Join(tmp, p)); err != nil {
t.Fatal(err)
}
}
}
// We should be able to create two drivers with the same dir structure
func TestNewDriverFromExistingDir(t *testing.T) {
if err := os.MkdirAll(tmp, 0755); err != nil {
t.Fatal(err)
}
testInit(tmp, t)
testInit(tmp, t)
os.RemoveAll(tmp)
}
func TestCreateNewDir(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
}
func TestCreateNewDirStructure(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
paths := []string{
"mnt",
"diff",
"layers",
}
for _, p := range paths {
if _, err := os.Stat(path.Join(tmp, p, "1")); err != nil {
t.Fatal(err)
}
}
}
func TestRemoveImage(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
if err := d.Remove("1"); err != nil {
t.Fatal(err)
}
paths := []string{
"mnt",
"diff",
"layers",
}
for _, p := range paths {
if _, err := os.Stat(path.Join(tmp, p, "1")); err == nil {
t.Fatalf("Error should not be nil because dirs with id 1 should be delted: %s", p)
}
}
}
func TestGetWithoutParent(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
diffPath, err := d.Get("1", "")
if err != nil {
t.Fatal(err)
}
expected := path.Join(tmp, "diff", "1")
if diffPath != expected {
t.Fatalf("Expected path %s got %s", expected, diffPath)
}
}
func TestCleanupWithNoDirs(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Cleanup(); err != nil {
t.Fatal(err)
}
}
func TestCleanupWithDir(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
if err := d.Cleanup(); err != nil {
t.Fatal(err)
}
}
func TestMountedFalseResponse(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
response, err := d.mounted(d.active["1"])
if err != nil {
t.Fatal(err)
}
if response != false {
t.Fatalf("Response if dir id 1 is mounted should be false")
}
}
func TestMountedTrueReponse(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
defer d.Cleanup()
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
if err := d.Create("2", "1", ""); err != nil {
t.Fatal(err)
}
_, err := d.Get("2", "")
if err != nil {
t.Fatal(err)
}
response, err := d.mounted(d.active["2"])
if err != nil {
t.Fatal(err)
}
if response != true {
t.Fatalf("Response if dir id 2 is mounted should be true")
}
}
func TestMountWithParent(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
if err := d.Create("2", "1", ""); err != nil {
t.Fatal(err)
}
defer func() {
if err := d.Cleanup(); err != nil {
t.Fatal(err)
}
}()
mntPath, err := d.Get("2", "")
if err != nil {
t.Fatal(err)
}
if mntPath == "" {
t.Fatal("mntPath should not be empty string")
}
expected := path.Join(tmp, "mnt", "2")
if mntPath != expected {
t.Fatalf("Expected %s got %s", expected, mntPath)
}
}
func TestRemoveMountedDir(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
if err := d.Create("2", "1", ""); err != nil {
t.Fatal(err)
}
defer func() {
if err := d.Cleanup(); err != nil {
t.Fatal(err)
}
}()
mntPath, err := d.Get("2", "")
if err != nil {
t.Fatal(err)
}
if mntPath == "" {
t.Fatal("mntPath should not be empty string")
}
mounted, err := d.mounted(d.active["2"])
if err != nil {
t.Fatal(err)
}
if !mounted {
t.Fatalf("Dir id 2 should be mounted")
}
if err := d.Remove("2"); err != nil {
t.Fatal(err)
}
}
func TestCreateWithInvalidParent(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "docker", ""); err == nil {
t.Fatalf("Error should not be nil with parent does not exist")
}
}
func TestGetDiff(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
diffPath, err := d.Get("1", "")
if err != nil {
t.Fatal(err)
}
// Add a file to the diff path with a fixed size
size := int64(1024)
f, err := os.Create(path.Join(diffPath, "test_file"))
if err != nil {
t.Fatal(err)
}
if err := f.Truncate(size); err != nil {
t.Fatal(err)
}
f.Close()
a, err := d.Diff("1", "")
if err != nil {
t.Fatal(err)
}
if a == nil {
t.Fatalf("Archive should not be nil")
}
}
func TestChanges(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
if err := d.Create("2", "1", ""); err != nil {
t.Fatal(err)
}
defer func() {
if err := d.Cleanup(); err != nil {
t.Fatal(err)
}
}()
mntPoint, err := d.Get("2", "")
if err != nil {
t.Fatal(err)
}
// Create a file to save in the mountpoint
f, err := os.Create(path.Join(mntPoint, "test.txt"))
if err != nil {
t.Fatal(err)
}
if _, err := f.WriteString("testline"); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
changes, err := d.Changes("2", "")
if err != nil {
t.Fatal(err)
}
if len(changes) != 1 {
t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
}
change := changes[0]
expectedPath := "/test.txt"
if change.Path != expectedPath {
t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
}
if change.Kind != archive.ChangeAdd {
t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
}
if err := d.Create("3", "2", ""); err != nil {
t.Fatal(err)
}
mntPoint, err = d.Get("3", "")
if err != nil {
t.Fatal(err)
}
// Create a file to save in the mountpoint
f, err = os.Create(path.Join(mntPoint, "test2.txt"))
if err != nil {
t.Fatal(err)
}
if _, err := f.WriteString("testline"); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
changes, err = d.Changes("3", "")
if err != nil {
t.Fatal(err)
}
if len(changes) != 1 {
t.Fatalf("Dir 2 should have one change from parent got %d", len(changes))
}
change = changes[0]
expectedPath = "/test2.txt"
if change.Path != expectedPath {
t.Fatalf("Expected path %s got %s", expectedPath, change.Path)
}
if change.Kind != archive.ChangeAdd {
t.Fatalf("Change kind should be ChangeAdd got %s", change.Kind)
}
}
func TestDiffSize(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
diffPath, err := d.Get("1", "")
if err != nil {
t.Fatal(err)
}
// Add a file to the diff path with a fixed size
size := int64(1024)
f, err := os.Create(path.Join(diffPath, "test_file"))
if err != nil {
t.Fatal(err)
}
if err := f.Truncate(size); err != nil {
t.Fatal(err)
}
s, err := f.Stat()
if err != nil {
t.Fatal(err)
}
size = s.Size()
if err := f.Close(); err != nil {
t.Fatal(err)
}
diffSize, err := d.DiffSize("1", "")
if err != nil {
t.Fatal(err)
}
if diffSize != size {
t.Fatalf("Expected size to be %d got %d", size, diffSize)
}
}
func TestChildDiffSize(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
defer d.Cleanup()
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
diffPath, err := d.Get("1", "")
if err != nil {
t.Fatal(err)
}
// Add a file to the diff path with a fixed size
size := int64(1024)
f, err := os.Create(path.Join(diffPath, "test_file"))
if err != nil {
t.Fatal(err)
}
if err := f.Truncate(size); err != nil {
t.Fatal(err)
}
s, err := f.Stat()
if err != nil {
t.Fatal(err)
}
size = s.Size()
if err := f.Close(); err != nil {
t.Fatal(err)
}
diffSize, err := d.DiffSize("1", "")
if err != nil {
t.Fatal(err)
}
if diffSize != size {
t.Fatalf("Expected size to be %d got %d", size, diffSize)
}
if err := d.Create("2", "1", ""); err != nil {
t.Fatal(err)
}
diffSize, err = d.DiffSize("2", "")
if err != nil {
t.Fatal(err)
}
// The diff size for the child should be zero
if diffSize != 0 {
t.Fatalf("Expected size to be %d got %d", 0, diffSize)
}
}
func TestExists(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
defer d.Cleanup()
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
if d.Exists("none") {
t.Fatal("id name should not exist in the driver")
}
if !d.Exists("1") {
t.Fatal("id 1 should exist in the driver")
}
}
func TestStatus(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
defer d.Cleanup()
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
status := d.Status()
if status == nil || len(status) == 0 {
t.Fatal("Status should not be nil or empty")
}
rootDir := status[0]
dirs := status[2]
if rootDir[0] != "Root Dir" {
t.Fatalf("Expected Root Dir got %s", rootDir[0])
}
if rootDir[1] != d.rootPath() {
t.Fatalf("Expected %s got %s", d.rootPath(), rootDir[1])
}
if dirs[0] != "Dirs" {
t.Fatalf("Expected Dirs got %s", dirs[0])
}
if dirs[1] != "1" {
t.Fatalf("Expected 1 got %s", dirs[1])
}
}
func TestApplyDiff(t *testing.T) {
d := newDriver(t)
defer os.RemoveAll(tmp)
defer d.Cleanup()
if err := d.Create("1", "", ""); err != nil {
t.Fatal(err)
}
diffPath, err := d.Get("1", "")
if err != nil {
t.Fatal(err)
}
// Add a file to the diff path with a fixed size
size := int64(1024)
f, err := os.Create(path.Join(diffPath, "test_file"))
if err != nil {
t.Fatal(err)
}
if err := f.Truncate(size); err != nil {
t.Fatal(err)
}
f.Close()
diff, err := d.Diff("1", "")
if err != nil {
t.Fatal(err)
}
if err := d.Create("2", "", ""); err != nil {
t.Fatal(err)
}
if err := d.Create("3", "2", ""); err != nil {
t.Fatal(err)
}
if err := d.applyDiff("3", diff); err != nil {
t.Fatal(err)
}
// Ensure that the file is in the mount point for id 3
mountPoint, err := d.Get("3", "")
if err != nil {
t.Fatal(err)
}
if _, err := os.Stat(path.Join(mountPoint, "test_file")); err != nil {
t.Fatal(err)
}
}
func hash(c string) string {
h := sha256.New()
fmt.Fprint(h, c)
return hex.EncodeToString(h.Sum(nil))
}
func testMountMoreThan42Layers(t *testing.T, mountPath string) {
if err := os.MkdirAll(mountPath, 0755); err != nil {
t.Fatal(err)
}
defer os.RemoveAll(mountPath)
d := testInit(mountPath, t).(*Driver)
defer d.Cleanup()
var last string
var expected int
for i := 1; i < 127; i++ {
expected++
var (
parent = fmt.Sprintf("%d", i-1)
current = fmt.Sprintf("%d", i)
)
if parent == "0" {
parent = ""
} else {
parent = hash(parent)
}
current = hash(current)
if err := d.Create(current, parent, ""); err != nil {
t.Logf("Current layer %d", i)
t.Error(err)
}
point, err := d.Get(current, "")
if err != nil {
t.Logf("Current layer %d", i)
t.Error(err)
}
f, err := os.Create(path.Join(point, current))
if err != nil {
t.Logf("Current layer %d", i)
t.Error(err)
}
f.Close()
if i%10 == 0 {
if err := os.Remove(path.Join(point, parent)); err != nil {
t.Logf("Current layer %d", i)
t.Error(err)
}
expected--
}
last = current
}
// Perform the actual mount for the top most image
point, err := d.Get(last, "")
if err != nil {
t.Error(err)
}
files, err := ioutil.ReadDir(point)
if err != nil {
t.Error(err)
}
if len(files) != expected {
t.Errorf("Expected %d got %d", expected, len(files))
}
}
func TestMountMoreThan42Layers(t *testing.T) {
os.RemoveAll(tmpOuter)
testMountMoreThan42Layers(t, tmp)
}
func TestMountMoreThan42LayersMatchingPathLength(t *testing.T) {
defer os.RemoveAll(tmpOuter)
zeroes := "0"
for {
// This finds a mount path so that when combined into aufs mount options
// 4096 byte boundary would be in between the paths or in permission
// section. For '/tmp' it will use '/tmp/aufs-tests/00000000/aufs'
mountPath := path.Join(tmpOuter, zeroes, "aufs")
pathLength := 77 + len(mountPath)
if mod := 4095 % pathLength; mod == 0 || mod > pathLength-2 {
t.Logf("Using path: %s", mountPath)
testMountMoreThan42Layers(t, mountPath)
return
}
zeroes += "0"
}
}
func BenchmarkConcurrentAccess(b *testing.B) {
b.StopTimer()
b.ResetTimer()
d := newDriver(b)
defer os.RemoveAll(tmp)
defer d.Cleanup()
numConcurent := 256
// create a bunch of ids
var ids []string
for i := 0; i < numConcurent; i++ {
ids = append(ids, stringid.GenerateNonCryptoID())
}
if err := d.Create(ids[0], "", ""); err != nil {
b.Fatal(err)
}
if err := d.Create(ids[1], ids[0], ""); err != nil {
b.Fatal(err)
}
parent := ids[1]
ids = append(ids[2:])
chErr := make(chan error, numConcurent)
var outerGroup sync.WaitGroup
outerGroup.Add(len(ids))
b.StartTimer()
// here's the actual bench
for _, id := range ids {
go func(id string) {
defer outerGroup.Done()
if err := d.Create(id, parent, ""); err != nil {
b.Logf("Create %s failed", id)
chErr <- err
return
}
var innerGroup sync.WaitGroup
for i := 0; i < b.N; i++ {
innerGroup.Add(1)
go func() {
d.Get(id, "")
d.Put(id)
innerGroup.Done()
}()
}
innerGroup.Wait()
d.Remove(id)
}(id)
}
outerGroup.Wait()
b.StopTimer()
close(chErr)
for err := range chErr {
if err != nil {
b.Log(err)
b.Fail()
}
}
}