@ -20,21 +20,21 @@ import (
"golang.org/x/sync/errgroup"
"golang.org/x/sync/errgroup"
)
)
// NewResult Context wraps a call to client.Build, additionally returning a
// NewResult Handle wraps a call to client.Build, additionally returning a
// Result Context alongside the standard response and error.
// Result Handle alongside the standard response and error.
//
//
// This Result Context can be used to execute additional build steps in the same
// This Result Handle can be used to execute additional build steps in the same
// context as the build occurred, which can allow easy debugging of build
// context as the build occurred, which can allow easy debugging of build
// failures and successes.
// failures and successes.
//
//
// If the returned Result Context is not nil, the caller must call Done() on it.
// If the returned Result Handle is not nil, the caller must call Done() on it.
func NewResult Context ( ctx context . Context , cc * client . Client , opt client . SolveOpt , product string , buildFunc gateway . BuildFunc , ch chan * client . SolveStatus ) ( * Result Context , * client . SolveResponse , error ) {
func NewResult Handle ( ctx context . Context , cc * client . Client , opt client . SolveOpt , product string , buildFunc gateway . BuildFunc , ch chan * client . SolveStatus ) ( * Result Handle , * client . SolveResponse , error ) {
// Create a new context to wrap the original, and cancel it when the
// Create a new context to wrap the original, and cancel it when the
// caller-provided context is cancelled.
// caller-provided context is cancelled.
//
//
// We derive the context from the background context so that we can forbid
// We derive the context from the background context so that we can forbid
// cancellation of the build request after <-done is closed (which we do
// cancellation of the build request after <-done is closed (which we do
// before returning the Result Context ).
// before returning the Result Handle ).
baseCtx := ctx
baseCtx := ctx
ctx , cancel := context . WithCancelCause ( context . Background ( ) )
ctx , cancel := context . WithCancelCause ( context . Background ( ) )
done := make ( chan struct { } )
done := make ( chan struct { } )
@ -43,7 +43,7 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
case <- baseCtx . Done ( ) :
case <- baseCtx . Done ( ) :
cancel ( baseCtx . Err ( ) )
cancel ( baseCtx . Err ( ) )
case <- done :
case <- done :
// Once done is closed, we've recorded a Result Context , so we
// Once done is closed, we've recorded a Result Handle , so we
// shouldn't allow cancelling the underlying build request anymore.
// shouldn't allow cancelling the underlying build request anymore.
}
}
} ( )
} ( )
@ -52,9 +52,9 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
//
//
// We do this so that we can discard status messages after the main portion
// We do this so that we can discard status messages after the main portion
// of the build is complete. This is necessary for the solve error case,
// of the build is complete. This is necessary for the solve error case,
// where the original gateway is kept open until the Result Context is
// where the original gateway is kept open until the Result Handle is
// closed - we don't want progress messages from operations in that
// closed - we don't want progress messages from operations in that
// Result Context to display after this function exits.
// Result Handle to display after this function exits.
//
//
// Additionally, callers should wait for the progress channel to be closed.
// Additionally, callers should wait for the progress channel to be closed.
// If we keep the session open and never close the progress channel, the
// If we keep the session open and never close the progress channel, the
@ -77,9 +77,9 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
} ( )
} ( )
defer close ( baseCh )
defer close ( baseCh )
var resCtx * ResultContext
var resp * client . SolveResponse
var resp * client . SolveResponse
var respErr error
var respErr error
var respHandle * ResultHandle
go func ( ) {
go func ( ) {
defer cancel ( context . Canceled ) // ensure no dangling processes
defer cancel ( context . Canceled ) // ensure no dangling processes
@ -104,14 +104,14 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
// Scenario 1: we failed to evaluate a node somewhere in the
// Scenario 1: we failed to evaluate a node somewhere in the
// build graph.
// build graph.
//
//
// In this case, we construct a Result Context from this
// In this case, we construct a Result Handle from this
// original Build session, and return it alongside the original
// original Build session, and return it alongside the original
// build error. We then need to keep the gateway session open
// build error. We then need to keep the gateway session open
// until the caller explicitly closes the Result Context .
// until the caller explicitly closes the Result Handle .
var se * errdefs . SolveError
var se * errdefs . SolveError
if errors . As ( err , & se ) {
if errors . As ( err , & se ) {
res Ctx = & ResultContext {
res pHandle = & ResultHandle {
done : make ( chan struct { } ) ,
done : make ( chan struct { } ) ,
solveErr : se ,
solveErr : se ,
gwClient : c ,
gwClient : c ,
@ -120,21 +120,21 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
respErr = se
respErr = se
close ( done )
close ( done )
// Block until the caller closes the Result Context .
// Block until the caller closes the Result Handle .
select {
select {
case <- res Ctx . done :
case <- res pHandle . done :
case <- ctx . Done ( ) :
case <- ctx . Done ( ) :
}
}
}
}
}
}
return res , err
return res , err
} , ch )
} , ch )
if res Ctx != nil {
if res pHandle != nil {
return
return
}
}
if err != nil {
if err != nil {
// Something unexpected failed during the build, we didn't succeed,
// Something unexpected failed during the build, we didn't succeed,
// but we also didn't make it far enough to create a Result Context .
// but we also didn't make it far enough to create a Result Handle .
respErr = err
respErr = err
close ( done )
close ( done )
return
return
@ -144,7 +144,7 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
//
//
// In this case, the original gateway session has now been closed
// In this case, the original gateway session has now been closed
// since the Build has been completed. So, we need to create a new
// since the Build has been completed. So, we need to create a new
// gateway session to populate the Result Context . To do this, we
// gateway session to populate the Result Handle . To do this, we
// need to re-evaluate the target result, in this new session. This
// need to re-evaluate the target result, in this new session. This
// should be instantaneous since the result should be cached.
// should be instantaneous since the result should be cached.
@ -165,7 +165,7 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
// successfully evaluated the same result with no issues.
// successfully evaluated the same result with no issues.
return nil , errors . Wrap ( err , "inconsistent solve result" )
return nil , errors . Wrap ( err , "inconsistent solve result" )
}
}
res Ctx = & ResultContext {
res pHandle = & ResultHandle {
done : make ( chan struct { } ) ,
done : make ( chan struct { } ) ,
res : res ,
res : res ,
gwClient : c ,
gwClient : c ,
@ -173,14 +173,14 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
}
}
close ( done )
close ( done )
// Block until the caller closes the Result Context .
// Block until the caller closes the Result Handle .
select {
select {
case <- res Ctx . done :
case <- res pHandle . done :
case <- ctx . Done ( ) :
case <- ctx . Done ( ) :
}
}
return nil , ctx . Err ( )
return nil , ctx . Err ( )
} , nil )
} , nil )
if res Ctx != nil {
if res pHandle != nil {
return
return
}
}
close ( done )
close ( done )
@ -194,7 +194,7 @@ func NewResultContext(ctx context.Context, cc *client.Client, opt client.SolveOp
respErr = baseCtx . Err ( )
respErr = baseCtx . Err ( )
}
}
}
}
return res Ctx , resp , respErr
return res pHandle , resp , respErr
}
}
// getDefinition converts a gateway result into a collection of definitions for
// getDefinition converts a gateway result into a collection of definitions for
@ -248,8 +248,8 @@ func evalDefinition(ctx context.Context, c gateway.Client, defs *result.Result[*
return res , nil
return res , nil
}
}
// Result Context is a build result with the client that built it.
// Result Handle is a build result with the client that built it.
type Result Context struct {
type Result Handle struct {
res * gateway . Result
res * gateway . Result
solveErr * errdefs . SolveError
solveErr * errdefs . SolveError
@ -263,7 +263,7 @@ type ResultContext struct {
cleanupsMu sync . Mutex
cleanupsMu sync . Mutex
}
}
func ( r * Result Context ) Done ( ) {
func ( r * Result Handle ) Done ( ) {
r . doneOnce . Do ( func ( ) {
r . doneOnce . Do ( func ( ) {
r . cleanupsMu . Lock ( )
r . cleanupsMu . Lock ( )
cleanups := r . cleanups
cleanups := r . cleanups
@ -278,18 +278,18 @@ func (r *ResultContext) Done() {
} )
} )
}
}
func ( r * Result Context ) registerCleanup ( f func ( ) ) {
func ( r * Result Handle ) registerCleanup ( f func ( ) ) {
r . cleanupsMu . Lock ( )
r . cleanupsMu . Lock ( )
r . cleanups = append ( r . cleanups , f )
r . cleanups = append ( r . cleanups , f )
r . cleanupsMu . Unlock ( )
r . cleanupsMu . Unlock ( )
}
}
func ( r * Result Context ) build ( buildFunc gateway . BuildFunc ) ( err error ) {
func ( r * Result Handle ) build ( buildFunc gateway . BuildFunc ) ( err error ) {
_ , err = buildFunc ( r . gwCtx , r . gwClient )
_ , err = buildFunc ( r . gwCtx , r . gwClient )
return err
return err
}
}
func ( r * Result Context ) getContainerConfig ( ctx context . Context , c gateway . Client , cfg * controllerapi . InvokeConfig ) ( containerCfg gateway . NewContainerRequest , _ error ) {
func ( r * Result Handle ) getContainerConfig ( ctx context . Context , c gateway . Client , cfg * controllerapi . InvokeConfig ) ( containerCfg gateway . NewContainerRequest , _ error ) {
if r . res != nil && r . solveErr == nil {
if r . res != nil && r . solveErr == nil {
logrus . Debugf ( "creating container from successful build" )
logrus . Debugf ( "creating container from successful build" )
ccfg , err := containerConfigFromResult ( ctx , r . res , c , * cfg )
ccfg , err := containerConfigFromResult ( ctx , r . res , c , * cfg )
@ -308,7 +308,7 @@ func (r *ResultContext) getContainerConfig(ctx context.Context, c gateway.Client
return containerCfg , nil
return containerCfg , nil
}
}
func ( r * Result Context ) getProcessConfig ( cfg * controllerapi . InvokeConfig , stdin io . ReadCloser , stdout io . WriteCloser , stderr io . WriteCloser ) ( _ gateway . StartRequest , err error ) {
func ( r * Result Handle ) getProcessConfig ( cfg * controllerapi . InvokeConfig , stdin io . ReadCloser , stdout io . WriteCloser , stderr io . WriteCloser ) ( _ gateway . StartRequest , err error ) {
processCfg := newStartRequest ( stdin , stdout , stderr )
processCfg := newStartRequest ( stdin , stdout , stderr )
if r . res != nil && r . solveErr == nil {
if r . res != nil && r . solveErr == nil {
logrus . Debugf ( "creating container from successful build" )
logrus . Debugf ( "creating container from successful build" )