Skip to content

Commit f588c27

Browse files
committed
feat(op-acceptor): add extended logging
1 parent 9083dfd commit f588c27

File tree

5 files changed

+91
-64
lines changed

5 files changed

+91
-64
lines changed

op-acceptor/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ RUN just build
1414

1515
FROM golang:1.23.5-alpine3.21
1616

17-
RUN apk add --no-cache ca-certificates build-base
17+
RUN apk add --no-cache ca-certificates build-base git
1818

1919
# Increase the proxy connect timeout to 60 seconds
2020
ENV GOPROXYCONNECTTIMEOUT=60s
@@ -27,6 +27,7 @@ ENV GOPATH="/go"
2727
ENV PATH="${GOPATH}/bin:${PATH}"
2828

2929
WORKDIR /app
30+
3031
RUN addgroup -S app && adduser -S app -G app && \
3132
mkdir -p /devnets && \
3233
chown -R app:app /devnets

op-acceptor/config.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ type Config struct {
2222
AllowSkips bool // Allow tests to be skipped instead of failing when preconditions are not met
2323
DefaultTimeout time.Duration // Default timeout for individual tests, can be overridden by test config
2424
LogDir string // Directory to store test logs
25-
26-
Log log.Logger
25+
OutputTestLogs bool // Whether to output test logs to the console
26+
TestLogLevel string // Log level to be used for the tests
27+
Log log.Logger
2728
}
2829

2930
// NewConfig creates a new Config instance
@@ -72,6 +73,8 @@ func NewConfig(ctx *cli.Context, log log.Logger, testDir string, validatorConfig
7273
RunOnce: runOnce,
7374
AllowSkips: ctx.Bool(flags.AllowSkips.Name),
7475
DefaultTimeout: ctx.Duration(flags.DefaultTimeout.Name),
76+
OutputTestLogs: ctx.Bool(flags.OutputTestLogs.Name),
77+
TestLogLevel: ctx.String(flags.TestLogLevel.Name),
7578
LogDir: logDir,
7679
Log: log,
7780
}, nil

op-acceptor/flags/flags.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ var (
6666
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "LOGDIR"),
6767
Usage: "Directory to store test logs. Defaults to 'logs' if not specified.",
6868
}
69+
TestLogLevel = &cli.StringFlag{
70+
Name: "test-log-level",
71+
Value: "info",
72+
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "TEST_LOG_LEVEL"),
73+
Usage: "Log level to be used for the tests. Defaults to 'info'.",
74+
}
75+
OutputTestLogs = &cli.BoolFlag{
76+
Name: "output-test-logs",
77+
Value: false,
78+
EnvVars: opservice.PrefixEnvVar(EnvVarPrefix, "OUTPUT_TEST_LOGS"),
79+
Usage: "Output test logs to the console. Defaults to false.",
80+
}
6981
)
7082

