2017-06-08 00:53:36 +00:00
package control
import (
2018-01-16 22:30:10 +00:00
"context"
2017-10-13 18:54:26 +00:00
"github.com/docker/distribution/reference"
2017-06-22 20:15:46 +00:00
controlapi "github.com/moby/buildkit/api/services/control"
2017-12-15 08:06:54 +00:00
"github.com/moby/buildkit/cache/cacheimport"
2017-06-22 20:15:46 +00:00
"github.com/moby/buildkit/client"
2017-07-10 20:03:38 +00:00
"github.com/moby/buildkit/exporter"
2017-08-25 20:08:18 +00:00
"github.com/moby/buildkit/frontend"
2017-07-11 17:12:12 +00:00
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/grpchijack"
2017-06-22 20:15:46 +00:00
"github.com/moby/buildkit/solver"
"github.com/moby/buildkit/worker"
2017-06-09 01:16:19 +00:00
"github.com/pkg/errors"
2017-07-19 01:05:19 +00:00
"github.com/sirupsen/logrus"
2018-01-16 22:30:10 +00:00
netcontext "golang.org/x/net/context"
2017-06-14 00:15:55 +00:00
"golang.org/x/sync/errgroup"
2017-06-08 00:53:36 +00:00
"google.golang.org/grpc"
)
type Opt struct {
2017-07-11 17:12:12 +00:00
SessionManager * session . Manager
2017-11-21 08:08:36 +00:00
WorkerController * worker . Controller
2017-08-25 20:08:18 +00:00
Frontends map [ string ] frontend . Frontend
2017-12-15 08:06:54 +00:00
CacheExporter * cacheimport . CacheExporter
CacheImporter * cacheimport . CacheImporter
2017-06-08 00:53:36 +00:00
}
2017-06-08 22:56:44 +00:00
type Controller struct { // TODO: ControlService
opt Opt
2017-12-15 08:06:54 +00:00
solver * solver . Solver
2017-06-08 00:53:36 +00:00
}
func NewController ( opt Opt ) ( * Controller , error ) {
c := & Controller {
2017-12-15 08:06:54 +00:00
opt : opt ,
solver : solver . NewLLBOpSolver ( solver . LLBOpt {
WorkerController : opt . WorkerController ,
Frontends : opt . Frontends ,
CacheExporter : opt . CacheExporter ,
CacheImporter : opt . CacheImporter ,
} ) ,
2017-06-08 00:53:36 +00:00
}
return c , nil
}
func ( c * Controller ) Register ( server * grpc . Server ) error {
controlapi . RegisterControlServer ( server , c )
return nil
}
2018-01-16 22:30:10 +00:00
func ( c * Controller ) DiskUsage ( ctx netcontext . Context , r * controlapi . DiskUsageRequest ) ( * controlapi . DiskUsageResponse , error ) {
2017-06-08 00:53:36 +00:00
resp := & controlapi . DiskUsageResponse { }
2017-12-19 09:34:34 +00:00
workers , err := c . opt . WorkerController . List ( )
if err != nil {
return nil , err
}
for _ , w := range workers {
2017-12-15 08:06:54 +00:00
du , err := w . DiskUsage ( ctx , client . DiskUsageInfo {
2017-11-21 08:08:36 +00:00
Filter : r . Filter ,
2017-06-08 00:53:36 +00:00
} )
2017-11-21 08:08:36 +00:00
if err != nil {
return nil , err
}
for _ , r := range du {
resp . Record = append ( resp . Record , & controlapi . UsageRecord {
// TODO: add worker info
ID : r . ID ,
Mutable : r . Mutable ,
InUse : r . InUse ,
Size_ : r . Size ,
Parent : r . Parent ,
UsageCount : int64 ( r . UsageCount ) ,
Description : r . Description ,
CreatedAt : r . CreatedAt ,
LastUsedAt : r . LastUsedAt ,
} )
}
2017-06-08 00:53:36 +00:00
}
return resp , nil
}
2017-06-08 22:56:44 +00:00
2017-12-27 01:22:50 +00:00
func ( c * Controller ) Prune ( req * controlapi . PruneRequest , stream controlapi . Control_PruneServer ) error {
ch := make ( chan client . UsageInfo )
eg , ctx := errgroup . WithContext ( stream . Context ( ) )
workers , err := c . opt . WorkerController . List ( )
if err != nil {
return errors . Wrap ( err , "failed to list workers for prune" )
}
for _ , w := range workers {
func ( w worker . Worker ) {
eg . Go ( func ( ) error {
return w . Prune ( ctx , ch )
} )
} ( w )
}
eg2 , ctx := errgroup . WithContext ( stream . Context ( ) )
eg2 . Go ( func ( ) error {
defer close ( ch )
return eg . Wait ( )
} )
eg2 . Go ( func ( ) error {
for r := range ch {
if err := stream . Send ( & controlapi . UsageRecord {
// TODO: add worker info
ID : r . ID ,
Mutable : r . Mutable ,
InUse : r . InUse ,
Size_ : r . Size ,
Parent : r . Parent ,
UsageCount : int64 ( r . UsageCount ) ,
Description : r . Description ,
CreatedAt : r . CreatedAt ,
LastUsedAt : r . LastUsedAt ,
} ) ; err != nil {
return err
}
}
return nil
} )
return eg2 . Wait ( )
}
2018-01-16 22:30:10 +00:00
func ( c * Controller ) Solve ( ctx netcontext . Context , req * controlapi . SolveRequest ) ( * controlapi . SolveResponse , error ) {
2017-08-25 20:08:18 +00:00
var frontend frontend . Frontend
if req . Frontend != "" {
var ok bool
frontend , ok = c . opt . Frontends [ req . Frontend ]
if ! ok {
return nil , errors . Errorf ( "frontend %s not found" , req . Frontend )
}
}
2017-08-03 22:24:02 +00:00
ctx = session . NewContext ( ctx , req . Session )
2017-07-10 20:03:38 +00:00
var expi exporter . ExporterInstance
2017-11-21 08:08:36 +00:00
// TODO: multiworker
// This is actually tricky, as the exporter should come from the worker that has the returned reference. We may need to delay this so that the solver loads this.
w , err := c . opt . WorkerController . GetDefault ( )
if err != nil {
return nil , err
}
2017-07-10 20:03:38 +00:00
if req . Exporter != "" {
2017-12-15 08:06:54 +00:00
exp , err := w . Exporter ( req . Exporter )
if err != nil {
return nil , err
2017-07-10 20:03:38 +00:00
}
expi , err = exp . Resolve ( ctx , req . ExporterAttrs )
if err != nil {
return nil , err
}
}
2017-10-13 18:54:26 +00:00
exportCacheRef := ""
if ref := req . Cache . ExportRef ; ref != "" {
parsed , err := reference . ParseNormalizedNamed ( ref )
if err != nil {
return nil , err
}
exportCacheRef = reference . TagNameOnly ( parsed ) . String ( )
}
importCacheRef := ""
if ref := req . Cache . ImportRef ; ref != "" {
parsed , err := reference . ParseNormalizedNamed ( ref )
if err != nil {
return nil , err
}
importCacheRef = reference . TagNameOnly ( parsed ) . String ( )
}
2017-10-05 05:31:18 +00:00
if err := c . solver . Solve ( ctx , req . Ref , solver . SolveRequest {
2017-10-13 18:54:26 +00:00
Frontend : frontend ,
Definition : req . Definition ,
Exporter : expi ,
FrontendOpt : req . FrontendAttrs ,
ExportCacheRef : exportCacheRef ,
ImportCacheRef : importCacheRef ,
2017-10-05 05:31:18 +00:00
} ) ; err != nil {
2017-06-08 22:56:44 +00:00
return nil , err
}
return & controlapi . SolveResponse { } , nil
}
2017-06-13 21:42:51 +00:00
2017-06-14 00:15:55 +00:00
func ( c * Controller ) Status ( req * controlapi . StatusRequest , stream controlapi . Control_StatusServer ) error {
ch := make ( chan * client . SolveStatus , 8 )
eg , ctx := errgroup . WithContext ( stream . Context ( ) )
eg . Go ( func ( ) error {
return c . solver . Status ( ctx , req . Ref , ch )
} )
eg . Go ( func ( ) error {
for {
2017-09-26 03:57:38 +00:00
ss , ok := <- ch
if ! ok {
return nil
}
sr := controlapi . StatusResponse { }
for _ , v := range ss . Vertexes {
sr . Vertexes = append ( sr . Vertexes , & controlapi . Vertex {
Digest : v . Digest ,
Inputs : v . Inputs ,
Name : v . Name ,
Started : v . Started ,
Completed : v . Completed ,
Error : v . Error ,
Cached : v . Cached ,
} )
}
for _ , v := range ss . Statuses {
sr . Statuses = append ( sr . Statuses , & controlapi . VertexStatus {
ID : v . ID ,
Vertex : v . Vertex ,
Name : v . Name ,
Current : v . Current ,
Total : v . Total ,
Timestamp : v . Timestamp ,
Started : v . Started ,
Completed : v . Completed ,
} )
}
for _ , v := range ss . Logs {
sr . Logs = append ( sr . Logs , & controlapi . VertexLog {
Vertex : v . Vertex ,
Stream : int64 ( v . Stream ) ,
Msg : v . Data ,
Timestamp : v . Timestamp ,
} )
}
if err := stream . SendMsg ( & sr ) ; err != nil {
return err
2017-06-14 00:15:55 +00:00
}
}
} )
return eg . Wait ( )
}
2017-07-11 17:12:12 +00:00
func ( c * Controller ) Session ( stream controlapi . Control_SessionServer ) error {
logrus . Debugf ( "session started" )
2018-01-06 16:54:10 +00:00
conn , closeCh , opts := grpchijack . Hijack ( stream )
2017-07-11 17:12:12 +00:00
defer conn . Close ( )
2018-01-06 16:54:10 +00:00
ctx , cancel := context . WithCancel ( stream . Context ( ) )
go func ( ) {
<- closeCh
cancel ( )
} ( )
err := c . opt . SessionManager . HandleConn ( ctx , conn , opts )
2017-07-11 17:12:12 +00:00
logrus . Debugf ( "session finished: %v" , err )
return err
}
2017-12-19 09:34:34 +00:00
2018-01-16 22:30:10 +00:00
func ( c * Controller ) ListWorkers ( ctx netcontext . Context , r * controlapi . ListWorkersRequest ) ( * controlapi . ListWorkersResponse , error ) {
2017-12-19 09:34:34 +00:00
resp := & controlapi . ListWorkersResponse { }
workers , err := c . opt . WorkerController . List ( r . Filter ... )
if err != nil {
return nil , err
}
for _ , w := range workers {
resp . Record = append ( resp . Record , & controlapi . WorkerRecord {
ID : w . ID ( ) ,
Labels : w . Labels ( ) ,
} )
}
return resp , nil
}