b3bc7b28 |
package daemon
import ( |
51360965 |
"io"
|
b3bc7b28 |
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/builder"
"github.com/docker/docker/image"
"github.com/docker/docker/layer" |
7a7357da |
"github.com/docker/docker/pkg/containerfs" |
bd5f92d2 |
"github.com/docker/docker/pkg/idtools" |
b3bc7b28 |
"github.com/docker/docker/pkg/stringid" |
0cba7740 |
"github.com/docker/docker/pkg/system" |
b3bc7b28 |
"github.com/docker/docker/registry"
"github.com/pkg/errors" |
1009e6a4 |
"github.com/sirupsen/logrus" |
b3bc7b28 |
"golang.org/x/net/context"
)
type releaseableLayer struct { |
c268d9da |
released bool |
6c28e8ed |
layerStore layer.Store
roLayer layer.Layer
rwLayer layer.RWLayer |
b3bc7b28 |
}
|
7a7357da |
func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) { |
b3bc7b28 |
var err error |
7a7357da |
var mountPath containerfs.ContainerFS |
51360965 |
var chainID layer.ChainID
if rl.roLayer != nil {
chainID = rl.roLayer.ChainID()
}
|
6c28e8ed |
mountID := stringid.GenerateRandomID() |
afd305c4 |
rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, nil) |
b3bc7b28 |
if err != nil { |
7a7357da |
return nil, errors.Wrap(err, "failed to create rwlayer") |
b3bc7b28 |
}
|
1d457999 |
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)
layer.LogReleaseMetadata(metadata)
if err != nil {
logrus.Errorf("Failed to release RWLayer: %s", err)
}
rl.rwLayer = nil |
7a7357da |
return nil, err |
1d457999 |
}
return mountPath, nil |
6c28e8ed |
}
|
afd305c4 |
func (rl *releaseableLayer) Commit() (builder.ReleaseableLayer, error) { |
51360965 |
var chainID layer.ChainID
if rl.roLayer != nil {
chainID = rl.roLayer.ChainID()
}
stream, err := rl.rwLayer.TarStream()
if err != nil {
return nil, err
} |
1d457999 |
defer stream.Close() |
51360965 |
|
afd305c4 |
newLayer, err := rl.layerStore.Register(stream, chainID) |
51360965 |
if err != nil {
return nil, err
} |
0cba7740 |
// TODO: An optimization would be to handle empty layers before returning |
afd305c4 |
return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil |
6c28e8ed |
}
|
bd5f92d2 |
func (rl *releaseableLayer) DiffID() layer.DiffID { |
51360965 |
if rl.roLayer == nil {
return layer.DigestSHA256EmptyTar
} |
bd5f92d2 |
return rl.roLayer.DiffID()
}
|
51360965 |
func (rl *releaseableLayer) Release() error { |
c268d9da |
if rl.released {
return nil
} |
1d457999 |
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
} |
c268d9da |
rl.released = true |
1d457999 |
return nil |
51360965 |
}
|
6c28e8ed |
func (rl *releaseableLayer) releaseRWLayer() error {
if rl.rwLayer == nil {
return nil
} |
1d457999 |
if err := rl.rwLayer.Unmount(); err != nil {
logrus.Errorf("Failed to unmount RWLayer: %s", err)
return err
} |
6c28e8ed |
metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
layer.LogReleaseMetadata(metadata) |
b3bc7b28 |
if err != nil { |
6c28e8ed |
logrus.Errorf("Failed to release RWLayer: %s", err) |
b3bc7b28 |
} |
1d457999 |
rl.rwLayer = nil |
6c28e8ed |
return err |
b3bc7b28 |
}
|
6c28e8ed |
func (rl *releaseableLayer) releaseROLayer() error {
if rl.roLayer == nil {
return nil |
b3bc7b28 |
} |
6c28e8ed |
metadata, err := rl.layerStore.Release(rl.roLayer)
layer.LogReleaseMetadata(metadata) |
1d457999 |
if err != nil {
logrus.Errorf("Failed to release ROLayer: %s", err)
}
rl.roLayer = nil |
6c28e8ed |
return err
} |
b3bc7b28 |
|
afd305c4 |
func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (builder.ReleaseableLayer, error) { |
51360965 |
if img == nil || img.RootFS.ChainID() == "" { |
afd305c4 |
return &releaseableLayer{layerStore: layerStore}, nil |
b3bc7b28 |
} |
6c28e8ed |
// 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())
if err != nil {
return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID())
} |
afd305c4 |
return &releaseableLayer{layerStore: layerStore, roLayer: roLayer}, nil |
b3bc7b28 |
}
// TODO: could this use the regular daemon PullImage ? |
ce8e529e |
func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) { |
b3bc7b28 |
ref, err := reference.ParseNormalizedNamed(name)
if err != nil {
return nil, err
}
ref = reference.TagNameOnly(ref)
pullRegistryAuth := &types.AuthConfig{}
if len(authConfigs) > 0 { |
6c28e8ed |
// The request came with a full auth config, use it |
b3bc7b28 |
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
if err != nil {
return nil, err
}
resolvedConfig := registry.ResolveAuthConfig(authConfigs, repoInfo.Index)
pullRegistryAuth = &resolvedConfig
}
|
ce8e529e |
if err := daemon.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil { |
b3bc7b28 |
return nil, err
}
return daemon.GetImage(name)
}
|
6c28e8ed |
// 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) { |
51360965 |
if refOrID == "" { |
0cba7740 |
if !system.IsOSSupported(opts.OS) {
return nil, nil, system.ErrNotSupportedOperatingSystem
} |
afd305c4 |
layer, err := newReleasableLayerForImage(nil, daemon.layerStores[opts.OS]) |
51360965 |
return nil, layer, err
}
|
c268d9da |
if opts.PullOption != backend.PullOptionForcePull {
image, err := daemon.GetImage(refOrID)
if err != nil && opts.PullOption == backend.PullOptionNoPull {
return nil, nil, err
} |
b3bc7b28 |
// TODO: shouldn't we error out if error is different from "not found" ?
if image != nil { |
0cba7740 |
if !system.IsOSSupported(image.OperatingSystem()) {
return nil, nil, system.ErrNotSupportedOperatingSystem
} |
afd305c4 |
layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) |
6c28e8ed |
return image, layer, err |
b3bc7b28 |
}
}
|
0380fbff |
image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS) |
6c28e8ed |
if err != nil {
return nil, nil, err
} |
0cba7740 |
if !system.IsOSSupported(image.OperatingSystem()) {
return nil, nil, system.ErrNotSupportedOperatingSystem
} |
afd305c4 |
layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()]) |
6c28e8ed |
return image, layer, err |
b3bc7b28 |
} |
bd5f92d2 |
// CreateImage creates a new image by adding a config and ID to the image store.
// This is similar to LoadImage() except that it receives JSON encoded bytes of
// an image instead of a tar archive. |
ce8e529e |
func (daemon *Daemon) CreateImage(config []byte, parent string) (builder.Image, error) {
id, err := daemon.imageStore.Create(config) |
bd5f92d2 |
if err != nil { |
51360965 |
return nil, errors.Wrapf(err, "failed to create image") |
bd5f92d2 |
}
if parent != "" { |
ce8e529e |
if err := daemon.imageStore.SetParent(id, image.ID(parent)); err != nil { |
51360965 |
return nil, errors.Wrapf(err, "failed to set parent %s", parent) |
bd5f92d2 |
}
} |
51360965 |
|
ce8e529e |
return daemon.imageStore.Get(id) |
bd5f92d2 |
}
// IDMappings returns uid/gid mappings for the builder
func (daemon *Daemon) IDMappings() *idtools.IDMappings {
return daemon.idMappings
} |