bridge.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. package llbsolver
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/containerd/containerd/platforms"
  10. "github.com/moby/buildkit/cache"
  11. "github.com/moby/buildkit/cache/remotecache"
  12. "github.com/moby/buildkit/executor"
  13. "github.com/moby/buildkit/frontend"
  14. gw "github.com/moby/buildkit/frontend/gateway/client"
  15. "github.com/moby/buildkit/identity"
  16. "github.com/moby/buildkit/session"
  17. "github.com/moby/buildkit/solver"
  18. "github.com/moby/buildkit/util/tracing"
  19. "github.com/moby/buildkit/worker"
  20. digest "github.com/opencontainers/go-digest"
  21. specs "github.com/opencontainers/image-spec/specs-go/v1"
  22. "github.com/pkg/errors"
  23. "github.com/sirupsen/logrus"
  24. )
  25. type llbBridge struct {
  26. builder solver.Builder
  27. frontends map[string]frontend.Frontend
  28. resolveWorker func() (worker.Worker, error)
  29. eachWorker func(func(worker.Worker) error) error
  30. resolveCacheImporterFuncs map[string]remotecache.ResolveCacheImporterFunc
  31. cms map[string]solver.CacheManager
  32. cmsMu sync.Mutex
  33. platforms []specs.Platform
  34. sm *session.Manager
  35. }
  36. func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *frontend.Result, err error) {
  37. w, err := b.resolveWorker()
  38. if err != nil {
  39. return nil, err
  40. }
  41. var cms []solver.CacheManager
  42. for _, im := range req.CacheImports {
  43. b.cmsMu.Lock()
  44. var cm solver.CacheManager
  45. cmId := identity.NewID()
  46. if im.Type == "registry" {
  47. // For compatibility with < v0.4.0
  48. if ref := im.Attrs["ref"]; ref != "" {
  49. cmId = ref
  50. }
  51. }
  52. if prevCm, ok := b.cms[cmId]; !ok {
  53. func(cmId string) {
  54. cm = newLazyCacheManager(cmId, func() (solver.CacheManager, error) {
  55. var cmNew solver.CacheManager
  56. if err := inVertexContext(b.builder.Context(ctx), "importing cache manifest from "+cmId, "", func(ctx context.Context) error {
  57. resolveCI, ok := b.resolveCacheImporterFuncs[im.Type]
  58. if !ok {
  59. return errors.Errorf("unknown cache importer: %s", im.Type)
  60. }
  61. ci, desc, err := resolveCI(ctx, im.Attrs)
  62. if err != nil {
  63. return err
  64. }
  65. cmNew, err = ci.Resolve(ctx, desc, cmId, w)
  66. return err
  67. }); err != nil {
  68. logrus.Debugf("error while importing cache manifest from cmId=%s: %v", cmId, err)
  69. return nil, err
  70. }
  71. return cmNew, nil
  72. })
  73. }(cmId)
  74. b.cms[cmId] = cm
  75. } else {
  76. cm = prevCm
  77. }
  78. cms = append(cms, cm)
  79. b.cmsMu.Unlock()
  80. }
  81. if req.Definition != nil && req.Definition.Def != nil && req.Frontend != "" {
  82. return nil, errors.New("cannot solve with both Definition and Frontend specified")
  83. }
  84. if req.Definition != nil && req.Definition.Def != nil {
  85. ent, err := loadEntitlements(b.builder)
  86. if err != nil {
  87. return nil, err
  88. }
  89. dpc := &detectPrunedCacheID{}
  90. edge, err := Load(req.Definition, dpc.Load, ValidateEntitlements(ent), WithCacheSources(cms), RuntimePlatforms(b.platforms), WithValidateCaps())
  91. if err != nil {
  92. return nil, errors.Wrap(err, "failed to load LLB")
  93. }
  94. if len(dpc.ids) > 0 {
  95. ids := make([]string, 0, len(dpc.ids))
  96. for id := range dpc.ids {
  97. ids = append(ids, id)
  98. }
  99. if err := b.eachWorker(func(w worker.Worker) error {
  100. return w.PruneCacheMounts(ctx, ids)
  101. }); err != nil {
  102. return nil, err
  103. }
  104. }
  105. ref, err := b.builder.Build(ctx, edge)
  106. if err != nil {
  107. return nil, errors.Wrap(err, "failed to build LLB")
  108. }
  109. res = &frontend.Result{Ref: ref}
  110. } else if req.Frontend != "" {
  111. f, ok := b.frontends[req.Frontend]
  112. if !ok {
  113. return nil, errors.Errorf("invalid frontend: %s", req.Frontend)
  114. }
  115. res, err = f.Solve(ctx, b, req.FrontendOpt)
  116. if err != nil {
  117. return nil, errors.Wrapf(err, "failed to solve with frontend %s", req.Frontend)
  118. }
  119. } else {
  120. return &frontend.Result{}, nil
  121. }
  122. if err := res.EachRef(func(r solver.CachedResult) error {
  123. wr, ok := r.Sys().(*worker.WorkerRef)
  124. if !ok {
  125. return errors.Errorf("invalid reference for exporting: %T", r.Sys())
  126. }
  127. if wr.ImmutableRef != nil {
  128. if err := wr.ImmutableRef.Finalize(ctx, false); err != nil {
  129. return err
  130. }
  131. }
  132. return nil
  133. }); err != nil {
  134. return nil, err
  135. }
  136. return
  137. }
  138. func (s *llbBridge) Exec(ctx context.Context, meta executor.Meta, root cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) (err error) {
  139. w, err := s.resolveWorker()
  140. if err != nil {
  141. return err
  142. }
  143. span, ctx := tracing.StartSpan(ctx, strings.Join(meta.Args, " "))
  144. err = w.Exec(ctx, meta, root, stdin, stdout, stderr)
  145. tracing.FinishWithError(span, err)
  146. return err
  147. }
  148. func (s *llbBridge) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (dgst digest.Digest, config []byte, err error) {
  149. w, err := s.resolveWorker()
  150. if err != nil {
  151. return "", nil, err
  152. }
  153. if opt.LogName == "" {
  154. opt.LogName = fmt.Sprintf("resolve image config for %s", ref)
  155. }
  156. id := ref // make a deterministic ID for avoiding duplicates
  157. if platform := opt.Platform; platform == nil {
  158. id += platforms.Format(platforms.DefaultSpec())
  159. } else {
  160. id += platforms.Format(*platform)
  161. }
  162. err = inVertexContext(s.builder.Context(ctx), opt.LogName, id, func(ctx context.Context) error {
  163. dgst, config, err = w.ResolveImageConfig(ctx, ref, opt, s.sm)
  164. return err
  165. })
  166. return dgst, config, err
  167. }
  168. type lazyCacheManager struct {
  169. id string
  170. main solver.CacheManager
  171. waitCh chan struct{}
  172. err error
  173. }
  174. func (lcm *lazyCacheManager) ID() string {
  175. return lcm.id
  176. }
  177. func (lcm *lazyCacheManager) Query(inp []solver.CacheKeyWithSelector, inputIndex solver.Index, dgst digest.Digest, outputIndex solver.Index) ([]*solver.CacheKey, error) {
  178. lcm.wait()
  179. if lcm.main == nil {
  180. return nil, nil
  181. }
  182. return lcm.main.Query(inp, inputIndex, dgst, outputIndex)
  183. }
  184. func (lcm *lazyCacheManager) Records(ck *solver.CacheKey) ([]*solver.CacheRecord, error) {
  185. lcm.wait()
  186. if lcm.main == nil {
  187. return nil, nil
  188. }
  189. return lcm.main.Records(ck)
  190. }
  191. func (lcm *lazyCacheManager) Load(ctx context.Context, rec *solver.CacheRecord) (solver.Result, error) {
  192. if err := lcm.wait(); err != nil {
  193. return nil, err
  194. }
  195. return lcm.main.Load(ctx, rec)
  196. }
  197. func (lcm *lazyCacheManager) Save(key *solver.CacheKey, s solver.Result, createdAt time.Time) (*solver.ExportableCacheKey, error) {
  198. if err := lcm.wait(); err != nil {
  199. return nil, err
  200. }
  201. return lcm.main.Save(key, s, createdAt)
  202. }
  203. func (lcm *lazyCacheManager) wait() error {
  204. <-lcm.waitCh
  205. return lcm.err
  206. }
  207. func newLazyCacheManager(id string, fn func() (solver.CacheManager, error)) solver.CacheManager {
  208. lcm := &lazyCacheManager{id: id, waitCh: make(chan struct{})}
  209. go func() {
  210. defer close(lcm.waitCh)
  211. cm, err := fn()
  212. if err != nil {
  213. lcm.err = err
  214. return
  215. }
  216. lcm.main = cm
  217. }()
  218. return lcm
  219. }