imagecontext.go 5.1 KB

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