Skip to content

Commit 13d79e1

Browse files
committed
add support for running commands during test
1 parent 0ee3fc1 commit 13d79e1

12 files changed

+188
-116
lines changed

benchmarks/bench-kafka-kafka/bench-kafka-kafka.yml

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
infrastructure:
22
kafka:
3-
docker-compose: ../infra/docker-compose-kafka.yml
3+
compose: ../infra/compose-kafka.yml
44

55
tools:
66
kafka-connect:
7-
docker-compose: ../tools/docker-compose-kafka-connect.yml
7+
compose: ../tools/compose-kafka-connect.yml
88
conduit:
9-
docker-compose: ../tools/docker-compose-conduit.yml
9+
compose: ../tools/compose-conduit.yml
1010

1111
metrics:
1212
conduit:
@@ -22,7 +22,7 @@ tests:
2222

2323
tools:
2424
conduit:
25-
docker-compose: ./conduit/docker-compose-conduit.override.yml
25+
compose: ./conduit/compose-conduit.override.yml
2626

2727
steps:
2828
pre-infrastructure:
@@ -46,6 +46,14 @@ tests:
4646
post-tool:
4747
pre-test:
4848
during:
49+
- name: "Test"
50+
run: |
51+
n=1
52+
while true; do
53+
echo "count: $n"
54+
n=$(expr $n + 1)
55+
sleep 1
56+
done
4957
post-test:
5058
pre-cleanup:
5159
post-cleanup:
File renamed without changes.
File renamed without changes.

