deps: update buildkit, vendor changes

Signed-off-by: Laura Brehm <laurabrehm@hey.com>
This commit is contained in:
Laura Brehm
2023-12-19 12:36:24 +00:00
parent 8484fcdd57
commit 0f45b629ad
157 changed files with 17189 additions and 1232 deletions

View File

@ -7,6 +7,7 @@ import (
"sync"
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
)
var appContextCache context.Context
@ -27,16 +28,17 @@ func Context() context.Context {
ctx = f(ctx)
}
ctx, cancel := context.WithCancel(ctx)
ctx, cancel := context.WithCancelCause(ctx)
appContextCache = ctx
go func() {
for {
<-signals
cancel()
retries++
err := errors.Errorf("got %d SIGTERM/SIGINTs, forcing shutdown", retries)
cancel(err)
if retries >= exitLimit {
bklog.G(ctx).Errorf("got %d SIGTERM/SIGINTs, forcing shutdown", retries)
bklog.G(ctx).Errorf(err.Error())
os.Exit(1)
}
}

View File

@ -90,7 +90,7 @@ type call[T any] struct {
fn func(ctx context.Context) (T, error)
once sync.Once
closeProgressWriter func()
closeProgressWriter func(error)
progressState *progressState
progressCtx context.Context
}
@ -115,9 +115,9 @@ func newCall[T any](fn func(ctx context.Context) (T, error)) *call[T] {
}
func (c *call[T]) run() {
defer c.closeProgressWriter()
ctx, cancel := context.WithCancel(c.ctx)
defer cancel()
defer c.closeProgressWriter(errors.WithStack(context.Canceled))
ctx, cancel := context.WithCancelCause(c.ctx)
defer cancel(errors.WithStack(context.Canceled))
v, err := c.fn(ctx)
c.mu.Lock()
c.result = v
@ -155,8 +155,8 @@ func (c *call[T]) wait(ctx context.Context) (v T, err error) {
c.progressState.add(pw)
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
ctx, cancel := context.WithCancelCause(ctx)
defer cancel(errors.WithStack(context.Canceled))
c.ctxs = append(c.ctxs, ctx)
@ -175,7 +175,7 @@ func (c *call[T]) wait(ctx context.Context) (v T, err error) {
if ok {
c.progressState.close(pw)
}
return empty, ctx.Err()
return empty, context.Cause(ctx)
case <-c.ready:
return c.result, c.err // shared not implemented yet
}
@ -262,7 +262,9 @@ func (sc *sharedContext[T]) checkDone() bool {
for _, ctx := range sc.ctxs {
select {
case <-ctx.Done():
err = ctx.Err()
// Cause can't be used here because this error is returned for Err() in custom context
// implementation and unfortunately stdlib does not allow defining Cause() for custom contexts
err = ctx.Err() //nolint: forbidigo
default:
sc.mu.Unlock()
return false

View File

@ -11,14 +11,15 @@ type MultiReader struct {
main Reader
initialized bool
done chan struct{}
writers map[*progressWriter]func()
doneCause error
writers map[*progressWriter]func(error)
sent []*Progress
}
func NewMultiReader(pr Reader) *MultiReader {
mr := &MultiReader{
main: pr,
writers: make(map[*progressWriter]func()),
writers: make(map[*progressWriter]func(error)),
done: make(chan struct{}),
}
return mr
@ -46,9 +47,9 @@ func (mr *MultiReader) Reader(ctx context.Context) Reader {
go func() {
if isBehind {
close := func() {
close := func(err error) {
w.Close()
closeWriter()
closeWriter(err)
}
i := 0
for {
@ -58,11 +59,11 @@ func (mr *MultiReader) Reader(ctx context.Context) Reader {
if count == 0 {
select {
case <-ctx.Done():
close()
close(context.Cause(ctx))
mr.mu.Unlock()
return
case <-mr.done:
close()
close(mr.doneCause)
mr.mu.Unlock()
return
default:
@ -77,7 +78,7 @@ func (mr *MultiReader) Reader(ctx context.Context) Reader {
if i%100 == 0 {
select {
case <-ctx.Done():
close()
close(context.Cause(ctx))
return
default:
}
@ -110,10 +111,12 @@ func (mr *MultiReader) handle() error {
if err != nil {
if err == io.EOF {
mr.mu.Lock()
cancelErr := context.Canceled
for w, c := range mr.writers {
w.Close()
c()
c(cancelErr)
}
mr.doneCause = cancelErr
close(mr.done)
mr.mu.Unlock()
return nil

View File

@ -56,7 +56,7 @@ type WriterOption func(Writer)
// NewContext returns a new context and a progress reader that captures all
// progress items writtern to this context. Last returned parameter is a closer
// function to signal that no new writes will happen to this context.
func NewContext(ctx context.Context) (Reader, context.Context, func()) {
func NewContext(ctx context.Context) (Reader, context.Context, func(error)) {
pr, pw, cancel := pipe()
ctx = WithProgress(ctx, pw)
return pr, ctx, cancel
@ -141,7 +141,7 @@ func (pr *progressReader) Read(ctx context.Context) ([]*Progress, error) {
select {
case <-ctx.Done():
pr.mu.Unlock()
return nil, ctx.Err()
return nil, context.Cause(ctx)
default:
}
dmap := pr.dirty
@ -185,8 +185,8 @@ func (pr *progressReader) append(pw *progressWriter) {
}
}
func pipe() (*progressReader, *progressWriter, func()) {
ctx, cancel := context.WithCancel(context.Background())
func pipe() (*progressReader, *progressWriter, func(error)) {
ctx, cancel := context.WithCancelCause(context.Background())
pr := &progressReader{
ctx: ctx,
writers: make(map[*progressWriter]struct{}),

View File

@ -99,7 +99,7 @@ func (d Display) UpdateFrom(ctx context.Context, ch chan *client.SolveStatus) ([
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
return nil, context.Cause(ctx)
case <-ticker.C:
d.disp.refresh()
case ss, ok := <-ch:

View File

@ -67,8 +67,9 @@ http:
}
deferF.Append(stop)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
ctx, cancel := context.WithCancelCause(context.Background())
ctx, _ = context.WithTimeoutCause(ctx, 5*time.Second, errors.WithStack(context.DeadlineExceeded))
defer cancel(errors.WithStack(context.Canceled))
url, err = detectPort(ctx, rc)
if err != nil {
return "", nil, err

View File

@ -423,3 +423,10 @@ func prepareValueMatrix(tc testConf) []matrixValue {
}
return m
}
// Skips tests on Windows
func SkipOnPlatform(t *testing.T, goos string) {
if runtime.GOOS == goos {
t.Skipf("Skipped on %s", goos)
}
}

View File

@ -99,7 +99,7 @@ func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s
b, closer, err := w.New(ctx, cfg)
if err != nil {
return nil, nil, err
return nil, nil, errors.Wrap(err, "creating worker")
}
deferF.Append(closer)

View File

@ -5,7 +5,6 @@ import (
"context"
"fmt"
"io"
"net"
"os"
"os/exec"
"strings"
@ -100,13 +99,11 @@ func StartCmd(cmd *exec.Cmd, logs map[string]*bytes.Buffer) (func() error, error
}, nil
}
func WaitUnix(address string, d time.Duration, cmd *exec.Cmd) error {
address = strings.TrimPrefix(address, "unix://")
addr, err := net.ResolveUnixAddr("unix", address)
if err != nil {
return errors.Wrapf(err, "failed resolving unix addr: %s", address)
}
// WaitSocket will dial a socket opened by a command passed in as cmd.
// On Linux this socket is typically a Unix socket,
// while on Windows this will be a named pipe.
func WaitSocket(address string, d time.Duration, cmd *exec.Cmd) error {
address = strings.TrimPrefix(address, socketScheme)
step := 50 * time.Millisecond
i := 0
for {
@ -114,7 +111,7 @@ func WaitUnix(address string, d time.Duration, cmd *exec.Cmd) error {
return errors.Errorf("process exited: %s", cmd.String())
}
if conn, err := net.DialUnix("unix", nil, addr); err == nil {
if conn, err := dialPipe(address); err == nil {
conn.Close()
break
}

View File

@ -0,0 +1,23 @@
//go:build !windows
// +build !windows
package integration
import (
"net"
"github.com/pkg/errors"
)
var socketScheme = "unix://"
// abstracted function to handle pipe dialing on unix.
// some simplification has been made to discard
// laddr for unix -- left as nil.
func dialPipe(address string) (net.Conn, error) {
addr, err := net.ResolveUnixAddr("unix", address)
if err != nil {
return nil, errors.Wrapf(err, "failed resolving unix addr: %s", address)
}
return net.DialUnix("unix", nil, addr)
}

View File

@ -0,0 +1,15 @@
package integration
import (
"net"
"github.com/Microsoft/go-winio"
)
var socketScheme = "npipe://"
// abstracted function to handle pipe dialing on windows.
// some simplification has been made to discard timeout param.
func dialPipe(address string) (net.Conn, error) {
return winio.DialPipe(address, nil)
}

View File

@ -88,9 +88,11 @@ func (c *Containerd) New(ctx context.Context, cfg *integration.BackendConfig) (b
if err := integration.LookupBinary(c.Containerd); err != nil {
return nil, nil, err
}
if err := integration.LookupBinary("buildkitd"); err != nil {
return nil, nil, err
}
if err := requireRoot(); err != nil {
return nil, nil, err
}
@ -117,6 +119,7 @@ func (c *Containerd) New(ctx context.Context, cfg *integration.BackendConfig) (b
if err != nil {
return nil, nil, err
}
if rootless {
if err := os.Chown(tmpdir, c.UID, c.GID); err != nil {
return nil, nil, err
@ -125,7 +128,7 @@ func (c *Containerd) New(ctx context.Context, cfg *integration.BackendConfig) (b
deferF.Append(func() error { return os.RemoveAll(tmpdir) })
address := filepath.Join(tmpdir, "containerd.sock")
address := getContainerdSock(tmpdir)
config := fmt.Sprintf(`root = %q
state = %q
# CRI plugins listens on 10010/tcp for stream server.
@ -137,8 +140,11 @@ disabled_plugins = ["cri"]
[debug]
level = "debug"
address = %q
`, filepath.Join(tmpdir, "root"), filepath.Join(tmpdir, "state"), address, filepath.Join(tmpdir, "debug.sock"))
address = %q`,
filepath.Join(tmpdir, "root"),
filepath.Join(tmpdir, "state"),
address, getContainerdDebugSock(tmpdir),
)
var snBuildkitdArgs []string
if c.Snapshotter != "" {
@ -185,19 +191,23 @@ disabled_plugins = ["cri"]
if err != nil {
return nil, nil, err
}
if err := integration.WaitUnix(address, 10*time.Second, cmd); err != nil {
if err := integration.WaitSocket(address, 10*time.Second, cmd); err != nil {
ctdStop()
return nil, nil, errors.Wrapf(err, "containerd did not start up: %s", integration.FormatLogs(cfg.Logs))
}
deferF.Append(ctdStop)
buildkitdArgs := append([]string{"buildkitd",
"--oci-worker=false",
// handles only windows case, no effect on unix
address = normalizeAddress(address)
buildkitdArgs := []string{
"buildkitd",
"--containerd-worker-gc=false",
"--containerd-worker=true",
"--containerd-worker-addr", address,
"--containerd-worker-labels=org.mobyproject.buildkit.worker.sandbox=true", // Include use of --containerd-worker-labels to trigger https://github.com/moby/buildkit/pull/603
}, snBuildkitdArgs...)
}
buildkitdArgs = applyBuildkitdPlatformFlags(buildkitdArgs)
buildkitdArgs = append(buildkitdArgs, snBuildkitdArgs...)
if runtime.GOOS != "windows" && c.Snapshotter != "native" {
c.ExtraEnv = append(c.ExtraEnv, "BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF=true")
@ -266,7 +276,7 @@ func runStargzSnapshotter(cfg *integration.BackendConfig) (address string, cl fu
if err != nil {
return "", nil, err
}
if err = integration.WaitUnix(address, 10*time.Second, cmd); err != nil {
if err = integration.WaitSocket(address, 10*time.Second, cmd); err != nil {
snStop()
return "", nil, errors.Wrapf(err, "containerd-stargz-grpc did not start up: %s", integration.FormatLogs(cfg.Logs))
}

View File

@ -159,7 +159,7 @@ func (c Moby) New(ctx context.Context, cfg *integration.BackendConfig) (b integr
}
deferF.Append(d.StopWithError)
if err := integration.WaitUnix(d.Sock(), 5*time.Second, nil); err != nil {
if err := integration.WaitSocket(d.Sock(), 5*time.Second, nil); err != nil {
return nil, nil, errors.Errorf("dockerd did not start up: %q, %s", err, integration.FormatLogs(cfg.Logs))
}

View File

@ -4,31 +4,18 @@ import (
"context"
"fmt"
"log"
"os"
"runtime"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
)
// InitOCIWorker registers an integration test worker, which enables the --oci-worker
// flag in the test buildkitd instance and disables the --containerd-worker flag. This
// integration test worker is not supported on Windows.
func InitOCIWorker() {
integration.Register(&OCI{ID: "oci"})
// the rootless uid is defined in Dockerfile
if s := os.Getenv("BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR"); s != "" {
var uid, gid int
if _, err := fmt.Sscanf(s, "%d:%d", &uid, &gid); err != nil {
bklog.L.Fatalf("unexpected BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR: %q", s)
}
if integration.RootlessSupported(uid) {
integration.Register(&OCI{ID: "oci-rootless", UID: uid, GID: gid})
}
}
if s := os.Getenv("BUILDKIT_INTEGRATION_SNAPSHOTTER"); s != "" {
integration.Register(&OCI{ID: "oci-snapshotter-" + s, Snapshotter: s})
}
// calling platform specific
initOCIWorker()
}
type OCI struct {

View File

@ -0,0 +1,31 @@
//go:build !windows
// +build !windows
package workers
import (
"fmt"
"os"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/buildkit/util/testutil/integration"
)
func initOCIWorker() {
integration.Register(&OCI{ID: "oci"})
// the rootless uid is defined in Dockerfile
if s := os.Getenv("BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR"); s != "" {
var uid, gid int
if _, err := fmt.Sscanf(s, "%d:%d", &uid, &gid); err != nil {
bklog.L.Fatalf("unexpected BUILDKIT_INTEGRATION_ROOTLESS_IDPAIR: %q", s)
}
if integration.RootlessSupported(uid) {
integration.Register(&OCI{ID: "oci-rootless", UID: uid, GID: gid})
}
}
if s := os.Getenv("BUILDKIT_INTEGRATION_SNAPSHOTTER"); s != "" {
integration.Register(&OCI{ID: "oci-snapshotter-" + s, Snapshotter: s})
}
}

View File

@ -0,0 +1,7 @@
package workers
import "github.com/moby/buildkit/util/bklog"
func initOCIWorker() {
bklog.L.Info("OCI Worker not supported on Windows.")
}

View File

@ -1,23 +0,0 @@
//go:build !windows
// +build !windows
package workers
import (
"path/filepath"
"syscall"
)
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{
Setsid: true, // stretch sudo needs this for sigterm
}
}
func getBuildkitdAddr(tmpdir string) string {
return "unix://" + filepath.Join(tmpdir, "buildkitd.sock")
}
func getTraceSocketPath(tmpdir string) string {
return filepath.Join(tmpdir, "otel-grpc.sock")
}

View File

@ -1,21 +0,0 @@
//go:build windows
// +build windows
package workers
import (
"path/filepath"
"syscall"
)
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{}
}
func getBuildkitdAddr(tmpdir string) string {
return "//./pipe/buildkitd-" + filepath.Base(tmpdir)
}
func getTraceSocketPath(tmpdir string) string {
return `\\.\pipe\buildkit-otel-grpc-` + filepath.Base(tmpdir)
}

View File

@ -1,98 +1,17 @@
package workers
import (
"bufio"
"bytes"
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
)
func requireRoot() error {
if os.Getuid() != 0 {
return errors.Wrap(integration.ErrRequirements, "requires root")
}
return nil
}
func runBuildkitd(ctx context.Context, conf *integration.BackendConfig, args []string, logs map[string]*bytes.Buffer, uid, gid int, extraEnv []string) (address string, cl func() error, err error) {
deferF := &integration.MultiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
tmpdir, err := os.MkdirTemp("", "bktest_buildkitd")
if err != nil {
return "", nil, err
}
if err := os.Chown(tmpdir, uid, gid); err != nil {
return "", nil, err
}
if err := os.MkdirAll(filepath.Join(tmpdir, "tmp"), 0711); err != nil {
return "", nil, err
}
if err := os.Chown(filepath.Join(tmpdir, "tmp"), uid, gid); err != nil {
return "", nil, err
}
deferF.Append(func() error { return os.RemoveAll(tmpdir) })
cfgfile, err := integration.WriteConfig(append(conf.DaemonConfig, withOTELSocketPath(getTraceSocketPath(tmpdir))))
if err != nil {
return "", nil, err
}
deferF.Append(func() error {
return os.RemoveAll(filepath.Dir(cfgfile))
})
args = append(args, "--config="+cfgfile)
address = getBuildkitdAddr(tmpdir)
args = append(args, "--root", tmpdir, "--addr", address, "--debug")
cmd := exec.Command(args[0], args[1:]...) //nolint:gosec // test utility
cmd.Env = append(os.Environ(), "BUILDKIT_DEBUG_EXEC_OUTPUT=1", "BUILDKIT_DEBUG_PANIC_ON_ERROR=1", "TMPDIR="+filepath.Join(tmpdir, "tmp"))
cmd.Env = append(cmd.Env, extraEnv...)
cmd.SysProcAttr = getSysProcAttr()
stop, err := integration.StartCmd(cmd, logs)
if err != nil {
return "", nil, err
}
deferF.Append(stop)
if err := integration.WaitUnix(address, 15*time.Second, cmd); err != nil {
return "", nil, err
}
deferF.Append(func() error {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return errors.Wrap(err, "failed to open mountinfo")
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if strings.Contains(s.Text(), tmpdir) {
return errors.Errorf("leaked mountpoint for %s", tmpdir)
}
}
return s.Err()
})
return address, cl, err
}
func withOTELSocketPath(socketPath string) integration.ConfigUpdater {
return otelSocketPath(socketPath)
}
@ -106,3 +25,79 @@ func (osp otelSocketPath) UpdateConfigFile(in string) string {
socketPath = %q
`, in, osp)
}
func runBuildkitd(
ctx context.Context,
conf *integration.BackendConfig,
args []string,
logs map[string]*bytes.Buffer,
uid, gid int,
extraEnv []string,
) (address string, cl func() error, err error) {
deferF := &integration.MultiCloser{}
cl = deferF.F()
defer func() {
if err != nil {
deferF.F()()
cl = nil
}
}()
tmpdir, err := os.MkdirTemp("", "bktest_buildkitd")
if err != nil {
return "", nil, err
}
if err := chown(tmpdir, uid, gid); err != nil {
return "", nil, err
}
if err := os.MkdirAll(filepath.Join(tmpdir, "tmp"), 0711); err != nil {
return "", nil, err
}
if err := chown(filepath.Join(tmpdir, "tmp"), uid, gid); err != nil {
return "", nil, err
}
deferF.Append(func() error { return os.RemoveAll(tmpdir) })
cfgfile, err := integration.WriteConfig(
append(conf.DaemonConfig, withOTELSocketPath(getTraceSocketPath(tmpdir))))
if err != nil {
return "", nil, err
}
deferF.Append(func() error {
return os.RemoveAll(filepath.Dir(cfgfile))
})
args = append(args, "--config="+cfgfile)
address = getBuildkitdAddr(tmpdir)
args = append(args, "--root", tmpdir, "--addr", address, "--debug")
cmd := exec.Command(args[0], args[1:]...) //nolint:gosec // test utility
cmd.Env = append(
os.Environ(),
"BUILDKIT_DEBUG_EXEC_OUTPUT=1",
"BUILDKIT_DEBUG_PANIC_ON_ERROR=1",
"TMPDIR="+filepath.Join(tmpdir, "tmp"))
cmd.Env = append(cmd.Env, extraEnv...)
cmd.SysProcAttr = getSysProcAttr()
stop, err := integration.StartCmd(cmd, logs)
if err != nil {
return "", nil, err
}
deferF.Append(stop)
if err := integration.WaitSocket(address, 15*time.Second, cmd); err != nil {
return "", nil, err
}
// separated out since it's not required in windows
deferF.Append(func() error {
return mountInfo(tmpdir)
})
return address, cl, err
}

View File

@ -0,0 +1,74 @@
//go:build !windows
// +build !windows
package workers
import (
"bufio"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/moby/buildkit/util/testutil/integration"
"github.com/pkg/errors"
)
func applyBuildkitdPlatformFlags(args []string) []string {
return append(args, "--oci-worker=false")
}
func requireRoot() error {
if os.Getuid() != 0 {
return errors.Wrap(integration.ErrRequirements, "requires root")
}
return nil
}
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{
Setsid: true, // stretch sudo needs this for sigterm
}
}
func getBuildkitdAddr(tmpdir string) string {
return "unix://" + filepath.Join(tmpdir, "buildkitd.sock")
}
func getTraceSocketPath(tmpdir string) string {
return filepath.Join(tmpdir, "otel-grpc.sock")
}
func getContainerdSock(tmpdir string) string {
return filepath.Join(tmpdir, "containerd.sock")
}
func getContainerdDebugSock(tmpdir string) string {
return filepath.Join(tmpdir, "debug.sock")
}
func mountInfo(tmpdir string) error {
f, err := os.Open("/proc/self/mountinfo")
if err != nil {
return errors.Wrap(err, "failed to open mountinfo")
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
if strings.Contains(s.Text(), tmpdir) {
return errors.Errorf("leaked mountpoint for %s", tmpdir)
}
}
return s.Err()
}
// moved here since os.Chown is not supported on Windows.
// see no-op counterpart in util_windows.go
func chown(name string, uid, gid int) error {
return os.Chown(name, uid, gid)
}
func normalizeAddress(address string) string {
// for parity with windows, no effect for unix
return address
}

View File

@ -0,0 +1,53 @@
package workers
import (
"path/filepath"
"strings"
"syscall"
)
func applyBuildkitdPlatformFlags(args []string) []string {
return args
}
func requireRoot() error {
return nil
}
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{}
}
func getBuildkitdAddr(tmpdir string) string {
return "npipe:////./pipe/buildkitd-" + filepath.Base(tmpdir)
}
func getTraceSocketPath(tmpdir string) string {
return `\\.\pipe\buildkit-otel-grpc-` + filepath.Base(tmpdir)
}
func getContainerdSock(tmpdir string) string {
return `\\.\pipe\containerd-` + filepath.Base(tmpdir)
}
func getContainerdDebugSock(tmpdir string) string {
return `\\.\pipe\containerd-` + filepath.Base(tmpdir) + `debug`
}
// no-op for parity with unix
func mountInfo(tmpdir string) error {
return nil
}
func chown(name string, uid, gid int) error {
// Chown not supported on Windows
return nil
}
func normalizeAddress(address string) string {
address = filepath.ToSlash(address)
if !strings.HasPrefix(address, "npipe://") {
address = "npipe://" + address
}
return address
}

View File

@ -5,6 +5,7 @@ import (
"sync"
"github.com/moby/buildkit/util/tracing/detect"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
@ -13,8 +14,8 @@ const maxBuffer = 256
var exp = &Exporter{}
func init() {
detect.Register("delegated", func() (sdktrace.SpanExporter, error) {
return exp, nil
detect.Register("delegated", func() (sdktrace.SpanExporter, sdkmetric.Exporter, error) {
return exp, nil, nil
}, 100)
}

View File

@ -10,13 +10,16 @@ import (
"github.com/moby/buildkit/util/bklog"
"github.com/pkg/errors"
"go.opentelemetry.io/otel/exporters/prometheus"
"go.opentelemetry.io/otel/metric"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
"go.opentelemetry.io/otel/trace"
)
type ExporterDetector func() (sdktrace.SpanExporter, error)
type ExporterDetector func() (sdktrace.SpanExporter, sdkmetric.Exporter, error)
type detector struct {
f ExporterDetector
@ -26,10 +29,16 @@ type detector struct {
var ServiceName string
var Recorder *TraceRecorder
var Resource *resource.Resource
var detectors map[string]detector
var once sync.Once
var tp trace.TracerProvider
var exporter sdktrace.SpanExporter
var mp metric.MeterProvider
var exporter struct {
SpanExporter sdktrace.SpanExporter
MetricExporter sdkmetric.Exporter
}
var closers []func(context.Context) error
var err error
@ -43,14 +52,14 @@ func Register(name string, exp ExporterDetector, priority int) {
}
}
func detectExporter() (sdktrace.SpanExporter, error) {
func detectExporter() (texp sdktrace.SpanExporter, mexp sdkmetric.Exporter, err error) {
if n := os.Getenv("OTEL_TRACES_EXPORTER"); n != "" {
d, ok := detectors[n]
if !ok {
if n == "none" {
return nil, nil
return nil, nil, nil
}
return nil, errors.Errorf("unsupported opentelemetry tracer %v", n)
return nil, nil, errors.Errorf("unsupported opentelemetry tracer %v", n)
}
return d.f()
}
@ -61,84 +70,145 @@ func detectExporter() (sdktrace.SpanExporter, error) {
sort.Slice(arr, func(i, j int) bool {
return arr[i].priority < arr[j].priority
})
for _, d := range arr {
exp, err := d.f()
t, m, err := d.f()
if err != nil {
return nil, err
return nil, nil, err
}
if exp != nil {
return exp, nil
if texp == nil {
texp = t
}
if mexp == nil {
mexp = m
}
// Found a candidate for both exporters so just return now.
if texp != nil && mexp != nil {
return texp, mexp, nil
}
}
return nil, nil
return texp, mexp, nil
}
func getExporter() (sdktrace.SpanExporter, error) {
exp, err := detectExporter()
func getExporters() (sdktrace.SpanExporter, sdkmetric.Exporter, error) {
texp, mexp, err := detectExporter()
if err != nil {
return nil, err
return nil, nil, err
}
if Recorder != nil {
Recorder.SpanExporter = exp
exp = Recorder
Recorder.SpanExporter = texp
texp = Recorder
}
return exp, nil
return texp, mexp, nil
}
func detect() error {
tp = trace.NewNoopTracerProvider()
mp = sdkmetric.NewMeterProvider()
exp, err := getExporter()
if err != nil || exp == nil {
texp, mexp, err := getExporters()
if err != nil || (texp == nil && mexp == nil) {
return err
}
if Resource == nil {
res, err := resource.Detect(context.Background(), serviceNameDetector{})
if err != nil {
return err
}
res, err = resource.Merge(resource.Default(), res)
if err != nil {
return err
}
Resource = res
}
// enable log with traceID when valid exporter
bklog.EnableLogWithTraceID(true)
if texp != nil {
bklog.EnableLogWithTraceID(true)
res, err := resource.Detect(context.Background(), serviceNameDetector{})
if err != nil {
return err
}
res, err = resource.Merge(resource.Default(), res)
if err != nil {
return err
sp := sdktrace.NewBatchSpanProcessor(texp)
if Recorder != nil {
Recorder.flush = sp.ForceFlush
}
sdktp := sdktrace.NewTracerProvider(
sdktrace.WithSpanProcessor(sp),
sdktrace.WithResource(Resource),
)
closers = append(closers, sdktp.Shutdown)
exporter.SpanExporter = texp
tp = sdktp
}
sp := sdktrace.NewBatchSpanProcessor(exp)
if Recorder != nil {
Recorder.flush = sp.ForceFlush
var readers []sdkmetric.Reader
if mexp != nil {
// Create a new periodic reader using any configured metric exporter.
readers = append(readers, sdkmetric.NewPeriodicReader(mexp))
}
sdktp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sp), sdktrace.WithResource(res))
closers = append(closers, sdktp.Shutdown)
if r, err := prometheus.New(); err != nil {
// Log the error but do not fail if we could not configure the prometheus metrics.
bklog.G(context.Background()).
WithError(err).
Error("failed prometheus metrics configuration")
} else {
// Register the prometheus reader if there was no error.
readers = append(readers, r)
}
exporter = exp
tp = sdktp
if len(readers) > 0 {
opts := make([]sdkmetric.Option, 0, len(readers)+1)
opts = append(opts, sdkmetric.WithResource(Resource))
for _, r := range readers {
opts = append(opts, sdkmetric.WithReader(r))
}
sdkmp := sdkmetric.NewMeterProvider(opts...)
closers = append(closers, sdkmp.Shutdown)
exporter.MetricExporter = mexp
mp = sdkmp
}
return nil
}
func TracerProvider() (trace.TracerProvider, error) {
once.Do(func() {
if err1 := detect(); err1 != nil {
err = err1
}
})
b, _ := strconv.ParseBool(os.Getenv("OTEL_IGNORE_ERROR"))
if err != nil && !b {
if err := detectOnce(); err != nil {
return nil, err
}
return tp, nil
}
func Exporter() (sdktrace.SpanExporter, error) {
_, err := TracerProvider()
if err != nil {
func MeterProvider() (metric.MeterProvider, error) {
if err := detectOnce(); err != nil {
return nil, err
}
return exporter, nil
return mp, nil
}
func detectOnce() error {
once.Do(func() {
if err1 := detect(); err1 != nil {
b, _ := strconv.ParseBool(os.Getenv("OTEL_IGNORE_ERROR"))
if !b {
err = err1
}
}
})
return err
}
func Exporter() (sdktrace.SpanExporter, sdkmetric.Exporter, error) {
_, err := TracerProvider()
if err != nil {
return nil, nil, err
}
return exporter.SpanExporter, exporter.MetricExporter, nil
}
func Shutdown(ctx context.Context) error {

View File

@ -5,9 +5,13 @@ import (
"os"
"github.com/pkg/errors"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)
@ -15,7 +19,20 @@ func init() {
Register("otlp", otlpExporter, 10)
}
func otlpExporter() (sdktrace.SpanExporter, error) {
func otlpExporter() (sdktrace.SpanExporter, sdkmetric.Exporter, error) {
texp, err := otlpSpanExporter()
if err != nil {
return nil, nil, err
}
mexp, err := otlpMetricExporter()
if err != nil {
return nil, nil, err
}
return texp, mexp, nil
}
func otlpSpanExporter() (sdktrace.SpanExporter, error) {
set := os.Getenv("OTEL_TRACES_EXPORTER") == "otlp" || os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") != "" || os.Getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") != ""
if !set {
return nil, nil
@ -43,3 +60,36 @@ func otlpExporter() (sdktrace.SpanExporter, error) {
return otlptrace.New(context.Background(), c)
}
func otlpMetricExporter() (sdkmetric.Exporter, error) {
set := os.Getenv("OTEL_METRICS_EXPORTER") == "otlp" || os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") != "" || os.Getenv("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT") != ""
if !set {
return nil, nil
}
proto := os.Getenv("OTEL_EXPORTER_OTLP_METRICS_PROTOCOL")
if proto == "" {
proto = os.Getenv("OTEL_EXPORTER_OTLP_PROTOCOL")
}
if proto == "" {
proto = "grpc"
}
switch proto {
case "grpc":
return otlpmetricgrpc.New(context.Background(),
otlpmetricgrpc.WithTemporalitySelector(deltaTemporality),
)
case "http/protobuf":
return otlpmetrichttp.New(context.Background(),
otlpmetrichttp.WithTemporalitySelector(deltaTemporality),
)
// case "http/json": // unsupported by library
default:
return nil, errors.Errorf("unsupported otlp protocol %v", proto)
}
}
func deltaTemporality(_ sdkmetric.InstrumentKind) metricdata.Temporality {
return metricdata.DeltaTemporality
}

View File

@ -70,9 +70,10 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
}
ctx, cancel := c.connection.ContextWithStop(ctx)
defer cancel()
ctx, tCancel := context.WithTimeout(ctx, 30*time.Second)
defer tCancel()
defer cancel(errors.WithStack(context.Canceled))
ctx, tCancel := context.WithCancelCause(ctx)
ctx, _ = context.WithTimeoutCause(ctx, 30*time.Second, errors.WithStack(context.DeadlineExceeded))
defer tCancel(errors.WithStack(context.Canceled))
ctx = c.connection.ContextWithMetadata(ctx)
err := func() error {

View File

@ -22,6 +22,7 @@ import (
"time"
"unsafe"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
@ -185,7 +186,7 @@ func (c *Connection) Shutdown(ctx context.Context) error {
select {
case <-c.backgroundConnectionDoneCh:
case <-ctx.Done():
return ctx.Err()
return context.Cause(ctx)
}
c.mu.Lock()
@ -200,17 +201,17 @@ func (c *Connection) Shutdown(ctx context.Context) error {
return nil
}
func (c *Connection) ContextWithStop(ctx context.Context) (context.Context, context.CancelFunc) {
func (c *Connection) ContextWithStop(ctx context.Context) (context.Context, context.CancelCauseFunc) {
// Unify the parent context Done signal with the Connection's
// stop channel.
ctx, cancel := context.WithCancel(ctx)
go func(ctx context.Context, cancel context.CancelFunc) {
ctx, cancel := context.WithCancelCause(ctx)
go func(ctx context.Context, cancel context.CancelCauseFunc) {
select {
case <-ctx.Done():
// Nothing to do, either cancelled or deadline
// happened.
case <-c.stopCh:
cancel()
cancel(errors.WithStack(context.Canceled))
}
}(ctx, cancel)
return ctx, cancel