Browse Source

Simplify graphdriver interface: Create, Get. No more external mounting or Dir/Image interface

Solomon Hykes 11 years ago
parent
commit
f2bab1557c
7 changed files with 162 additions and 158 deletions
  1. 22 33
      container.go
  2. 43 43
      graph.go
  3. 9 10
      graphdriver/driver.go
  4. 20 40
      image.go
  5. 51 31
      runtime.go
  6. 2 1
      server.go
  7. 15 0
      utils/fs.go

+ 22 - 33
container.go

@@ -9,6 +9,7 @@ import (
 	"github.com/dotcloud/docker/archive"
 	"github.com/dotcloud/docker/term"
 	"github.com/dotcloud/docker/utils"
+	"github.com/dotcloud/docker/graphdriver" // FIXME: graphdriver.Change is a placeholder for archive.Change
 	"github.com/kr/pty"
 	"io"
 	"io/ioutil"
@@ -25,7 +26,8 @@ import (
 )
 
 type Container struct {
-	root string
+	root string	// Path to the "home" of the container, including metadata.
+	rootfs string	// Path to the root filesystem of the container.
 
 	ID string
 
@@ -767,6 +769,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
 		}
 	}
 
+	volumesDriver := container.runtime.volumes.driver
 	// Create the requested volumes if they don't exist
 	for volPath := range container.Config.Volumes {
 		volPath = path.Clean(volPath)
@@ -790,9 +793,9 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
 			if err != nil {
 				return err
 			}
-			srcPath, err = c.layer()
+			srcPath, err = volumesDriver.Get(c.ID)
 			if err != nil {
-				return err
+				return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
 			}
 			srcRW = true // RW by default
 		}
