From d04fa49a0dec89d2f71a813ce8eaa182184139c5 Mon Sep 17 00:00:00 2001 From: Derek McGowan Date: Wed, 16 Dec 2015 14:13:50 -0800 Subject: [PATCH] Refactor RWLayer to use referenced object instead of string RWLayer will now have more operations and be protected through a referenced type rather than always looked up by string in the layer store. Separates creation of RWLayer (write capture layer) from mounting of the layer. This allows mount labels to be applied after creation and allowing RWLayer objects to have the same lifespan as a container without performance regressions from requiring mount. Signed-off-by: Derek McGowan (github: dmcgowan) --- daemon/commit.go | 2 +- daemon/container_operations_windows.go | 2 +- daemon/create.go | 24 +++ daemon/daemon.go | 26 +-- daemon/delete.go | 2 +- daemon/inspect.go | 2 +- distribution/xfer/download_test.go | 18 +- layer/layer.go | 34 +++- layer/layer_store.go | 265 ++++++++++--------------- layer/layer_test.go | 82 ++++++-- layer/layer_unix.go | 9 + layer/layer_windows.go | 5 + layer/migration.go | 25 ++- layer/migration_test.go | 58 ++++-- layer/mount_test.go | 14 +- layer/mounted_layer.go | 118 +++++++++-- migrate/v1/migratev1.go | 11 +- migrate/v1/migratev1_test.go | 5 +- 18 files changed, 427 insertions(+), 275 deletions(-) create mode 100644 layer/layer_unix.go diff --git a/daemon/commit.go b/daemon/commit.go index 3e5744c0c2..ba083598b0 100644 --- a/daemon/commit.go +++ b/daemon/commit.go @@ -145,7 +145,7 @@ func (daemon *Daemon) exportContainerRw(container *container.Container) (archive } return ioutils.NewReadCloserWrapper(archive, func() error { archive.Close() - return daemon.layerStore.Unmount(container.ID) + return container.RWLayer.Unmount() }), nil } diff --git a/daemon/container_operations_windows.go b/daemon/container_operations_windows.go index ebbefa3eb4..2c4a24c463 100644 --- a/daemon/container_operations_windows.go +++ b/daemon/container_operations_windows.go @@ -98,7 +98,7 @@ func (daemon *Daemon) populateCommand(c *container.Container, env []string) erro } } - m, err := daemon.layerStore.Metadata(c.ID) + m, err := c.RWLayer.Metadata() if err != nil { return derr.ErrorCodeGetLayerMetadata.WithArgs(err) } diff --git a/daemon/create.go b/daemon/create.go index aa3d97896e..26b7c1c26c 100644 --- a/daemon/create.go +++ b/daemon/create.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/container" derr "github.com/docker/docker/errors" "github.com/docker/docker/image" + "github.com/docker/docker/layer" "github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/volume" @@ -95,6 +96,11 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig) (*container.Con } }() + // Set RWLayer for container after mount labels have been set + if err := daemon.setRWLayer(container); err != nil { + return nil, err + } + if err := daemon.createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig, img); err != nil { return nil, err } @@ -126,6 +132,24 @@ func (daemon *Daemon) generateSecurityOpt(ipcMode containertypes.IpcMode, pidMod return nil, nil } +func (daemon *Daemon) setRWLayer(container *container.Container) error { + var layerID layer.ChainID + if container.ImageID != "" { + img, err := daemon.imageStore.Get(container.ImageID) + if err != nil { + return err + } + layerID = img.RootFS.ChainID() + } + rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.MountLabel, daemon.setupInitLayer) + if err != nil { + return err + } + container.RWLayer = rwLayer + + return nil +} + // VolumeCreate creates a volume with the specified name, driver, and opts // This is called directly from the remote API func (daemon *Daemon) VolumeCreate(name, driverName string, opts map[string]string) (*types.Volume, error) { diff --git a/daemon/daemon.go b/daemon/daemon.go index 7f84de7c4f..fb8ed4600f 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -323,6 +323,13 @@ func (daemon *Daemon) restore() error { continue } + rwlayer, err := daemon.layerStore.GetRWLayer(container.ID) + if err != nil { + logrus.Errorf("Failed to load container mount %v: %v", id, err) + continue + } + container.RWLayer = rwlayer + // Ignore the container if it does not support the current driver being used by the graph if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver { logrus.Debugf("Loaded container %v", container.ID) @@ -961,19 +968,7 @@ func (daemon *Daemon) Shutdown() error { // Mount sets container.BaseFS // (is it not set coming in? why is it unset?) func (daemon *Daemon) Mount(container *container.Container) error { - var layerID layer.ChainID - if container.ImageID != "" { - img, err := daemon.imageStore.Get(container.ImageID) - if err != nil { - return err - } - layerID = img.RootFS.ChainID() - } - rwlayer, err := daemon.layerStore.Mount(container.ID, layerID, container.GetMountLabel(), daemon.setupInitLayer) - if err != nil { - return err - } - dir, err := rwlayer.Path() + dir, err := container.RWLayer.Mount(container.GetMountLabel()) if err != nil { return err } @@ -990,13 +985,12 @@ func (daemon *Daemon) Mount(container *container.Container) error { } } container.BaseFS = dir // TODO: combine these fields - container.RWLayer = rwlayer return nil } // Unmount unsets the container base filesystem func (daemon *Daemon) Unmount(container *container.Container) { - if err := daemon.layerStore.Unmount(container.ID); err != nil { + if err := container.RWLayer.Unmount(); err != nil { logrus.Errorf("Error unmounting container %s: %s", container.ID, err) } } @@ -1029,7 +1023,7 @@ func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch cha } func (daemon *Daemon) changes(container *container.Container) ([]archive.Change, error) { - return daemon.layerStore.Changes(container.ID) + return container.RWLayer.Changes() } // TagImage creates a tag in the repository reponame, pointing to the image named diff --git a/daemon/delete.go b/daemon/delete.go index a282f3a9f9..e2ce1a8568 100644 --- a/daemon/delete.go +++ b/daemon/delete.go @@ -130,7 +130,7 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo return derr.ErrorCodeRmFS.WithArgs(container.ID, err) } - metadata, err := daemon.layerStore.DeleteMount(container.ID) + metadata, err := daemon.layerStore.ReleaseRWLayer(container.RWLayer) layer.LogReleaseMetadata(metadata) if err != nil && err != layer.ErrMountDoesNotExist { return derr.ErrorCodeRmDriverFS.WithArgs(daemon.driver, container.ID, err) diff --git a/daemon/inspect.go b/daemon/inspect.go index 60bebcc598..b1e4eb43a7 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -163,7 +163,7 @@ func (daemon *Daemon) getInspectData(container *container.Container, size bool) contJSONBase.GraphDriver.Name = container.Driver - graphDriverData, err := daemon.layerStore.Metadata(container.ID) + graphDriverData, err := container.RWLayer.Metadata() if err != nil { return nil, err } diff --git a/distribution/xfer/download_test.go b/distribution/xfer/download_test.go index a09cd244e1..474e523694 100644 --- a/distribution/xfer/download_test.go +++ b/distribution/xfer/download_test.go @@ -12,7 +12,6 @@ import ( "github.com/docker/distribution/digest" "github.com/docker/docker/image" "github.com/docker/docker/layer" - "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/progress" "golang.org/x/net/context" ) @@ -115,25 +114,18 @@ func (ls *mockLayerStore) Get(chainID layer.ChainID) (layer.Layer, error) { func (ls *mockLayerStore) Release(l layer.Layer) ([]layer.Metadata, error) { return []layer.Metadata{}, nil } - -func (ls *mockLayerStore) Mount(id string, parent layer.ChainID, label string, init layer.MountInit) (layer.RWLayer, error) { +func (ls *mockLayerStore) CreateRWLayer(string, layer.ChainID, string, layer.MountInit) (layer.RWLayer, error) { return nil, errors.New("not implemented") } -func (ls *mockLayerStore) Unmount(id string) error { - return errors.New("not implemented") +func (ls *mockLayerStore) GetRWLayer(string) (layer.RWLayer, error) { + return nil, errors.New("not implemented") + } -func (ls *mockLayerStore) DeleteMount(id string) ([]layer.Metadata, error) { +func (ls *mockLayerStore) ReleaseRWLayer(layer.RWLayer) ([]layer.Metadata, error) { return nil, errors.New("not implemented") -} -func (ls *mockLayerStore) Changes(id string) ([]archive.Change, error) { - return nil, errors.New("not implemented") -} - -func (ls *mockLayerStore) Metadata(id string) (map[string]string, error) { - return nil, errors.New("not implemented") } type mockDownloadDescriptor struct { diff --git a/layer/layer.go b/layer/layer.go index eed5937e1e..f2fa5bc6c9 100644 --- a/layer/layer.go +++ b/layer/layer.go @@ -31,6 +31,11 @@ var ( // attempted on a mount layer which does not exist. ErrMountDoesNotExist = errors.New("mount does not exist") + // ErrMountNameConflict is used when a mount is attempted + // to be created but there is already a mount with the name + // used for creation. + ErrMountNameConflict = errors.New("mount already exists with name") + // ErrActiveMount is used when an operation on a // mount is attempted but the layer is still // mounted and the operation cannot be performed. @@ -103,18 +108,33 @@ type Layer interface { type RWLayer interface { TarStreamer - // Path returns the filesystem path to the writable - // layer. - Path() (string, error) + // Name of mounted layer + Name() string // Parent returns the layer which the writable // layer was created from. Parent() Layer + // Mount mounts the RWLayer and returns the filesystem path + // the to the writable layer. + Mount(mountLabel string) (string, error) + + // Unmount unmounts the RWLayer. This should be called + // for every mount. If there are multiple mount calls + // this operation will only decrement the internal mount counter. + Unmount() error + // Size represents the size of the writable layer // as calculated by the total size of the files // changed in the mutable layer. Size() (int64, error) + + // Changes returns the set of changes for the mutable layer + // from the base layer. + Changes() ([]archive.Change, error) + + // Metadata returns the low level metadata for the mutable layer + Metadata() (map[string]string, error) } // Metadata holds information about a @@ -147,11 +167,9 @@ type Store interface { Get(ChainID) (Layer, error) Release(Layer) ([]Metadata, error) - Mount(id string, parent ChainID, label string, init MountInit) (RWLayer, error) - Unmount(id string) error - DeleteMount(id string) ([]Metadata, error) - Changes(id string) ([]archive.Change, error) - Metadata(id string) (map[string]string, error) + CreateRWLayer(id string, parent ChainID, mountLabel string, initFunc MountInit) (RWLayer, error) + GetRWLayer(id string) (RWLayer, error) + ReleaseRWLayer(RWLayer) ([]Metadata, error) } // MetadataTransaction represents functions for setting layer metadata diff --git a/layer/layer_store.go b/layer/layer_store.go index d67092f7d9..80dedfbfbc 100644 --- a/layer/layer_store.go +++ b/layer/layer_store.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "io/ioutil" - "runtime" "sync" "github.com/Sirupsen/logrus" @@ -144,6 +143,7 @@ func (ls *layerStore) loadMount(mount string) error { mountID: mountID, initID: initID, layerStore: ls, + references: map[RWLayer]*referencedRWLayer{}, } if parent != "" { @@ -382,15 +382,114 @@ func (ls *layerStore) Release(l Layer) ([]Metadata, error) { return ls.releaseLayer(layer) } -func (ls *layerStore) mount(m *mountedLayer, mountLabel string) error { - dir, err := ls.driver.Get(m.mountID, mountLabel) - if err != nil { - return err +func (ls *layerStore) CreateRWLayer(name string, parent ChainID, mountLabel string, initFunc MountInit) (RWLayer, error) { + ls.mountL.Lock() + defer ls.mountL.Unlock() + m, ok := ls.mounts[name] + if ok { + return nil, ErrMountNameConflict } - m.path = dir - m.activityCount++ - return nil + var err error + var pid string + var p *roLayer + if string(parent) != "" { + p = ls.get(parent) + if p == nil { + return nil, ErrLayerDoesNotExist + } + pid = p.cacheID + + // Release parent chain if error + defer func() { + if err != nil { + ls.layerL.Lock() + ls.releaseLayer(p) + ls.layerL.Unlock() + } + }() + } + + m = &mountedLayer{ + name: name, + parent: p, + mountID: ls.mountID(name), + layerStore: ls, + references: map[RWLayer]*referencedRWLayer{}, + } + + if initFunc != nil { + pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc) + if err != nil { + return nil, err + } + m.initID = pid + } + + if err = ls.driver.Create(m.mountID, pid, ""); err != nil { + return nil, err + } + + if err = ls.saveMount(m); err != nil { + return nil, err + } + + return m.getReference(), nil +} + +func (ls *layerStore) GetRWLayer(id string) (RWLayer, error) { + ls.mountL.Lock() + defer ls.mountL.Unlock() + mount, ok := ls.mounts[id] + if !ok { + return nil, ErrMountDoesNotExist + } + + return mount.getReference(), nil +} + +func (ls *layerStore) ReleaseRWLayer(l RWLayer) ([]Metadata, error) { + ls.mountL.Lock() + defer ls.mountL.Unlock() + m, ok := ls.mounts[l.Name()] + if !ok { + return []Metadata{}, nil + } + + if err := m.deleteReference(l); err != nil { + return nil, err + } + + if m.hasReferences() { + return []Metadata{}, nil + } + + if err := ls.driver.Remove(m.mountID); err != nil { + logrus.Errorf("Error removing mounted layer %s: %s", m.name, err) + return nil, err + } + + if m.initID != "" { + if err := ls.driver.Remove(m.initID); err != nil { + logrus.Errorf("Error removing init layer %s: %s", m.name, err) + return nil, err + } + } + + if err := ls.store.RemoveMount(m.name); err != nil { + logrus.Errorf("Error removing mount metadata: %s: %s", m.name, err) + return nil, err + } + + delete(ls.mounts, m.Name()) + + ls.layerL.Lock() + defer ls.layerL.Unlock() + if m.parent != nil { + return ls.releaseLayer(m.parent) + } + + return []Metadata{}, nil } func (ls *layerStore) saveMount(mount *mountedLayer) error { @@ -442,145 +541,6 @@ func (ls *layerStore) initMount(graphID, parent, mountLabel string, initFunc Mou return initID, nil } -func (ls *layerStore) Mount(name string, parent ChainID, mountLabel string, initFunc MountInit) (l RWLayer, err error) { - ls.mountL.Lock() - defer ls.mountL.Unlock() - m, ok := ls.mounts[name] - if ok { - // Check if has path - if err := ls.mount(m, mountLabel); err != nil { - return nil, err - } - return m, nil - } - - var pid string - var p *roLayer - if string(parent) != "" { - p = ls.get(parent) - if p == nil { - return nil, ErrLayerDoesNotExist - } - pid = p.cacheID - - // Release parent chain if error - defer func() { - if err != nil { - ls.layerL.Lock() - ls.releaseLayer(p) - ls.layerL.Unlock() - } - }() - } - - mountID := name - if runtime.GOOS != "windows" { - // windows has issues if container ID doesn't match mount ID - mountID = stringid.GenerateRandomID() - } - - m = &mountedLayer{ - name: name, - parent: p, - mountID: mountID, - layerStore: ls, - } - - if initFunc != nil { - pid, err = ls.initMount(m.mountID, pid, mountLabel, initFunc) - if err != nil { - return nil, err - } - m.initID = pid - } - - if err = ls.driver.Create(m.mountID, pid, ""); err != nil { - return nil, err - } - - if err = ls.saveMount(m); err != nil { - return nil, err - } - - if err = ls.mount(m, mountLabel); err != nil { - return nil, err - } - - return m, nil -} - -func (ls *layerStore) Unmount(name string) error { - ls.mountL.Lock() - defer ls.mountL.Unlock() - - m := ls.mounts[name] - if m == nil { - return ErrMountDoesNotExist - } - - m.activityCount-- - - if err := ls.driver.Put(m.mountID); err != nil { - return err - } - - return nil -} - -func (ls *layerStore) DeleteMount(name string) ([]Metadata, error) { - ls.mountL.Lock() - defer ls.mountL.Unlock() - - m := ls.mounts[name] - if m == nil { - return nil, ErrMountDoesNotExist - } - if m.activityCount > 0 { - return nil, ErrActiveMount - } - - delete(ls.mounts, name) - - if err := ls.driver.Remove(m.mountID); err != nil { - logrus.Errorf("Error removing mounted layer %s: %s", m.name, err) - return nil, err - } - - if m.initID != "" { - if err := ls.driver.Remove(m.initID); err != nil { - logrus.Errorf("Error removing init layer %s: %s", m.name, err) - return nil, err - } - } - - if err := ls.store.RemoveMount(m.name); err != nil { - logrus.Errorf("Error removing mount metadata: %s: %s", m.name, err) - return nil, err - } - - ls.layerL.Lock() - defer ls.layerL.Unlock() - if m.parent != nil { - return ls.releaseLayer(m.parent) - } - - return []Metadata{}, nil -} - -func (ls *layerStore) Changes(name string) ([]archive.Change, error) { - ls.mountL.Lock() - m := ls.mounts[name] - ls.mountL.Unlock() - if m == nil { - return nil, ErrMountDoesNotExist - } - pid := m.initID - if pid == "" && m.parent != nil { - pid = m.parent.cacheID - } - return ls.driver.Changes(m.mountID, pid) -} - func (ls *layerStore) assembleTar(graphID string, metadata io.ReadCloser, size *int64) (io.ReadCloser, error) { type diffPathDriver interface { DiffPath(string) (string, func() error, error) @@ -621,17 +581,6 @@ func (ls *layerStore) assembleTar(graphID string, metadata io.ReadCloser, size * return pR, nil } -// Metadata returns the low level metadata from the mount with the given name -func (ls *layerStore) Metadata(name string) (map[string]string, error) { - ls.mountL.Lock() - m := ls.mounts[name] - ls.mountL.Unlock() - if m == nil { - return nil, ErrMountDoesNotExist - } - return ls.driver.GetMetadata(m.mountID) -} - type naiveDiffPathDriver struct { graphdriver.Driver } diff --git a/layer/layer_test.go b/layer/layer_test.go index c3e4cf1d52..65cc15b2ae 100644 --- a/layer/layer_test.go +++ b/layer/layer_test.go @@ -82,12 +82,12 @@ type layerInit func(root string) error func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) { containerID := stringid.GenerateRandomID() - mount, err := ls.Mount(containerID, parent, "", nil) + mount, err := ls.CreateRWLayer(containerID, parent, "", nil) if err != nil { return nil, err } - path, err := mount.Path() + path, err := mount.Mount("") if err != nil { return nil, err } @@ -107,11 +107,11 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) { return nil, err } - if err := ls.Unmount(containerID); err != nil { + if err := mount.Unmount(); err != nil { return nil, err } - if _, err := ls.DeleteMount(containerID); err != nil { + if _, err := ls.ReleaseRWLayer(mount); err != nil { return nil, err } @@ -171,6 +171,13 @@ func getCachedLayer(l Layer) *roLayer { return l.(*roLayer) } +func getMountLayer(l RWLayer) *mountedLayer { + if rl, ok := l.(*referencedRWLayer); ok { + return rl.mountedLayer + } + return l.(*mountedLayer) +} + func createMetadata(layers ...Layer) []Metadata { metadata := make([]Metadata, len(layers)) for i := range layers { @@ -270,12 +277,12 @@ func TestMountAndRegister(t *testing.T) { size, _ := layer.Size() t.Logf("Layer size: %d", size) - mount2, err := ls.Mount("new-test-mount", layer.ChainID(), "", nil) + mount2, err := ls.CreateRWLayer("new-test-mount", layer.ChainID(), "", nil) if err != nil { t.Fatal(err) } - path2, err := mount2.Path() + path2, err := mount2.Mount("") if err != nil { t.Fatal(err) } @@ -289,11 +296,11 @@ func TestMountAndRegister(t *testing.T) { t.Fatalf("Wrong file data, expected %q, got %q", expected, string(b)) } - if err := ls.Unmount("new-test-mount"); err != nil { + if err := mount2.Unmount(); err != nil { t.Fatal(err) } - if _, err := ls.DeleteMount("new-test-mount"); err != nil { + if _, err := ls.ReleaseRWLayer(mount2); err != nil { t.Fatal(err) } } @@ -370,12 +377,12 @@ func TestStoreRestore(t *testing.T) { t.Fatal(err) } - m, err := ls.Mount("some-mount_name", layer3.ChainID(), "", nil) + m, err := ls.CreateRWLayer("some-mount_name", layer3.ChainID(), "", nil) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -383,11 +390,14 @@ func TestStoreRestore(t *testing.T) { if err := ioutil.WriteFile(filepath.Join(path, "testfile.txt"), []byte("nothing here"), 0644); err != nil { t.Fatal(err) } + assertActivityCount(t, m, 1) - if err := ls.Unmount("some-mount_name"); err != nil { + if err := m.Unmount(); err != nil { t.Fatal(err) } + assertActivityCount(t, m, 0) + ls2, err := NewStore(ls.(*layerStore).store, ls.(*layerStore).driver) if err != nil { t.Fatal(err) @@ -400,18 +410,39 @@ func TestStoreRestore(t *testing.T) { assertLayerEqual(t, layer3b, layer3) - // Mount again with same name, should already be loaded - m2, err := ls2.Mount("some-mount_name", layer3b.ChainID(), "", nil) + // Create again with same name, should return error + if _, err := ls2.CreateRWLayer("some-mount_name", layer3b.ChainID(), "", nil); err == nil { + t.Fatal("Expected error creating mount with same name") + } else if err != ErrMountNameConflict { + t.Fatal(err) + } + + m2, err := ls2.GetRWLayer("some-mount_name") if err != nil { t.Fatal(err) } - path2, err := m2.Path() - if err != nil { + if mountPath, err := m2.Mount(""); err != nil { + t.Fatal(err) + } else if path != mountPath { + t.Fatalf("Unexpected path %s, expected %s", mountPath, path) + } + + assertActivityCount(t, m2, 1) + + if mountPath, err := m2.Mount(""); err != nil { + t.Fatal(err) + } else if path != mountPath { + t.Fatalf("Unexpected path %s, expected %s", mountPath, path) + } + assertActivityCount(t, m2, 2) + if err := m2.Unmount(); err != nil { t.Fatal(err) } - b, err := ioutil.ReadFile(filepath.Join(path2, "testfile.txt")) + assertActivityCount(t, m2, 1) + + b, err := ioutil.ReadFile(filepath.Join(path, "testfile.txt")) if err != nil { t.Fatal(err) } @@ -419,11 +450,19 @@ func TestStoreRestore(t *testing.T) { t.Fatalf("Unexpected content %q, expected %q", string(b), expected) } - if err := ls2.Unmount("some-mount_name"); err != nil { + if err := m2.Unmount(); err != nil { t.Fatal(err) } - if metadata, err := ls2.DeleteMount("some-mount_name"); err != nil { + assertActivityCount(t, m2, 0) + + if metadata, err := ls2.ReleaseRWLayer(m2); err != nil { + t.Fatal(err) + } else if len(metadata) != 0 { + t.Fatalf("Unexpectedly deleted layers: %#v", metadata) + } + + if metadata, err := ls2.ReleaseRWLayer(m2); err != nil { t.Fatal(err) } else if len(metadata) != 0 { t.Fatalf("Unexpectedly deleted layers: %#v", metadata) @@ -627,6 +666,13 @@ func assertReferences(t *testing.T, references ...Layer) { } } +func assertActivityCount(t *testing.T, l RWLayer, expected int) { + rl := l.(*referencedRWLayer) + if rl.activityCount != expected { + t.Fatalf("Unexpected activity count %d, expected %d", rl.activityCount, expected) + } +} + func TestRegisterExistingLayer(t *testing.T) { ls, cleanup := newTestStore(t) defer cleanup() diff --git a/layer/layer_unix.go b/layer/layer_unix.go new file mode 100644 index 0000000000..524b97e8d1 --- /dev/null +++ b/layer/layer_unix.go @@ -0,0 +1,9 @@ +// +build linux freebsd darwin + +package layer + +import "github.com/docker/docker/pkg/stringid" + +func (ls *layerStore) mountID(name string) string { + return stringid.GenerateRandomID() +} diff --git a/layer/layer_windows.go b/layer/layer_windows.go index 369281448a..c40144d860 100644 --- a/layer/layer_windows.go +++ b/layer/layer_windows.go @@ -89,3 +89,8 @@ func (ls *layerStore) RegisterDiffID(graphID string, size int64) (Layer, error) return layer.getReference(), nil } + +func (ls *layerStore) mountID(name string) string { + // windows has issues if container ID doesn't match mount ID + return name +} diff --git a/layer/migration.go b/layer/migration.go index bc448a59a5..50dbcaec80 100644 --- a/layer/migration.go +++ b/layer/migration.go @@ -14,30 +14,33 @@ import ( "github.com/vbatts/tar-split/tar/storage" ) -func (ls *layerStore) MountByGraphID(name string, graphID string, parent ChainID) (l RWLayer, err error) { +// CreateRWLayerByGraphID creates a RWLayer in the layer store using +// the provided name with the given graphID. To get the RWLayer +// after migration the layer may be retrieved by the given name. +func (ls *layerStore) CreateRWLayerByGraphID(name string, graphID string, parent ChainID) (err error) { ls.mountL.Lock() defer ls.mountL.Unlock() m, ok := ls.mounts[name] if ok { if m.parent.chainID != parent { - return nil, errors.New("name conflict, mismatched parent") + return errors.New("name conflict, mismatched parent") } if m.mountID != graphID { - return nil, errors.New("mount already exists") + return errors.New("mount already exists") } - return m, nil + return nil } if !ls.driver.Exists(graphID) { - return nil, errors.New("graph ID does not exist") + return errors.New("graph ID does not exist") } var p *roLayer if string(parent) != "" { p = ls.get(parent) if p == nil { - return nil, ErrLayerDoesNotExist + return ErrLayerDoesNotExist } // Release parent chain if error @@ -57,6 +60,7 @@ func (ls *layerStore) MountByGraphID(name string, graphID string, parent ChainID parent: p, mountID: graphID, layerStore: ls, + references: map[RWLayer]*referencedRWLayer{}, } // Check for existing init layer @@ -66,15 +70,10 @@ func (ls *layerStore) MountByGraphID(name string, graphID string, parent ChainID } if err = ls.saveMount(m); err != nil { - return nil, err + return err } - // TODO: provide a mount label - if err = ls.mount(m, ""); err != nil { - return nil, err - } - - return m, nil + return nil } func (ls *layerStore) migrateLayer(tx MetadataTransaction, tarDataFile string, layer *roLayer) error { diff --git a/layer/migration_test.go b/layer/migration_test.go index 11614ffde0..509e0c4eb5 100644 --- a/layer/migration_test.go +++ b/layer/migration_test.go @@ -303,12 +303,20 @@ func TestMountMigration(t *testing.T) { t.Fatal(err) } - rwLayer1, err := ls.(*layerStore).MountByGraphID("migration-mount", containerID, layer1.ChainID()) + if err := ls.(*layerStore).CreateRWLayerByGraphID("migration-mount", containerID, layer1.ChainID()); err != nil { + t.Fatal(err) + } + + rwLayer1, err := ls.GetRWLayer("migration-mount") if err != nil { t.Fatal(err) } - changes, err := ls.Changes("migration-mount") + if _, err := rwLayer1.Mount(""); err != nil { + t.Fatal(err) + } + + changes, err := rwLayer1.Changes() if err != nil { t.Fatal(err) } @@ -341,39 +349,63 @@ func TestMountMigration(t *testing.T) { Kind: archive.ChangeAdd, }) - if expectedCount := 1; rwLayer1.(*mountedLayer).activityCount != expectedCount { - t.Fatalf("Wrong activity count %d, expected %d", rwLayer1.(*mountedLayer).activityCount, expectedCount) + assertActivityCount(t, rwLayer1, 1) + + if _, err := ls.CreateRWLayer("migration-mount", layer1.ChainID(), "", nil); err == nil { + t.Fatal("Expected error creating mount with same name") + } else if err != ErrMountNameConflict { + t.Fatal(err) } - rwLayer2, err := ls.Mount("migration-mount", layer1.ChainID(), "", nil) + rwLayer2, err := ls.GetRWLayer("migration-mount") if err != nil { t.Fatal(err) } - if rwLayer1 != rwLayer2 { - t.Fatalf("Wrong rwlayer %v, expected %v", rwLayer2, rwLayer1) + if getMountLayer(rwLayer1) != getMountLayer(rwLayer2) { + t.Fatal("Expected same layer from get with same name as from migrate") } - if expectedCount := 2; rwLayer2.(*mountedLayer).activityCount != expectedCount { - t.Fatalf("Wrong activity count %d, expected %d", rwLayer2.(*mountedLayer).activityCount, expectedCount) + if _, err := rwLayer2.Mount(""); err != nil { + t.Fatal(err) } + assertActivityCount(t, rwLayer2, 1) + assertActivityCount(t, rwLayer1, 1) + + if _, err := rwLayer2.Mount(""); err != nil { + t.Fatal(err) + } + + assertActivityCount(t, rwLayer2, 2) + assertActivityCount(t, rwLayer1, 1) + if metadata, err := ls.Release(layer1); err != nil { t.Fatal(err) } else if len(metadata) > 0 { t.Fatalf("Expected no layers to be deleted, deleted %#v", metadata) } - if err := ls.Unmount("migration-mount"); err != nil { + if err := rwLayer1.Unmount(); err != nil { t.Fatal(err) } - if _, err := ls.DeleteMount("migration-mount"); err == nil { + assertActivityCount(t, rwLayer2, 2) + assertActivityCount(t, rwLayer1, 0) + + if _, err := ls.ReleaseRWLayer(rwLayer1); err != nil { + t.Fatal(err) + } + + if err := rwLayer2.Unmount(); err != nil { + t.Fatal(err) + } + if _, err := ls.ReleaseRWLayer(rwLayer2); err == nil { t.Fatal("Expected error deleting active mount") } - if err := ls.Unmount("migration-mount"); err != nil { + if err := rwLayer2.Unmount(); err != nil { t.Fatal(err) } - metadata, err := ls.DeleteMount("migration-mount") + metadata, err := ls.ReleaseRWLayer(rwLayer2) if err != nil { t.Fatal(err) } diff --git a/layer/mount_test.go b/layer/mount_test.go index 195f81193d..6889912e6d 100644 --- a/layer/mount_test.go +++ b/layer/mount_test.go @@ -27,12 +27,12 @@ func TestMountInit(t *testing.T) { return initfile.ApplyFile(root) } - m, err := ls.Mount("fun-mount", layer.ChainID(), "", mountInit) + m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), "", mountInit) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -80,12 +80,12 @@ func TestMountSize(t *testing.T) { return newTestFile("file-init", contentInit, 0777).ApplyFile(root) } - m, err := ls.Mount("mount-size", layer.ChainID(), "", mountInit) + m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), "", mountInit) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -125,12 +125,12 @@ func TestMountChanges(t *testing.T) { return initfile.ApplyFile(root) } - m, err := ls.Mount("mount-changes", layer.ChainID(), "", mountInit) + m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), "", mountInit) if err != nil { t.Fatal(err) } - path, err := m.Path() + path, err := m.Mount("") if err != nil { t.Fatal(err) } @@ -155,7 +155,7 @@ func TestMountChanges(t *testing.T) { t.Fatal(err) } - changes, err := ls.Changes("mount-changes") + changes, err := m.Changes() if err != nil { t.Fatal(err) } diff --git a/layer/mounted_layer.go b/layer/mounted_layer.go index 35f43609c6..b3d6568833 100644 --- a/layer/mounted_layer.go +++ b/layer/mounted_layer.go @@ -1,15 +1,20 @@ package layer -import "io" +import ( + "io" + "sync" + + "github.com/docker/docker/pkg/archive" +) type mountedLayer struct { - name string - mountID string - initID string - parent *roLayer - path string - layerStore *layerStore - activityCount int + name string + mountID string + initID string + parent *roLayer + layerStore *layerStore + + references map[RWLayer]*referencedRWLayer } func (ml *mountedLayer) cacheParent() string { @@ -30,11 +35,8 @@ func (ml *mountedLayer) TarStream() (io.ReadCloser, error) { return archiver, nil } -func (ml *mountedLayer) Path() (string, error) { - if ml.path == "" { - return "", ErrNotMounted - } - return ml.path, nil +func (ml *mountedLayer) Name() string { + return ml.name } func (ml *mountedLayer) Parent() Layer { @@ -47,6 +49,96 @@ func (ml *mountedLayer) Parent() Layer { return nil } +func (ml *mountedLayer) Mount(mountLabel string) (string, error) { + return ml.layerStore.driver.Get(ml.mountID, mountLabel) +} + +func (ml *mountedLayer) Unmount() error { + return ml.layerStore.driver.Put(ml.mountID) +} + func (ml *mountedLayer) Size() (int64, error) { return ml.layerStore.driver.DiffSize(ml.mountID, ml.cacheParent()) } + +func (ml *mountedLayer) Changes() ([]archive.Change, error) { + return ml.layerStore.driver.Changes(ml.mountID, ml.cacheParent()) +} + +func (ml *mountedLayer) Metadata() (map[string]string, error) { + return ml.layerStore.driver.GetMetadata(ml.mountID) +} + +func (ml *mountedLayer) getReference() RWLayer { + ref := &referencedRWLayer{ + mountedLayer: ml, + } + ml.references[ref] = ref + + return ref +} + +func (ml *mountedLayer) hasReferences() bool { + return len(ml.references) > 0 +} + +func (ml *mountedLayer) deleteReference(ref RWLayer) error { + rl, ok := ml.references[ref] + if !ok { + return ErrLayerNotRetained + } + + if err := rl.release(); err != nil { + return err + } + delete(ml.references, ref) + + return nil +} + +type referencedRWLayer struct { + *mountedLayer + + activityL sync.Mutex + activityCount int +} + +func (rl *referencedRWLayer) release() error { + rl.activityL.Lock() + defer rl.activityL.Unlock() + + if rl.activityCount > 0 { + return ErrActiveMount + } + + rl.activityCount = -1 + + return nil +} + +func (rl *referencedRWLayer) Mount(mountLabel string) (string, error) { + rl.activityL.Lock() + defer rl.activityL.Unlock() + + if rl.activityCount == -1 { + return "", ErrLayerNotRetained + } + + rl.activityCount++ + return rl.mountedLayer.Mount(mountLabel) +} + +func (rl *referencedRWLayer) Unmount() error { + rl.activityL.Lock() + defer rl.activityL.Unlock() + + if rl.activityCount == 0 { + return ErrNotMounted + } + if rl.activityCount == -1 { + return ErrLayerNotRetained + } + rl.activityCount-- + + return rl.mountedLayer.Unmount() +} diff --git a/migrate/v1/migratev1.go b/migrate/v1/migratev1.go index a9110bca9c..c92c0f2107 100644 --- a/migrate/v1/migratev1.go +++ b/migrate/v1/migratev1.go @@ -24,8 +24,7 @@ type graphIDRegistrar interface { } type graphIDMounter interface { - MountByGraphID(string, string, layer.ChainID) (layer.RWLayer, error) - Unmount(string) error + CreateRWLayerByGraphID(string, string, layer.ChainID) error } const ( @@ -172,13 +171,7 @@ func migrateContainers(root string, ls graphIDMounter, is image.Store, imageMapp return err } - _, err = ls.MountByGraphID(id, id, img.RootFS.ChainID()) - if err != nil { - return err - } - - err = ls.Unmount(id) - if err != nil { + if err := ls.CreateRWLayerByGraphID(id, id, img.RootFS.ChainID()); err != nil { return err } diff --git a/migrate/v1/migratev1_test.go b/migrate/v1/migratev1_test.go index 067d04c382..5ee8206fdf 100644 --- a/migrate/v1/migratev1_test.go +++ b/migrate/v1/migratev1_test.go @@ -338,10 +338,9 @@ type mockMounter struct { count int } -func (r *mockMounter) MountByGraphID(name string, graphID string, parent layer.ChainID) (layer.RWLayer, error) { +func (r *mockMounter) CreateRWLayerByGraphID(name string, graphID string, parent layer.ChainID) error { r.mounts = append(r.mounts, mountInfo{name, graphID, string(parent)}) - r.count++ - return nil, nil + return nil } func (r *mockMounter) Unmount(string) error { r.count--