build.go 6.5 KB

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