github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/builder/dockerfile/imagecontext.go (about)

     1  package dockerfile
     2  
     3  import (
     4  	"strconv"
     5  	"strings"
     6  
     7  	"github.com/Sirupsen/logrus"
     8  	"github.com/docker/docker/api/types/backend"
     9  	"github.com/docker/docker/api/types/container"
    10  	"github.com/docker/docker/builder"
    11  	"github.com/docker/docker/builder/remotecontext"
    12  	"github.com/pkg/errors"
    13  	"golang.org/x/net/context"
    14  )
    15  
    16  type buildStage struct {
    17  	id     string
    18  	config *container.Config
    19  }
    20  
    21  func newBuildStageFromImage(image builder.Image) *buildStage {
    22  	return &buildStage{id: image.ImageID(), config: image.RunConfig()}
    23  }
    24  
    25  func (b *buildStage) ImageID() string {
    26  	return b.id
    27  }
    28  
    29  func (b *buildStage) RunConfig() *container.Config {
    30  	return b.config
    31  }
    32  
    33  func (b *buildStage) update(imageID string, runConfig *container.Config) {
    34  	b.id = imageID
    35  	b.config = runConfig
    36  }
    37  
    38  var _ builder.Image = &buildStage{}
    39  
    40  // buildStages tracks each stage of a build so they can be retrieved by index
    41  // or by name.
    42  type buildStages struct {
    43  	sequence []*buildStage
    44  	byName   map[string]*buildStage
    45  }
    46  
    47  func newBuildStages() *buildStages {
    48  	return &buildStages{byName: make(map[string]*buildStage)}
    49  }
    50  
    51  func (s *buildStages) getByName(name string) (builder.Image, bool) {
    52  	stage, ok := s.byName[strings.ToLower(name)]
    53  	return stage, ok
    54  }
    55  
    56  func (s *buildStages) get(indexOrName string) (builder.Image, error) {
    57  	index, err := strconv.Atoi(indexOrName)
    58  	if err == nil {
    59  		if err := s.validateIndex(index); err != nil {
    60  			return nil, err
    61  		}
    62  		return s.sequence[index], nil
    63  	}
    64  	if im, ok := s.byName[strings.ToLower(indexOrName)]; ok {
    65  		return im, nil
    66  	}
    67  	return nil, nil
    68  }
    69  
    70  func (s *buildStages) validateIndex(i int) error {
    71  	if i < 0 || i >= len(s.sequence)-1 {
    72  		if i == len(s.sequence)-1 {
    73  			return errors.New("refers to current build stage")
    74  		}
    75  		return errors.New("index out of bounds")
    76  	}
    77  	return nil
    78  }
    79  
    80  func (s *buildStages) add(name string, image builder.Image) error {
    81  	stage := newBuildStageFromImage(image)
    82  	name = strings.ToLower(name)
    83  	if len(name) > 0 {
    84  		if _, ok := s.byName[name]; ok {
    85  			return errors.Errorf("duplicate name %s", name)
    86  		}
    87  		s.byName[name] = stage
    88  	}
    89  	s.sequence = append(s.sequence, stage)
    90  	return nil
    91  }
    92  
    93  func (s *buildStages) update(imageID string, runConfig *container.Config) {
    94  	s.sequence[len(s.sequence)-1].update(imageID, runConfig)
    95  }
    96  
    97  type getAndMountFunc func(string) (builder.Image, builder.ReleaseableLayer, error)
    98  
    99  // imageSources mounts images and provides a cache for mounted images. It tracks
   100  // all images so they can be unmounted at the end of the build.
   101  type imageSources struct {
   102  	byImageID map[string]*imageMount
   103  	getImage  getAndMountFunc
   104  	cache     pathCache // TODO: remove
   105  }
   106  
   107  func newImageSources(ctx context.Context, options builderOptions) *imageSources {
   108  	getAndMount := func(idOrRef string) (builder.Image, builder.ReleaseableLayer, error) {
   109  		return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{
   110  			ForcePull:  options.Options.PullParent,
   111  			AuthConfig: options.Options.AuthConfigs,
   112  			Output:     options.ProgressWriter.Output,
   113  		})
   114  	}
   115  
   116  	return &imageSources{
   117  		byImageID: make(map[string]*imageMount),
   118  		getImage:  getAndMount,
   119  	}
   120  }
   121  
   122  func (m *imageSources) Get(idOrRef string) (*imageMount, error) {
   123  	if im, ok := m.byImageID[idOrRef]; ok {
   124  		return im, nil
   125  	}
   126  
   127  	image, layer, err := m.getImage(idOrRef)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  	im := newImageMount(image, layer)
   132  	m.byImageID[image.ImageID()] = im
   133  	return im, nil
   134  }
   135  
   136  func (m *imageSources) Unmount() (retErr error) {
   137  	for _, im := range m.byImageID {
   138  		if err := im.unmount(); err != nil {
   139  			logrus.Error(err)
   140  			retErr = err
   141  		}
   142  	}
   143  	return
   144  }
   145  
   146  // imageMount is a reference to an image that can be used as a builder.Source
   147  type imageMount struct {
   148  	image  builder.Image
   149  	source builder.Source
   150  	layer  builder.ReleaseableLayer
   151  }
   152  
   153  func newImageMount(image builder.Image, layer builder.ReleaseableLayer) *imageMount {
   154  	im := &imageMount{image: image, layer: layer}
   155  	return im
   156  }
   157  
   158  func (im *imageMount) Source() (builder.Source, error) {
   159  	if im.source == nil {
   160  		if im.layer == nil {
   161  			return nil, errors.Errorf("empty context")
   162  		}
   163  		mountPath, err := im.layer.Mount()
   164  		if err != nil {
   165  			return nil, errors.Wrapf(err, "failed to mount %s", im.image.ImageID())
   166  		}
   167  		source, err := remotecontext.NewLazyContext(mountPath)
   168  		if err != nil {
   169  			return nil, errors.Wrapf(err, "failed to create lazycontext for %s", mountPath)
   170  		}
   171  		im.source = source
   172  	}
   173  	return im.source, nil
   174  }
   175  
   176  func (im *imageMount) unmount() error {
   177  	if im.layer == nil {
   178  		return nil
   179  	}
   180  	if err := im.layer.Release(); err != nil {
   181  		return errors.Wrapf(err, "failed to unmount previous build image %s", im.image.ImageID())
   182  	}
   183  	return nil
   184  }
   185  
   186  func (im *imageMount) Image() builder.Image {
   187  	return im.image
   188  }
   189  
   190  func (im *imageMount) ImageID() string {
   191  	return im.image.ImageID()
   192  }