175 lines
4.2 KiB
Go
175 lines
4.2 KiB
Go
package llbsolver
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/moby/buildkit/cache"
|
|
"github.com/moby/buildkit/cache/remotecache"
|
|
"github.com/moby/buildkit/client"
|
|
"github.com/moby/buildkit/exporter"
|
|
"github.com/moby/buildkit/frontend"
|
|
"github.com/moby/buildkit/session"
|
|
"github.com/moby/buildkit/solver"
|
|
"github.com/moby/buildkit/util/progress"
|
|
"github.com/moby/buildkit/worker"
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type ExporterRequest struct {
|
|
Exporter exporter.ExporterInstance
|
|
CacheExporter *remotecache.RegistryCacheExporter
|
|
CacheExportMode solver.CacheExportMode
|
|
}
|
|
|
|
// ResolveWorkerFunc returns default worker for the temporary default non-distributed use cases
|
|
type ResolveWorkerFunc func() (worker.Worker, error)
|
|
|
|
type Solver struct {
|
|
solver *solver.Solver
|
|
resolveWorker ResolveWorkerFunc
|
|
frontends map[string]frontend.Frontend
|
|
ci *remotecache.CacheImporter
|
|
platforms []specs.Platform
|
|
}
|
|
|
|
func New(wc *worker.Controller, f map[string]frontend.Frontend, cacheStore solver.CacheKeyStorage, ci *remotecache.CacheImporter) (*Solver, error) {
|
|
s := &Solver{
|
|
resolveWorker: defaultResolver(wc),
|
|
frontends: f,
|
|
ci: ci,
|
|
}
|
|
|
|
results := newCacheResultStorage(wc)
|
|
|
|
cache := solver.NewCacheManager("local", cacheStore, results)
|
|
|
|
// executing is currently only allowed on default worker
|
|
w, err := wc.GetDefault()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.platforms = w.Platforms()
|
|
|
|
s.solver = solver.NewSolver(solver.SolverOpt{
|
|
ResolveOpFunc: s.resolver(),
|
|
DefaultCache: cache,
|
|
})
|
|
return s, nil
|
|
}
|
|
|
|
func (s *Solver) resolver() solver.ResolveOpFunc {
|
|
return func(v solver.Vertex, b solver.Builder) (solver.Op, error) {
|
|
w, err := s.resolveWorker()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return w.ResolveOp(v, s.Bridge(b))
|
|
}
|
|
}
|
|
|
|
func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge {
|
|
return &llbBridge{
|
|
builder: b,
|
|
frontends: s.frontends,
|
|
resolveWorker: s.resolveWorker,
|
|
ci: s.ci,
|
|
cms: map[string]solver.CacheManager{},
|
|
platforms: s.platforms,
|
|
}
|
|
}
|
|
|
|
func (s *Solver) Solve(ctx context.Context, id string, req frontend.SolveRequest, exp ExporterRequest) (*client.SolveResponse, error) {
|
|
j, err := s.solver.NewJob(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer j.Discard()
|
|
|
|
j.SessionID = session.FromContext(ctx)
|
|
|
|
res, exporterOpt, err := s.Bridge(j).Solve(ctx, req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer func() {
|
|
if res != nil {
|
|
go res.Release(context.TODO())
|
|
}
|
|
}()
|
|
|
|
var exporterResponse map[string]string
|
|
if exp := exp.Exporter; exp != nil {
|
|
var immutable cache.ImmutableRef
|
|
if res != nil {
|
|
workerRef, ok := res.Sys().(*worker.WorkerRef)
|
|
if !ok {
|
|
return nil, errors.Errorf("invalid reference: %T", res.Sys())
|
|
}
|
|
immutable = workerRef.ImmutableRef
|
|
}
|
|
|
|
if err := j.Call(ctx, exp.Name(), func(ctx context.Context) error {
|
|
exporterResponse, err = exp.Export(ctx, immutable, exporterOpt)
|
|
return err
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if e := exp.CacheExporter; e != nil {
|
|
if err := j.Call(ctx, "exporting cache", func(ctx context.Context) error {
|
|
prepareDone := oneOffProgress(ctx, "preparing build cache for export")
|
|
if _, err := res.CacheKey().Exporter.ExportTo(ctx, e, solver.CacheExportOpt{
|
|
Convert: workerRefConverter,
|
|
Mode: exp.CacheExportMode,
|
|
}); err != nil {
|
|
return prepareDone(err)
|
|
}
|
|
prepareDone(nil)
|
|
|
|
return e.Finalize(ctx)
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &client.SolveResponse{
|
|
ExporterResponse: exporterResponse,
|
|
}, nil
|
|
}
|
|
|
|
func (s *Solver) Status(ctx context.Context, id string, statusChan chan *client.SolveStatus) error {
|
|
j, err := s.solver.Get(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return j.Status(ctx, statusChan)
|
|
}
|
|
|
|
func defaultResolver(wc *worker.Controller) ResolveWorkerFunc {
|
|
return func() (worker.Worker, error) {
|
|
return wc.GetDefault()
|
|
}
|
|
}
|
|
|
|
func oneOffProgress(ctx context.Context, id string) func(err error) error {
|
|
pw, _, _ := progress.FromContext(ctx)
|
|
now := time.Now()
|
|
st := progress.Status{
|
|
Started: &now,
|
|
}
|
|
pw.Write(id, st)
|
|
return func(err error) error {
|
|
// TODO: set error on status
|
|
now := time.Now()
|
|
st.Completed = &now
|
|
pw.Write(id, st)
|
|
pw.Close()
|
|
return err
|
|
}
|
|
}
|