cmd/benchi/internal/containermonitor.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ var (
4242

4343
type ContainerMonitorModel struct {
4444
id int32
45+
logger *slog.Logger
4546
ctx context.Context
4647
client client.APIClient
4748
interval time.Duration
@@ -63,6 +64,7 @@ func NewContainerMonitorModel(ctx context.Context, client client.APIClient, cont
6364
}
6465
return ContainerMonitorModel{
6566
id: containerMonitorModelID.Add(1),
67+
logger: slog.Default().With("model", "container-monitor"),
6668
ctx: ctx,
6769
client: client,
6870
containers: containers,
@@ -131,13 +133,13 @@ func (m ContainerMonitorModel) scheduleRefreshCmd() tea.Cmd {
131133
return tea.Tick(m.interval, func(time.Time) tea.Msg {
132134
containersTmp := slices.Clone(m.containers)
133135
for i, c := range containersTmp {
134-
slog.Debug("Inspecting container", "name", c.Name)
136+
m.logger.Debug("Inspecting container", "name", c.Name)
135137
inspect, err := m.client.ContainerInspect(m.ctx, c.Name)
136138
if err != nil {
137139
if errdefs.IsNotFound(err) {
138-
slog.Debug("Container not found", "name", c.Name)
140+
m.logger.Debug("Container not found", "name", c.Name)
139141
} else {
140-
slog.Error("Failed to inspect container", "name", c.Name, "error", err)
142+
m.logger.Error("Failed to inspect container", "name", c.Name, "error", err)
141143
}
142144
containersTmp[i] = types.ContainerJSON{ContainerJSONBase: &types.ContainerJSONBase{Name: c.Name}}
143145
continue

cmd/benchi/main.go

+48-40
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import (
4242

4343
var (
4444
configPathFlag = flag.String("config", "", "path to the benchmark config file")
45-
outPathFlag = flag.String("out", "./results", "path to the output folder")
45+
outPathFlag = flag.String("out", "./results/${now}", "path to the output folder")
4646
toolsFlag = internal.StringsFlag("tool", nil, "filter tool to be tested (can be provided multiple times)")
4747
testsFlag = internal.StringsFlag("tests", nil, "filter test to run (can be provided multiple times)")
4848
)
@@ -61,26 +61,31 @@ func mainE() error {
6161
return fmt.Errorf("config path is required")
6262
}
6363

64+
if outPathFlag == nil || strings.TrimSpace(*outPathFlag) == "" {
65+
return fmt.Errorf("output path is required")
66+
}
67+
68+
*outPathFlag = strings.ReplaceAll(*outPathFlag, "${now}", time.Now().Format("20060102150405"))
69+
6470
// Create output directory if it does not exist.
6571
err := os.MkdirAll(*outPathFlag, 0o755)
6672
if err != nil {
6773
return fmt.Errorf("failed to create output directory: %w", err)
6874
}
6975

70-
now := time.Now()
71-
infoReader, errorReader, closeLog, err := prepareLogger(now)
76+
infoReader, errorReader, closeLog, err := prepareLogger()
7277
if err != nil {
7378
return fmt.Errorf("failed to prepare logger: %w", err)
7479
}
7580
defer closeLog()
7681

77-
_, err = tea.NewProgram(newMainModel(infoReader, errorReader, now)).Run()
82+
_, err = tea.NewProgram(newMainModel(infoReader, errorReader)).Run()
7883
return err //nolint:wrapcheck // Wrapping this error wouldn't add any value.
7984
}
8085

81-
func prepareLogger(now time.Time) (io.Reader, io.Reader, func(), error) {
86+
func prepareLogger() (io.Reader, io.Reader, func(), error) {
8287
// Create log file.
83-
logPath := filepath.Join(*outPathFlag, fmt.Sprintf("%s_benchi.log", now.Format("20060102150405")))
88+
logPath := filepath.Join(*outPathFlag, "benchi.log")
8489
logFile, err := os.Create(logPath)
8590
if err != nil {
8691
return nil, nil, nil, fmt.Errorf("failed to create log file: %w", err)
@@ -122,7 +127,6 @@ type mainModel struct {
122127
// These fields are initialized in Init
123128
config config.Config
124129
resultsDir string
125-
startedAt time.Time
126130
dockerClient client.APIClient
127131

128132
tests []testModel
@@ -136,17 +140,18 @@ type mainModel struct {
136140
type mainModelMsgInitDone struct {
137141
config config.Config
138142
resultsDir string
139-
startedAt time.Time
140143
dockerClient client.APIClient
141144

142145
testRunners benchi.TestRunners
146+
147+
err error
143148
}
144149

145150
type mainModelMsgNextTest struct {
146151
testIndex int
147152
}
148153

149-
func newMainModel(infoReader, errorReader io.Reader, now time.Time) mainModel {
154+
func newMainModel(infoReader, errorReader io.Reader) mainModel {
150155
ctx, ctxCancel := context.WithCancel(context.Background())
151156
cleanupCtx, cleanupCtxCancel := context.WithCancel(context.Background())
152157
return mainModel{
@@ -155,8 +160,6 @@ func newMainModel(infoReader, errorReader io.Reader, now time.Time) mainModel {
155160
cleanupCtx: cleanupCtx,
156161
cleanupCtxCancel: cleanupCtxCancel,
157162

158-
startedAt: now,
159-
160163
infoLogModel: internal.NewLogModel(infoReader, 10),
161164
errorLogModel: internal.NewLogModel(errorReader, 0),
162165
}
@@ -182,52 +185,51 @@ func (mainModel) parseConfig(path string) (config.Config, error) {
182185

183186
func (m mainModel) initCmd() tea.Cmd {
184187
return func() tea.Msg {
185-
now := time.Now()
188+
var msg mainModelMsgInitDone
186189

187190
// Resolve absolute paths.
188191
resultsDir, configPath, err := m.initPaths()
189192
if err != nil {
190-
return fmt.Errorf("failed to resolve paths: %w", err)
193+
msg.err = fmt.Errorf("failed to resolve paths: %w", err)
194+
return msg
191195
}
196+
msg.resultsDir = resultsDir
192197

193198
// Parse config.
194-
cfg, err := m.parseConfig(configPath)
199+
msg.config, err = m.parseConfig(configPath)
195200
if err != nil {
196-
return fmt.Errorf("failed to parse config: %w", err)
201+
msg.err = fmt.Errorf("failed to parse config: %w", err)
202+
return msg
197203
}
198204
slog.Info("Parsed config", "path", configPath)
199205

200206
// Change working directory to config path, all relative paths are relative
201207
// to the config file.
202208
err = os.Chdir(filepath.Dir(configPath))
203209
if err != nil {
204-
return fmt.Errorf("could not change working directory: %w", err)
210+
msg.err = fmt.Errorf("could not change working directory: %w", err)
211+
return msg
205212
}
206213

207214
// Create docker client and initialize network.
208-
dockerClient, err := m.initDocker()
215+
msg.dockerClient, err = m.initDocker()
209216
if err != nil {
210-
return fmt.Errorf("failed to initialize docker: %w", err)
217+
msg.err = fmt.Errorf("failed to initialize docker: %w", err)
218+
return msg
211219
}
212220

213-
testRunners, err := benchi.BuildTestRunners(cfg, benchi.TestRunnerOptions{
214-
ResultsDir: resultsDir,
215-
StartedAt: now,
221+
msg.testRunners, err = benchi.BuildTestRunners(msg.config, benchi.TestRunnerOptions{
222+
ResultsDir: msg.resultsDir,
216223
FilterTests: *testsFlag,
217224
FilterTools: *toolsFlag,
218-
DockerClient: dockerClient,
225+
DockerClient: msg.dockerClient,
219226
})
220227
if err != nil {
221-
return fmt.Errorf("failed to build test runners: %w", err)
228+
msg.err = fmt.Errorf("failed to build test runners: %w", err)
229+
return msg
222230
}
223231

224-
return mainModelMsgInitDone{
225-
config: cfg,
226-
resultsDir: resultsDir,
227-
startedAt: now,
228-
dockerClient: dockerClient,
229-
testRunners: testRunners,
230-
}
232+
return msg
231233
}
232234
}
233235

@@ -273,14 +275,16 @@ func (mainModel) runTestCmd(index int) tea.Cmd {
273275

274276
func (m mainModel) quitCmd() tea.Cmd {
275277
return func() tea.Msg {
276-
slog.Info("Removing docker network", "network", benchi.NetworkName)
277-
err := dockerutil.RemoveNetwork(m.cleanupCtx, m.dockerClient, benchi.NetworkName)
278-
if err != nil {
279-
slog.Error("Failed to remove network", "network", benchi.NetworkName, "error", err)
280-
}
281-
err = m.dockerClient.Close()
282-
if err != nil {
283-
slog.Error("Failed to close docker client", "error", err)
278+
if m.dockerClient != nil {
279+
slog.Info("Removing docker network", "network", benchi.NetworkName)
280+
err := dockerutil.RemoveNetwork(m.cleanupCtx, m.dockerClient, benchi.NetworkName)
281+
if err != nil {
282+
slog.Error("Failed to remove network", "network", benchi.NetworkName, "error", err)
283+
}
284+
err = m.dockerClient.Close()
285+
if err != nil {
286+
slog.Error("Failed to close docker client", "error", err)
287+
}
284288
}
285289
return tea.Quit()
286290
}
@@ -295,7 +299,6 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
295299
case mainModelMsgInitDone:
296300
m.config = msg.config
297301
m.resultsDir = msg.resultsDir
298-
m.startedAt = msg.startedAt
299302
m.dockerClient = msg.dockerClient
300303

301304
tests := make([]testModel, len(msg.testRunners))
@@ -307,8 +310,13 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
307310
tests[i] = test
308311
}
309312
m.tests = tests
310-
m.initialized = true
311313

314+
if msg.err != nil {
315+
slog.Error("Error initializing", "error", msg.err)
316+
return m, m.quitCmd()
317+
}
318+
319+
m.initialized = true
312320
return m, m.runTestCmd(0)
313321
case mainModelMsgNextTest:
314322
if msg.testIndex >= len(m.tests) {

config/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ type ServiceConfig struct {
3333
// DockerCompose is the path to the Docker Compose file for the service. If
3434
// it's a relative path, it will be resolved relative to the configuration
3535
// file.
36-
DockerCompose string `yaml:"docker-compose"`
36+
DockerCompose string `yaml:"compose"`
3737
}
3838

3939
// Infrastructure represents a map of service configurations for the infrastructure.

0 commit comments

Comments
 (0)