2018-04-13 21:05:29 +00:00
package llbsolver
2018-01-02 18:06:50 +00:00
import (
"strings"
2018-06-23 00:31:55 +00:00
"github.com/containerd/containerd/platforms"
2018-05-11 05:58:41 +00:00
"github.com/moby/buildkit/solver"
2018-01-02 18:06:50 +00:00
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/source"
2018-08-04 19:42:01 +00:00
"github.com/moby/buildkit/util/entitlements"
2018-01-02 18:06:50 +00:00
digest "github.com/opencontainers/go-digest"
2018-06-23 00:31:55 +00:00
specs "github.com/opencontainers/image-spec/specs-go/v1"
2018-01-02 18:06:50 +00:00
"github.com/pkg/errors"
)
type vertex struct {
2018-02-01 23:41:14 +00:00
sys interface { }
options solver . VertexOptions
inputs [ ] solver . Edge
digest digest . Digest
name string
2018-01-02 18:06:50 +00:00
}
func ( v * vertex ) Digest ( ) digest . Digest {
return v . digest
}
func ( v * vertex ) Sys ( ) interface { } {
return v . sys
}
2018-02-01 23:41:14 +00:00
func ( v * vertex ) Options ( ) solver . VertexOptions {
return v . options
2018-01-02 18:06:50 +00:00
}
func ( v * vertex ) Inputs ( ) [ ] solver . Edge {
return v . inputs
}
func ( v * vertex ) Name ( ) string {
2018-07-28 00:27:41 +00:00
if name , ok := v . options . Description [ "llb.customname" ] ; ok {
return name
}
2018-01-02 18:06:50 +00:00
return v . name
}
2018-06-23 00:31:55 +00:00
type LoadOpt func ( * pb . Op , * pb . OpMetadata , * solver . VertexOptions ) error
2018-02-02 18:37:32 +00:00
2018-07-18 23:32:44 +00:00
func WithValidateCaps ( ) LoadOpt {
cs := pb . Caps . CapSet ( pb . Caps . All ( ) )
return func ( _ * pb . Op , md * pb . OpMetadata , opt * solver . VertexOptions ) error {
if md != nil {
for c := range md . Caps {
if err := cs . Supports ( c ) ; err != nil {
return err
}
}
}
return nil
}
}
2018-04-25 17:49:15 +00:00
func WithCacheSources ( cms [ ] solver . CacheManager ) LoadOpt {
2018-06-23 00:31:55 +00:00
return func ( _ * pb . Op , _ * pb . OpMetadata , opt * solver . VertexOptions ) error {
2018-04-25 17:49:15 +00:00
opt . CacheSources = cms
2018-06-23 00:31:55 +00:00
return nil
}
}
func RuntimePlatforms ( p [ ] specs . Platform ) LoadOpt {
var defaultPlatform * pb . Platform
2018-07-11 02:31:33 +00:00
pp := make ( [ ] specs . Platform , len ( p ) )
2018-06-23 00:31:55 +00:00
for i := range p {
2018-07-11 02:31:33 +00:00
pp [ i ] = platforms . Normalize ( p [ i ] )
2018-06-23 00:31:55 +00:00
}
return func ( op * pb . Op , _ * pb . OpMetadata , opt * solver . VertexOptions ) error {
if op . Platform == nil {
if defaultPlatform == nil {
p := platforms . DefaultSpec ( )
defaultPlatform = & pb . Platform {
OS : p . OS ,
Architecture : p . Architecture ,
2019-03-12 02:16:06 +00:00
Variant : p . Variant ,
2018-06-23 00:31:55 +00:00
}
}
op . Platform = defaultPlatform
}
2019-03-12 02:16:06 +00:00
platform := specs . Platform { OS : op . Platform . OS , Architecture : op . Platform . Architecture , Variant : op . Platform . Variant }
normalizedPlatform := platforms . Normalize ( platform )
op . Platform = & pb . Platform {
OS : normalizedPlatform . OS ,
Architecture : normalizedPlatform . Architecture ,
Variant : normalizedPlatform . Variant ,
}
2018-06-23 00:31:55 +00:00
if _ , ok := op . Op . ( * pb . Op_Exec ) ; ok {
var found bool
2018-07-11 02:31:33 +00:00
for _ , pp := range pp {
2018-06-23 00:31:55 +00:00
if pp . OS == op . Platform . OS && pp . Architecture == op . Platform . Architecture && pp . Variant == op . Platform . Variant {
found = true
break
}
}
if ! found {
return errors . Errorf ( "runtime execution on platform %s not supported" , platforms . Format ( specs . Platform { OS : op . Platform . OS , Architecture : op . Platform . Architecture , Variant : op . Platform . Variant } ) )
}
}
return nil
2018-02-02 18:37:32 +00:00
}
}
2018-08-04 19:42:01 +00:00
func ValidateEntitlements ( ent entitlements . Set ) LoadOpt {
return func ( op * pb . Op , _ * pb . OpMetadata , opt * solver . VertexOptions ) error {
switch op := op . Op . ( type ) {
case * pb . Op_Exec :
if op . Exec . Network == pb . NetMode_HOST {
if ! ent . Allowed ( entitlements . EntitlementNetworkHost ) {
return errors . Errorf ( "%s is not allowed" , entitlements . EntitlementNetworkHost )
}
}
if op . Exec . Network == pb . NetMode_NONE {
if ! ent . Allowed ( entitlements . EntitlementNetworkNone ) {
return errors . Errorf ( "%s is not allowed" , entitlements . EntitlementNetworkNone )
}
}
}
return nil
}
}
2018-02-02 18:37:32 +00:00
func Load ( def * pb . Definition , opts ... LoadOpt ) ( solver . Edge , error ) {
2018-01-02 18:06:50 +00:00
return loadLLB ( def , func ( dgst digest . Digest , pbOp * pb . Op , load func ( digest . Digest ) ( solver . Vertex , error ) ) ( solver . Vertex , error ) {
opMetadata := def . Metadata [ dgst ]
2018-02-02 18:37:32 +00:00
vtx , err := newVertex ( dgst , pbOp , & opMetadata , load , opts ... )
2018-01-02 18:06:50 +00:00
if err != nil {
return nil , err
}
return vtx , nil
} )
}
2018-02-02 18:37:32 +00:00
func newVertex ( dgst digest . Digest , op * pb . Op , opMeta * pb . OpMetadata , load func ( digest . Digest ) ( solver . Vertex , error ) , opts ... LoadOpt ) ( * vertex , error ) {
2018-02-01 23:41:14 +00:00
opt := solver . VertexOptions { }
2018-02-01 22:23:42 +00:00
if opMeta != nil {
2018-02-01 23:41:14 +00:00
opt . IgnoreCache = opMeta . IgnoreCache
opt . Description = opMeta . Description
2018-05-07 21:20:25 +00:00
if opMeta . ExportCache != nil {
opt . ExportCache = & opMeta . ExportCache . Value
}
2018-02-01 22:23:42 +00:00
}
2018-02-02 18:37:32 +00:00
for _ , fn := range opts {
2018-06-23 00:31:55 +00:00
if err := fn ( op , opMeta , & opt ) ; err != nil {
return nil , err
}
2018-02-02 18:37:32 +00:00
}
2018-06-22 23:07:13 +00:00
vtx := & vertex { sys : op , options : opt , digest : dgst , name : llbOpName ( op ) }
2018-01-02 18:06:50 +00:00
for _ , in := range op . Inputs {
sub , err := load ( in . Digest )
if err != nil {
return nil , err
}
vtx . inputs = append ( vtx . inputs , solver . Edge { Index : solver . Index ( in . Index ) , Vertex : sub } )
}
return vtx , nil
}
// loadLLB loads LLB.
// fn is executed sequentially.
func loadLLB ( def * pb . Definition , fn func ( digest . Digest , * pb . Op , func ( digest . Digest ) ( solver . Vertex , error ) ) ( solver . Vertex , error ) ) ( solver . Edge , error ) {
if len ( def . Def ) == 0 {
return solver . Edge { } , errors . New ( "invalid empty definition" )
}
allOps := make ( map [ digest . Digest ] * pb . Op )
var dgst digest . Digest
for _ , dt := range def . Def {
var op pb . Op
if err := ( & op ) . Unmarshal ( dt ) ; err != nil {
return solver . Edge { } , errors . Wrap ( err , "failed to parse llb proto op" )
}
dgst = digest . FromBytes ( dt )
allOps [ dgst ] = & op
}
lastOp := allOps [ dgst ]
delete ( allOps , dgst )
dgst = lastOp . Inputs [ 0 ] . Digest
cache := make ( map [ digest . Digest ] solver . Vertex )
var rec func ( dgst digest . Digest ) ( solver . Vertex , error )
rec = func ( dgst digest . Digest ) ( solver . Vertex , error ) {
if v , ok := cache [ dgst ] ; ok {
return v , nil
}
2019-02-05 01:06:23 +00:00
op , ok := allOps [ dgst ]
if ! ok {
return nil , errors . Errorf ( "invalid missing input digest %s" , dgst )
}
v , err := fn ( dgst , op , rec )
2018-01-02 18:06:50 +00:00
if err != nil {
return nil , err
}
cache [ dgst ] = v
return v , nil
}
v , err := rec ( dgst )
if err != nil {
return solver . Edge { } , err
}
return solver . Edge { Vertex : v , Index : solver . Index ( lastOp . Inputs [ 0 ] . Index ) } , nil
}
func llbOpName ( op * pb . Op ) string {
switch op := op . Op . ( type ) {
case * pb . Op_Source :
2018-06-24 05:52:19 +00:00
if id , err := source . FromLLB ( op , nil ) ; err == nil {
2018-01-02 18:06:50 +00:00
if id , ok := id . ( * source . LocalIdentifier ) ; ok {
if len ( id . IncludePatterns ) == 1 {
return op . Source . Identifier + " (" + id . IncludePatterns [ 0 ] + ")"
}
}
}
return op . Source . Identifier
case * pb . Op_Exec :
return strings . Join ( op . Exec . Meta . Args , " " )
case * pb . Op_Build :
return "build"
default :
return "unknown"
}
}