backend.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package build // import "github.com/docker/docker/api/server/backend/build"
  2. import (
  3. "context"
  4. "fmt"
  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. buildkit "github.com/docker/docker/builder/builder-next"
  10. "github.com/docker/docker/builder/fscache"
  11. "github.com/docker/docker/image"
  12. "github.com/docker/docker/pkg/stringid"
  13. "github.com/pkg/errors"
  14. "golang.org/x/sync/errgroup"
  15. )
  16. // ImageComponent provides an interface for working with images
  17. type ImageComponent interface {
  18. SquashImage(from string, to string) (string, error)
  19. TagImageWithReference(image.ID, reference.Named) error
  20. }
  21. // Builder defines interface for running a build
  22. type Builder interface {
  23. Build(context.Context, backend.BuildConfig) (*builder.Result, error)
  24. }
  25. // Backend provides build functionality to the API router
  26. type Backend struct {
  27. builder Builder
  28. fsCache *fscache.FSCache
  29. imageComponent ImageComponent
  30. buildkit *buildkit.Builder
  31. }
  32. // NewBackend creates a new build backend from components
  33. func NewBackend(components ImageComponent, builder Builder, fsCache *fscache.FSCache, buildkit *buildkit.Builder) (*Backend, error) {
  34. return &Backend{imageComponent: components, builder: builder, fsCache: fsCache, buildkit: buildkit}, nil
  35. }
  36. // Build builds an image from a Source
  37. func (b *Backend) Build(ctx context.Context, config backend.BuildConfig) (string, error) {
  38. options := config.Options
  39. useBuildKit := options.Version == types.BuilderBuildKit
  40. tagger, err := NewTagger(b.imageComponent, config.ProgressWriter.StdoutFormatter, options.Tags)
  41. if err != nil {
  42. return "", err
  43. }
  44. var build *builder.Result
  45. if useBuildKit {
  46. build, err = b.buildkit.Build(ctx, config)
  47. if err != nil {
  48. return "", err
  49. }
  50. } else {
  51. build, err = b.builder.Build(ctx, config)
  52. if err != nil {
  53. return "", err
  54. }
  55. }
  56. if build == nil {
  57. return "", nil
  58. }
  59. var imageID = build.ImageID
  60. if options.Squash {
  61. if imageID, err = squashBuild(build, b.imageComponent); err != nil {
  62. return "", err
  63. }
  64. if config.ProgressWriter.AuxFormatter != nil {
  65. if err = config.ProgressWriter.AuxFormatter.Emit("moby.image.id", types.BuildResult{ID: imageID}); err != nil {
  66. return "", err
  67. }
  68. }
  69. }
  70. if !useBuildKit {
  71. stdout := config.ProgressWriter.StdoutFormatter
  72. fmt.Fprintf(stdout, "Successfully built %s\n", stringid.TruncateID(imageID))
  73. err = tagger.TagImages(image.ID(imageID))
  74. }
  75. return imageID, err
  76. }
  77. // PruneCache removes all cached build sources
  78. func (b *Backend) PruneCache(ctx context.Context, opts types.BuildCachePruneOptions) (*types.BuildCachePruneReport, error) {
  79. eg, ctx := errgroup.WithContext(ctx)
  80. var fsCacheSize uint64
  81. eg.Go(func() error {
  82. var err error
  83. fsCacheSize, err = b.fsCache.Prune(ctx)
  84. if err != nil {
  85. return errors.Wrap(err, "failed to prune fscache")
  86. }
  87. return nil
  88. })
  89. var buildCacheSize int64
  90. var cacheIDs []string
  91. eg.Go(func() error {
  92. var err error
  93. buildCacheSize, cacheIDs, err = b.buildkit.Prune(ctx, opts)
  94. if err != nil {
  95. return errors.Wrap(err, "failed to prune build cache")
  96. }
  97. return nil
  98. })
  99. if err := eg.Wait(); err != nil {
  100. return nil, err
  101. }
  102. return &types.BuildCachePruneReport{SpaceReclaimed: fsCacheSize + uint64(buildCacheSize), CachesDeleted: cacheIDs}, nil
  103. }
  104. // Cancel cancels the build by ID
  105. func (b *Backend) Cancel(ctx context.Context, id string) error {
  106. return b.buildkit.Cancel(ctx, id)
  107. }
  108. func squashBuild(build *builder.Result, imageComponent ImageComponent) (string, error) {
  109. var fromID string
  110. if build.FromImage != nil {
  111. fromID = build.FromImage.ImageID()
  112. }
  113. imageID, err := imageComponent.SquashImage(build.ImageID, fromID)
  114. if err != nil {
  115. return "", errors.Wrap(err, "error squashing image")
  116. }
  117. return imageID, nil
  118. }