imagecontext.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package dockerfile
  2. import (
  3. "runtime"
  4. "github.com/docker/docker/api/types/backend"
  5. "github.com/docker/docker/builder"
  6. "github.com/docker/docker/builder/remotecontext"
  7. dockerimage "github.com/docker/docker/image"
  8. "github.com/docker/docker/pkg/system"
  9. "github.com/pkg/errors"
  10. "github.com/sirupsen/logrus"
  11. "golang.org/x/net/context"
  12. )
  13. type getAndMountFunc func(string, bool) (builder.Image, builder.ReleaseableLayer, error)
  14. // imageSources mounts images and provides a cache for mounted images. It tracks
  15. // all images so they can be unmounted at the end of the build.
  16. type imageSources struct {
  17. byImageID map[string]*imageMount
  18. mounts []*imageMount
  19. getImage getAndMountFunc
  20. }
  21. func newImageSources(ctx context.Context, options builderOptions) *imageSources {
  22. getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ReleaseableLayer, error) {
  23. pullOption := backend.PullOptionNoPull
  24. if !localOnly {
  25. if options.Options.PullParent {
  26. pullOption = backend.PullOptionForcePull
  27. } else {
  28. pullOption = backend.PullOptionPreferLocal
  29. }
  30. }
  31. optionsPlatform := system.ParsePlatform(options.Options.Platform)
  32. return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{
  33. PullOption: pullOption,
  34. AuthConfig: options.Options.AuthConfigs,
  35. Output: options.ProgressWriter.Output,
  36. OS: optionsPlatform.OS,
  37. })
  38. }
  39. return &imageSources{
  40. byImageID: make(map[string]*imageMount),
  41. getImage: getAndMount,
  42. }
  43. }
  44. func (m *imageSources) Get(idOrRef string, localOnly bool) (*imageMount, error) {
  45. if im, ok := m.byImageID[idOrRef]; ok {
  46. return im, nil
  47. }
  48. image, layer, err := m.getImage(idOrRef, localOnly)
  49. if err != nil {
  50. return nil, err
  51. }
  52. im := newImageMount(image, layer)
  53. m.Add(im)
  54. return im, nil
  55. }
  56. func (m *imageSources) Unmount() (retErr error) {
  57. for _, im := range m.mounts {
  58. if err := im.unmount(); err != nil {
  59. logrus.Error(err)
  60. retErr = err
  61. }
  62. }
  63. return
  64. }
  65. func (m *imageSources) Add(im *imageMount) {
  66. switch im.image {
  67. case nil:
  68. // set the OS for scratch images
  69. os := runtime.GOOS
  70. // Windows does not support scratch except for LCOW
  71. if runtime.GOOS == "windows" {
  72. os = "linux"
  73. }
  74. im.image = &dockerimage.Image{V1Image: dockerimage.V1Image{OS: os}}
  75. default:
  76. m.byImageID[im.image.ImageID()] = im
  77. }
  78. m.mounts = append(m.mounts, im)
  79. }
  80. // imageMount is a reference to an image that can be used as a builder.Source
  81. type imageMount struct {
  82. image builder.Image
  83. source builder.Source
  84. layer builder.ReleaseableLayer
  85. }
  86. func newImageMount(image builder.Image, layer builder.ReleaseableLayer) *imageMount {
  87. im := &imageMount{image: image, layer: layer}
  88. return im
  89. }
  90. func (im *imageMount) Source() (builder.Source, error) {
  91. if im.source == nil {
  92. if im.layer == nil {
  93. return nil, errors.Errorf("empty context")
  94. }
  95. mountPath, err := im.layer.Mount()
  96. if err != nil {
  97. return nil, errors.Wrapf(err, "failed to mount %s", im.image.ImageID())
  98. }
  99. source, err := remotecontext.NewLazySource(mountPath)
  100. if err != nil {
  101. return nil, errors.Wrapf(err, "failed to create lazycontext for %s", mountPath)
  102. }
  103. im.source = source
  104. }
  105. return im.source, nil
  106. }
  107. func (im *imageMount) unmount() error {
  108. if im.layer == nil {
  109. return nil
  110. }
  111. if err := im.layer.Release(); err != nil {
  112. return errors.Wrapf(err, "failed to unmount previous build image %s", im.image.ImageID())
  113. }
  114. im.layer = nil
  115. return nil
  116. }
  117. func (im *imageMount) Image() builder.Image {
  118. return im.image
  119. }
  120. func (im *imageMount) Layer() builder.ReleaseableLayer {
  121. return im.layer
  122. }
  123. func (im *imageMount) ImageID() string {
  124. return im.image.ImageID()
  125. }