|
@@ -15,130 +15,126 @@ import (
|
|
|
"github.com/docker/docker/pkg/system"
|
|
|
"github.com/docker/docker/registry"
|
|
|
"github.com/pkg/errors"
|
|
|
- "github.com/sirupsen/logrus"
|
|
|
"golang.org/x/net/context"
|
|
|
)
|
|
|
|
|
|
-type releaseableLayer struct {
|
|
|
+type roLayer struct {
|
|
|
released bool
|
|
|
layerStore layer.Store
|
|
|
roLayer layer.Layer
|
|
|
- rwLayer layer.RWLayer
|
|
|
}
|
|
|
|
|
|
-func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) {
|
|
|
- var err error
|
|
|
- var mountPath containerfs.ContainerFS
|
|
|
- var chainID layer.ChainID
|
|
|
- if rl.roLayer != nil {
|
|
|
- chainID = rl.roLayer.ChainID()
|
|
|
+func (l *roLayer) DiffID() layer.DiffID {
|
|
|
+ if l.roLayer == nil {
|
|
|
+ return layer.DigestSHA256EmptyTar
|
|
|
}
|
|
|
+ return l.roLayer.DiffID()
|
|
|
+}
|
|
|
|
|
|
- mountID := stringid.GenerateRandomID()
|
|
|
- rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, nil)
|
|
|
- if err != nil {
|
|
|
- return nil, errors.Wrap(err, "failed to create rwlayer")
|
|
|
+func (l *roLayer) Release() error {
|
|
|
+ if l.released {
|
|
|
+ return nil
|
|
|
}
|
|
|
-
|
|
|
- mountPath, err = rl.rwLayer.Mount("")
|
|
|
- if err != nil {
|
|
|
- // Clean up the layer if we fail to mount it here.
|
|
|
- metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
|
|
|
+ if l.roLayer != nil {
|
|
|
+ metadata, err := l.layerStore.Release(l.roLayer)
|
|
|
layer.LogReleaseMetadata(metadata)
|
|
|
if err != nil {
|
|
|
- logrus.Errorf("Failed to release RWLayer: %s", err)
|
|
|
+ return errors.Wrap(err, "failed to release ROLayer")
|
|
|
}
|
|
|
- rl.rwLayer = nil
|
|
|
- return nil, err
|
|
|
}
|
|
|
-
|
|
|
- return mountPath, nil
|
|
|
+ l.roLayer = nil
|
|
|
+ l.released = true
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
-func (rl *releaseableLayer) Commit() (builder.ReleaseableLayer, error) {
|
|
|
+func (l *roLayer) NewRWLayer() (builder.RWLayer, error) {
|
|
|
var chainID layer.ChainID
|
|
|
- if rl.roLayer != nil {
|
|
|
- chainID = rl.roLayer.ChainID()
|
|
|
+ if l.roLayer != nil {
|
|
|
+ chainID = l.roLayer.ChainID()
|
|
|
}
|
|
|
|
|
|
- stream, err := rl.rwLayer.TarStream()
|
|
|
+ mountID := stringid.GenerateRandomID()
|
|
|
+ newLayer, err := l.layerStore.CreateRWLayer(mountID, chainID, nil)
|
|
|
if err != nil {
|
|
|
- return nil, err
|
|
|
+ return nil, errors.Wrap(err, "failed to create rwlayer")
|
|
|
}
|
|
|
- defer stream.Close()
|
|
|
|
|
|
- newLayer, err := rl.layerStore.Register(stream, chainID)
|
|
|
+ rwLayer := &rwLayer{layerStore: l.layerStore, rwLayer: newLayer}
|
|
|
+
|
|
|
+ fs, err := newLayer.Mount("")
|
|
|
if err != nil {
|
|
|
+ rwLayer.Release()
|
|
|
return nil, err
|
|
|
}
|
|
|
- // TODO: An optimization would be to handle empty layers before returning
|
|
|
- return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil
|
|
|
+
|
|
|
+ rwLayer.fs = fs
|
|
|
+
|
|
|
+ return rwLayer, nil
|
|
|
}
|
|
|
|
|
|
-func (rl *releaseableLayer) DiffID() layer.DiffID {
|
|
|
- if rl.roLayer == nil {
|
|
|
- return layer.DigestSHA256EmptyTar
|
|
|
- }
|
|
|
- return rl.roLayer.DiffID()
|
|
|
+type rwLayer struct {
|
|
|
+ released bool
|
|
|
+ layerStore layer.Store
|
|
|
+ rwLayer layer.RWLayer
|
|
|
+ fs containerfs.ContainerFS
|
|
|
}
|
|
|
|
|
|
-func (rl *releaseableLayer) Release() error {
|
|
|
- if rl.released {
|
|
|
- return nil
|
|
|
- }
|
|
|
- if err := rl.releaseRWLayer(); err != nil {
|
|
|
- // Best effort attempt at releasing read-only layer before returning original error.
|
|
|
- rl.releaseROLayer()
|
|
|
- return err
|
|
|
- }
|
|
|
- if err := rl.releaseROLayer(); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- rl.released = true
|
|
|
- return nil
|
|
|
+func (l *rwLayer) Root() containerfs.ContainerFS {
|
|
|
+ return l.fs
|
|
|
}
|
|
|
|
|
|
-func (rl *releaseableLayer) releaseRWLayer() error {
|
|
|
- if rl.rwLayer == nil {
|
|
|
- return nil
|
|
|
+func (l *rwLayer) Commit() (builder.ROLayer, error) {
|
|
|
+ stream, err := l.rwLayer.TarStream()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- if err := rl.rwLayer.Unmount(); err != nil {
|
|
|
- logrus.Errorf("Failed to unmount RWLayer: %s", err)
|
|
|
- return err
|
|
|
+ defer stream.Close()
|
|
|
+
|
|
|
+ var chainID layer.ChainID
|
|
|
+ if parent := l.rwLayer.Parent(); parent != nil {
|
|
|
+ chainID = parent.ChainID()
|
|
|
}
|
|
|
- metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
|
|
|
- layer.LogReleaseMetadata(metadata)
|
|
|
+
|
|
|
+ newLayer, err := l.layerStore.Register(stream, chainID)
|
|
|
if err != nil {
|
|
|
- logrus.Errorf("Failed to release RWLayer: %s", err)
|
|
|
+ return nil, err
|
|
|
}
|
|
|
- rl.rwLayer = nil
|
|
|
- return err
|
|
|
+ // TODO: An optimization would be to handle empty layers before returning
|
|
|
+ return &roLayer{layerStore: l.layerStore, roLayer: newLayer}, nil
|
|
|
}
|
|
|
|
|
|
-func (rl *releaseableLayer) releaseROLayer() error {
|
|
|
- if rl.roLayer == nil {
|
|
|
+func (l *rwLayer) Release() error {
|
|
|
+ if l.released {
|
|
|
return nil
|
|
|
}
|
|
|
- metadata, err := rl.layerStore.Release(rl.roLayer)
|
|
|
+
|
|
|
+ if l.fs != nil {
|
|
|
+ if err := l.rwLayer.Unmount(); err != nil {
|
|
|
+ return errors.Wrap(err, "failed to unmount RWLayer")
|
|
|
+ }
|
|
|
+ l.fs = nil
|
|
|
+ }
|
|
|
+
|
|
|
+ metadata, err := l.layerStore.ReleaseRWLayer(l.rwLayer)
|
|
|
layer.LogReleaseMetadata(metadata)
|
|
|
if err != nil {
|
|
|
- logrus.Errorf("Failed to release ROLayer: %s", err)
|
|
|
+ return errors.Wrap(err, "failed to release RWLayer")
|
|
|
}
|
|
|
- rl.roLayer = nil
|
|
|
- return err
|
|
|
+ l.released = true
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
-func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (builder.ReleaseableLayer, error) {
|
|
|
+func newROLayerForImage(img *image.Image, layerStore layer.Store) (builder.ROLayer, error) {
|
|
|
if img == nil || img.RootFS.ChainID() == "" {
|
|
|
- return &releaseableLayer{layerStore: layerStore}, nil
|
|
|
+ return &roLayer{layerStore: layerStore}, nil
|
|
|
}
|
|
|
// Hold a reference to the image layer so that it can't be removed before
|
|
|
// it is released
|
|
|
- roLayer, err := layerStore.Get(img.RootFS.ChainID())
|
|
|
+ layer, err := layerStore.Get(img.RootFS.ChainID())
|
|
|
if err != nil {
|
|
|
return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID())
|
|
|
}
|
|
|
- return &releaseableLayer{layerStore: layerStore, roLayer: roLayer}, nil
|
|
|
+ return &roLayer{layerStore: layerStore, roLayer: layer}, nil
|
|
|
}
|
|
|
|
|
|
// TODO: could this use the regular daemon PullImage ?
|
|
@@ -170,12 +166,12 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi
|
|
|
// GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
|
|
|
// Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
|
|
|
// leaking of layers.
|
|
|
-func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
|
|
|
+func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
|
|
|
if refOrID == "" {
|
|
|
if !system.IsOSSupported(opts.OS) {
|
|
|
return nil, nil, system.ErrNotSupportedOperatingSystem
|
|
|
}
|
|
|
- layer, err := newReleasableLayerForImage(nil, daemon.layerStores[opts.OS])
|
|
|
+ layer, err := newROLayerForImage(nil, daemon.layerStores[opts.OS])
|
|
|
return nil, layer, err
|
|
|
}
|
|
|
|
|
@@ -189,7 +185,7 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
|
|
|
if !system.IsOSSupported(image.OperatingSystem()) {
|
|
|
return nil, nil, system.ErrNotSupportedOperatingSystem
|
|
|
}
|
|
|
- layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
|
|
|
+ layer, err := newROLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
|
|
|
return image, layer, err
|
|
|
}
|
|
|
}
|
|
@@ -201,7 +197,7 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
|
|
|
if !system.IsOSSupported(image.OperatingSystem()) {
|
|
|
return nil, nil, system.ErrNotSupportedOperatingSystem
|
|
|
}
|
|
|
- layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
|
|
|
+ layer, err := newROLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
|
|
|
return image, layer, err
|
|
|
}
|
|
|
|