Files
buildkit/util/appcontext/appcontext.go
Jonathan A. Sternberg d4ac72d232 control: forward traces in a non-blocking goroutine
Traces are now forwarded in a non-blocking goroutine when sent through
the traces exporter. This prevents traces forwarded from the client from
being stalled while waiting for an upstream uploader to appear.

In addition, adds a shutdown context to `appcontext` that will only
cancel when an interrupt has been received twice. One interrupt will
signal the program should clean up and shut down, the second indicates
we should skip shutdown procedures (more forceful), and the third will
indicate that we should immediately terminate the program.

This gives a bit more of a degree of control to shutdown procedures like
the traces and metrics exporter so there's a difference between forcibly
calling exit and just waiting a long time for the shutdown to happen.

Includes a more aggressive shutdown timeout for `buildctl` that is
similar to the export timeout on `docker-buildx` for the tracing
shutdown as another preventative measure to ensure the CLI hangs up at
an appropriate time interval.

Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
2026-06-05 15:55:22 +02:00

66 lines
1.5 KiB
Go

package appcontext
import (
"context"
"os"
"os/signal"
"sync"
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
)
// Context returns a static context that reacts to termination signals of the
// running process. Useful in CLI tools.
func Context() context.Context {
initContexts()
return appContext
}
// Shutdown returns a static context that closes when multiple interrupt signals
// have been received to indicate a faster shutdown. Useful in CLI tools.
func Shutdown() context.Context {
initContexts()
return shutdownContext
}
var (
appContext context.Context
shutdownContext context.Context
initContextsOnce sync.Once
)
func initContexts() {
initContextsOnce.Do(func() {
signals := make(chan os.Signal, 2048)
signal.Notify(signals, terminationSignals...)
ctx := context.Background()
for _, f := range inits {
ctx = f(ctx) //nolint:fatcontext
}
ctx, cancel := context.WithCancelCause(ctx)
appContext = ctx //nolint:fatcontext
shutdownCtx, shutdownCancel := context.WithCancelCause(context.Background())
shutdownContext = shutdownCtx
// We just allow this goroutine to be orphaned since program termination
// will clean it up.
go func() {
<-signals
err := errors.New("got SIGTERM/SIGINT, forcing shutdown")
cancel(err)
<-signals
err = errors.New("got 2 SIGTERM/SIGINTs, skipping shutdown")
shutdownCancel(err)
<-signals
err = errors.New("got 3 SIGTERM/SIGINTs, forcibly terminating")
bklog.G(ctx).Fatal(err.Error())
}()
})
}