image_builder.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. package images // import "github.com/docker/docker/daemon/images"
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "runtime"
  7. "github.com/containerd/containerd/platforms"
  8. "github.com/containerd/log"
  9. "github.com/distribution/reference"
  10. "github.com/docker/docker/api/types/backend"
  11. imagetypes "github.com/docker/docker/api/types/image"
  12. "github.com/docker/docker/api/types/registry"
  13. "github.com/docker/docker/builder"
  14. "github.com/docker/docker/errdefs"
  15. "github.com/docker/docker/image"
  16. "github.com/docker/docker/layer"
  17. "github.com/docker/docker/pkg/progress"
  18. "github.com/docker/docker/pkg/streamformatter"
  19. "github.com/docker/docker/pkg/stringid"
  20. registrypkg "github.com/docker/docker/registry"
  21. "github.com/opencontainers/go-digest"
  22. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  23. "github.com/pkg/errors"
  24. )
  25. type roLayer struct {
  26. released bool
  27. layerStore layer.Store
  28. roLayer layer.Layer
  29. }
  30. func (l *roLayer) ContentStoreDigest() digest.Digest {
  31. return ""
  32. }
  33. func (l *roLayer) DiffID() layer.DiffID {
  34. if l.roLayer == nil {
  35. return layer.DigestSHA256EmptyTar
  36. }
  37. return l.roLayer.DiffID()
  38. }
  39. func (l *roLayer) Release() error {
  40. if l.released {
  41. return nil
  42. }
  43. if l.roLayer != nil {
  44. metadata, err := l.layerStore.Release(l.roLayer)
  45. layer.LogReleaseMetadata(metadata)
  46. if err != nil {
  47. return errors.Wrap(err, "failed to release ROLayer")
  48. }
  49. }
  50. l.roLayer = nil
  51. l.released = true
  52. return nil
  53. }
  54. func (l *roLayer) NewRWLayer() (builder.RWLayer, error) {
  55. var chainID layer.ChainID
  56. if l.roLayer != nil {
  57. chainID = l.roLayer.ChainID()
  58. }
  59. mountID := stringid.GenerateRandomID()
  60. newLayer, err := l.layerStore.CreateRWLayer(mountID, chainID, nil)
  61. if err != nil {
  62. return nil, errors.Wrap(err, "failed to create rwlayer")
  63. }
  64. rwLayer := &rwLayer{layerStore: l.layerStore, rwLayer: newLayer}
  65. fs, err := newLayer.Mount("")
  66. if err != nil {
  67. rwLayer.Release()
  68. return nil, err
  69. }
  70. rwLayer.fs = fs
  71. return rwLayer, nil
  72. }
  73. type rwLayer struct {
  74. released bool
  75. layerStore layer.Store
  76. rwLayer layer.RWLayer
  77. fs string
  78. }
  79. func (l *rwLayer) Root() string {
  80. return l.fs
  81. }
  82. func (l *rwLayer) Commit() (builder.ROLayer, error) {
  83. stream, err := l.rwLayer.TarStream()
  84. if err != nil {
  85. return nil, err
  86. }
  87. defer stream.Close()
  88. var chainID layer.ChainID
  89. if parent := l.rwLayer.Parent(); parent != nil {
  90. chainID = parent.ChainID()
  91. }
  92. newLayer, err := l.layerStore.Register(stream, chainID)
  93. if err != nil {
  94. return nil, err
  95. }
  96. // TODO: An optimization would be to handle empty layers before returning
  97. return &roLayer{layerStore: l.layerStore, roLayer: newLayer}, nil
  98. }
  99. func (l *rwLayer) Release() error {
  100. if l.released {
  101. return nil
  102. }
  103. if l.fs != "" {
  104. if err := l.rwLayer.Unmount(); err != nil {
  105. return errors.Wrap(err, "failed to unmount RWLayer")
  106. }
  107. l.fs = ""
  108. }
  109. metadata, err := l.layerStore.ReleaseRWLayer(l.rwLayer)
  110. layer.LogReleaseMetadata(metadata)
  111. if err != nil {
  112. return errors.Wrap(err, "failed to release RWLayer")
  113. }
  114. l.released = true
  115. return nil
  116. }
  117. func newROLayerForImage(img *image.Image, layerStore layer.Store) (builder.ROLayer, error) {
  118. if img == nil || img.RootFS.ChainID() == "" {
  119. return &roLayer{layerStore: layerStore}, nil
  120. }
  121. // Hold a reference to the image layer so that it can't be removed before
  122. // it is released
  123. lyr, err := layerStore.Get(img.RootFS.ChainID())
  124. if err != nil {
  125. return nil, errors.Wrapf(err, "failed to get layer for image %s", img.ImageID())
  126. }
  127. return &roLayer{layerStore: layerStore, roLayer: lyr}, nil
  128. }
  129. // TODO: could this use the regular daemon PullImage ?
  130. func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConfigs map[string]registry.AuthConfig, output io.Writer, platform *ocispec.Platform) (*image.Image, error) {
  131. ref, err := reference.ParseNormalizedNamed(name)
  132. if err != nil {
  133. return nil, err
  134. }
  135. ref = reference.TagNameOnly(ref)
  136. pullRegistryAuth := &registry.AuthConfig{}
  137. if len(authConfigs) > 0 {
  138. // The request came with a full auth config, use it
  139. repoInfo, err := i.registryService.ResolveRepository(ref)
  140. if err != nil {
  141. return nil, err
  142. }
  143. resolvedConfig := registrypkg.ResolveAuthConfig(authConfigs, repoInfo.Index)
  144. pullRegistryAuth = &resolvedConfig
  145. }
  146. if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
  147. return nil, err
  148. }
  149. img, err := i.GetImage(ctx, name, imagetypes.GetImageOpts{Platform: platform})
  150. if errdefs.IsNotFound(err) && img != nil && platform != nil {
  151. imgPlat := ocispec.Platform{
  152. OS: img.OS,
  153. Architecture: img.BaseImgArch(),
  154. Variant: img.BaseImgVariant(),
  155. }
  156. p := *platform
  157. if !platforms.Only(p).Match(imgPlat) {
  158. po := streamformatter.NewJSONProgressOutput(output, false)
  159. progress.Messagef(po, "", `
  160. WARNING: Pulled image with specified platform (%s), but the resulting image's configured platform (%s) does not match.
  161. This is most likely caused by a bug in the build system that created the fetched image (%s).
  162. Please notify the image author to correct the configuration.`,
  163. platforms.Format(p), platforms.Format(imgPlat), name,
  164. )
  165. log.G(ctx).WithError(err).WithField("image", name).Warn("Ignoring error about platform mismatch where the manifest list points to an image whose configuration does not match the platform in the manifest.")
  166. err = nil
  167. }
  168. }
  169. return img, err
  170. }
  171. // GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
  172. // Every call to GetImageAndReleasableLayer MUST call releasableLayer.Release() to prevent
  173. // leaking of layers.
  174. func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ROLayer, error) {
  175. if refOrID == "" { // FROM scratch
  176. if runtime.GOOS == "windows" {
  177. return nil, nil, fmt.Errorf(`"FROM scratch" is not supported on Windows`)
  178. }
  179. if opts.Platform != nil {
  180. if err := image.CheckOS(opts.Platform.OS); err != nil {
  181. return nil, nil, err
  182. }
  183. }
  184. lyr, err := newROLayerForImage(nil, i.layerStore)
  185. return nil, lyr, err
  186. }
  187. if opts.PullOption != backend.PullOptionForcePull {
  188. img, err := i.GetImage(ctx, refOrID, imagetypes.GetImageOpts{Platform: opts.Platform})
  189. if err != nil && opts.PullOption == backend.PullOptionNoPull {
  190. return nil, nil, err
  191. }
  192. if err != nil && !errdefs.IsNotFound(err) {
  193. return nil, nil, err
  194. }
  195. if img != nil {
  196. if err := image.CheckOS(img.OperatingSystem()); err != nil {
  197. return nil, nil, err
  198. }
  199. lyr, err := newROLayerForImage(img, i.layerStore)
  200. return img, lyr, err
  201. }
  202. }
  203. img, err := i.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.Platform)
  204. if err != nil {
  205. return nil, nil, err
  206. }
  207. if err := image.CheckOS(img.OperatingSystem()); err != nil {
  208. return nil, nil, err
  209. }
  210. lyr, err := newROLayerForImage(img, i.layerStore)
  211. return img, lyr, err
  212. }
  213. // CreateImage creates a new image by adding a config and ID to the image store.
  214. // This is similar to LoadImage() except that it receives JSON encoded bytes of
  215. // an image instead of a tar archive.
  216. func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent string, _ digest.Digest) (builder.Image, error) {
  217. id, err := i.imageStore.Create(config)
  218. if err != nil {
  219. return nil, errors.Wrapf(err, "failed to create image")
  220. }
  221. if parent != "" {
  222. if err := i.imageStore.SetParent(id, image.ID(parent)); err != nil {
  223. return nil, errors.Wrapf(err, "failed to set parent %s", parent)
  224. }
  225. }
  226. return i.imageStore.Get(id)
  227. }