1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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

This commit is contained in:
Solomon Hykes 2013-11-07 20:34:01 +00:00
parent a63ff8da46
commit f2bab1557c
7 changed files with 162 additions and 158 deletions

View file

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

View file

@ -3,10 +3,8 @@ package docker
import ( import (
"fmt" "fmt"
"github.com/dotcloud/docker/archive" "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/utils"
"github.com/dotcloud/docker/graphdriver"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@ -25,7 +23,7 @@ type Graph struct {
// NewGraph instantiates a new graph at the given root path in the filesystem. // NewGraph instantiates a new graph at the given root path in the filesystem.
// `root` will be created if it doesn't exist. // `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) abspath, err := filepath.Abs(root)
if err != nil { if err != nil {
return nil, err return nil, err
@ -35,10 +33,6 @@ func NewGraph(root string) (*Graph, error) {
return nil, err return nil, err
} }
driver, err := graphdriver.New(root)
if err != nil {
return nil, err
}
graph := &Graph{ graph := &Graph{
Root: abspath, Root: abspath,
@ -89,16 +83,22 @@ func (graph *Graph) Get(name string) (*Image, error) {
if err != nil { if err != nil {
return nil, err 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 { if img.ID != id {
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID) return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
} }
img.graph = graph img.graph = graph
if img.Size == 0 { if img.Size == 0 {
root, err := img.root() size, err := utils.TreeSize(rootfs)
if err != nil { 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 return nil, err
} }
} }
@ -142,7 +142,17 @@ func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Im
if err != nil { if err != nil {
return fmt.Errorf("Mktemp failed: %s", err) 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 return err
} }
// Commit // Commit
@ -163,7 +173,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
if err != nil { if err != nil {
return nil, err return nil, err
} }
tmp, err := graph.tmp() tmp, err := graph.Mktemp("")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -171,7 +181,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression,
if err != nil { if err != nil {
return nil, err 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. // 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 == "" { if id == "" {
id = GenerateID() id = GenerateID()
} }
tmp, err := graph.tmp() // 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)
}
dir, err := graph.driver.Get(id)
if err != nil { if err != nil {
return "", fmt.Errorf("Couldn't create temp: %s", err) return "", fmt.Errorf("Driver %s couldn't get temporary directory %s: %s", graph.driver, id, err)
} }
if tmp.Exists(id) { return dir, nil
return "", fmt.Errorf("Image %s already exists", id)
}
return tmp.imageRoot(id), 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 // for bind-mounting dockerinit into the container. The mountpoint is simply an
// empty file at /.dockerinit // empty file at /.dockerinit
// //
// This extra layer is used by all containers as the top-most ro layer. It protects // 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. // the container from unwanted side-effects on the rw layer.
func (graph *Graph) getDockerInitLayer() (string, error) { func setupInitLayer(initLayer 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
}
for pth, typ := range map[string]string{ for pth, typ := range map[string]string{
"/dev/pts": "dir", "/dev/pts": "dir",
"/dev/shm": "dir", "/dev/shm": "dir",
@ -225,32 +227,27 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
switch typ { switch typ {
case "dir": case "dir":
if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil { if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
return "", err return err
} }
case "file": case "file":
if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil { 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 { if f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755); err != nil {
return "", err return err
} else { } else {
f.Close() f.Close()
} }
} }
} else { } else {
return "", err return err
} }
} }
} }
// Layer is ready to use, if it wasn't before. // Layer is ready to use, if it wasn't before.
return initLayer, nil return 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"))
} }
// Check if given error is "not empty". // Check if given error is "not empty".
@ -282,6 +279,9 @@ func (graph *Graph) Delete(name string) error {
if err != nil { if err != nil {
return err return err
} }
// Remove rootfs data from the driver
graph.driver.Remove(id)
// Remove the trashed image directory
return os.RemoveAll(tmp) return os.RemoveAll(tmp)
} }

View file

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

View file

