2017-03-27 21:36:28 -04:00
|
|
|
package daemon
|
|
|
|
|
|
|
|
import (
|
2017-05-25 17:03:29 -04:00
|
|
|
"io"
|
2017-06-20 13:34:55 -04:00
|
|
|
"runtime"
|
2017-05-25 17:03:29 -04:00
|
|
|
|
2017-03-27 21:36:28 -04:00
|
|
|
"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"
|
2017-05-14 14:18:48 -04:00
|
|
|
"github.com/docker/docker/pkg/idtools"
|
2017-03-27 21:36:28 -04:00
|
|
|
"github.com/docker/docker/pkg/stringid"
|
|
|
|
"github.com/docker/docker/registry"
|
|
|
|
"github.com/pkg/errors"
|
2017-07-26 17:42:13 -04:00
|
|
|
"github.com/sirupsen/logrus"
|
2017-03-27 21:36:28 -04:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
|
|
|
type releaseableLayer struct {
|
2017-06-19 20:15:23 -04:00
|
|
|
released bool
|
2017-05-05 18:52:11 -04:00
|
|
|
layerStore layer.Store
|
|
|
|
roLayer layer.Layer
|
|
|
|
rwLayer layer.RWLayer
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
|
|
|
|
2017-05-05 18:52:11 -04:00
|
|
|
func (rl *releaseableLayer) Mount() (string, error) {
|
2017-03-27 21:36:28 -04:00
|
|
|
var err error
|
2017-07-19 17:35:23 -04:00
|
|
|
var mountPath string
|
2017-05-25 17:03:29 -04:00
|
|
|
var chainID layer.ChainID
|
|
|
|
if rl.roLayer != nil {
|
|
|
|
chainID = rl.roLayer.ChainID()
|
|
|
|
}
|
|
|
|
|
2017-05-05 18:52:11 -04:00
|
|
|
mountID := stringid.GenerateRandomID()
|
2017-05-25 17:03:29 -04:00
|
|
|
rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, nil)
|
2017-03-27 21:36:28 -04:00
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "failed to create rwlayer")
|
|
|
|
}
|
|
|
|
|
2017-07-19 17:35:23 -04:00
|
|
|
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
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return mountPath, nil
|
2017-05-05 18:52:11 -04:00
|
|
|
}
|
|
|
|
|
2017-06-20 13:34:55 -04:00
|
|
|
func (rl *releaseableLayer) Commit(platform string) (builder.ReleaseableLayer, error) {
|
2017-05-25 17:03:29 -04:00
|
|
|
var chainID layer.ChainID
|
|
|
|
if rl.roLayer != nil {
|
|
|
|
chainID = rl.roLayer.ChainID()
|
|
|
|
}
|
|
|
|
|
|
|
|
stream, err := rl.rwLayer.TarStream()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-07-19 17:35:23 -04:00
|
|
|
defer stream.Close()
|
2017-05-25 17:03:29 -04:00
|
|
|
|
2017-06-20 13:34:55 -04:00
|
|
|
newLayer, err := rl.layerStore.Register(stream, chainID, layer.Platform(platform))
|
2017-05-25 17:03:29 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if layer.IsEmpty(newLayer.DiffID()) {
|
|
|
|
_, err := rl.layerStore.Release(newLayer)
|
|
|
|
return &releaseableLayer{layerStore: rl.layerStore}, err
|
|
|
|
}
|
|
|
|
return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil
|
2017-05-05 18:52:11 -04:00
|
|
|
}
|
|
|
|
|
2017-05-14 14:18:48 -04:00
|
|
|
func (rl *releaseableLayer) DiffID() layer.DiffID {
|
2017-05-25 17:03:29 -04:00
|
|
|
if rl.roLayer == nil {
|
|
|
|
return layer.DigestSHA256EmptyTar
|
|
|
|
}
|
2017-05-14 14:18:48 -04:00
|
|
|
return rl.roLayer.DiffID()
|
|
|
|
}
|
|
|
|
|
2017-05-25 17:03:29 -04:00
|
|
|
func (rl *releaseableLayer) Release() error {
|
2017-06-19 20:15:23 -04:00
|
|
|
if rl.released {
|
|
|
|
return nil
|
|
|
|
}
|
2017-07-19 17:35:23 -04:00
|
|
|
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
|
|
|
|
}
|
2017-06-19 20:15:23 -04:00
|
|
|
rl.released = true
|
2017-07-19 17:35:23 -04:00
|
|
|
return nil
|
2017-05-25 17:03:29 -04:00
|
|
|
}
|
|
|
|
|
2017-05-05 18:52:11 -04:00
|
|
|
func (rl *releaseableLayer) releaseRWLayer() error {
|
|
|
|
if rl.rwLayer == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2017-07-19 17:35:23 -04:00
|
|
|
if err := rl.rwLayer.Unmount(); err != nil {
|
|
|
|
logrus.Errorf("Failed to unmount RWLayer: %s", err)
|
|
|
|
return err
|
|
|
|
}
|
2017-05-05 18:52:11 -04:00
|
|
|
metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
|
|
|
|
layer.LogReleaseMetadata(metadata)
|
2017-03-27 21:36:28 -04:00
|
|
|
if err != nil {
|
2017-05-05 18:52:11 -04:00
|
|
|
logrus.Errorf("Failed to release RWLayer: %s", err)
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
2017-07-19 17:35:23 -04:00
|
|
|
rl.rwLayer = nil
|
2017-05-05 18:52:11 -04:00
|
|
|
return err
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
|
|
|
|
2017-05-05 18:52:11 -04:00
|
|
|
func (rl *releaseableLayer) releaseROLayer() error {
|
|
|
|
if rl.roLayer == nil {
|
|
|
|
return nil
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
2017-05-05 18:52:11 -04:00
|
|
|
metadata, err := rl.layerStore.Release(rl.roLayer)
|
|
|
|
layer.LogReleaseMetadata(metadata)
|
2017-07-19 17:35:23 -04:00
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Failed to release ROLayer: %s", err)
|
|
|
|
}
|
|
|
|
rl.roLayer = nil
|
2017-05-05 18:52:11 -04:00
|
|
|
return err
|
|
|
|
}
|
2017-03-27 21:36:28 -04:00
|
|
|
|
2017-05-05 18:52:11 -04:00
|
|
|
func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (builder.ReleaseableLayer, error) {
|
2017-05-25 17:03:29 -04:00
|
|
|
if img == nil || img.RootFS.ChainID() == "" {
|
|
|
|
return &releaseableLayer{layerStore: layerStore}, nil
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
2017-05-05 18:52:11 -04:00
|
|
|
// 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())
|
|
|
|
}
|
|
|
|
return &releaseableLayer{layerStore: layerStore, roLayer: roLayer}, nil
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: could this use the regular daemon PullImage ?
|
2017-05-17 20:08:01 -04:00
|
|
|
func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, platform string) (*image.Image, error) {
|
2017-03-27 21:36:28 -04:00
|
|
|
ref, err := reference.ParseNormalizedNamed(name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ref = reference.TagNameOnly(ref)
|
|
|
|
|
|
|
|
pullRegistryAuth := &types.AuthConfig{}
|
|
|
|
if len(authConfigs) > 0 {
|
2017-05-05 18:52:11 -04:00
|
|
|
// The request came with a full auth config, use it
|
2017-03-27 21:36:28 -04:00
|
|
|
repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
resolvedConfig := registry.ResolveAuthConfig(authConfigs, repoInfo.Index)
|
|
|
|
pullRegistryAuth = &resolvedConfig
|
|
|
|
}
|
|
|
|
|
2017-05-17 20:08:01 -04:00
|
|
|
if err := daemon.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
|
2017-03-27 21:36:28 -04:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return daemon.GetImage(name)
|
|
|
|
}
|
|
|
|
|
2017-05-05 18:52:11 -04:00
|
|
|
// 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) {
|
2017-05-25 17:03:29 -04:00
|
|
|
if refOrID == "" {
|
2017-06-20 13:34:55 -04:00
|
|
|
layer, err := newReleasableLayerForImage(nil, daemon.stores[opts.Platform].layerStore)
|
2017-05-25 17:03:29 -04:00
|
|
|
return nil, layer, err
|
|
|
|
}
|
|
|
|
|
2017-06-19 20:15:23 -04:00
|
|
|
if opts.PullOption != backend.PullOptionForcePull {
|
|
|
|
image, err := daemon.GetImage(refOrID)
|
|
|
|
if err != nil && opts.PullOption == backend.PullOptionNoPull {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2017-03-27 21:36:28 -04:00
|
|
|
// TODO: shouldn't we error out if error is different from "not found" ?
|
|
|
|
if image != nil {
|
2017-05-17 20:08:01 -04:00
|
|
|
layer, err := newReleasableLayerForImage(image, daemon.stores[opts.Platform].layerStore)
|
2017-05-05 18:52:11 -04:00
|
|
|
return image, layer, err
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 20:08:01 -04:00
|
|
|
image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.Platform)
|
2017-05-05 18:52:11 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2017-05-17 20:08:01 -04:00
|
|
|
layer, err := newReleasableLayerForImage(image, daemon.stores[opts.Platform].layerStore)
|
2017-05-05 18:52:11 -04:00
|
|
|
return image, layer, err
|
2017-03-27 21:36:28 -04:00
|
|
|
}
|
2017-05-14 14:18:48 -04:00
|
|
|
|
|
|
|
// 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.
|
2017-06-20 13:34:55 -04:00
|
|
|
func (daemon *Daemon) CreateImage(config []byte, parent string, platform string) (builder.Image, error) {
|
|
|
|
if platform == "" {
|
|
|
|
platform = runtime.GOOS
|
|
|
|
}
|
|
|
|
id, err := daemon.stores[platform].imageStore.Create(config)
|
2017-05-14 14:18:48 -04:00
|
|
|
if err != nil {
|
2017-05-25 17:03:29 -04:00
|
|
|
return nil, errors.Wrapf(err, "failed to create image")
|
2017-05-14 14:18:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if parent != "" {
|
2017-06-20 13:34:55 -04:00
|
|
|
if err := daemon.stores[platform].imageStore.SetParent(id, image.ID(parent)); err != nil {
|
2017-05-25 17:03:29 -04:00
|
|
|
return nil, errors.Wrapf(err, "failed to set parent %s", parent)
|
2017-05-14 14:18:48 -04:00
|
|
|
}
|
|
|
|
}
|
2017-05-25 17:03:29 -04:00
|
|
|
|
2017-06-20 13:34:55 -04:00
|
|
|
return daemon.stores[platform].imageStore.Get(id)
|
2017-05-14 14:18:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// IDMappings returns uid/gid mappings for the builder
|
|
|
|
func (daemon *Daemon) IDMappings() *idtools.IDMappings {
|
|
|
|
return daemon.idMappings
|
|
|
|
}
|