image_builder.go 7.2 KB

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