mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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:
parent
577cf61afa
commit
d04fa49a0d
18 changed files with 427 additions and 275 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
9
layer/layer_unix.go
Normal 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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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--
|
||||
|
|
Loading…
Reference in a new issue