فهرست منبع

Halfway through fs branch fixes, TestUser not passing

shin- 12 سال پیش
والد
کامیت
2ebf3464a1
10فایلهای تغییر یافته به همراه143 افزوده شده و 515 حذف شده
  1. 15 10
      container.go
  2. 24 22
      container_test.go
  3. 20 7
      docker.go
  4. 49 8
      docker_test.go
  5. 0 252
      dockerd/dockerweb.html
  6. 0 208
      filesystem_test.go
  7. 1 1
      fs/layers.go
  8. 32 5
      fs/store.go
  9. 1 1
      fs/store_test.go
  10. 1 1
      lxc_template.go

+ 15 - 10
container.go

@@ -14,6 +14,7 @@ import (
 	"strings"
 	"strings"
 	"syscall"
 	"syscall"
 	"time"
 	"time"
+	"./fs"
 )
 )
 
 
 var sysInitPath string
 var sysInitPath string
@@ -32,7 +33,7 @@ type Container struct {
 	Args []string
 	Args []string
 
 
 	Config     *Config
 	Config     *Config
-	Filesystem *Filesystem
+	Mountpoint *fs.Mountpoint
 	State      *State
 	State      *State
 
 
 	SysInitPath   string
 	SysInitPath   string
@@ -55,7 +56,11 @@ type Config struct {
 	OpenStdin bool // Open stdin
 	OpenStdin bool // Open stdin
 }
 }
 
 
