| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"github.com/dotcloud/docker/archive" |
| 10 | 10 |
"github.com/dotcloud/docker/term" |
| 11 | 11 |
"github.com/dotcloud/docker/utils" |
| 12 |
+ "github.com/dotcloud/docker/graphdriver" // FIXME: graphdriver.Change is a placeholder for archive.Change |
|
| 12 | 13 |
"github.com/kr/pty" |
| 13 | 14 |
"io" |
| 14 | 15 |
"io/ioutil" |
| ... | ... |
@@ -25,7 +26,8 @@ import ( |
| 25 | 25 |
) |
| 26 | 26 |
|
| 27 | 27 |
type Container struct {
|
| 28 |
- root string |
|
| 28 |
+ root string // Path to the "home" of the container, including metadata. |
|
| 29 |
+ rootfs string // Path to the root filesystem of the container. |
|
| 29 | 30 |
|
| 30 | 31 |
ID string |
| 31 | 32 |
|
| ... | ... |
@@ -767,6 +769,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 767 | 767 |
} |
| 768 | 768 |
} |
| 769 | 769 |
|
| 770 |
+ volumesDriver := container.runtime.volumes.driver |
|
| 770 | 771 |
// Create the requested volumes if they don't exist |
| 771 | 772 |
for volPath := range container.Config.Volumes {
|
| 772 | 773 |
volPath = path.Clean(volPath) |
| ... | ... |
@@ -790,9 +793,9 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
|
| 790 | 790 |
if err != nil {
|
| 791 | 791 |
return err |
| 792 | 792 |
} |
| 793 |
- srcPath, err = c.layer() |
|
| 793 |
+ srcPath, err = volumesDriver.Get(c.ID) |
|
| 794 | 794 |
if err != nil {
|
| 795 |
- return err |
|
| 795 |
+ return fmt.Errorf("Driver %s failed to get volume rootfs %s: %s", volumesDriver, c.ID, err)
|
|
| 796 | 796 |
} |
| 797 | 797 |
srcRW = true // RW by default |
| 798 | 798 |
} |
| ... | ... |
@@ -1338,15 +1341,10 @@ func (container *Container) Resize(h, w int) error {
|
| 1338 | 1338 |
} |
| 1339 | 1339 |
|
| 1340 | 1340 |
func (container *Container) ExportRw() (archive.Archive, error) {
|
| 1341 |
- return archive.Tar(container.rwPath(), archive.Uncompressed) |
|
| 1342 |
-} |
|
| 1343 |
- |
|
| 1344 |
-func (container *Container) RwChecksum() (string, error) {
|
|
| 1345 |
- rwData, err := archive.Tar(container.rwPath(), archive.Xz) |
|
| 1346 |
- if err != nil {
|
|
| 1347 |
- return "", err |
|
| 1341 |
+ if container.runtime == nil {
|
|
| 1342 |
+ return nil, fmt.Errorf("Can't load storage driver for unregistered container %s", container.ID)
|
|
| 1348 | 1343 |
} |
| 1349 |
- return utils.HashData(rwData) |
|
| 1344 |
+ return container.runtime.driver.Diff(container.ID) |
|
| 1350 | 1345 |
} |
| 1351 | 1346 |
|
| 1352 | 1347 |
func (container *Container) Export() (archive.Archive, error) {
|
| ... | ... |
@@ -1372,11 +1370,8 @@ func (container *Container) WaitTimeout(timeout time.Duration) error {
|
| 1372 | 1372 |
} |
| 1373 | 1373 |
|
| 1374 | 1374 |
func (container *Container) EnsureMounted() error {
|
| 1375 |
- if mounted, err := container.Mounted(); err != nil {
|
|
| 1376 |
- return err |
|
| 1377 |
- } else if mounted {
|
|
| 1378 |
- return nil |
|
| 1379 |
- } |
|
| 1375 |
+ // FIXME: EnsureMounted is deprecated because drivers are now responsible |
|
| 1376 |
+ // for re-entrant mounting in their Get() method. |
|
| 1380 | 1377 |
return container.Mount() |
| 1381 | 1378 |
} |
| 1382 | 1379 |
|
| ... | ... |
@@ -1384,7 +1379,7 @@ func (container *Container) Mount() error {
|
| 1384 | 1384 |
return container.runtime.Mount(container) |
| 1385 | 1385 |
} |
| 1386 | 1386 |
|
| 1387 |
-func (container *Container) Changes() ([]Change, error) {
|
|
| 1387 |
+func (container *Container) Changes() ([]graphdriver.Change, error) {
|
|
| 1388 | 1388 |
return container.runtime.Changes(container) |
| 1389 | 1389 |
} |
| 1390 | 1390 |
|
| ... | ... |
@@ -1395,10 +1390,6 @@ func (container *Container) GetImage() (*Image, error) {
|
| 1395 | 1395 |
return container.runtime.graph.Get(container.Image) |
| 1396 | 1396 |
} |
| 1397 | 1397 |
|
| 1398 |
-func (container *Container) Mounted() (bool, error) {
|
|
| 1399 |
- return container.runtime.Mounted(container) |
|
| 1400 |
-} |
|
| 1401 |
- |
|
| 1402 | 1398 |
func (container *Container) Unmount() error {
|
| 1403 | 1399 |
return container.runtime.Unmount(container) |
| 1404 | 1400 |
} |
| ... | ... |
@@ -1437,11 +1428,7 @@ func (container *Container) lxcConfigPath() string {
|
| 1437 | 1437 |
|
| 1438 | 1438 |
// This method must be exported to be used from the lxc template |
| 1439 | 1439 |
func (container *Container) RootfsPath() string {
|
| 1440 |
- return path.Join(container.root, "rootfs") |
|
| 1441 |
-} |
|
| 1442 |
- |
|
| 1443 |
-func (container *Container) rwPath() string {
|
|
| 1444 |
- return path.Join(container.root, "rw") |
|
| 1440 |
+ return container.rootfs |
|
| 1445 | 1441 |
} |
| 1446 | 1442 |
|
| 1447 | 1443 |
func validateID(id string) error {
|
| ... | ... |
@@ -1455,18 +1442,20 @@ func validateID(id string) error {
|
| 1455 | 1455 |
func (container *Container) GetSize() (int64, int64) {
|
| 1456 | 1456 |
var sizeRw, sizeRootfs int64 |
| 1457 | 1457 |
|
| 1458 |
- filepath.Walk(container.rwPath(), func(path string, fileInfo os.FileInfo, err error) error {
|
|
| 1459 |
- if fileInfo != nil {
|
|
| 1460 |
- sizeRw += fileInfo.Size() |
|
| 1461 |
- } |
|
| 1462 |
- return nil |
|
| 1463 |
- }) |
|
| 1458 |
+ driver := container.runtime.driver |
|
| 1459 |
+ sizeRw, err := driver.DiffSize(container.ID) |
|
| 1460 |
+ if err != nil {
|
|
| 1461 |
+ utils.Errorf("Warning: driver %s couldn't return diff size of container %s: %s", driver, container.ID, err)
|
|
| 1462 |
+ // FIXME: GetSize should return an error. Not changing it now in case |
|
| 1463 |
+ // there is a side-effect. |
|
| 1464 |
+ sizeRw = -1 |
|
| 1465 |
+ } |
|
| 1464 | 1466 |
|
| 1465 | 1467 |
if err := container.EnsureMounted(); err != nil {
|
| 1466 | 1468 |
utils.Errorf("Warning: failed to compute size of container rootfs %s: %s", container.ID, err)
|
| 1467 | 1469 |
return sizeRw, sizeRootfs |
| 1468 | 1470 |
} |
| 1469 |
- _, err := os.Stat(container.RootfsPath()) |
|
| 1471 |
+ _, err = os.Stat(container.RootfsPath()) |
|
| 1470 | 1472 |
if err == nil {
|
| 1471 | 1473 |
filepath.Walk(container.RootfsPath(), func(path string, fileInfo os.FileInfo, err error) error {
|
| 1472 | 1474 |
if fileInfo != nil {
|
| ... | ... |
@@ -3,10 +3,8 @@ package docker |
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"github.com/dotcloud/docker/archive" |
| 6 |
- _ "github.com/dotcloud/docker/aufs" |
|
| 7 |
- _ "github.com/dotcloud/docker/devmapper" |
|
| 8 |
- "github.com/dotcloud/docker/graphdriver" |
|
| 9 | 6 |
"github.com/dotcloud/docker/utils" |
| 7 |
+ "github.com/dotcloud/docker/graphdriver" |
|
| 10 | 8 |
"io" |
| 11 | 9 |
"io/ioutil" |
| 12 | 10 |
"os" |
| ... | ... |
@@ -25,7 +23,7 @@ type Graph struct {
|
| 25 | 25 |
|
| 26 | 26 |
// NewGraph instantiates a new graph at the given root path in the filesystem. |
| 27 | 27 |
// `root` will be created if it doesn't exist. |
| 28 |
-func NewGraph(root string) (*Graph, error) {
|
|
| 28 |
+func NewGraph(root string, driver graphdriver.Driver) (*Graph, error) {
|
|
| 29 | 29 |
abspath, err := filepath.Abs(root) |
| 30 | 30 |
if err != nil {
|
| 31 | 31 |
return nil, err |
| ... | ... |
@@ -35,10 +33,6 @@ func NewGraph(root string) (*Graph, error) {
|
| 35 | 35 |
return nil, err |
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 |
- driver, err := graphdriver.New(root) |
|
| 39 |
- if err != nil {
|
|
| 40 |
- return nil, err |
|
| 41 |
- } |
|
| 42 | 38 |
|
| 43 | 39 |
graph := &Graph{
|
| 44 | 40 |
Root: abspath, |
| ... | ... |
@@ -89,16 +83,22 @@ func (graph *Graph) Get(name string) (*Image, error) {
|
| 89 | 89 |
if err != nil {
|
| 90 | 90 |
return nil, err |
| 91 | 91 |
} |
| 92 |
+ // Check that the filesystem layer exists |
|
| 93 |
+ rootfs, err := graph.driver.Get(img.ID) |
|
| 94 |
+ if err != nil {
|
|
| 95 |
+ return nil, fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
|
|
| 96 |
+ } |
|
| 92 | 97 |
if img.ID != id {
|
| 93 | 98 |
return nil, fmt.Errorf("Image stored at '%s' has wrong id '%s'", id, img.ID)
|
| 94 | 99 |
} |
| 95 | 100 |
img.graph = graph |
| 96 | 101 |
if img.Size == 0 {
|
| 97 |
- root, err := img.root() |
|
| 102 |
+ size, err := utils.TreeSize(rootfs) |
|
| 98 | 103 |
if err != nil {
|
| 99 |
- return nil, err |
|
| 104 |
+ return nil, fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
|
|
| 100 | 105 |
} |
| 101 |
- if err := StoreSize(img, root); err != nil {
|
|
| 106 |
+ img.Size = size |
|
| 107 |
+ if err := img.SaveSize(graph.imageRoot(id)); err != nil {
|
|
| 102 | 108 |
return nil, err |
| 103 | 109 |
} |
| 104 | 110 |
} |
| ... | ... |
@@ -142,7 +142,17 @@ func (graph *Graph) Register(jsonData []byte, layerData archive.Archive, img *Im |
| 142 | 142 |
if err != nil {
|
| 143 | 143 |
return fmt.Errorf("Mktemp failed: %s", err)
|
| 144 | 144 |
} |
| 145 |
- if err := StoreImage(img, jsonData, layerData, tmp); err != nil {
|
|
| 145 |
+ |
|
| 146 |
+ // Create root filesystem in the driver |
|
| 147 |
+ if err := graph.driver.Create(img.ID, img.Parent); err != nil {
|
|
| 148 |
+ return fmt.Errorf("Driver %s failed to create image rootfs %s: %s", graph.driver, img.ID, err)
|
|
| 149 |
+ } |
|
| 150 |
+ // Mount the root filesystem so we can apply the diff/layer |
|
| 151 |
+ rootfs, err := graph.driver.Get(img.ID) |
|
| 152 |
+ if err != nil {
|
|
| 153 |
+ return fmt.Errorf("Driver %s failed to get image rootfs %s: %s", graph.driver, img.ID, err)
|
|
| 154 |
+ } |
|
| 155 |
+ if err := StoreImage(img, jsonData, layerData, tmp, rootfs); err != nil {
|
|
| 146 | 156 |
return err |
| 147 | 157 |
} |
| 148 | 158 |
// Commit |
| ... | ... |
@@ -163,7 +173,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, |
| 163 | 163 |
if err != nil {
|
| 164 | 164 |
return nil, err |
| 165 | 165 |
} |
| 166 |
- tmp, err := graph.tmp() |
|
| 166 |
+ tmp, err := graph.Mktemp("")
|
|
| 167 | 167 |
if err != nil {
|
| 168 | 168 |
return nil, err |
| 169 | 169 |
} |
| ... | ... |
@@ -171,7 +181,7 @@ func (graph *Graph) TempLayerArchive(id string, compression archive.Compression, |
| 171 | 171 |
if err != nil {
|
| 172 | 172 |
return nil, err |
| 173 | 173 |
} |
| 174 |
- return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp.Root)
|
|
| 174 |
+ return archive.NewTempArchive(utils.ProgressReader(ioutil.NopCloser(a), 0, output, sf.FormatProgress("", "Buffering to disk", "%v/%v (%v)"), sf, true), tmp)
|
|
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 | 177 |
// Mktemp creates a temporary sub-directory inside the graph's filesystem. |
| ... | ... |
@@ -179,34 +189,26 @@ func (graph *Graph) Mktemp(id string) (string, error) {
|
| 179 | 179 |
if id == "" {
|
| 180 | 180 |
id = GenerateID() |
| 181 | 181 |
} |
| 182 |
- tmp, err := graph.tmp() |
|
| 183 |
- if err != nil {
|
|
| 184 |
- return "", fmt.Errorf("Couldn't create temp: %s", err)
|
|
| 182 |
+ // FIXME: use a separate "tmp" driver instead of the regular driver, |
|
| 183 |
+ // to allow for removal at cleanup. |
|
| 184 |
+ // Right now temp directories are never removed! |
|
| 185 |
+ if err := graph.driver.Create(id, ""); err != nil {
|
|
| 186 |
+ return "", fmt.Errorf("Driver %s couldn't create temporary directory %s: %s", graph.driver, id, err)
|
|
| 185 | 187 |
} |
| 186 |
- if tmp.Exists(id) {
|
|
| 187 |
- return "", fmt.Errorf("Image %s already exists", id)
|
|
| 188 |
+ dir, err := graph.driver.Get(id) |
|
| 189 |
+ if err != nil {
|
|
| 190 |
+ return "", fmt.Errorf("Driver %s couldn't get temporary directory %s: %s", graph.driver, id, err)
|
|
| 188 | 191 |
} |
| 189 |
- return tmp.imageRoot(id), nil |
|
| 192 |
+ return dir, nil |
|
| 190 | 193 |
} |
| 191 | 194 |
|
| 192 |
-// getDockerInitLayer returns the path of a layer containing a mountpoint suitable |
|
| 195 |
+// setupInitLayer populates a directory with mountpoints suitable |
|
| 193 | 196 |
// for bind-mounting dockerinit into the container. The mountpoint is simply an |
| 194 | 197 |
// empty file at /.dockerinit |
| 195 | 198 |
// |
| 196 | 199 |
// This extra layer is used by all containers as the top-most ro layer. It protects |
| 197 | 200 |
// the container from unwanted side-effects on the rw layer. |
| 198 |
-func (graph *Graph) getDockerInitLayer() (string, error) {
|
|
| 199 |
- tmp, err := graph.tmp() |
|
| 200 |
- if err != nil {
|
|
| 201 |
- return "", err |
|
| 202 |
- } |
|
| 203 |
- initLayer := tmp.imageRoot("_dockerinit")
|
|
| 204 |
- if err := os.Mkdir(initLayer, 0755); err != nil && !os.IsExist(err) {
|
|
| 205 |
- // If directory already existed, keep going. |
|
| 206 |
- // For all other errors, abort. |
|
| 207 |
- return "", err |
|
| 208 |
- } |
|
| 209 |
- |
|
| 201 |
+func setupInitLayer(initLayer string) error {
|
|
| 210 | 202 |
for pth, typ := range map[string]string{
|
| 211 | 203 |
"/dev/pts": "dir", |
| 212 | 204 |
"/dev/shm": "dir", |
| ... | ... |
@@ -225,32 +227,27 @@ func (graph *Graph) getDockerInitLayer() (string, error) {
|
| 225 | 225 |
switch typ {
|
| 226 | 226 |
case "dir": |
| 227 | 227 |
if err := os.MkdirAll(path.Join(initLayer, pth), 0755); err != nil {
|
| 228 |
- return "", err |
|
| 228 |
+ return err |
|
| 229 | 229 |
} |
| 230 | 230 |
case "file": |
| 231 | 231 |
if err := os.MkdirAll(path.Join(initLayer, path.Dir(pth)), 0755); err != nil {
|
| 232 |
- return "", err |
|
| 232 |
+ return err |
|
| 233 | 233 |
} |
| 234 | 234 |
|
| 235 | 235 |
if f, err := os.OpenFile(path.Join(initLayer, pth), os.O_CREATE, 0755); err != nil {
|
| 236 |
- return "", err |
|
| 236 |
+ return err |
|
| 237 | 237 |
} else {
|
| 238 | 238 |
f.Close() |
| 239 | 239 |
} |
| 240 | 240 |
} |
| 241 | 241 |
} else {
|
| 242 |
- return "", err |
|
| 242 |
+ return err |
|
| 243 | 243 |
} |
| 244 | 244 |
} |
| 245 | 245 |
} |
| 246 | 246 |
|
| 247 | 247 |
// Layer is ready to use, if it wasn't before. |
| 248 |
- return initLayer, nil |
|
| 249 |
-} |
|
| 250 |
- |
|
| 251 |
-func (graph *Graph) tmp() (*Graph, error) {
|
|
| 252 |
- // Changed to _tmp from :tmp:, because it messed with ":" separators in aufs branch syntax... |
|
| 253 |
- return NewGraph(path.Join(graph.Root, "_tmp")) |
|
| 248 |
+ return nil |
|
| 254 | 249 |
} |
| 255 | 250 |
|
| 256 | 251 |
// Check if given error is "not empty". |
| ... | ... |
@@ -282,6 +279,9 @@ func (graph *Graph) Delete(name string) error {
|
| 282 | 282 |
if err != nil {
|
| 283 | 283 |
return err |
| 284 | 284 |
} |
| 285 |
+ // Remove rootfs data from the driver |
|
| 286 |
+ graph.driver.Remove(id) |
|
| 287 |
+ // Remove the trashed image directory |
|
| 285 | 288 |
return os.RemoveAll(tmp) |
| 286 | 289 |
} |
| 287 | 290 |
|
| ... | ... |
@@ -7,21 +7,20 @@ import ( |
| 7 | 7 |
|
| 8 | 8 |
type InitFunc func(root string) (Driver, error) |
| 9 | 9 |
|
| 10 |
-type Dir interface {
|
|
| 11 |
- ID() string |
|
| 12 |
- Path() string |
|
| 13 |
- Parent() (Dir, error) |
|
| 10 |
+// FIXME: this is a temporary placeholder for archive.Change |
|
| 11 |
+// (to be merged from master) |
|
| 12 |
+type Change interface {
|
|
| 14 | 13 |
} |
| 15 | 14 |
|
| 16 | 15 |
type Driver interface {
|
| 17 |
- OnCreate(dir Dir, layer archive.Archive) error |
|
| 18 |
- OnRemove(dir Dir) error |
|
| 16 |
+ Create(id, parent string) error |
|
| 17 |
+ Remove(id string) error |
|
| 19 | 18 |
|
| 20 |
- OnMount(dir Dir, dest string) error |
|
| 21 |
- OnUnmount(dest string) error |
|
| 22 |
- Mounted(dest string) (bool, error) |
|
| 19 |
+ Get(id string) (dir string, err error) |
|
| 23 | 20 |
|
| 24 |
- Layer(dir Dir, dest string) (archive.Archive, error) |
|
| 21 |
+ Diff(id string) (archive.Archive, error) |
|
| 22 |
+ DiffSize(id string) (bytes int64, err error) |
|
| 23 |
+ Changes(id string) ([]Change, error) |
|
| 25 | 24 |
|
| 26 | 25 |
Cleanup() error |
| 27 | 26 |
} |
| ... | ... |
@@ -11,7 +11,6 @@ import ( |
| 11 | 11 |
"io/ioutil" |
| 12 | 12 |
"os" |
| 13 | 13 |
"path" |
| 14 |
- "path/filepath" |
|
| 15 | 14 |
"strconv" |
| 16 | 15 |
"strings" |
| 17 | 16 |
"time" |
| ... | ... |
@@ -59,19 +58,10 @@ func LoadImage(root string) (*Image, error) {
|
| 59 | 59 |
} |
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 |
- // Check that the filesystem layer exists |
|
| 63 |
- if stat, err := os.Stat(layerPath(root)); err != nil {
|
|
| 64 |
- if os.IsNotExist(err) {
|
|
| 65 |
- return nil, fmt.Errorf("Couldn't load image %s: no filesystem layer", img.ID)
|
|
| 66 |
- } |
|
| 67 |
- return nil, err |
|
| 68 |
- } else if !stat.IsDir() {
|
|
| 69 |
- return nil, fmt.Errorf("Couldn't load image %s: %s is not a directory", img.ID, layerPath(root))
|
|
| 70 |
- } |
|
| 71 | 62 |
return img, nil |
| 72 | 63 |
} |
| 73 | 64 |
|
| 74 |
-func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root string) error {
|
|
| 65 |
+func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root, rootfs string) error {
|
|
| 75 | 66 |
// Check that root doesn't already exist |
| 76 | 67 |
if _, err := os.Stat(root); err == nil {
|
| 77 | 68 |
return fmt.Errorf("Image %s already exists", img.ID)
|
| ... | ... |
@@ -79,7 +69,7 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str |
| 79 | 79 |
return err |
| 80 | 80 |
} |
| 81 | 81 |
// Store the layer |
| 82 |
- layer := layerPath(root) |
|
| 82 |
+ layer := rootfs |
|
| 83 | 83 |
if err := os.MkdirAll(layer, 0755); err != nil {
|
| 84 | 84 |
return err |
| 85 | 85 |
} |
| ... | ... |
@@ -106,29 +96,25 @@ func StoreImage(img *Image, jsonData []byte, layerData archive.Archive, root str |
| 106 | 106 |
return err |
| 107 | 107 |
} |
| 108 | 108 |
} |
| 109 |
- |
|
| 110 |
- return StoreSize(img, root) |
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-func StoreSize(img *Image, root string) error {
|
|
| 114 |
- layer := layerPath(root) |
|
| 115 |
- |
|
| 116 |
- var totalSize int64 = 0 |
|
| 117 |
- filepath.Walk(layer, func(path string, fileInfo os.FileInfo, err error) error {
|
|
| 118 |
- totalSize += fileInfo.Size() |
|
| 119 |
- return nil |
|
| 120 |
- }) |
|
| 121 |
- img.Size = totalSize |
|
| 122 |
- |
|
| 123 |
- if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(totalSize))), 0600); err != nil {
|
|
| 124 |
- return nil |
|
| 109 |
+ // Compute and save the size of the rootfs |
|
| 110 |
+ size, err := utils.TreeSize(rootfs) |
|
| 111 |
+ if err != nil {
|
|
| 112 |
+ return fmt.Errorf("Error computing size of rootfs %s: %s", img.ID, err)
|
|
| 113 |
+ } |
|
| 114 |
+ img.Size = size |
|
| 115 |
+ if err := img.SaveSize(root); err != nil {
|
|
| 116 |
+ return err |
|
| 125 | 117 |
} |
| 126 | 118 |
|
| 127 | 119 |
return nil |
| 128 | 120 |
} |
| 129 | 121 |
|
| 130 |
-func layerPath(root string) string {
|
|
| 131 |
- return path.Join(root, "layer") |
|
| 122 |
+// SaveSize stores the current `size` value of `img` in the directory `root`. |
|
| 123 |
+func (img *Image) SaveSize(root string) error {
|
|
| 124 |
+ if err := ioutil.WriteFile(path.Join(root, "layersize"), []byte(strconv.Itoa(int(img.Size))), 0600); err != nil {
|
|
| 125 |
+ return fmt.Errorf("Error storing image size in %s/layersize: %s", root, err)
|
|
| 126 |
+ } |
|
| 127 |
+ return nil |
|
| 132 | 128 |
} |
| 133 | 129 |
|
| 134 | 130 |
func jsonPath(root string) string {
|
| ... | ... |
@@ -137,7 +123,10 @@ func jsonPath(root string) string {
|
| 137 | 137 |
|
| 138 | 138 |
// TarLayer returns a tar archive of the image's filesystem layer. |
| 139 | 139 |
func (image *Image) TarLayer(compression archive.Compression) (archive.Archive, error) {
|
| 140 |
- layerPath, err := image.layer() |
|
| 140 |
+ if image.graph == nil {
|
|
| 141 |
+ return nil, fmt.Errorf("Can't load storage driver for unregistered image %s", image.ID)
|
|
| 142 |
+ } |
|
| 143 |
+ layerPath, err := image.graph.driver.Get(image.ID) |
|
| 141 | 144 |
if err != nil {
|
| 142 | 145 |
return nil, err |
| 143 | 146 |
} |
| ... | ... |
@@ -216,15 +205,6 @@ func (img *Image) root() (string, error) {
|
| 216 | 216 |
return img.graph.imageRoot(img.ID), nil |
| 217 | 217 |
} |
| 218 | 218 |
|
| 219 |
-// Return the path of an image's layer |
|
| 220 |
-func (img *Image) layer() (string, error) {
|
|
| 221 |
- root, err := img.root() |
|
| 222 |
- if err != nil {
|
|
| 223 |
- return "", err |
|
| 224 |
- } |
|
| 225 |
- return layerPath(root), nil |
|
| 226 |
-} |
|
| 227 |
- |
|
| 228 | 219 |
func (img *Image) getParentsSize(size int64) int64 {
|
| 229 | 220 |
parentImage, err := img.GetParent() |
| 230 | 221 |
if err != nil || parentImage == nil {
|
| ... | ... |
@@ -7,6 +7,7 @@ import ( |
| 7 | 7 |
"fmt" |
| 8 | 8 |
"github.com/dotcloud/docker/gograph" |
| 9 | 9 |
"github.com/dotcloud/docker/utils" |
| 10 |
+ "github.com/dotcloud/docker/graphdriver" |
|
| 10 | 11 |
"io" |
| 11 | 12 |
"io/ioutil" |
| 12 | 13 |
"log" |
| ... | ... |
@@ -38,6 +39,7 @@ type Runtime struct {
|
| 38 | 38 |
srv *Server |
| 39 | 39 |
config *DaemonConfig |
| 40 | 40 |
containerGraph *gograph.Database |
| 41 |
+ driver graphdriver.Driver |
|
| 41 | 42 |
} |
| 42 | 43 |
|
| 43 | 44 |
// List returns an array of all containers registered in the runtime. |
| ... | ... |
@@ -113,6 +115,13 @@ func (runtime *Runtime) Register(container *Container) error {
|
| 113 | 113 |
return err |
| 114 | 114 |
} |
| 115 | 115 |
|
| 116 |
+ // Get the root filesystem from the driver |
|
| 117 |
+ rootfs, err := runtime.driver.Get(container.ID) |
|
| 118 |
+ if err != nil {
|
|
| 119 |
+ return fmt.Errorf("Error getting container filesystem %s from driver %s: %s", container.ID, runtime.driver, err)
|
|
| 120 |
+ } |
|
| 121 |
+ container.rootfs = rootfs |
|
| 122 |
+ |
|
| 116 | 123 |
// init the wait lock |
| 117 | 124 |
container.waitLock = make(chan struct{})
|
| 118 | 125 |
|
| ... | ... |
@@ -200,12 +209,8 @@ func (runtime *Runtime) Destroy(container *Container) error {
|
| 200 | 200 |
return err |
| 201 | 201 |
} |
| 202 | 202 |
|
| 203 |
- if mounted, err := container.Mounted(); err != nil {
|
|
| 204 |
- return err |
|
| 205 |
- } else if mounted {
|
|
| 206 |
- if err := container.Unmount(); err != nil {
|
|
| 207 |
- return fmt.Errorf("Unable to unmount container %v: %v", container.ID, err)
|
|
| 208 |
- } |
|
| 203 |
+ if err := runtime.driver.Remove(container.ID); err != nil {
|
|
| 204 |
+ return fmt.Errorf("Driver %s failed to remove root filesystem %s: %s", runtime.driver, container.ID, err)
|
|
| 209 | 205 |
} |
| 210 | 206 |
|
| 211 | 207 |
if _, err := runtime.containerGraph.Purge(container.ID); err != nil {
|
| ... | ... |
@@ -413,6 +418,21 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin |
| 413 | 413 |
return nil, nil, err |
| 414 | 414 |
} |
| 415 | 415 |
|
| 416 |
+ initID := fmt.Sprintf("%s-init", container.ID)
|
|
| 417 |
+ if err := runtime.driver.Create(initID, img.ID); err != nil {
|
|
| 418 |
+ return nil, nil, err |
|
| 419 |
+ } |
|
| 420 |
+ initPath, err := runtime.driver.Get(initID) |
|
| 421 |
+ if err != nil {
|
|
| 422 |
+ return nil, nil, err |
|
| 423 |
+ } |
|
| 424 |
+ if err := setupInitLayer(initPath); err != nil {
|
|
| 425 |
+ return nil, nil, err |
|
| 426 |
+ } |
|
| 427 |
+ |
|
| 428 |
+ if err := runtime.driver.Create(container.ID, initID); err != nil {
|
|
| 429 |
+ return nil, nil, err |
|
| 430 |
+ } |
|
| 416 | 431 |
resolvConf, err := utils.GetResolvConf() |
| 417 | 432 |
if err != nil {
|
| 418 | 433 |
return nil, nil, err |
| ... | ... |
@@ -568,17 +588,23 @@ func NewRuntime(config *DaemonConfig) (*Runtime, error) {
|
| 568 | 568 |
} |
| 569 | 569 |
|
| 570 | 570 |
func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 571 |
+ // Load storage driver |
|
| 572 |
+ driver, err := graphdriver.New(config.Root) |
|
| 573 |
+ if err != nil {
|
|
| 574 |
+ return nil, err |
|
| 575 |
+ } |
|
| 576 |
+ |
|
| 571 | 577 |
runtimeRepo := path.Join(config.Root, "containers") |
| 572 | 578 |
|
| 573 | 579 |
if err := os.MkdirAll(runtimeRepo, 0700); err != nil && !os.IsExist(err) {
|
| 574 | 580 |
return nil, err |
| 575 | 581 |
} |
| 576 | 582 |
|
| 577 |
- g, err := NewGraph(path.Join(config.Root, "graph")) |
|
| 583 |
+ g, err := NewGraph(path.Join(config.Root, "graph"), driver) |
|
| 578 | 584 |
if err != nil {
|
| 579 | 585 |
return nil, err |
| 580 | 586 |
} |
| 581 |
- volumes, err := NewGraph(path.Join(config.Root, "volumes")) |
|
| 587 |
+ volumes, err := NewGraph(path.Join(config.Root, "volumes"), driver) |
|
| 582 | 588 |
if err != nil {
|
| 583 | 589 |
return nil, err |
| 584 | 590 |
} |
| ... | ... |
@@ -612,6 +638,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 612 | 612 |
return nil, err |
| 613 | 613 |
} |
| 614 | 614 |
|
| 615 |
+ |
|
| 615 | 616 |
runtime := &Runtime{
|
| 616 | 617 |
repository: runtimeRepo, |
| 617 | 618 |
containers: list.New(), |
| ... | ... |
@@ -623,6 +650,7 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 623 | 623 |
volumes: volumes, |
| 624 | 624 |
config: config, |
| 625 | 625 |
containerGraph: graph, |
| 626 |
+ driver: driver, |
|
| 626 | 627 |
} |
| 627 | 628 |
|
| 628 | 629 |
if err := runtime.restore(); err != nil {
|
| ... | ... |
@@ -633,40 +661,32 @@ func NewRuntimeFromDirectory(config *DaemonConfig) (*Runtime, error) {
|
| 633 | 633 |
|
| 634 | 634 |
func (runtime *Runtime) Close() error {
|
| 635 | 635 |
runtime.networkManager.Close() |
| 636 |
+ runtime.driver.Cleanup() |
|
| 636 | 637 |
return runtime.containerGraph.Close() |
| 637 | 638 |
} |
| 638 | 639 |
|
| 639 | 640 |
func (runtime *Runtime) Mount(container *Container) error {
|
| 640 |
- if mounted, err := runtime.Mounted(container); err != nil {
|
|
| 641 |
- return err |
|
| 642 |
- } else if mounted {
|
|
| 643 |
- return fmt.Errorf("%s is already mounted", container.RootfsPath())
|
|
| 644 |
- } |
|
| 645 |
- img, err := container.GetImage() |
|
| 641 |
+ dir, err := runtime.driver.Get(container.ID) |
|
| 646 | 642 |
if err != nil {
|
| 647 |
- return err |
|
| 643 |
+ return fmt.Errorf("Error getting container %s from driver %s: %s", container.ID, runtime.driver, err)
|
|
| 644 |
+ } |
|
| 645 |
+ if container.rootfs == "" {
|
|
| 646 |
+ container.rootfs = dir |
|
| 647 |
+ } else if container.rootfs != dir {
|
|
| 648 |
+ return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
|
|
| 649 |
+ runtime.driver, container.ID, container.rootfs, dir) |
|
| 648 | 650 |
} |
| 649 |
- return runtime.graph.driver.Mount(img, container.root) |
|
| 651 |
+ return nil |
|
| 650 | 652 |
} |
| 651 | 653 |
|
| 652 | 654 |
func (runtime *Runtime) Unmount(container *Container) error {
|
| 653 |
- return runtime.graph.driver.Unmount(container.root) |
|
| 654 |
-} |
|
| 655 |
- |
|
| 656 |
-func (runtime *Runtime) Mounted(container *Container) (bool, error) {
|
|
| 657 |
- return runtime.graph.driver.Mounted(container.root) |
|
| 655 |
+ // FIXME: Unmount is deprecated because drivers are responsible for mounting |
|
| 656 |
+ // and unmounting when necessary. Use driver.Remove() instead. |
|
| 657 |
+ return nil |
|
| 658 | 658 |
} |
| 659 | 659 |
|
| 660 |
-func (runtime *Runtime) Changes(container *Container) ([]Change, error) {
|
|
| 661 |
- img, err := container.GetImage() |
|
| 662 |
- if err != nil {
|
|
| 663 |
- return nil, err |
|
| 664 |
- } |
|
| 665 |
- layers, err := img.Layers() |
|
| 666 |
- if err != nil {
|
|
| 667 |
- return nil, err |
|
| 668 |
- } |
|
| 669 |
- return Changes(layers, container.rwPath()) |
|
| 660 |
+func (runtime *Runtime) Changes(container *Container) ([]graphdriver.Change, error) {
|
|
| 661 |
+ return runtime.driver.Changes(container.ID) |
|
| 670 | 662 |
} |
| 671 | 663 |
|
| 672 | 664 |
// History is a convenience type for storing a list of containers, |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"github.com/dotcloud/docker/gograph" |
| 12 | 12 |
"github.com/dotcloud/docker/registry" |
| 13 | 13 |
"github.com/dotcloud/docker/utils" |
| 14 |
+ "github.com/dotcloud/docker/graphdriver" // FIXME: graphdriver.Change is a placeholder for archive.Change |
|
| 14 | 15 |
"io" |
| 15 | 16 |
"io/ioutil" |
| 16 | 17 |
"log" |
| ... | ... |
@@ -430,7 +431,7 @@ func (srv *Server) ContainerTop(name, ps_args string) (*APITop, error) {
|
| 430 | 430 |
return nil, fmt.Errorf("No such container: %s", name)
|
| 431 | 431 |
} |
| 432 | 432 |
|
| 433 |
-func (srv *Server) ContainerChanges(name string) ([]Change, error) {
|
|
| 433 |
+func (srv *Server) ContainerChanges(name string) ([]graphdriver.Change, error) {
|
|
| 434 | 434 |
if container := srv.runtime.Get(name); container != nil {
|
| 435 | 435 |
return container.Changes() |
| 436 | 436 |
} |
| 437 | 437 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,15 @@ |
| 0 |
+package utils |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "path/filepath" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// TreeSize walks a directory tree and returns its total size in bytes. |
|
| 8 |
+func TreeSize(dir string) (size int64, err error) {
|
|
| 9 |
+ err = filepath.Walk(dir, func(d string, fileInfo os.FileInfo, e error) error {
|
|
| 10 |
+ size += fileInfo.Size() |
|
| 11 |
+ return nil |
|
| 12 |
+ }) |
|
| 13 |
+ return |
|
| 14 |
+} |