daemon/build.go
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
 }