github.com/rhatdan/docker@v0.7.7-0.20180119204836-47a0dcbcd20a/daemon/build.go (about)

     1  package daemon
     2  
     3  import (
     4  	"io"
     5  
     6  	"github.com/docker/distribution/reference"
     7  	"github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/api/types/backend"
     9  	"github.com/docker/docker/builder"
    10  	"github.com/docker/docker/image"
    11  	"github.com/docker/docker/layer"
    12  	"github.com/docker/docker/pkg/containerfs"
    13  	"github.com/docker/docker/pkg/idtools"
    14  	"github.com/docker/docker/pkg/stringid"
    15  	"github.com/docker/docker/pkg/system"
    16  	"github.com/docker/docker/registry"
    17  	"github.com/pkg/errors"
    18  	"github.com/sirupsen/logrus"
    19  	"golang.org/x/net/context"
    20  )
    21  
    22  type releaseableLayer struct {
    23  	released   bool
    24  	layerStore layer.Store
    25  	roLayer    layer.Layer
    26  	rwLayer    layer.RWLayer
    27  }
    28  
    29  func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) {
    30  	var err error
    31  	var mountPath containerfs.ContainerFS
    32  	var chainID layer.ChainID
    33  	if rl.roLayer != nil {
    34  		chainID = rl.roLayer.ChainID()
    35  	}
    36  
    37  	mountID := stringid.GenerateRandomID()
    38  	rl.rwLayer, err = rl.layerStore.CreateRWLayer(mountID, chainID, nil)
    39  	if err != nil {
    40  		return nil, errors.Wrap(err, "failed to create rwlayer")
    41  	}
    42  
    43  	mountPath, err = rl.rwLayer.Mount("")
    44  	if err != nil {
    45  		// Clean up the layer if we fail to mount it here.
    46  		metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
    47  		layer.LogReleaseMetadata(metadata)
    48  		if err != nil {
    49  			logrus.Errorf("Failed to release RWLayer: %s", err)
    50  		}
    51  		rl.rwLayer = nil
    52  		return nil, err
    53  	}
    54  
    55  	return mountPath, nil
    56  }
    57  
    58  func (rl *releaseableLayer) Commit() (builder.ReleaseableLayer, error) {
    59  	var chainID layer.ChainID
    60  	if rl.roLayer != nil {
    61  		chainID = rl.roLayer.ChainID()
    62  	}
    63  
    64  	stream, err := rl.rwLayer.TarStream()
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	defer stream.Close()
    69  
    70  	newLayer, err := rl.layerStore.Register(stream, chainID)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	// TODO: An optimization would be to handle empty layers before returning
    75  	return &releaseableLayer{layerStore: rl.layerStore, roLayer: newLayer}, nil
    76  }
    77  
    78  func (rl *releaseableLayer) DiffID() layer.DiffID {
    79  	if rl.roLayer == nil {
    80  		return layer.DigestSHA256EmptyTar
    81  	}
    82  	return rl.roLayer.DiffID()
    83  }
    84  
    85  func (rl *releaseableLayer) Release() error {
    86  	if rl.released {
    87  		return nil
    88  	}
    89  	if err := rl.releaseRWLayer(); err != nil {
    90  		// Best effort attempt at releasing read-only layer before returning original error.
    91  		rl.releaseROLayer()
    92  		return err
    93  	}
    94  	if err := rl.releaseROLayer(); err != nil {
    95  		return err
    96  	}
    97  	rl.released = true
    98  	return nil
    99  }
   100  
   101  func (rl *releaseableLayer) releaseRWLayer() error {
   102  	if rl.rwLayer == nil {
   103  		return nil
   104  	}
   105  	if err := rl.rwLayer.Unmount(); err != nil {
   106  		logrus.Errorf("Failed to unmount RWLayer: %s", err)
   107  		return err
   108  	}
   109  	metadata, err := rl.layerStore.ReleaseRWLayer(rl.rwLayer)
   110  	layer.LogReleaseMetadata(metadata)
   111  	if err != nil {
   112  		logrus.Errorf("Failed to release RWLayer: %s", err)
   113  	}
   114  	rl.rwLayer = nil
   115  	return err
   116  }
   117  
   118  func (rl *releaseableLayer) releaseROLayer() error {
   119  	if rl.roLayer == nil {
   120  		return nil
   121  	}
   122  	metadata, err := rl.layerStore.Release(rl.roLayer)
   123  	layer.LogReleaseMetadata(metadata)
   124  	if err != nil {
   125  		logrus.Errorf("Failed to release ROLayer: %s", err)
   126  	}
   127  	rl.roLayer = nil
   128  	return err
   129  }
   130  
   131  func newReleasableLayerForImage(img *image.Image, layerStore layer.Store) (builder.ReleaseableLayer, error) {
   132  	if img == nil || img.RootFS.ChainID() == "" {
   133  		return &releaseableLayer{layerStore: layerStore}, nil
   134  	}
   135  	// Hold a reference to the image layer so that it can't be removed before
   136  	// it is released
   137  	roLayer, err := layerStore.Get(img.RootFS.ChainID())
   138  	if err != nil {
   139  		return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID())
   140  	}
   141  	return &releaseableLayer{layerStore: layerStore, roLayer: roLayer}, nil
   142  }
   143  
   144  // TODO: could this use the regular daemon PullImage ?
   145  func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfigs map[string]types.AuthConfig, output io.Writer, os string) (*image.Image, error) {
   146  	ref, err := reference.ParseNormalizedNamed(name)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  	ref = reference.TagNameOnly(ref)
   151  
   152  	pullRegistryAuth := &types.AuthConfig{}
   153  	if len(authConfigs) > 0 {
   154  		// The request came with a full auth config, use it
   155  		repoInfo, err := daemon.RegistryService.ResolveRepository(ref)
   156  		if err != nil {
   157  			return nil, err
   158  		}
   159  
   160  		resolvedConfig := registry.ResolveAuthConfig(authConfigs, repoInfo.Index)
   161  		pullRegistryAuth = &resolvedConfig
   162  	}
   163  
   164  	if err := daemon.pullImageWithReference(ctx, ref, os, nil, pullRegistryAuth, output); err != nil {
   165  		return nil, err
   166  	}
   167  	return daemon.GetImage(name)
   168  }
   169  
   170  // GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
   171  // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
   172  // leaking of layers.
   173  func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
   174  	if refOrID == "" {
   175  		if !system.IsOSSupported(opts.OS) {
   176  			return nil, nil, system.ErrNotSupportedOperatingSystem
   177  		}
   178  		layer, err := newReleasableLayerForImage(nil, daemon.layerStores[opts.OS])
   179  		return nil, layer, err
   180  	}
   181  
   182  	if opts.PullOption != backend.PullOptionForcePull {
   183  		image, err := daemon.GetImage(refOrID)
   184  		if err != nil && opts.PullOption == backend.PullOptionNoPull {
   185  			return nil, nil, err
   186  		}
   187  		// TODO: shouldn't we error out if error is different from "not found" ?
   188  		if image != nil {
   189  			if !system.IsOSSupported(image.OperatingSystem()) {
   190  				return nil, nil, system.ErrNotSupportedOperatingSystem
   191  			}
   192  			layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
   193  			return image, layer, err
   194  		}
   195  	}
   196  
   197  	image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS)
   198  	if err != nil {
   199  		return nil, nil, err
   200  	}
   201  	if !system.IsOSSupported(image.OperatingSystem()) {
   202  		return nil, nil, system.ErrNotSupportedOperatingSystem
   203  	}
   204  	layer, err := newReleasableLayerForImage(image, daemon.layerStores[image.OperatingSystem()])
   205  	return image, layer, err
   206  }
   207  
   208  // CreateImage creates a new image by adding a config and ID to the image store.
   209  // This is similar to LoadImage() except that it receives JSON encoded bytes of
   210  // an image instead of a tar archive.
   211  func (daemon *Daemon) CreateImage(config []byte, parent string) (builder.Image, error) {
   212  	id, err := daemon.imageStore.Create(config)
   213  	if err != nil {
   214  		return nil, errors.Wrapf(err, "failed to create image")
   215  	}
   216  
   217  	if parent != "" {
   218  		if err := daemon.imageStore.SetParent(id, image.ID(parent)); err != nil {
   219  			return nil, errors.Wrapf(err, "failed to set parent %s", parent)
   220  		}
   221  	}
   222  
   223  	return daemon.imageStore.Get(id)
   224  }
   225  
   226  // IDMappings returns uid/gid mappings for the builder
   227  func (daemon *Daemon) IDMappings() *idtools.IDMappings {
   228  	return daemon.idMappings
   229  }