Skip to content

Commit 58b5e6a

Browse files
committed
integration: log goroutine trace on test timeout
Add maximum timeout to each test and log all buildkitd goroutines when a test gets stuck. Currently tests can time out and will print daemon logs but these would already be after shutdown signal is sent to the daemon. Signed-off-by: Tonis Tiigi <[email protected]>
1 parent a7d4e8c commit 58b5e6a

File tree

8 files changed

+80
-15
lines changed

8 files changed

+80
-15
lines changed

util/testutil/integration/run.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Backend interface {
3939
Address() string
4040
DockerAddress() string
4141
ContainerdAddress() string
42+
DebugAddress() string
4243

4344
Rootless() bool
4445
NetNSDetached() bool
@@ -200,7 +201,7 @@ func Run(t *testing.T, testCases []Test, opt ...TestOpt) {
200201
ctx, cancel := context.WithCancelCause(ctx)
201202
defer func() { cancel(errors.WithStack(context.Canceled)) }()
202203

203-
sb, closer, err := newSandbox(ctx, br, getMirror(), mv)
204+
sb, closer, err := newSandbox(ctx, t, br, getMirror(), mv)
204205
require.NoError(t, err)
205206
t.Cleanup(func() { _ = closer() })
206207
defer func() {

util/testutil/integration/sandbox.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ import (
55
"bytes"
66
"context"
77
"fmt"
8+
"io"
9+
"net"
10+
"net/http"
811
"os"
912
"os/exec"
1013
"path/filepath"
1114
"strings"
1215
"testing"
16+
"time"
1317

1418
"github.com/google/shlex"
1519
"github.com/moby/buildkit/util/bklog"
@@ -18,6 +22,8 @@ import (
1822

1923
const buildkitdConfigFile = "buildkitd.toml"
2024

25+
const maxSandboxTimeout = 5 * time.Minute
26+
2127
type sandbox struct {
2228
Backend
2329

@@ -79,7 +85,7 @@ func (sb *sandbox) Value(k string) interface{} {
7985
return sb.mv.values[k].value
8086
}
8187

82-
func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s Sandbox, cl func() error, err error) {
88+
func newSandbox(ctx context.Context, t *testing.T, w Worker, mirror string, mv matrixValue) (s Sandbox, cl func() error, err error) {
8389
cfg := &BackendConfig{
8490
Logs: make(map[string]*bytes.Buffer),
8591
}
@@ -110,6 +116,24 @@ func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s
110116
}
111117
deferF.Append(closer)
112118

119+
ctx, cancel := context.WithCancelCause(ctx)
120+
121+
go func() {
122+
timeoutContext, cancelTimeout := context.WithTimeoutCause(ctx, maxSandboxTimeout, errors.WithStack(context.DeadlineExceeded))
123+
defer cancelTimeout()
124+
<-timeoutContext.Done()
125+
select {
126+
case <-ctx.Done():
127+
return
128+
default:
129+
t.Logf("sandbox timeout reached, stopping worker")
130+
if addr := b.DebugAddress(); addr != "" {
131+
printBuildkitdDebugLogs(t, addr)
132+
}
133+
cancel(errors.WithStack(context.Canceled))
134+
}
135+
}()
136+
113137
return &sandbox{
114138
Backend: b,
115139
logs: cfg.Logs,
@@ -120,6 +144,30 @@ func newSandbox(ctx context.Context, w Worker, mirror string, mv matrixValue) (s
120144
}, cl, nil
121145
}
122146

147+
func printBuildkitdDebugLogs(t *testing.T, addr string) {
148+
if !strings.HasPrefix(addr, socketScheme) {
149+
t.Logf("invalid debug address %q", addr)
150+
return
151+
}
152+
153+
client := &http.Client{Transport: &http.Transport{DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
154+
return dialPipe(strings.TrimPrefix(addr, socketScheme))
155+
}}}
156+
157+
resp, err := client.Get("http://localhost/debug/pprof/goroutine?debug=2") //nolint:noctx // never cancel
158+
if err != nil {
159+
t.Fatalf("failed to get debug logs: %v", err)
160+
return
161+
}
162+
defer resp.Body.Close()
163+
dt, err := io.ReadAll(resp.Body)
164+
if err != nil {
165+
t.Fatalf("failed to read debug logs: %v", err)
166+
return
167+
}
168+
t.Logf("buildkitd debug logs:\n%s", dt)
169+
}
170+
123171
func RootlessSupported(uid int) bool {
124172
cmd := exec.Command("sudo", "-u", fmt.Sprintf("#%d", uid), "-i", "--", "exec", "unshare", "-U", "true") //nolint:gosec // test utility
125173
b, err := cmd.CombinedOutput()

util/testutil/workers/backend.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type backend struct {
99
address string
1010
dockerAddress string
1111
containerdAddress string
12+
debugAddress string
1213
rootless bool
1314
netnsDetached bool
1415
snapshotter string
@@ -29,6 +30,10 @@ func (b backend) ContainerdAddress() string {
2930
return b.containerdAddress
3031
}
3132

33+
func (b backend) DebugAddress() string {
34+
return b.debugAddress
35+
}
36+
3237
func (b backend) Rootless() bool {
3338
return b.rootless
3439
}

util/testutil/workers/containerd.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ disabled_plugins = ["io.containerd.grpc.v1.cri"]
232232
"nsenter", "-U", "--preserve-credentials", "-m", "-t", fmt.Sprintf("%d", pid)},
233233
append(buildkitdArgs, "--containerd-worker-snapshotter=native")...)
234234
}
235-
buildkitdSock, stop, err := runBuildkitd(cfg, buildkitdArgs, cfg.Logs, c.UID, c.GID, c.ExtraEnv)
235+
buildkitdSock, debugSock, stop, err := runBuildkitd(cfg, buildkitdArgs, cfg.Logs, c.UID, c.GID, c.ExtraEnv)
236236
if err != nil {
237237
integration.PrintLogs(cfg.Logs, log.Println)
238238
return nil, nil, err
@@ -242,6 +242,7 @@ disabled_plugins = ["io.containerd.grpc.v1.cri"]
242242
return backend{
243243
address: buildkitdSock,
244244
containerdAddress: address,
245+
debugAddress: debugSock,
245246
rootless: rootless,
246247
netnsDetached: false,
247248
snapshotter: c.Snapshotter,

util/testutil/workers/oci.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,15 @@ func (s *OCI) New(ctx context.Context, cfg *integration.BackendConfig) (integrat
7777
if runtime.GOOS != "windows" && s.Snapshotter != "native" {
7878
extraEnv = append(extraEnv, "BUILDKIT_DEBUG_FORCE_OVERLAY_DIFF=true")
7979
}
80-
buildkitdSock, stop, err := runBuildkitd(cfg, buildkitdArgs, cfg.Logs, s.UID, s.GID, extraEnv)
80+
buildkitdSock, debugSock, stop, err := runBuildkitd(cfg, buildkitdArgs, cfg.Logs, s.UID, s.GID, extraEnv)
8181
if err != nil {
8282
integration.PrintLogs(cfg.Logs, log.Println)
8383
return nil, nil, err
8484
}
8585

8686
return backend{
8787
address: buildkitdSock,
88+
debugAddress: debugSock,
8889
rootless: s.UID != 0,
8990
netnsDetached: s.NetNSDetached(),
9091
snapshotter: s.Snapshotter,

util/testutil/workers/util.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func runBuildkitd(
3131
logs map[string]*bytes.Buffer,
3232
uid, gid int,
3333
extraEnv []string,
34-
) (address string, cl func() error, err error) {
34+
) (_, _ string, cl func() error, err error) {
3535
deferF := &integration.MultiCloser{}
3636
cl = deferF.F()
3737

@@ -44,35 +44,36 @@ func runBuildkitd(
4444

4545
tmpdir, err := os.MkdirTemp("", "bktest_buildkitd")
4646
if err != nil {
47-
return "", nil, err
47+
return "", "", nil, err
4848
}
4949

5050
if err := chown(tmpdir, uid, gid); err != nil {
51-
return "", nil, err
51+
return "", "", nil, err
5252
}
5353

5454
if err := os.MkdirAll(filepath.Join(tmpdir, "tmp"), 0711); err != nil {
55-
return "", nil, err
55+
return "", "", nil, err
5656
}
5757

5858
if err := chown(filepath.Join(tmpdir, "tmp"), uid, gid); err != nil {
59-
return "", nil, err
59+
return "", "", nil, err
6060
}
6161
deferF.Append(func() error { return os.RemoveAll(tmpdir) })
6262

6363
cfgfile, err := integration.WriteConfig(
6464
append(conf.DaemonConfig, withOTELSocketPath(getTraceSocketPath(tmpdir))))
6565
if err != nil {
66-
return "", nil, err
66+
return "", "", nil, err
6767
}
6868
deferF.Append(func() error {
6969
return os.RemoveAll(filepath.Dir(cfgfile))
7070
})
7171

7272
args = append(args, "--config="+cfgfile)
73-
address = getBuildkitdAddr(tmpdir)
73+
address := getBuildkitdAddr(tmpdir)
74+
debugAddress := getBuildkitdDebugAddr(tmpdir)
7475

75-
args = append(args, "--root", tmpdir, "--addr", address, "--debug")
76+
args = append(args, "--root", tmpdir, "--addr", address, "--debugaddr", debugAddress, "--debug")
7677
cmd := exec.Command(args[0], args[1:]...) //nolint:gosec // test utility
7778
cmd.Env = append(
7879
os.Environ(),
@@ -88,18 +89,18 @@ func runBuildkitd(
8889

8990
stop, err := integration.StartCmd(cmd, logs)
9091
if err != nil {
91-
return "", nil, err
92+
return "", "", nil, err
9293
}
9394
deferF.Append(stop)
9495

9596
if err := integration.WaitSocket(address, 15*time.Second, cmd); err != nil {
96-
return "", nil, err
97+
return "", "", nil, err
9798
}
9899

99100
// separated out since it's not required in windows
100101
deferF.Append(func() error {
101102
return mountInfo(tmpdir)
102103
})
103104

104-
return address, cl, err
105+
return address, debugAddress, cl, err
105106
}

util/testutil/workers/util_unix.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ func getBuildkitdAddr(tmpdir string) string {
3434
return "unix://" + filepath.Join(tmpdir, "buildkitd.sock")
3535
}
3636

37+
func getBuildkitdDebugAddr(tmpdir string) string {
38+
return "unix://" + filepath.Join(tmpdir, "buildkitd-debug.sock")
39+
}
40+
3741
func getTraceSocketPath(tmpdir string) string {
3842
return filepath.Join(tmpdir, "otel-grpc.sock")
3943
}

util/testutil/workers/util_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func getBuildkitdAddr(tmpdir string) string {
2222
return "npipe:////./pipe/buildkitd-" + filepath.Base(tmpdir)
2323
}
2424

25+
func getBuildkitdDebugAddr(tmpdir string) string {
26+
return "npipe:////./pipe/buildkitd-debug-" + filepath.Base(tmpdir)
27+
}
28+
2529
func getTraceSocketPath(tmpdir string) string {
2630
return `\\.\pipe\buildkit-otel-grpc-` + filepath.Base(tmpdir)
2731
}

0 commit comments

Comments
 (0)