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 <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
Derek McGowan 2015-12-16 14:13:50 -08:00
parent 577cf61afa
commit d04fa49a0d
18 changed files with 427 additions and 275 deletions

View File

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

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()

9
layer/layer_unix.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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