github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/images/image_builder.go (about)

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