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:
parent
a63ff8da46
commit
f2bab1557c
7 changed files with 162 additions and 158 deletions
55
container.go
55
container.go
|
@ -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 {
|
||||||
|
|
86
graph.go
86
graph.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
60
image.go
60
image.go
|
@ -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 {
|
||||||
|
|
82
runtime.go
82
runtime.go
|
@ -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,
|
||||||
|
|
|
@ -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
15
utils/fs.go
Normal 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
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue