github.com/sams1990/dockerrepo@v17.12.1-ce-rc2+incompatible/daemon/build.go (about)

     1  package daemon
     2  
     3  import (
     4  	"io"
     5  	"runtime"
     6  
     7  	"github.com/docker/distribution/reference"
     8  	"github.com/docker/docker/api/types"
     9  	"github.com/docker/docker/api/types/backend"
    10  	"github.com/docker/docker/builder"
    11  	"github.com/docker/docker/image"
    12  	"github.com/docker/docker/layer"
    13  	"github.com/docker/docker/pkg/containerfs"
    14  	"github.com/docker/docker/pkg/idtools"
    15  	"github.com/docker/docker/pkg/stringid"
    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(os string) (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, layer.OS(os))
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  	// TODO: An optimization woudld 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, platform 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, platform, 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  		layer, err := newReleasableLayerForImage(nil, daemon.stores[opts.OS].layerStore)
   176  		return nil, layer, err
   177  	}
   178  
   179  	if opts.PullOption != backend.PullOptionForcePull {
   180  		image, err := daemon.GetImage(refOrID)
   181  		if err != nil && opts.PullOption == backend.PullOptionNoPull {
   182  			return nil, nil, err
   183  		}
   184  		// TODO: shouldn't we error out if error is different from "not found" ?
   185  		if image != nil {
   186  			layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore)
   187  			return image, layer, err
   188  		}
   189  	}
   190  
   191  	image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS)
   192  	if err != nil {
   193  		return nil, nil, err
   194  	}
   195  	layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore)
   196  	return image, layer, err
   197  }
   198  
   199  // CreateImage creates a new image by adding a config and ID to the image store.
   200  // This is similar to LoadImage() except that it receives JSON encoded bytes of
   201  // an image instead of a tar archive.
   202  func (daemon *Daemon) CreateImage(config []byte, parent string, platform string) (builder.Image, error) {
   203  	if platform == "" {
   204  		platform = runtime.GOOS
   205  	}
   206  	id, err := daemon.stores[platform].imageStore.Create(config)
   207  	if err != nil {
   208  		return nil, errors.Wrapf(err, "failed to create image")
   209  	}
   210  
   211  	if parent != "" {
   212  		if err := daemon.stores[platform].imageStore.SetParent(id, image.ID(parent)); err != nil {
   213  			return nil, errors.Wrapf(err, "failed to set parent %s", parent)
   214  		}
   215  	}
   216  
   217  	return daemon.stores[platform].imageStore.Get(id)
   218  }
   219  
   220  // IDMappings returns uid/gid mappings for the builder
   221  func (daemon *Daemon) IDMappings() *idtools.IDMappings {
   222  	return daemon.idMappings
   223  }