2018-04-13 21:05:29 +00:00
|
|
|
package llbsolver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2018-04-25 22:22:49 +00:00
|
|
|
"github.com/docker/distribution/reference"
|
2018-04-13 21:05:29 +00:00
|
|
|
"github.com/moby/buildkit/cache"
|
2018-05-07 21:24:28 +00:00
|
|
|
"github.com/moby/buildkit/cache/remotecache"
|
2018-04-13 21:05:29 +00:00
|
|
|
"github.com/moby/buildkit/executor"
|
|
|
|
"github.com/moby/buildkit/frontend"
|
2018-07-25 06:18:21 +00:00
|
|
|
gw "github.com/moby/buildkit/frontend/gateway/client"
|
2018-05-11 05:58:41 +00:00
|
|
|
"github.com/moby/buildkit/solver"
|
2018-04-13 21:05:29 +00:00
|
|
|
"github.com/moby/buildkit/util/tracing"
|
|
|
|
"github.com/moby/buildkit/worker"
|
|
|
|
digest "github.com/opencontainers/go-digest"
|
2018-06-23 00:31:55 +00:00
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
2018-04-13 21:05:29 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
type llbBridge struct {
|
2018-07-03 09:59:33 +00:00
|
|
|
builder solver.Builder
|
|
|
|
frontends map[string]frontend.Frontend
|
|
|
|
resolveWorker func() (worker.Worker, error)
|
|
|
|
resolveCacheImporter remotecache.ResolveCacheImporterFunc
|
|
|
|
cms map[string]solver.CacheManager
|
|
|
|
cmsMu sync.Mutex
|
|
|
|
platforms []specs.Platform
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
2018-07-11 23:51:41 +00:00
|
|
|
func (b *llbBridge) Solve(ctx context.Context, req frontend.SolveRequest) (res *frontend.Result, err error) {
|
2018-07-03 09:59:33 +00:00
|
|
|
w, err := b.resolveWorker()
|
|
|
|
if err != nil {
|
2018-07-11 23:51:41 +00:00
|
|
|
return nil, err
|
2018-07-03 09:59:33 +00:00
|
|
|
}
|
2018-04-25 17:49:15 +00:00
|
|
|
var cms []solver.CacheManager
|
|
|
|
for _, ref := range req.ImportCacheRefs {
|
2018-04-13 21:05:29 +00:00
|
|
|
b.cmsMu.Lock()
|
2018-04-25 17:49:15 +00:00
|
|
|
var cm solver.CacheManager
|
2018-04-13 21:05:29 +00:00
|
|
|
if prevCm, ok := b.cms[ref]; !ok {
|
2018-04-25 22:22:49 +00:00
|
|
|
r, err := reference.ParseNormalizedNamed(ref)
|
|
|
|
if err != nil {
|
2018-07-11 23:51:41 +00:00
|
|
|
return nil, err
|
2018-04-25 22:22:49 +00:00
|
|
|
}
|
|
|
|
ref = reference.TagNameOnly(r).String()
|
|
|
|
func(ref string) {
|
|
|
|
cm = newLazyCacheManager(ref, func() (solver.CacheManager, error) {
|
|
|
|
var cmNew solver.CacheManager
|
2018-07-27 22:39:14 +00:00
|
|
|
if err := inVertexContext(b.builder.Context(ctx), "importing cache manifest from "+ref, func(ctx context.Context) error {
|
2018-07-03 09:59:33 +00:00
|
|
|
if b.resolveCacheImporter == nil {
|
|
|
|
return errors.New("no cache importer is available")
|
|
|
|
}
|
|
|
|
typ := "" // TODO: support non-registry type
|
|
|
|
ci, desc, err := b.resolveCacheImporter(ctx, typ, ref)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cmNew, err = ci.Resolve(ctx, desc, ref, w)
|
2018-04-25 22:22:49 +00:00
|
|
|
return err
|
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return cmNew, nil
|
|
|
|
})
|
|
|
|
}(ref)
|
2018-04-13 21:05:29 +00:00
|
|
|
b.cms[ref] = cm
|
|
|
|
} else {
|
|
|
|
cm = prevCm
|
|
|
|
}
|
2018-04-25 17:49:15 +00:00
|
|
|
cms = append(cms, cm)
|
2018-04-13 21:05:29 +00:00
|
|
|
b.cmsMu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.Definition != nil && req.Definition.Def != nil {
|
2018-07-18 23:32:44 +00:00
|
|
|
edge, err := Load(req.Definition, WithCacheSources(cms), RuntimePlatforms(b.platforms), WithValidateCaps())
|
2018-04-13 21:05:29 +00:00
|
|
|
if err != nil {
|
2018-07-11 23:51:41 +00:00
|
|
|
return nil, err
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
2018-07-05 23:32:01 +00:00
|
|
|
ref, err := b.builder.Build(ctx, edge)
|
2018-04-13 21:05:29 +00:00
|
|
|
if err != nil {
|
2018-07-11 23:51:41 +00:00
|
|
|
return nil, err
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
2018-07-11 23:51:41 +00:00
|
|
|
|
|
|
|
res = &frontend.Result{Ref: ref}
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
|
|
|
if req.Frontend != "" {
|
|
|
|
f, ok := b.frontends[req.Frontend]
|
|
|
|
if !ok {
|
2018-07-11 23:51:41 +00:00
|
|
|
return nil, errors.Errorf("invalid frontend: %s", req.Frontend)
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
2018-07-11 23:51:41 +00:00
|
|
|
res, err = f.Solve(ctx, b, req.FrontendOpt)
|
2018-04-13 21:05:29 +00:00
|
|
|
if err != nil {
|
2018-07-11 23:51:41 +00:00
|
|
|
return nil, err
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if req.Definition == nil || req.Definition.Def == nil {
|
2018-07-11 23:51:41 +00:00
|
|
|
return &frontend.Result{}, nil
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-11 23:51:41 +00:00
|
|
|
if err := res.EachRef(func(r solver.CachedResult) error {
|
|
|
|
wr, ok := r.Sys().(*worker.WorkerRef)
|
|
|
|
if !ok {
|
|
|
|
return errors.Errorf("invalid reference for exporting: %T", r.Sys())
|
|
|
|
}
|
|
|
|
if wr.ImmutableRef != nil {
|
|
|
|
if err := wr.ImmutableRef.Finalize(ctx, false); err != nil {
|
|
|
|
return err
|
2018-05-22 22:58:22 +00:00
|
|
|
}
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
2018-07-11 23:51:41 +00:00
|
|
|
return nil
|
|
|
|
}); err != nil {
|
|
|
|
return nil, err
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *llbBridge) Exec(ctx context.Context, meta executor.Meta, root cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) (err error) {
|
|
|
|
w, err := s.resolveWorker()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
span, ctx := tracing.StartSpan(ctx, strings.Join(meta.Args, " "))
|
|
|
|
err = w.Exec(ctx, meta, root, stdin, stdout, stderr)
|
|
|
|
tracing.FinishWithError(span, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-25 06:18:21 +00:00
|
|
|
func (s *llbBridge) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) {
|
2018-04-13 21:05:29 +00:00
|
|
|
w, err := s.resolveWorker()
|
|
|
|
if err != nil {
|
|
|
|
return "", nil, err
|
|
|
|
}
|
2018-07-25 06:18:21 +00:00
|
|
|
return w.ResolveImageConfig(ctx, ref, opt)
|
2018-04-13 21:05:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type lazyCacheManager struct {
|
|
|
|
id string
|
|
|
|
main solver.CacheManager
|
|
|
|
|
|
|
|
waitCh chan struct{}
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lcm *lazyCacheManager) ID() string {
|
|
|
|
return lcm.id
|
|
|
|
}
|
|
|
|
func (lcm *lazyCacheManager) Query(inp []solver.CacheKeyWithSelector, inputIndex solver.Index, dgst digest.Digest, outputIndex solver.Index) ([]*solver.CacheKey, error) {
|
|
|
|
if err := lcm.wait(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return lcm.main.Query(inp, inputIndex, dgst, outputIndex)
|
|
|
|
}
|
|
|
|
func (lcm *lazyCacheManager) Records(ck *solver.CacheKey) ([]*solver.CacheRecord, error) {
|
|
|
|
if err := lcm.wait(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return lcm.main.Records(ck)
|
|
|
|
}
|
|
|
|
func (lcm *lazyCacheManager) Load(ctx context.Context, rec *solver.CacheRecord) (solver.Result, error) {
|
|
|
|
if err := lcm.wait(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return lcm.main.Load(ctx, rec)
|
|
|
|
}
|
|
|
|
func (lcm *lazyCacheManager) Save(key *solver.CacheKey, s solver.Result) (*solver.ExportableCacheKey, error) {
|
|
|
|
if err := lcm.wait(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return lcm.main.Save(key, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lcm *lazyCacheManager) wait() error {
|
|
|
|
<-lcm.waitCh
|
|
|
|
return lcm.err
|
|
|
|
}
|
|
|
|
|
|
|
|
func newLazyCacheManager(id string, fn func() (solver.CacheManager, error)) solver.CacheManager {
|
|
|
|
lcm := &lazyCacheManager{id: id, waitCh: make(chan struct{})}
|
|
|
|
go func() {
|
|
|
|
defer close(lcm.waitCh)
|
|
|
|
cm, err := fn()
|
|
|
|
if err != nil {
|
|
|
|
lcm.err = err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
lcm.main = cm
|
|
|
|
}()
|
|
|
|
return lcm
|
|
|
|
}
|