imagecontext.go 4.7 KB

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