diff --git a/aufs/aufs.go b/aufs/aufs.go new file mode 100644 index 0000000000..528a77f59f --- /dev/null +++ b/aufs/aufs.go @@ -0,0 +1,78 @@ +package aufs + +import ( + "fmt" + "github.com/dotcloud/docker/graphdriver" + "log" + "os" + "os/exec" + "path" +) + +type AufsDriver struct { +} + +// New returns a new AUFS driver. +// An error is returned if AUFS is not supported. +func New() (*AufsDriver, error) { + return &AufsDriver{}, nil +} + +func (a *AufsDriver) Mount(img graphdriver.Image, root string) error { + layers, err := img.Layers() + if err != nil { + return err + } + + target := path.Join(root, "rootfs") + rw := path.Join(root, "rw") + + // Create the target directories if they don't exist + if err := os.Mkdir(target, 0755); err != nil && !os.IsExist(err) { + return err + } + if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) { + return err + } + if err := a.aufsMount(layers, rw, target); err != nil { + return err + } + return nil +} + +func (a *AufsDriver) Unmount(root string) error { + target := path.Join(root, "rootfs") + if _, err := os.Stat(target); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + return Unmount(target) +} + +func (a *AufsDriver) Mounted(root string) (bool, error) { + return Mounted(path.Join(root, "rootfs")) +} + +func (a *AufsDriver) aufsMount(ro []string, rw, target string) error { + rwBranch := fmt.Sprintf("%v=rw", rw) + roBranches := "" + for _, layer := range ro { + roBranches += fmt.Sprintf("%v=ro+wh:", layer) + } + branches := fmt.Sprintf("br:%v:%v,xino=/dev/shm/aufs.xino", rwBranch, roBranches) + + //if error, try to load aufs kernel module + if err := mount("none", target, "aufs", 0, branches); err != nil { + log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...") + if err := exec.Command("modprobe", "aufs").Run(); err != nil { + return fmt.Errorf("Unable to load the AUFS module") + } + log.Printf("...module loaded.") + if err := mount("none", target, "aufs", 0, branches); err != nil { + return fmt.Errorf("Unable to mount using aufs") + } + } + return nil +} diff --git a/mount.go b/aufs/mount.go similarity index 98% rename from mount.go rename to aufs/mount.go index f4a4dfbae1..15cb2da93f 100644 --- a/mount.go +++ b/aufs/mount.go @@ -1,4 +1,4 @@ -package docker +package aufs import ( "fmt" diff --git a/mount_darwin.go b/aufs/mount_darwin.go similarity index 92% rename from mount_darwin.go rename to aufs/mount_darwin.go index aeac78cda5..ce448036f2 100644 --- a/mount_darwin.go +++ b/aufs/mount_darwin.go @@ -1,4 +1,4 @@ -package docker +package aufs import "errors" diff --git a/mount_linux.go b/aufs/mount_linux.go similarity index 92% rename from mount_linux.go rename to aufs/mount_linux.go index 0efb253003..7122b73cea 100644 --- a/mount_linux.go +++ b/aufs/mount_linux.go @@ -1,4 +1,4 @@ -package docker +package aufs import "syscall" diff --git a/container.go b/container.go index fe29e7de90..6994c3b460 100644 --- a/container.go +++ b/container.go @@ -1382,19 +1382,11 @@ func (container *Container) EnsureMounted() error { } func (container *Container) Mount() error { - image, err := container.GetImage() - if err != nil { - return err - } - return image.Mount(container.RootfsPath(), container.rwPath()) + return container.runtime.Mount(container) } func (container *Container) Changes() ([]Change, error) { - image, err := container.GetImage() - if err != nil { - return nil, err - } - return image.Changes(container.rwPath()) + return container.runtime.Changes(container) } func (container *Container) GetImage() (*Image, error) { @@ -1405,17 +1397,11 @@ func (container *Container) GetImage() (*Image, error) { } func (container *Container) Mounted() (bool, error) { - return Mounted(container.RootfsPath()) + return container.runtime.Mounted(container) } func (container *Container) Unmount() error { - if _, err := os.Stat(container.RootfsPath()); err != nil { - if os.IsNotExist(err) { - return nil - } - return err - } - return Unmount(container.RootfsPath()) + return container.runtime.Unmount(container) } // ShortID returns a shorthand version of the container's id for convenience. diff --git a/graph.go b/graph.go index f10c8e1bd9..449c3871e8 100644 --- a/graph.go +++ b/graph.go @@ -3,6 +3,7 @@ package docker import ( "fmt" "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/utils" "io" "io/ioutil" @@ -17,11 +18,12 @@ import ( type Graph struct { Root string idIndex *utils.TruncIndex + driver graphdriver.Driver } // NewGraph instantiates a new graph at the given root path in the filesystem. // `root` will be created if it doesn't exist. -func NewGraph(root string) (*Graph, error) { +func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) { abspath, err := filepath.Abs(root) if err != nil { return nil, err @@ -33,6 +35,7 @@ func NewGraph(root string) (*Graph, error) { graph := &Graph{ Root: abspath, idIndex: utils.NewTruncIndex(), + driver: driver, } if err := graph.restore(); err != nil { return nil, err @@ -239,7 +242,7 @@ func (graph *Graph) getDockerInitLayer() (string, error) { func (graph *Graph) tmp() (*Graph, error) { // Changed to _tmp from :tmp:, because it messed with ":" separators in aufs branch syntax... - return NewGraph(path.Join(graph.Root, "_tmp")) + return NewGraph(path.Join(graph.Root, "_tmp"), graph.driver) } // Check if given error is "not empty". diff --git a/graph_test.go b/graph_test.go index 1102129aba..7e306c815e 100644 --- a/graph_test.go +++ b/graph_test.go @@ -5,6 +5,7 @@ import ( "bytes" "errors" "github.com/dotcloud/docker/archive" + "github.com/dotcloud/docker/aufs" "github.com/dotcloud/docker/utils" "io" "io/ioutil" @@ -145,12 +146,12 @@ func TestMount(t *testing.T) { if err := os.MkdirAll(rw, 0700); err != nil { t.Fatal(err) } - if err := image.Mount(rootfs, rw); err != nil { + if err := graph.driver.Mount(image, tmp); err != nil { t.Fatal(err) } // FIXME: test for mount contents defer func() { - if err := Unmount(rootfs); err != nil { + if err := graph.driver.Unmount(tmp); err != nil { t.Error(err) } }() @@ -295,7 +296,11 @@ func tempGraph(t *testing.T) *Graph { if err != nil { t.Fatal(err) } - graph, err := NewGraph(tmp) + backend, err := aufs.New() + if err != nil { + t.Fatal(err) + } + graph, err := NewGraph(tmp, backend) if err != nil { t.Fatal(err) } diff --git a/graphdriver/driver.go b/graphdriver/driver.go new file mode 100644 index 0000000000..5570865e84 --- /dev/null +++ b/graphdriver/driver.go @@ -0,0 +1,16 @@ +package graphdriver + +type Image interface { + Layers() ([]string, error) +} + +type Driver interface { + // Create(img *Image) error + // Delete(img *Image) error + Mount(img Image, root string) error + Unmount(root string) error + Mounted(root string) (bool, error) + // UnmountAll(img *Image) error + // Changes(img *Image, dest string) ([]Change, error) + // Layer(img *Image, dest string) (Archive, error) +} diff --git a/image.go b/image.go index 94cccaac67..dc3eb3c92a 100644 --- a/image.go +++ b/image.go @@ -9,9 +9,7 @@ import ( "github.com/dotcloud/docker/utils" "io" "io/ioutil" - "log" "os" - "os/exec" "path" "path/filepath" "strconv" @@ -137,31 +135,6 @@ func jsonPath(root string) string { return path.Join(root, "json") } -func MountAUFS(ro []string, rw string, target string) error { - // FIXME: Now mount the layers - rwBranch := fmt.Sprintf("%v=rw", rw) - roBranches := "" - for _, layer := range ro { - roBranches += fmt.Sprintf("%v=ro+wh:", layer) - } - branches := fmt.Sprintf("br:%v:%v", rwBranch, roBranches) - - branches += ",xino=/dev/shm/aufs.xino" - - //if error, try to load aufs kernel module - if err := mount("none", target, "aufs", 0, branches); err != nil { - log.Printf("Kernel does not support AUFS, trying to load the AUFS module with modprobe...") - if err := exec.Command("modprobe", "aufs").Run(); err != nil { - return fmt.Errorf("Unable to load the AUFS module") - } - log.Printf("...module loaded.") - if err := mount("none", target, "aufs", 0, branches); err != nil { - return fmt.Errorf("Unable to mount using aufs") - } - } - return nil -} - // TarLayer returns a tar archive of the image's filesystem layer. func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, error) { layerPath, err := image.layer() @@ -171,37 +144,6 @@ func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, return archive.Tar(layerPath, compression) } -func (image *Image) Mount(root, rw string) error { - if mounted, err := Mounted(root); err != nil { - return err - } else if mounted { - return fmt.Errorf("%s is already mounted", root) - } - layers, err := image.layers() - if err != nil { - return err - } - // Create the target directories if they don't exist - if err := os.Mkdir(root, 0755); err != nil && !os.IsExist(err) { - return err - } - if err := os.Mkdir(rw, 0755); err != nil && !os.IsExist(err) { - return err - } - if err := MountAUFS(layers, rw, root); err != nil { - return err - } - return nil -} - -func (image *Image) Changes(rw string) ([]Change, error) { - layers, err := image.layers() - if err != nil { - return nil, err - } - return Changes(layers, rw) -} - func (image *Image) ShortID() string { return utils.TruncateID(image.ID) } @@ -244,7 +186,11 @@ func (img *Image) History() ([]*Image, error) { // layers returns all the filesystem layers needed to mount an image // FIXME: @shykes refactor this function with the new error handling // (I'll do it if I have time tonight, I focus on the rest) -func (img *Image) layers() ([]string, error) { +func (img *Image) Layers() ([]string, error) { + if img.graph == nil { + + return nil, fmt.Errorf("Can't lookup dockerinit layer of unregistered image") + } var list []string var e error if err := img.WalkHistory( @@ -266,7 +212,7 @@ func (img *Image) layers() ([]string, error) { } // Inject the dockerinit layer (empty place-holder for mount-binding dockerinit) - if dockerinitLayer, err := img.getDockerInitLayer(); err != nil { + if dockerinitLayer, err := img.graph.getDockerInitLayer(); err != nil { return nil, err } else { list = append([]string{dockerinitLayer}, list...) @@ -300,13 +246,6 @@ func (img *Image) GetParent() (*Image, error) { return img.graph.Get(img.Parent) } -func (img *Image) getDockerInitLayer() (string, error) { - if img.graph == nil { - return "", fmt.Errorf("Can't lookup dockerinit layer of unregistered image") - } - return img.graph.getDockerInitLayer() -} - func (img *Image) root() (string, error) { if img.graph == nil { return "", fmt.Errorf("Can't lookup root of unregistered image") diff --git a/runtime.go b/runtime.go index 6d5139390a..a040b5aa53 100644 --- a/runtime.go +++ b/runtime.go @@ -5,6 +5,7 @@ import ( "container/list" "database/sql" "fmt" + "github.com/dotcloud/docker/aufs" "github.com/dotcloud/docker/gograph" "github.com/dotcloud/docker/utils" "io" @@ -573,12 +574,16 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) { if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) { return nil, err } - - g, err := NewGraph(path.Join(config.Root, "graph")) + driver, err := aufs.New() if err != nil { return nil, err } - volumes, err := NewGraph(path.Join(config.Root, "volumes")) + + g, err := NewGraph(path.Join(config.Root, "graph"), driver) + if err != nil { + return nil, err + } + volumes, err := NewGraph(path.Join(config.Root, "volumes"), driver) if err != nil { return nil, err } @@ -636,6 +641,39 @@ func (runtime *Runtime) Close() error { return runtime.containerGraph.Close() } +func (runtime *Runtime) Mount(container *Container) error { + if mounted, err := runtime.Mounted(container); err != nil { + return err + } else if mounted { + return fmt.Errorf("%s is already mounted", container.RootfsPath()) + } + img, err := container.GetImage() + if err != nil { + return err + } + return runtime.graph.driver.Mount(img, container.root) +} + +func (runtime *Runtime) Unmount(container *Container) error { + return runtime.graph.driver.Unmount(container.root) +} + +func (runtime *Runtime) Mounted(container *Container) (bool, error) { + return runtime.graph.driver.Mounted(container.root) +} + +func (runtime *Runtime) Changes(container *Container) ([]Change, error) { + img, err := container.GetImage() + if err != nil { + return nil, err + } + layers, err := img.Layers() + if err != nil { + return nil, err + } + return Changes(layers, container.rwPath()) +} + // History is a convenience type for storing a list of containers, // ordered by creation date. type History []*Container