From 7b5bb8d40c4377bd6c1147dc675bcedabbe45016 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Mon, 28 Jan 2013 11:51:41 -0800 Subject: [PATCH 1/5] filesystem: Added IsMounted() unit tests Upstream-commit: d8bc912238dfa75dff0902b9d97133e200807bea Component: engine --- components/engine/filesystem_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/components/engine/filesystem_test.go b/components/engine/filesystem_test.go index b4deb02b36..fb57344ede 100644 --- a/components/engine/filesystem_test.go +++ b/components/engine/filesystem_test.go @@ -27,21 +27,41 @@ func TestFilesystem(t *testing.T) { t.Errorf("Umount succeeded even though the filesystem was not mounted") } + if filesystem.IsMounted() { + t.Fatal("Filesystem should not be mounted") + } + if err := filesystem.Mount(); err != nil { t.Fatal(err) } + if !filesystem.IsMounted() { + t.Fatal("Filesystem should be mounted") + } + if err := filesystem.Mount(); err == nil { t.Errorf("Double mount succeeded") } + if !filesystem.IsMounted() { + t.Fatal("Filesystem should be mounted") + } + if err := filesystem.Umount(); err != nil { t.Fatal(err) } + if filesystem.IsMounted() { + t.Fatal("Filesystem should not be mounted") + } + if err := filesystem.Umount(); err == nil { t.Errorf("Umount succeeded even though the filesystem was already umounted") } + + if filesystem.IsMounted() { + t.Fatal("Filesystem should not be mounted") + } } func TestFilesystemMultiLayer(t *testing.T) { From 9632a09e603332490a9792d19831ade1e175e5b4 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Mon, 28 Jan 2013 11:51:51 -0800 Subject: [PATCH 2/5] Added sequential and parallel benchmarks in order to stress test docker (create, run, destroy workflow) Upstream-commit: a2d7dd1a1908fc3dc6ac5648b5ea12eaecb19b1b Component: engine --- components/engine/container_test.go | 86 +++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/components/engine/container_test.go b/components/engine/container_test.go index 6eedc6c658..161b32d76b 100644 --- a/components/engine/container_test.go +++ b/components/engine/container_test.go @@ -1,7 +1,9 @@ package docker import ( + "fmt" "testing" + "time" ) func TestStart(t *testing.T) { @@ -240,3 +242,87 @@ func TestMultipleContainers(t *testing.T) { t.Fatal(err) } } + +func BenchmarkRunSequencial(b *testing.B) { + docker, err := newTestDocker() + if err != nil { + b.Fatal(err) + } + for i := 0; i < b.N; i++ { + container, err := docker.Create( + fmt.Sprintf("bench_%v", i), + "echo", + []string{"-n", "foo"}, + []string{"/var/lib/docker/images/ubuntu"}, + &Config{}, + ) + if err != nil { + b.Fatal(err) + } + defer docker.Destroy(container) + output, err := container.Output() + if err != nil { + b.Fatal(err) + } + if string(output) != "foo" { + b.Fatalf("Unexecpted output: %v", string(output)) + } + if err := docker.Destroy(container); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkRunParallel(b *testing.B) { + docker, err := newTestDocker() + if err != nil { + b.Fatal(err) + } + + var tasks []chan error + + for i := 0; i < b.N; i++ { + complete := make(chan error) + tasks = append(tasks, complete) + go func(i int, complete chan error) { + container, err := docker.Create( + fmt.Sprintf("bench_%v", i), + "echo", + []string{"-n", "foo"}, + []string{"/var/lib/docker/images/ubuntu"}, + &Config{}, + ) + if err != nil { + complete <- err + return + } + defer docker.Destroy(container) + if err := container.Start(); err != nil { + complete <- err + return + } + if err := container.WaitTimeout(15 * time.Second); err != nil { + complete <- err + return + } + // if string(output) != "foo" { + // complete <- fmt.Errorf("Unexecpted output: %v", string(output)) + // } + if err := docker.Destroy(container); err != nil { + complete <- err + return + } + complete <- nil + }(i, complete) + } + var errors []error + for _, task := range tasks { + err := <-task + if err != nil { + errors = append(errors, err) + } + } + if len(errors) > 0 { + b.Fatal(errors) + } +} From c2dc6c5c0b547f8f17c45e5f0dffe79e18abb327 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Mon, 28 Jan 2013 11:53:58 -0800 Subject: [PATCH 3/5] Filesystem: Re-implemented Umount() and IsMounted() to work around AUFS issues. Umount() will now attempt to remove the mntpoint after umounting. It will keep retrying for some time until the mntpoint is deleted. Upstream-commit: 174f25909c525015cb84f1b82376091a1ac9c118 Component: engine --- components/engine/filesystem.go | 45 +++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/components/engine/filesystem.go b/components/engine/filesystem.go index 41dacd5fdd..3a16bd6983 100644 --- a/components/engine/filesystem.go +++ b/components/engine/filesystem.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "syscall" + "time" ) type Filesystem struct { @@ -38,33 +39,57 @@ func (fs *Filesystem) Mount() error { roBranches += fmt.Sprintf("%v=ro:", layer) } branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches) - return syscall.Mount("none", fs.RootFS, "aufs", 0, branches) + if err := syscall.Mount("none", fs.RootFS, "aufs", 0, branches); err != nil { + return err + } + if !fs.IsMounted() { + return errors.New("Mount failed") + } + return nil } func (fs *Filesystem) Umount() error { if !fs.IsMounted() { return errors.New("Umount: Filesystem not mounted") } - return syscall.Unmount(fs.RootFS, 0) + if err := syscall.Unmount(fs.RootFS, 0); err != nil { + return err + } + if fs.IsMounted() { + return fmt.Errorf("Umount: Filesystem still mounted after calling umount(%v)", fs.RootFS) + } + for retries := 0; retries < 1000; retries++ { + err := os.Remove(fs.RootFS) + if err == nil { + // rm mntpoint succeeded + return nil + } + if os.IsNotExist(err) { + // mntpoint doesn't exist anymore. Success. + return nil + } + // fmt.Printf("(%v) Remove %v returned: %v\n", retries, fs.RootFS, err) + time.Sleep(10 * time.Millisecond) + } + return fmt.Errorf("Umount: Failed to umount %v", fs.RootFS) } func (fs *Filesystem) IsMounted() bool { - f, err := os.Open(fs.RootFS) + mntpoint, err := os.Stat(fs.RootFS) if err != nil { if os.IsNotExist(err) { return false } panic(err) } - list, err := f.Readdirnames(1) - f.Close() + parent, err := os.Stat(filepath.Join(fs.RootFS, "..")) if err != nil { - return false + panic(err) } - if len(list) > 0 { - return true - } - return false + + mntpointSt := mntpoint.Sys().(*syscall.Stat_t) + parentSt := parent.Sys().(*syscall.Stat_t) + return mntpointSt.Dev != parentSt.Dev } type ChangeType int From 9c18c9de845992c3a7d2d383f1b2f700032f2015 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Mon, 28 Jan 2013 11:58:59 -0800 Subject: [PATCH 4/5] docker: Make sure to umount the container if it's still mounted at destruction Upstream-commit: fb40a7880494afffe384ac955f5f4e3a4971c676 Component: engine --- components/engine/docker.go | 10 +++++++--- components/engine/filesystem.go | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/engine/docker.go b/components/engine/docker.go index 8a79e989a9..7f9442306a 100644 --- a/components/engine/docker.go +++ b/components/engine/docker.go @@ -67,10 +67,14 @@ func (docker *Docker) Destroy(container *Container) error { if err := container.Stop(); err != nil { return err } - if err := os.RemoveAll(container.Root); err != nil { - return err + if container.Filesystem.IsMounted() { + if err := container.Filesystem.Umount(); err != nil { + log.Printf("Unable to umount container %v: %v", container.Id, err) + } + } + if err := os.RemoveAll(container.Root); err != nil { + log.Printf("Unable to remove filesystem for %v: %v", container.Id, err) } - docker.containers.Remove(element) return nil } diff --git a/components/engine/filesystem.go b/components/engine/filesystem.go index 3a16bd6983..1e4679fc04 100644 --- a/components/engine/filesystem.go +++ b/components/engine/filesystem.go @@ -58,6 +58,8 @@ func (fs *Filesystem) Umount() error { if fs.IsMounted() { return fmt.Errorf("Umount: Filesystem still mounted after calling umount(%v)", fs.RootFS) } + // Even though we just unmounted the filesystem, AUFS will prevent deleting the mntpoint + // for some time. We'll just keep retrying until it succeeds. for retries := 0; retries < 1000; retries++ { err := os.Remove(fs.RootFS) if err == nil { From 2c168355638a044c93d980636c8d3114911f8a91 Mon Sep 17 00:00:00 2001 From: Andrea Luzzardi Date: Mon, 28 Jan 2013 12:11:30 -0800 Subject: [PATCH 5/5] Fixed broken unit test after merge Upstream-commit: d802a31be538d7c90d3f33474595dfdb77b7e50e Component: engine --- components/engine/container.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/components/engine/container.go b/components/engine/container.go index 1c4b58fd33..fc73f13466 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -59,10 +59,6 @@ func createContainer(id string, root string, command string, args []string, laye stdoutLog: new(bytes.Buffer), stderrLog: new(bytes.Buffer), } - if err := container.Filesystem.createMountPoints(); err != nil { - return nil, err - } - container.stdout.AddWriter(NopWriteCloser(container.stdoutLog)) container.stderr.AddWriter(NopWriteCloser(container.stderrLog))