@ -11,7 +11,6 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"time" "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 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 // Check that root doesn't already exist
if _, err := os.Stat(root); err == nil { if _, err := os.Stat(root); err == nil {
return fmt.Errorf("Image %s already exists", img.ID) 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 return err
} }
// Store the layer // Store the layer
layer := layerPath(root) layer := rootfs
if err := os.MkdirAll(layer, 0755); err != nil { if err := os.MkdirAll(layer, 0755); err != nil {
return err return err
} }
@ -106,29 +96,25 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str
return err return err
} }
} }
// Compute and save the size of the rootfs
return StoreSize(img, root) size, err := utils.TreeSize(rootfs)
} if err != nil {
return fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
func StoreSize(img *Image, root string) error { }
layer := layerPath(root) img.Size = size
if err := img.SaveSize(root); err != nil {
var totalSize int64 = 0 return err
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
} }
return nil return nil
} }
func layerPath(root string) string { // SaveSize stores the current `size` value of `img` in the directory `root`.
return path.Join(root, "layer") 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 { 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. // TarLayer returns a tar archive of the image's filesystem layer.
func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, error) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -216,15 +205,6 @@ func (img *Image) root() (string, error) {
return img.graph.imageRoot(img.ID), nil 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 { func (img *Image) getParentsSize(size int64) int64 {
parentImage, err := img.GetParent() parentImage, err := img.GetParent()
if err != nil || parentImage == nil { if err != nil || parentImage == nil {

View file

@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"github.com/dotcloud/docker/gograph" "github.com/dotcloud/docker/gograph"
"github.com/dotcloud/docker/utils" "github.com/dotcloud/docker/utils"
"github.com/dotcloud/docker/graphdriver"
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
@ -38,6 +39,7 @@ type Runtime struct {
srv *Server srv *Server
config *DaemonConfig config *DaemonConfig
containerGraph *gograph.Database containerGraph *gograph.Database
driver graphdriver.Driver
} }
// List returns an array of all containers registered in the runtime. // List returns an array of all containers registered in the runtime.
@ -113,6 +115,13 @@ func (runtime *Runtime) Register(container *Container) error {
return err 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 // init the wait lock
container.waitLock = make(chan struct{}) container.waitLock = make(chan struct{})
@ -200,12 +209,8 @@ func (runtime *Runtime) Destroy(container *Container) error {
return err return err
} }
if mounted, err := container.Mounted(); err != nil { if err := runtime.driver.Remove(container.ID); err != nil {
return err return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, 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.containerGraph.Purge(container.ID); err != nil { 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 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() resolvConf, err := utils.GetResolvConf()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -568,17 +588,23 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
} }
func NewRuntimeFromDirectory(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") runtimeRepo := path.Join(config.Root, "containers")
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) { if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
return nil, err return nil, err
} }
g, err := NewGraph(path.Join(config.Root, "graph")) g, err := NewGraph(path.Join(config.Root, "graph"), driver)
if err != nil { if err != nil {
return nil, err return nil, err
} }
volumes, err := NewGraph(path.Join(config.Root, "volumes")) volumes, err := NewGraph(path.Join(config.Root, "volumes"), driver)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -612,6 +638,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
return nil, err return nil, err
} }
runtime := &Runtime{ runtime := &Runtime{
repository: runtimeRepo, repository: runtimeRepo,
containers: list.New(), containers: list.New(),
@ -623,6 +650,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
volumes: volumes, volumes: volumes,
config: config, config: config,
containerGraph: graph, containerGraph: graph,
driver: driver,
} }
if err := runtime.restore(); err != nil { if err := runtime.restore(); err != nil {
@ -633,40 +661,32 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
func (runtime *Runtime) Close() error { func (runtime *Runtime) Close() error {
runtime.networkManager.Close() runtime.networkManager.Close()
runtime.driver.Cleanup()
return runtime.containerGraph.Close() return runtime.containerGraph.Close()
} }
func (runtime *Runtime) Mount(container *Container) error { func (runtime *Runtime) Mount(container *Container) error {
if mounted, err := runtime.Mounted(container); err != nil { dir, err := runtime.driver.Get(container.ID)
return err
} else if mounted {
return fmt.Errorf("%s is already mounted", container.RootfsPath())
}
img, err := container.GetImage()
if err != nil { if err != nil {
return err return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
} }
return runtime.graph.driver.Mount(img, container.root) 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 nil
} }
func (runtime *Runtime) Unmount(container *Container) error { func (runtime *Runtime) Unmount(container *Container) error {
return runtime.graph.driver.Unmount(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) Mounted(container *Container) (bool, error) { func (runtime *Runtime) Changes(container *Container) ([]graphdriver.Change, error) {
return runtime.graph.driver.Mounted(container.root) return runtime.driver.Changes(container.ID)
}
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, // History is a convenience type for storing a list of containers,

View file

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

15
utils/fs.go Normal file
View file

@ -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
}