7183
var requiredFlags = []cli.Flag{
@@ -80,6 +92,8 @@ var optionalFlags = []cli.Flag{
8092
AllowSkips,
8193
DefaultTimeout,
8294
LogDir,
95+
TestLogLevel,
96+
OutputTestLogs,
8397
}
8498
var Flags []cli.Flag
8599

op-acceptor/nat.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,16 @@ func New(ctx context.Context, config *Config, version string, shutdownCallback f
7676

7777
// Create runner with registry
7878
testRunner, err := runner.NewTestRunner(runner.Config{
79-
Registry: reg,
80-
WorkDir: config.TestDir,
81-
Log: config.Log,
82-
TargetGate: config.TargetGate,
83-
GoBinary: config.GoBinary,
84-
AllowSkips: config.AllowSkips,
85-
NetworkName: networkName,
86-
DevnetEnv: env,
79+
Registry: reg,
80+
WorkDir: config.TestDir,
81+
Log: config.Log,
82+
TargetGate: config.TargetGate,
83+
GoBinary: config.GoBinary,
84+
AllowSkips: config.AllowSkips,
85+
OutputTestLogs: config.OutputTestLogs,
86+
TestLogLevel: config.TestLogLevel,
87+
NetworkName: networkName,
88+
DevnetEnv: env,
8789
})
8890
if err != nil {
8991
return nil, fmt.Errorf("failed to create test runner: %w", err)

op-acceptor/runner/runner.go

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8+
"io"
89
"net/url"
910
"os"
1011
"os/exec"
@@ -93,30 +94,34 @@ type TestRunnerWithFileLogger interface {
9394

9495
// runner struct implements TestRunner interface
9596
type runner struct {
96-
registry *registry.Registry
97-
validators []types.ValidatorMetadata
98-
workDir string // Directory for running tests
99-
log log.Logger
100-
runID string
101-
goBinary string // Path to the Go binary
102-
allowSkips bool // Whether to allow skipping tests when preconditions are not met
103-
fileLogger *logging.FileLogger // Logger for storing test results
104-
networkName string // Name of the network being tested
105-
env *env.DevnetEnv
106-
tracer trace.Tracer
97+
registry *registry.Registry
98+
validators []types.ValidatorMetadata
99+
workDir string // Directory for running tests
100+
log log.Logger
101+
runID string
102+
goBinary string // Path to the Go binary
103+
allowSkips bool // Whether to allow skipping tests when preconditions are not met
104+
outputTestLogs bool // Whether to output test logs to the console
105+
testLogLevel string // Log level to be used for the tests
106+
fileLogger *logging.FileLogger // Logger for storing test results
107+
networkName string // Name of the network being tested
108+
env *env.DevnetEnv
109+
tracer trace.Tracer
107110
}
108111

109112
// Config holds configuration for creating a new runner
110113
type Config struct {
111-
Registry *registry.Registry
112-
TargetGate string
113-
WorkDir string
114-
Log log.Logger
115-
GoBinary string // path to the Go binary
116-
AllowSkips bool // Whether to allow skipping tests when preconditions are not met
117-
FileLogger *logging.FileLogger // Logger for storing test results
118-
NetworkName string // Name of the network being tested
119-
DevnetEnv *env.DevnetEnv
114+
Registry *registry.Registry
115+
TargetGate string
116+
WorkDir string
117+
Log log.Logger
118+
GoBinary string // path to the Go binary
119+
AllowSkips bool // Whether to allow skipping tests when preconditions are not met
120+
OutputTestLogs bool // Whether to output test logs to the console
121+
TestLogLevel string // Log level to be used for the tests
122+
FileLogger *logging.FileLogger // Logger for storing test results
123+
NetworkName string // Name of the network being tested
124+
DevnetEnv *env.DevnetEnv
120125
}
121126

122127
// NewTestRunner creates a new test runner instance
@@ -352,27 +357,10 @@ func (r *runner) getTestKey(validator types.ValidatorMetadata) string {
352357
return validator.FuncName
353358
}
354359

355-
func (r *runner) isGitInstalled() error {
356-
gitCmd := exec.Command("git", "version")
357-
err := gitCmd.Run()
358-
if err != nil {
359-
return fmt.Errorf("git is not installed")
360-
}
361-
return nil
362-
}
363-
364360
func isLocalPath(pkg string) bool {
365361
return strings.HasPrefix(pkg, "./") || strings.HasPrefix(pkg, "/") || strings.HasPrefix(pkg, "../")
366362
}
367363

368-
func isGitRemotePath(pkg string) bool {
369-
return strings.HasPrefix(pkg, "git::") ||
370-
strings.HasPrefix(pkg, "git@") ||
371-
strings.Contains(pkg, "github.com/") ||
372-
strings.Contains(pkg, "bitbucket.org/") ||
373-
strings.Contains(pkg, "golang.org/")
374-
}
375-
376364
// RunTest implements the TestRunner interface
377365
func (r *runner) RunTest(ctx context.Context, metadata types.ValidatorMetadata) (*types.TestResult, error) {
378366
// Use defer and recover to catch panics and convert them to errors
@@ -401,7 +389,7 @@ func (r *runner) RunTest(ctx context.Context, metadata types.ValidatorMetadata)
401389
}
402390
}()
403391

404-
// Check if the path is available locally, otherwise check if git is installed
392+
// Check if the path is available locally
405393
if isLocalPath(metadata.Package) {
406394
fullPath := filepath.Join(r.workDir, metadata.Package)
407395
if _, statErr := os.Stat(fullPath); os.IsNotExist(statErr) {
@@ -412,15 +400,6 @@ func (r *runner) RunTest(ctx context.Context, metadata types.ValidatorMetadata)
412400
Error: fmt.Errorf("local package path does not exist: %s", fullPath),
413401
}, nil
414402
}
415-
} else if isGitRemotePath(metadata.Package) {
416-
if err := r.isGitInstalled(); err != nil {
417-
r.log.Error("Git is not installed but required for remote package, failing test", "validator", metadata.ID, "package", metadata.Package)
418-
return &types.TestResult{
419-
Metadata: metadata,
420-
Status: types.TestStatusFail,
421-
Error: fmt.Errorf("git is not installed"),
422-
}, nil
423-
}
424403
}
425404

426405
r.log.Info("Running validator", "validator", metadata.ID)
@@ -562,8 +541,16 @@ func (r *runner) runSingleTest(ctx context.Context, metadata types.ValidatorMeta
562541
defer cleanup()
563542

564543
var stdout, stderr bytes.Buffer
565-
cmd.Stdout = &stdout
566-
cmd.Stderr = &stderr
544+
if r.outputTestLogs {
545+
stdoutLogger := &logWriter{logFn: func(msg string) { r.log.Info("Test output", "test", metadata.FuncName, "output", msg) }}
546+
stderrLogger := &logWriter{logFn: func(msg string) { r.log.Error("Test error output", "test", metadata.FuncName, "error", msg) }}
547+
548+
cmd.Stdout = io.MultiWriter(&stdout, stdoutLogger)
549+
cmd.Stderr = io.MultiWriter(&stderr, stderrLogger)
550+
} else {
551+
cmd.Stdout = &stdout
552+
cmd.Stderr = &stderr
553+
}
567554

568555
r.log.Info("Running test", "test", metadata.FuncName)
569556
r.log.Debug("Running test command",
@@ -609,6 +596,7 @@ func (r *runner) runSingleTest(ctx context.Context, metadata types.ValidatorMeta
609596

610597
// If we couldn't parse the output for some reason, create a minimal failing result
611598
if parsedResult == nil {
599+
r.log.Error("test exited with non-zero exit code", "exitCode", cmd.ProcessState.ExitCode())
612600
parsedResult = &types.TestResult{
613601
Metadata: metadata,
614602
Status: types.TestStatusFail,
@@ -1210,16 +1198,16 @@ func (r *runner) testCommandContext(ctx context.Context, name string, arg ...str
12101198
r.log.Error("Failed to write env to temp file", "error", err)
12111199
}
12121200

1213-
env := append(
1214-
os.Environ(),
1201+
logLevelStr := r.testLogLevel
1202+
runEnv := append([]string{fmt.Sprintf("LOG_LEVEL=%s", logLevelStr)}, os.Environ()...)
1203+
runEnv = append(runEnv,
12151204
// override the env URL with the one from the temp file
12161205
fmt.Sprintf("%s=%s", env.EnvURLVar, envFile.Name()),
12171206
// override the control resolution scheme with the original one
12181207
fmt.Sprintf("%s=%s", env.EnvCtrlVar, url.Scheme),
1219-
// Set DEVNET_EXPECT_PRECONDITIONS_MET=true to make tests fail instead of skip when preconditions are not met
12201208
"DEVNET_EXPECT_PRECONDITIONS_MET=true",
12211209
)
1222-
cmd.Env = telemetry.InstrumentEnvironment(ctx, env)
1210+
cmd.Env = telemetry.InstrumentEnvironment(ctx, runEnv)
12231211
}
12241212
cleanup := func() {
12251213
if envFile != nil {
@@ -1233,3 +1221,22 @@ func (r *runner) testCommandContext(ctx context.Context, name string, arg ...str
12331221
// Make sure the runner type implements both interfaces
12341222
var _ TestRunner = &runner{}
12351223
var _ TestRunnerWithFileLogger = &runner{}
1224+
1225+
type logWriter struct {
1226+
logFn func(msg string)
1227+
buf []byte
1228+
}
1229+
1230+
func (w *logWriter) Write(p []byte) (n int, err error) {
1231+
w.buf = append(w.buf, p...)
1232+
for {
1233+
idx := bytes.IndexByte(w.buf, '\n')
1234+
if idx == -1 {
1235+
break
1236+
}
1237+
line := w.buf[:idx]
1238+
w.logFn(string(line))
1239+
w.buf = w.buf[idx+1:]
1240+
}
1241+
return len(p), nil
1242+
}

0 commit comments

Comments
 (0)