-func createContainer(id string, root string, command string, args []string, layers []string, config *Config) (*Container, error) {
+func createContainer(id string, root string, command string, args []string, image *fs.Image, config *Config) (*Container, error) {
+	mountpoint, err := image.Mountpoint(path.Join(root, "rootfs"), path.Join(root, "rw"))
+	if err != nil {
+		return nil, err
+	}
 	container := &Container{
 	container := &Container{
 		Id:         id,
 		Id:         id,
 		Root:       root,
 		Root:       root,
@@ -63,7 +68,7 @@ func createContainer(id string, root string, command string, args []string, laye
 		Path:       command,
 		Path:       command,
 		Args:       args,
 		Args:       args,
 		Config:     config,
 		Config:     config,
-		Filesystem: newFilesystem(path.Join(root, "rootfs"), path.Join(root, "rw"), layers),
+		Mountpoint: mountpoint,
 		State:      newState(),
 		State:      newState(),
 
 
 		SysInitPath:   sysInitPath,
 		SysInitPath:   sysInitPath,
@@ -84,9 +89,9 @@ func createContainer(id string, root string, command string, args []string, laye
 	if err := os.Mkdir(root, 0700); err != nil {
 	if err := os.Mkdir(root, 0700); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := container.Filesystem.createMountPoints(); err != nil {
+	/*if err := container.Filesystem.createMountPoints(); err != nil {
 		return nil, err
 		return nil, err
-	}
+	}*/
 	if err := container.save(); err != nil {
 	if err := container.save(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -108,9 +113,9 @@ func loadContainer(containerPath string) (*Container, error) {
 	if err := json.Unmarshal(data, container); err != nil {
 	if err := json.Unmarshal(data, container); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	if err := container.Filesystem.createMountPoints(); err != nil {
-		return nil, err
-	}
+	// if err := container.Filesystem.createMountPoints(); err != nil {
+	// 	return nil, err
+	// }
 	if container.Config.OpenStdin {
 	if container.Config.OpenStdin {
 		container.stdin, container.stdinPipe = io.Pipe()
 		container.stdin, container.stdinPipe = io.Pipe()
 	} else {
 	} else {
@@ -260,7 +265,7 @@ func (container *Container) start() error {
 }
 }
 
 
 func (container *Container) Start() error {
 func (container *Container) Start() error {
-	if err := container.Filesystem.EnsureMounted(); err != nil {
+	if err := container.Mountpoint.EnsureMounted(); err != nil {
 		return err
 		return err
 	}
 	}
 	if err := container.generateLXCConfig(); err != nil {
 	if err := container.generateLXCConfig(); err != nil {
@@ -351,7 +356,7 @@ func (container *Container) monitor() {
 	// Cleanup
 	// Cleanup
 	container.stdout.Close()
 	container.stdout.Close()
 	container.stderr.Close()
 	container.stderr.Close()
-	if err := container.Filesystem.Umount(); err != nil {
+	if err := container.Mountpoint.Umount(); err != nil {
 		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
 		log.Printf("%v: Failed to umount filesystem: %v", container.Id, err)
 	}
 	}
 
 

+ 24 - 22
container_test.go

@@ -18,7 +18,7 @@ func TestStart(t *testing.T) {
 		"start_test",
 		"start_test",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			Ram: 33554432,
 			Ram: 33554432,
 		},
 		},
@@ -54,7 +54,7 @@ func TestRun(t *testing.T) {
 		"run_test",
 		"run_test",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			Ram: 33554432,
 			Ram: 33554432,
 		},
 		},
@@ -84,7 +84,7 @@ func TestOutput(t *testing.T) {
 		"output_test",
 		"output_test",
 		"echo",
 		"echo",
 		[]string{"-n", "foobar"},
 		[]string{"-n", "foobar"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -109,7 +109,7 @@ func TestKill(t *testing.T) {
 		"stop_test",
 		"stop_test",
 		"cat",
 		"cat",
 		[]string{"/dev/zero"},
 		[]string{"/dev/zero"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -152,7 +152,7 @@ func TestExitCode(t *testing.T) {
 		"exit_test_1",
 		"exit_test_1",
 		"/bin/true",
 		"/bin/true",
 		[]string{""},
 		[]string{""},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -167,7 +167,7 @@ func TestExitCode(t *testing.T) {
 		"exit_test_2",
 		"exit_test_2",
 		"/bin/false",
 		"/bin/false",
 		[]string{""},
 		[]string{""},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -196,7 +196,7 @@ func TestRestart(t *testing.T) {
 		"restart_test",
 		"restart_test",
 		"echo",
 		"echo",
 		[]string{"-n", "foobar"},
 		[]string{"-n", "foobar"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -230,7 +230,7 @@ func TestRestartStdin(t *testing.T) {
 		"restart_stdin_test",
 		"restart_stdin_test",
 		"cat",
 		"cat",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -281,7 +281,7 @@ func TestUser(t *testing.T) {
 		"user_default",
 		"user_default",
 		"id",
 		"id",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -301,7 +301,7 @@ func TestUser(t *testing.T) {
 		"user_root",
 		"user_root",
 		"id",
 		"id",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			User: "root",
 			User: "root",
 		},
 		},
@@ -323,7 +323,7 @@ func TestUser(t *testing.T) {
 		"user_uid0",
 		"user_uid0",
 		"id",
 		"id",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			User: "0",
 			User: "0",
 		},
 		},
@@ -343,9 +343,9 @@ func TestUser(t *testing.T) {
 	// Set a different user by uid
 	// Set a different user by uid
 	container, err = docker.Create(
 	container, err = docker.Create(
 		"user_uid1",
 		"user_uid1",
-		"id",
+		"/usr/bin/id",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			User: "1",
 			User: "1",
 		},
 		},
@@ -355,8 +355,10 @@ func TestUser(t *testing.T) {
 	}
 	}
 	defer docker.Destroy(container)
 	defer docker.Destroy(container)
 	output, err = container.Output()
 	output, err = container.Output()
-	if err != nil || container.State.ExitCode != 0 {
+	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
+	} else if container.State.ExitCode != 0 {
+		t.Fatalf("Container exit code is invalid: %d\nOutput:\n%s\n", container.State.ExitCode, output)
 	}
 	}
 	if !strings.Contains(string(output), "uid=1(daemon) gid=1(daemon)") {
 	if !strings.Contains(string(output), "uid=1(daemon) gid=1(daemon)") {
 		t.Error(string(output))
 		t.Error(string(output))
@@ -365,9 +367,9 @@ func TestUser(t *testing.T) {
 	// Set a different user by username
 	// Set a different user by username
 	container, err = docker.Create(
 	container, err = docker.Create(
 		"user_daemon",
 		"user_daemon",
-		"id",
+		"/usr/bin/id",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			User: "daemon",
 			User: "daemon",
 		},
 		},
@@ -395,7 +397,7 @@ func TestMultipleContainers(t *testing.T) {
 		"container1",
 		"container1",
 		"cat",
 		"cat",
 		[]string{"/dev/zero"},
 		[]string{"/dev/zero"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -407,7 +409,7 @@ func TestMultipleContainers(t *testing.T) {
 		"container2",
 		"container2",
 		"cat",
 		"cat",
 		[]string{"/dev/zero"},
 		[]string{"/dev/zero"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -450,7 +452,7 @@ func TestStdin(t *testing.T) {
 		"stdin_test",
 		"stdin_test",
 		"cat",
 		"cat",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -485,7 +487,7 @@ func TestTty(t *testing.T) {
 		"tty_test",
 		"tty_test",
 		"cat",
 		"cat",
 		[]string{},
 		[]string{},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{
 		&Config{
 			OpenStdin: true,
 			OpenStdin: true,
 		},
 		},
@@ -521,7 +523,7 @@ func BenchmarkRunSequencial(b *testing.B) {
 			fmt.Sprintf("bench_%v", i),
 			fmt.Sprintf("bench_%v", i),
 			"echo",
 			"echo",
 			[]string{"-n", "foo"},
 			[]string{"-n", "foo"},
-			[]string{testLayerPath},
+			GetTestImage(docker),
 			&Config{},
 			&Config{},
 		)
 		)
 		if err != nil {
 		if err != nil {
@@ -557,7 +559,7 @@ func BenchmarkRunParallel(b *testing.B) {
 				fmt.Sprintf("bench_%v", i),
 				fmt.Sprintf("bench_%v", i),
 				"echo",
 				"echo",
 				[]string{"-n", "foo"},
 				[]string{"-n", "foo"},
-				[]string{testLayerPath},
+				GetTestImage(docker),
 				&Config{},
 				&Config{},
 			)
 			)
 			if err != nil {
 			if err != nil {

+ 20 - 7
docker.go

@@ -8,12 +8,14 @@ import (
 	"os"
 	"os"
 	"path"
 	"path"
 	"sort"
 	"sort"
+	"./fs"
 )
 )
 
 
 type Docker struct {
 type Docker struct {
-	root       string
-	repository string
-	containers *list.List
+	root       	string
+	repository 	string
+	containers 	*list.List
+	Store		*fs.Store
 }
 }
 
 
 func (docker *Docker) List() []*Container {
 func (docker *Docker) List() []*Container {
@@ -46,12 +48,12 @@ func (docker *Docker) Exists(id string) bool {
 	return docker.Get(id) != nil
 	return docker.Get(id) != nil
 }
 }
 
 
-func (docker *Docker) Create(id string, command string, args []string, layers []string, config *Config) (*Container, error) {
+func (docker *Docker) Create(id string, command string, args []string, image *fs.Image, config *Config) (*Container, error) {
 	if docker.Exists(id) {
 	if docker.Exists(id) {
 		return nil, fmt.Errorf("Container %v already exists", id)
 		return nil, fmt.Errorf("Container %v already exists", id)
 	}
 	}
 	root := path.Join(docker.repository, id)
 	root := path.Join(docker.repository, id)
-	container, err := createContainer(id, root, command, args, layers, config)
+	container, err := createContainer(id, root, command, args, image, config)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -68,10 +70,14 @@ func (docker *Docker) Destroy(container *Container) error {
 	if err := container.Stop(); err != nil {
 	if err := container.Stop(); err != nil {
 		return err
 		return err
 	}
 	}
-	if container.Filesystem.IsMounted() {
-		if err := container.Filesystem.Umount(); err != nil {
+	if container.Mountpoint.Mounted() {
+		if err := container.Mountpoint.Umount(); err != nil {
 			log.Printf("Unable to umount container %v: %v", container.Id, err)
 			log.Printf("Unable to umount container %v: %v", container.Id, err)
 		}
 		}
+
+		if err := container.Mountpoint.Deregister(); err != nil {
+			log.Printf("Unable to deregiser mountpoint %v: %v", container.Mountpoint.Root, err)
+		}
 	}
 	}
 	if err := os.RemoveAll(container.Root); err != nil {
 	if err := os.RemoveAll(container.Root); err != nil {
 		log.Printf("Unable to remove filesystem for %v: %v", container.Id, err)
 		log.Printf("Unable to remove filesystem for %v: %v", container.Id, err)
@@ -91,6 +97,7 @@ func (docker *Docker) restore() error {
 			log.Printf("Failed to load container %v: %v", v.Name(), err)
 			log.Printf("Failed to load container %v: %v", v.Name(), err)
 			continue
 			continue
 		}
 		}
+		container.Mountpoint.Store = docker.Store
 		docker.containers.PushBack(container)
 		docker.containers.PushBack(container)
 	}
 	}
 	return nil
 	return nil
@@ -101,10 +108,16 @@ func New() (*Docker, error) {
 }
 }
 
 
 func NewFromDirectory(root string) (*Docker, error) {
 func NewFromDirectory(root string) (*Docker, error) {
+	store, err := fs.New(path.Join(root, "images"))
+	if err != nil {
+		return nil, err
+	}
+
 	docker := &Docker{
 	docker := &Docker{
 		root:       root,
 		root:       root,
 		repository: path.Join(root, "containers"),
 		repository: path.Join(root, "containers"),
 		containers: list.New(),
 		containers: list.New(),
+		Store:		store,
 	}
 	}
 
 
 	if err := os.Mkdir(docker.repository, 0700); err != nil && !os.IsExist(err) {
 	if err := os.Mkdir(docker.repository, 0700); err != nil && !os.IsExist(err) {

+ 49 - 8
docker_test.go

@@ -5,9 +5,20 @@ import (
 	"log"
 	"log"
 	"os"
 	"os"
 	"testing"
 	"testing"
+	"io"
+	"./fs"
 )
 )
 
 
-const testLayerPath string = "/var/lib/docker/images/docker-ut"
+const testLayerPath string = "/var/lib/docker/docker-ut.tar"
+
+func layerArchive(tarfile string)  (io.Reader, error) {
+	// FIXME: need to close f somewhere
+	f, err := os.Open(tarfile)
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
+}
 
 
 func init() {
 func init() {
 	// Hack to run sys init during unit testing
 	// Hack to run sys init during unit testing
@@ -21,6 +32,7 @@ func init() {
 			panic(err)
 			panic(err)
 		}
 		}
 		log.Fatalf("Unit test base image not found. Please fix the problem by running \"debootstrap --arch=amd64 quantal %v\"", testLayerPath)
 		log.Fatalf("Unit test base image not found. Please fix the problem by running \"debootstrap --arch=amd64 quantal %v\"", testLayerPath)
+		return
 	}
 	}
 }
 }
 
 
@@ -33,9 +45,28 @@ func newTestDocker() (*Docker, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
+
+	if layer, err := layerArchive(testLayerPath); err != nil {
+		panic(err)
+	} else {
+		_, err = docker.Store.Create(layer, nil, "docker-ut", "unit tests")
+		if err != nil {
+			panic(err)
+		}
+	}
 	return docker, nil
 	return docker, nil
 }
 }
 
 
+func GetTestImage(docker *Docker) (*fs.Image) {
+	imgs, err := docker.Store.Images()
+	if err != nil {
+		panic(err)
+	} else if len(imgs) < 1 {
+		panic("GASP")
+	}
+	return imgs[0]
+}
+
 func TestCreate(t *testing.T) {
 func TestCreate(t *testing.T) {
 	docker, err := newTestDocker()
 	docker, err := newTestDocker()
 	if err != nil {
 	if err != nil {
@@ -50,7 +81,7 @@ func TestCreate(t *testing.T) {
 		"test_create",
 		"test_create",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -98,7 +129,7 @@ func TestDestroy(t *testing.T) {
 		"test_destroy",
 		"test_destroy",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -146,7 +177,7 @@ func TestGet(t *testing.T) {
 		"test1",
 		"test1",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -158,7 +189,7 @@ func TestGet(t *testing.T) {
 		"test2",
 		"test2",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -170,7 +201,7 @@ func TestGet(t *testing.T) {
 		"test3",
 		"test3",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {
@@ -198,16 +229,26 @@ func TestRestore(t *testing.T) {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	// Create a container with one instance of docker
 	docker1, err := NewFromDirectory(root)
 	docker1, err := NewFromDirectory(root)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
+
+	if layer, err := layerArchive(testLayerPath); err != nil {
+		panic(err)
+	} else {
+		_, err = docker1.Store.Create(layer, nil, "docker-ut", "unit tests")
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	// Create a container with one instance of docker
 	container1, err := docker1.Create(
 	container1, err := docker1.Create(
 		"restore_test",
 		"restore_test",
 		"ls",
 		"ls",
 		[]string{"-al"},
 		[]string{"-al"},
-		[]string{testLayerPath},
+		GetTestImage(docker1),
 		&Config{},
 		&Config{},
 	)
 	)
 	if err != nil {
 	if err != nil {

+ 0 - 252
dockerd/dockerweb.html

@@ -1,252 +0,0 @@
-package docker
-
-import (
-	"errors"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"strings"
-	"syscall"
-	"time"
-)
-
-type Filesystem struct {
-	RootFS string
-	RWPath string
-	// The layers to be mounted on top of each other via aufs.
-	// Layers are ordered top-to-bottom: the first layer in the list will be mounted on top of the others.
-	// In other words, THE BASE IMAGE SHOULD BE LAST!
-	Layers []string
-}
-
-func (fs *Filesystem) createMountPoints() error {
-	if err := os.Mkdir(fs.RootFS, 0755); err != nil && !os.IsExist(err) {
-		return err
-	}
-	if err := os.Mkdir(fs.RWPath, 0755); err != nil && !os.IsExist(err) {
-		return err
-	}
-	return nil
-}
-
-func (fs *Filesystem) Mount() error {
-	if fs.IsMounted() {
-		return errors.New("Mount: Filesystem already mounted")
-	}
-	if err := fs.createMountPoints(); err != nil {
-		return err
-	}
-	rwBranch := fmt.Sprintf("%v=rw", fs.RWPath)
-	roBranches := ""
-	for _, layer := range fs.Layers {
-		roBranches += fmt.Sprintf("%v=ro:", layer)
-	}
-	branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches)
-	if err := 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")
-	}
-	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)
-	}
-	// 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 {
-			// 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 {
-	mntpoint, err := os.Stat(fs.RootFS)
-	if err != nil {
-		if os.IsNotExist(err) {
-			return false
-		}
-		panic(err)
-	}
-	parent, err := os.Stat(filepath.Join(fs.RootFS, ".."))
-	if err != nil {
-		panic(err)
-	}
-
-	mntpointSt := mntpoint.Sys().(*syscall.Stat_t)
-	parentSt := parent.Sys().(*syscall.Stat_t)
-	return mntpointSt.Dev != parentSt.Dev
-}
-
-// Tar returns the contents of the filesystem as an uncompressed tar stream
-func (fs *Filesystem) Tar() (io.Reader, error) {
-	if err := fs.EnsureMounted(); err != nil {
-		return nil, err
-	}
-	return Tar(fs.RootFS)
-}
-
-func (fs *Filesystem) EnsureMounted() error {
-	if !fs.IsMounted() {
-		if err := fs.Mount(); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-type ChangeType int
-
-const (
-	ChangeModify = iota
-	ChangeAdd
-	ChangeDelete
-)
-
-type Change struct {
-	Path string
-	Kind ChangeType
-}
-
-func (change *Change) String() string {
-	var kind string
-	switch change.Kind {
-	case ChangeModify:
-		kind = "C"
-	case ChangeAdd:
-		kind = "A"
-	case ChangeDelete:
-		kind = "D"
-	}
-	return fmt.Sprintf("%s %s", kind, change.Path)
-}
-
-func (fs *Filesystem) Changes() ([]Change, error) {
-	var changes []Change
-	err := filepath.Walk(fs.RWPath, func(path string, f os.FileInfo, err error) error {
-		if err != nil {
-			return err
-		}
-
-		// Rebase path
-		path, err = filepath.Rel(fs.RWPath, path)
-		if err != nil {
-			return err
-		}
-		path = filepath.Join("/", path)
-
-		// Skip root
-		if path == "/" {
-			return nil
-		}
-
-		// Skip AUFS metadata
-		if matched, err := filepath.Match("/.wh..wh.*", path); err != nil || matched {
-			return err
-		}
-
-		change := Change{
-			Path: path,
-		}
-
-		// Find out what kind of modification happened
-		file := filepath.Base(path)
-		// If there is a whiteout, then the file was removed
-		if strings.HasPrefix(file, ".wh.") {
-			originalFile := strings.TrimLeft(file, ".wh.")
-			change.Path = filepath.Join(filepath.Dir(path), originalFile)
-			change.Kind = ChangeDelete
-		} else {
-			// Otherwise, the file was added
-			change.Kind = ChangeAdd
-
-			// ...Unless it already existed in a top layer, in which case, it's a modification
-			for _, layer := range fs.Layers {
-				stat, err := os.Stat(filepath.Join(layer, path))
-				if err != nil && !os.IsNotExist(err) {
-					return err
-				}
-				if err == nil {
-					// The file existed in the top layer, so that's a modification
-
-					// However, if it's a directory, maybe it wasn't actually modified.
-					// If you modify /foo/bar/baz, then /foo will be part of the changed files only because it's the parent of bar
-					if stat.IsDir() && f.IsDir() {
-						if f.Size() == stat.Size() && f.Mode() == stat.Mode() && f.ModTime() == stat.ModTime() {
-							// Both directories are the same, don't record the change
-							return nil
-						}
-					}
-					change.Kind = ChangeModify
-					break
-				}
-			}
-		}
-
-		// Record change
-		changes = append(changes, change)
-		return nil
-	})
-	if err != nil {
-		return nil, err
-	}
-	return changes, nil
-}
-
-// Reset removes all changes to the filesystem, reverting it to its initial state.
-func (fs *Filesystem) Reset() error {
-	if err := os.RemoveAll(fs.RWPath); err != nil {
-		return err
-	}
-	// We removed the RW directory itself along with its content: let's re-create an empty one.
-	if err := fs.createMountPoints(); err != nil {
-		return err
-	}
-	return nil
-}
-
-// Open opens the named file for reading.
-func (fs *Filesystem) OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
-	if err := fs.EnsureMounted(); err != nil {
-		return nil, err
-	}
-	return os.OpenFile(filepath.Join(fs.RootFS, path), flag, perm)
-}
-
-// ReadDir reads the directory named by dirname, relative to the Filesystem's root,
-// and returns a list of sorted directory entries
-func (fs *Filesystem) ReadDir(dirname string) ([]os.FileInfo, error) {
-	if err := fs.EnsureMounted(); err != nil {
-		return nil, err
-	}
-	return ioutil.ReadDir(filepath.Join(fs.RootFS, dirname))
-}
-
-func newFilesystem(rootfs string, rwpath string, layers []string) *Filesystem {
-	return &Filesystem{
-		RootFS: rootfs,
-		RWPath: rwpath,
-		Layers: layers,
-	}
-}

+ 0 - 208
filesystem_test.go

@@ -1,208 +0,0 @@
-package docker
-
-import (
-	"bytes"
-	"io/ioutil"
-	"os"
-	"path"
-	"testing"
-)
-
-func newTestFilesystem(t *testing.T, layers []string) (rootfs string, fs *Filesystem) {
-	rootfs, err := ioutil.TempDir("", "docker-test-root")
-	if err != nil {
-		t.Fatal(err)
-	}
-	rwpath, err := ioutil.TempDir("", "docker-test-rw")
-	if err != nil {
-		t.Fatal(err)
-	}
-	fs = newFilesystem(rootfs, rwpath, layers)
-	return
-}
-
-func TestFilesystem(t *testing.T) {
-	_, filesystem := newTestFilesystem(t, []string{testLayerPath})
-	if err := filesystem.Umount(); err == nil {
-		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) {
-	// Create a fake layer
-	fakeLayer, err := ioutil.TempDir("", "docker-layer")
-	if err != nil {
-		t.Fatal(err)
-	}
-	data := []byte("hello world")
-	if err := ioutil.WriteFile(path.Join(fakeLayer, "test_file"), data, 0700); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create the layered filesystem and add our fake layer on top
-	rootfs, filesystem := newTestFilesystem(t, []string{testLayerPath, fakeLayer})
-
-	// Mount it
-	if err := filesystem.Mount(); err != nil {
-		t.Fatal(err)
-	}
-	defer func() {
-		if err := filesystem.Umount(); err != nil {
-			t.Fatal(err)
-		}
-	}()
-
-	// Check to see whether we can access our fake layer
-	if _, err := os.Stat(path.Join(rootfs, "test_file")); err != nil {
-		t.Fatal(err)
-	}
-	fsdata, err := ioutil.ReadFile(path.Join(rootfs, "test_file"))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !bytes.Equal(data, fsdata) {
-		t.Error(string(fsdata))
-	}
-}
-
-func TestChanges(t *testing.T) {
-	rootfs, filesystem := newTestFilesystem(t, []string{testLayerPath})
-	// Mount it
-	if err := filesystem.Mount(); err != nil {
-		t.Fatal(err)
-	}
-	defer filesystem.Umount()
-
-	var changes []Change
-	var err error
-
-	// Test without changes
-	changes, err = filesystem.Changes()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(changes) != 0 {
-		t.Errorf("Unexpected changes :%v", changes)
-	}
-
-	// Test simple change
-	file, err := os.Create(path.Join(rootfs, "test_change"))
-	if err != nil {
-		t.Fatal(err)
-	}
-	file.Close()
-
-	changes, err = filesystem.Changes()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(changes) != 1 {
-		t.Errorf("Unexpected changes :%v", changes)
-	}
-	if changes[0].Path != "/test_change" || changes[0].Kind != ChangeAdd {
-		t.Errorf("Unexpected changes :%v", changes)
-	}
-
-	// Test subdirectory change
-	if err := os.Mkdir(path.Join(rootfs, "sub_change"), 0700); err != nil {
-		t.Fatal(err)
-	}
-
-	file, err = os.Create(path.Join(rootfs, "sub_change", "test"))
-	if err != nil {
-		t.Fatal(err)
-	}
-	file.Close()
-
-	changes, err = filesystem.Changes()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(changes) != 3 {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-	if changes[0].Path != "/sub_change" || changes[0].Kind != ChangeAdd || changes[1].Path != "/sub_change/test" || changes[1].Kind != ChangeAdd {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-
-	// Test permission change
-	if err := os.Chmod(path.Join(rootfs, "root"), 0000); err != nil {
-		t.Fatal(err)
-	}
-	changes, err = filesystem.Changes()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(changes) != 4 {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-	if changes[0].Path != "/root" || changes[0].Kind != ChangeModify {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-
-	// Test removal
-	if err := os.Remove(path.Join(rootfs, "etc", "passwd")); err != nil {
-		t.Fatal(err)
-	}
-	changes, err = filesystem.Changes()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(changes) != 6 {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-	if changes[0].Path != "/etc" || changes[0].Kind != ChangeModify || changes[1].Path != "/etc/passwd" || changes[1].Kind != ChangeDelete {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-
-	// Test sub-directory removal
-	if err := os.Remove(path.Join(rootfs, "usr", "bin", "sudo")); err != nil {
-		t.Fatal(err)
-	}
-	changes, err = filesystem.Changes()
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(changes) != 8 {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-	if changes[6].Path != "/usr/bin" || changes[6].Kind != ChangeModify || changes[7].Path != "/usr/bin/sudo" || changes[7].Kind != ChangeDelete {
-		t.Errorf("Unexpected changes: %v", changes)
-	}
-}

+ 1 - 1
fs/layers.go

@@ -9,7 +9,7 @@ import (
 	"os"
 	"os"
 	"os/exec"
 	"os/exec"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/future"
+	"../future"
 )
 )
 
 
 type LayerStore struct {
 type LayerStore struct {

+ 32 - 5
fs/store.go

@@ -25,8 +25,12 @@ type Store struct {
 type Archive io.Reader
 type Archive io.Reader
 
 
 func New(root string) (*Store, error) {
 func New(root string) (*Store, error) {
+	isNewStore := true
+
 	if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) {
 	if err := os.Mkdir(root, 0700); err != nil && !os.IsExist(err) {
 		return nil, err
 		return nil, err
+	} else if os.IsExist(err) {
+		isNewStore = false
 	}
 	}
 	db, err := sql.Open("sqlite3", path.Join(root, "db"))
 	db, err := sql.Open("sqlite3", path.Join(root, "db"))
 	if err != nil {
 	if err != nil {
@@ -37,9 +41,12 @@ func New(root string) (*Store, error) {
 	orm.AddTableWithName(Path{}, "paths").SetKeys(false, "Path", "Image")
 	orm.AddTableWithName(Path{}, "paths").SetKeys(false, "Path", "Image")
 	orm.AddTableWithName(Mountpoint{}, "mountpoints").SetKeys(false, "Root")
 	orm.AddTableWithName(Mountpoint{}, "mountpoints").SetKeys(false, "Root")
 	orm.AddTableWithName(Tag{}, "tags").SetKeys(false, "TagName")
 	orm.AddTableWithName(Tag{}, "tags").SetKeys(false, "TagName")
-	if err := orm.CreateTables(); err != nil {
-		return nil, err
+	if isNewStore {
+		if err := orm.CreateTables(); err != nil {
+			return nil, err
+		}
 	}
 	}
+
 	layers, err := NewLayerStore(path.Join(root, "layers"))
 	layers, err := NewLayerStore(path.Join(root, "layers"))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -100,7 +107,9 @@ func (store *Store) Get(id string) (*Image, error) {
 	if img == nil {
 	if img == nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return img.(*Image), err
+	res := img.(*Image)
+	res.store = store
+	return res, err
 }
 }
 
 
 func (store *Store) Create(layerData Archive, parent *Image, pth, comment string) (*Image, error) {
 func (store *Store) Create(layerData Archive, parent *Image, pth, comment string) (*Image, error) {
@@ -167,6 +176,7 @@ type Mountpoint struct {
 	Image string
 	Image string
 	Root  string
 	Root  string
 	Rw    string
 	Rw    string
+	Store *Store `db:"-"`
 }
 }
 
 
 func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) {
 func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) {
@@ -174,6 +184,7 @@ func (image *Image) Mountpoint(root, rw string) (*Mountpoint, error) {
 		Root:  path.Clean(root),
 		Root:  path.Clean(root),
 		Rw:    path.Clean(rw),
 		Rw:    path.Clean(rw),
 		Image: image.Id,
 		Image: image.Id,
+		Store: image.store,
 	}
 	}
 	if err := image.store.orm.Insert(mountpoint); err != nil {
 	if err := image.store.orm.Insert(mountpoint); err != nil {
 		return nil, err
 		return nil, err
@@ -252,6 +263,19 @@ func (image *Image) Mount(root, rw string) (*Mountpoint, error) {
 	return mountpoint, nil
 	return mountpoint, nil
 }
 }
 
 
+func (mp *Mountpoint) EnsureMounted() error {
+	if mp.Mounted() {
+		return nil
+	}
+	img, err := mp.Store.Get(mp.Image)
+	if err != nil {
+		return err
+	}
+
+	_, err = img.Mount(mp.Root, mp.Rw)
+	return err
+}
+
 func (mp *Mountpoint) createFolders() error {
 func (mp *Mountpoint) createFolders() error {
 	if err := os.Mkdir(mp.Root, 0755); err != nil && !os.IsExist(err) {
 	if err := os.Mkdir(mp.Root, 0755); err != nil && !os.IsExist(err) {
 		return err
 		return err
@@ -314,7 +338,8 @@ func (mp *Mountpoint) Deregister() error {
 		return errors.New("Mountpoint is currently mounted, can't deregister")
 		return errors.New("Mountpoint is currently mounted, can't deregister")
 	}
 	}
 
 
-	return errors.New("Not yet implemented")
+	_, err := mp.Store.orm.Delete(mp)
+	return err
 }
 }
 
 
 func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) {
 func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) {
@@ -325,7 +350,9 @@ func (image *Image) fetchMountpoint(root, rw string) (*Mountpoint, error) {
 		return nil, nil
 		return nil, nil
 	}
 	}
 
 
-	return res[0].(*Mountpoint), nil
+	mp := res[0].(*Mountpoint)
+	mp.Store = image.store
+	return mp, nil
 }
 }
 
 
 func (store *Store) AddTag(imageId, tagName string) error {
 func (store *Store) AddTag(imageId, tagName string) error {

+ 1 - 1
fs/store_test.go

@@ -3,7 +3,7 @@ package fs
 import (
 import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"github.com/dotcloud/docker/fake"
+	"../fake"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
 	"testing"
 	"testing"

+ 1 - 1
lxc_template.go

@@ -22,7 +22,7 @@ lxc.utsname = {{.Id}}
 #lxc.network.ipv4 = {ip_address}/{ip_prefix_len}
 #lxc.network.ipv4 = {ip_address}/{ip_prefix_len}
 
 
 # root filesystem
 # root filesystem
-{{$ROOTFS := .Filesystem.RootFS}}
+{{$ROOTFS := .Mountpoint.Root}}
 lxc.rootfs = {{$ROOTFS}}
 lxc.rootfs = {{$ROOTFS}}
 
 
 # use a dedicated pts for the container (and limit the number of pseudo terminal
 # use a dedicated pts for the container (and limit the number of pseudo terminal