@@ -1338,15 +1341,10 @@ func (container *Container) Resize(h, w int) error {
 }
 
 func (container *Container) ExportRw() (archive.Archive, error) {
-	return archive.Tar(container.rwPath(), archive.Uncompressed)
-}
-
-func (container *Container) RwChecksum() (string, error) {
-	rwData, err := archive.Tar(container.rwPath(), archive.Xz)
-	if err != nil {
-		return "", err
+	if container.runtime == nil {
+		return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
 	}
-	return utils.HashData(rwData)
+	return container.runtime.driver.Diff(container.ID)
 }
 
 func (container *Container) Export() (archive.Archive, error) {
@@ -1372,11 +1370,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
 }
 
 func (container *Container) EnsureMounted() error {
-	if mounted, err := container.Mounted(); err != nil {
-		return err
-	} else if mounted {
-		return nil
-	}
+	// FIXME: EnsureMounted is deprecated because drivers are now responsible
+	// for re-entrant mounting in their Get() method.
 	return container.Mount()
 }
 
@@ -1384,7 +1379,7 @@ func (container *Container) Mount() error {
 	return container.runtime.Mount(container)
 }
 
-func (container *Container) Changes() ([]Change, error) {
+func (container *Container) Changes() ([]graphdriver.Change, error) {
 	return container.runtime.Changes(container)
 }
 
@@ -1395,10 +1390,6 @@ func (container *Container) GetImage() (*Image, error) {
 	return container.runtime.graph.Get(container.Image)
 }
 
-func (container *Container) Mounted() (bool, error) {
-	return container.runtime.Mounted(container)
-}
-
 func (container *Container) Unmount() error {
 	return container.runtime.Unmount(container)
 }
@@ -1437,11 +1428,7 @@ func (container *Container) lxcConfigPath() string {
 
 // This method must be exported to be used from the lxc template
 func (container *Container) RootfsPath() string {
-	return path.Join(container.root, "rootfs")
-}
-
-func (container *Container) rwPath() string {
-	return path.Join(container.root, "rw")
+	return container.rootfs
 }
 
 func validateID(id string) error {
@@ -1455,18 +1442,20 @@ func validateID(id string) error {
 func (container *Container) GetSize() (int64, int64) {
 	var sizeRw, sizeRootfs int64
 
-	filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
-		if fileInfo != nil {
-			sizeRw += fileInfo.Size()
-		}
-		return nil
-	})
+	driver := container.runtime.driver
+	sizeRw, err := driver.DiffSize(container.ID)
+	if err != nil {
+		utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
+		// FIXME: GetSize should return an error. Not changing it now in case
+		// there is a side-effect.
+		sizeRw = -1
+	}
 
 	if err := container.EnsureMounted(); err != nil {
 		utils.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
 		return sizeRw, sizeRootfs
 	}
-	_, err := os.Stat(container.RootfsPath())
+	_, err = os.Stat(container.RootfsPath())
 	if err == nil {
 		filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
 			if fileInfo != nil {

+ 43 - 43
graph.go

@@ -3,10 +3,8 @@ package docker
 import (
 	"fmt"
 	"github.com/dotcloud/docker/archive"
-	_ "github.com/dotcloud/docker/aufs"
-	_ "github.com/dotcloud/docker/devmapper"
-	"github.com/dotcloud/docker/graphdriver"
 	"github.com/dotcloud/docker/utils"
+	"github.com/dotcloud/docker/graphdriver"
 	"io"
 	"io/ioutil"
 	"os"
@@ -25,7 +23,7 @@ type Graph struct {
 
 // 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
@@ -35,10 +33,6 @@ func NewGraph(root string) (*Graph, error) {
 		return nil, err
 	}
 
-	driver, err := graphdriver.New(root)
-	if err != nil {
-		return nil, err
-	}
 
 	graph := &Graph{
 		Root:    abspath,
@@ -89,16 +83,22 @@ func (graph *Graph) Get(name string) (*Image, error) {
 	if err != nil {
 		return nil, err
 	}
+	// Check that the filesystem layer exists
+	rootfs, err := graph.driver.Get(img.ID)
+	if err != nil {
+		return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
+	}
 	if img.ID != id {
 		return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
 	}
 	img.graph = graph
 	if img.Size == 0 {
-		root, err := img.root()
+		size, err := utils.TreeSize(rootfs)
 		if err != nil {
-			return nil, err
+			return nil, fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
 		}
-		if err := StoreSize(img, root); err != nil {
+		img.Size = size
+		if err := img.SaveSize(graph.imageRoot(id)); err != nil {
 			return nil, err
 		}
 	}
@@ -142,7 +142,17 @@ func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Im
 	if err != nil {
 		return fmt.Errorf("Mktemp failed: %s", err)
 	}
-	if err := StoreImage(img, jsonData, layerData, tmp); err != nil {
+
+	// Create root filesystem in the driver
+	if err := graph.driver.Create(img.ID, img.Parent); err != nil {
+		return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
+	}
+	// Mount the root filesystem so we can apply the diff/layer
+	rootfs, err := graph.driver.Get(img.ID)
+	if err != nil {
+		return fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
+	}
+	if err := StoreImage(img, jsonData, layerData, tmp, rootfs); err != nil {
 		return err
 	}
 	// Commit
@@ -163,7 +173,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
 	if err != nil {
 		return nil, err
 	}
-	tmp, err := graph.tmp()
+	tmp, err := graph.Mktemp("")
 	if err != nil {
 		return nil, err
 	}
@@ -171,7 +181,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
 	if err != nil {
 		return nil, err
 	}
-	return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
+	return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp)
 }
 
 // Mktemp creates a temporary sub-directory inside the graph's filesystem.
@@ -179,34 +189,26 @@ func (graph *Graph) Mktemp(id string) (string, error) {
 	if id == "" {
 		id = GenerateID()
 	}
-	tmp, err := graph.tmp()
-	if err != nil {
-		return "", fmt.Errorf("Couldn't create temp: %s", err)
+	// FIXME: use a separate "tmp" driver instead of the regular driver,
+	// to allow for removal at cleanup.
+	// Right now temp directories are never removed!
+	if err := graph.driver.Create(id, ""); err != nil {
+		return "", fmt.Errorf("Driver %s couldn't create temporary directory %s: %s", graph.driver, id, err)
 	}
-	if tmp.Exists(id) {
-		return "", fmt.Errorf("Image %s already exists", id)
+	dir, err := graph.driver.Get(id)
+	if err != nil {
+		return "", fmt.Errorf("Driver %s couldn't get temporary directory %s: %s", graph.driver, id, err)
 	}
-	return tmp.imageRoot(id), nil
+	return dir, nil
 }
 
-// getDockerInitLayer returns the path of a layer containing a mountpoint suitable
+// setupInitLayer populates a directory with mountpoints suitable
 // for bind-mounting dockerinit into the container. The mountpoint is simply an
 // empty file at /.dockerinit
 //
 // This extra layer is used by all containers as the top-most ro layer. It protects
 // the container from unwanted side-effects on the rw layer.
-func (graph *Graph) getDockerInitLayer() (string, error) {
-	tmp, err := graph.tmp()
-	if err != nil {
-		return "", err
-	}
-	initLayer := tmp.imageRoot("_dockerinit")
-	if err := os.Mkdir(initLayer, 0755); err != nil && !os.IsExist(err) {
-		// If directory already existed, keep going.
-		// For all other errors, abort.
-		return "", err
-	}
-
+func setupInitLayer(initLayer string) error {
 	for pth, typ := range map[string]string{
 		"/dev/pts":         "dir",
 		"/dev/shm":         "dir",
@@ -225,32 +227,27 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
 				switch typ {
 				case "dir":
 					if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
-						return "", err
+						return err
 					}
 				case "file":
 					if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
-						return "", err
+						return err
 					}
 
 					if f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755); err != nil {
-						return "", err
+						return err
 					} else {
 						f.Close()
 					}
 				}
 			} else {
-				return "", err
+				return err
 			}
 		}
 	}
 
 	// Layer is ready to use, if it wasn't before.
-	return initLayer, nil
-}
-
-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 nil
 }
 
 // Check if given error is "not empty".
@@ -282,6 +279,9 @@ func (graph *Graph) Delete(name string) error {
 	if err != nil {
 		return err
 	}
+	// Remove rootfs data from the driver
+	graph.driver.Remove(id)
+	// Remove the trashed image directory
 	return os.RemoveAll(tmp)
 }
 

+ 9 - 10
graphdriver/driver.go

@@ -7,21 +7,20 @@ import (
 
 type InitFunc func(root string) (Driver, error)
 
-type Dir interface {
-	ID() string
-	Path() string
-	Parent() (Dir, error)
+// FIXME: this is a temporary placeholder for archive.Change
+// (to be merged from master)
+type Change interface {
 }
 
 type Driver interface {
-	OnCreate(dir Dir, layer archive.Archive) error
-	OnRemove(dir Dir) error
+	Create(id, parent string) error
+	Remove(id string) error
 
-	OnMount(dir Dir, dest string) error
-	OnUnmount(dest string) error
-	Mounted(dest string) (bool, error)
+	Get(id string) (dir string, err error)
 
-	Layer(dir Dir, dest string) (archive.Archive, error)
+	Diff(id string) (archive.Archive, error)
+	DiffSize(id string) (bytes int64, err error)
+	Changes(id string) ([]Change, error)
 
 	Cleanup() error
 }

+ 20 - 40
image.go

@@ -11,7 +11,6 @@ import (
 	"io/ioutil"
 	"os"
 	"path"
-	"path/filepath"
 	"strconv"
 	"strings"
 	"time"
@@ -59,19 +58,10 @@ func LoadImage(root string) (*Image, error) {
 		}
 	}
 
-	// Check that the filesystem layer exists
-	if stat, err := os.Stat(layerPath(root)); err != nil {
-		if os.IsNotExist(err) {
-			return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
-		}
-		return nil, err
-	} else if !stat.IsDir() {
-		return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
-	}
 	return img, nil
 }
 
-func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root string) error {
+func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root, rootfs string) error {
 	// Check that root doesn't already exist
 	if _, err := os.Stat(root); err == nil {
 		return fmt.Errorf("Image %s already exists", img.ID)
@@ -79,7 +69,7 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str
 		return err
 	}
 	// Store the layer
-	layer := layerPath(root)
+	layer := rootfs
 	if err := os.MkdirAll(layer, 0755); err != nil {
 		return err
 	}
@@ -106,29 +96,25 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str
 			return err
 		}
 	}
-
-	return StoreSize(img, root)
-}
-
-func StoreSize(img *Image, root string) error {
-	layer := layerPath(root)
-
-	var totalSize int64 = 0
-	filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error {
-		totalSize += fileInfo.Size()
-		return nil
-	})
-	img.Size = totalSize
-
-	if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(totalSize))), 0600); err != nil {
-		return nil
+	// Compute and save the size of the rootfs
+	size, err := utils.TreeSize(rootfs)
+	if err != nil {
+		return fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
+	}
+	img.Size = size
+	if err := img.SaveSize(root); err != nil {
+		return err
 	}
 
 	return nil
 }
 
-func layerPath(root string) string {
-	return path.Join(root, "layer")
+// SaveSize stores the current `size` value of `img` in the directory `root`.
+func (img *Image) SaveSize(root string) error {
+	if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil {
+		return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err)
+	}
+	return nil
 }
 
 func jsonPath(root string) string {
@@ -137,7 +123,10 @@ func jsonPath(root string) string {
 
 // 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()
+	if image.graph == nil {
+		return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", image.ID)
+	}
+	layerPath, err := image.graph.driver.Get(image.ID)
 	if err != nil {
 		return nil, err
 	}
@@ -216,15 +205,6 @@ func (img *Image) root() (string, error) {
 	return img.graph.imageRoot(img.ID), nil
 }
 
-// Return the path of an image's layer
-func (img *Image) layer() (string, error) {
-	root, err := img.root()
-	if err != nil {
-		return "", err
-	}
-	return layerPath(root), nil
-}
-
 func (img *Image) getParentsSize(size int64) int64 {
 	parentImage, err := img.GetParent()
 	if err != nil || parentImage == nil {

+ 51 - 31
runtime.go

@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"github.com/dotcloud/docker/gograph"
 	"github.com/dotcloud/docker/utils"
+	"github.com/dotcloud/docker/graphdriver"
 	"io"
 	"io/ioutil"
 	"log"
@@ -38,6 +39,7 @@ type Runtime struct {
 	srv            *Server
 	config         *DaemonConfig
 	containerGraph *gograph.Database
+	driver         graphdriver.Driver
 }
 
 // List returns an array of all containers registered in the runtime.
@@ -113,6 +115,13 @@ func (runtime *Runtime) Register(container *Container) error {
 		return err
 	}
 
+	// Get the root filesystem from the driver
+	rootfs, err := runtime.driver.Get(container.ID)
+	if err != nil {
+		return fmt.Errorf("Error getting container filesystem %s from driver %s: %s", container.ID, runtime.driver, err)
+	}
+	container.rootfs = rootfs
+
 	// init the wait lock
 	container.waitLock = make(chan struct{})
 
@@ -200,12 +209,8 @@ func (runtime *Runtime) Destroy(container *Container) error {
 		return err
 	}
 
-	if mounted, err := container.Mounted(); err != nil {
-		return err
-	} else if mounted {
-		if err := container.Unmount(); err != nil {
-			return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
-		}
+	if err := runtime.driver.Remove(container.ID); err != nil {
+		return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
 	}
 
 	if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
@@ -413,6 +418,21 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
 		return nil, nil, err
 	}
 
+	initID := fmt.Sprintf("%s-init", container.ID)
+	if err := runtime.driver.Create(initID, img.ID); err != nil {
+		return nil, nil, err
+	}
+	initPath, err := runtime.driver.Get(initID)
+	if err != nil {
+		return nil, nil, err
+	}
+	if err := setupInitLayer(initPath); err != nil {
+		return nil, nil, err
+	}
+
+	if err := runtime.driver.Create(container.ID, initID); err != nil {
+		return nil, nil, err
+	}
 	resolvConf, err := utils.GetResolvConf()
 	if err != nil {
 		return nil, nil, err
@@ -568,17 +588,23 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
 }
 
 func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
+	// Load storage driver
+	driver, err := graphdriver.New(config.Root)
+	if err != nil {
+		return nil, err
+	}
+
 	runtimeRepo := path.Join(config.Root, "containers")
 
 	if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
 		return nil, err
 	}
 
-	g, err := NewGraph(path.Join(config.Root, "graph"))
+	g, err := NewGraph(path.Join(config.Root, "graph"), driver)
 	if err != nil {
 		return nil, err
 	}
-	volumes, err := NewGraph(path.Join(config.Root, "volumes"))
+	volumes, err := NewGraph(path.Join(config.Root, "volumes"), driver)
 	if err != nil {
 		return nil, err
 	}
@@ -612,6 +638,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
 		return nil, err
 	}
 
+
 	runtime := &Runtime{
 		repository:     runtimeRepo,
 		containers:     list.New(),
@@ -623,6 +650,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
 		volumes:        volumes,
 		config:         config,
 		containerGraph: graph,
+		driver:		driver,
 	}
 
 	if err := runtime.restore(); err != nil {
@@ -633,40 +661,32 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
 
 func (runtime *Runtime) Close() error {
 	runtime.networkManager.Close()
+	runtime.driver.Cleanup()
 	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()
+	dir, err := runtime.driver.Get(container.ID)
 	if err != nil {
-		return err
+		return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
+	}
+	if container.rootfs == "" {
+		container.rootfs = dir
+	} else if container.rootfs != dir {
+		return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
+			runtime.driver, container.ID, container.rootfs, dir)
 	}
-	return runtime.graph.driver.Mount(img, container.root)
+	return nil
 }
 
 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)
+	// FIXME: Unmount is deprecated because drivers are responsible for mounting
+	// and unmounting when necessary. Use driver.Remove() instead.
+	return nil
 }
 
-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())
+func (runtime *Runtime) Changes(container *Container) ([]graphdriver.Change, error) {
+	return runtime.driver.Changes(container.ID)
 }
 
 // History is a convenience type for storing a list of containers,

+ 2 - 1
server.go

@@ -11,6 +11,7 @@ import (
 	"github.com/dotcloud/docker/gograph"
 	"github.com/dotcloud/docker/registry"
 	"github.com/dotcloud/docker/utils"
+	"github.com/dotcloud/docker/graphdriver" // FIXME: graphdriver.Change is a placeholder for archive.Change
 	"io"
 	"io/ioutil"
 	"log"
@@ -430,7 +431,7 @@ func (srv *Server) ContainerTop(name, ps_args string) (*APITop, error) {
 	return nil, fmt.Errorf("No such container: %s", name)
 }
 
-func (srv *Server) ContainerChanges(name string) ([]Change, error) {
+func (srv *Server) ContainerChanges(name string) ([]graphdriver.Change, error) {
 	if container := srv.runtime.Get(name); container != nil {
 		return container.Changes()
 	}

+ 15 - 0
utils/fs.go

@@ -0,0 +1,15 @@
+package utils
+
+import (
+	"os"
+	"path/filepath"
+)
+
+// TreeSize walks a directory tree and returns its total size in bytes.
+func TreeSize(dir string) (size int64, err error) {
+	err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error {
+		size += fileInfo.Size()
+		return nil
+	})
+	return
+}