image_builder.go 6.1 KB

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