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 }