@@ -44,10 +44,10 @@ import (
44
44
)
45
45
46
46
var (
47
- configPath = flag .String ("config" , "" , "path to the benchmark config file" )
48
- outPath = flag .String ("out" , "./results" , "path to the output folder" )
49
- tools = internal .StringsFlag ("tool" , nil , "filter tool to be tested (can be provided multiple times)" )
50
- tests = internal .StringsFlag ("tests" , nil , "filter test to run (can be provided multiple times)" )
47
+ configPathFlag = flag .String ("config" , "" , "path to the benchmark config file" )
48
+ outPathFlag = flag .String ("out" , "./results" , "path to the output folder" )
49
+ toolsFlag = internal .StringsFlag ("tool" , nil , "filter tool to be tested (can be provided multiple times)" )
50
+ testsFlag = internal .StringsFlag ("tests" , nil , "filter test to run (can be provided multiple times)" )
51
51
)
52
52
53
53
func main () {
@@ -60,27 +60,30 @@ func main() {
60
60
func mainE () error {
61
61
flag .Parse ()
62
62
63
- if configPath == nil || strings .TrimSpace (* configPath ) == "" {
63
+ if configPathFlag == nil || strings .TrimSpace (* configPathFlag ) == "" {
64
64
return fmt .Errorf ("config path is required" )
65
65
}
66
66
67
67
// Create output directory if it does not exist.
68
- err := os .MkdirAll (* outPath , 0o755 )
68
+ err := os .MkdirAll (* outPathFlag , 0o755 )
69
69
if err != nil {
70
70
return fmt .Errorf ("failed to create output directory: %w" , err )
71
71
}
72
72
73
73
now := time .Now ()
74
74
infoReader , errorReader , closeLog , err := prepareLogger (now )
75
+ if err != nil {
76
+ return fmt .Errorf ("failed to prepare logger: %w" , err )
77
+ }
75
78
defer closeLog ()
76
79
77
80
_ , err = tea .NewProgram (newMainModel (infoReader , errorReader , now )).Run ()
78
- return err
81
+ return err //nolint:wrapcheck // Wrapping this error wouldn't add any value.
79
82
}
80
83
81
- func prepareLogger (now time.Time ) (io.Reader , io.Reader , func () error , error ) {
84
+ func prepareLogger (now time.Time ) (io.Reader , io.Reader , func (), error ) {
82
85
// Create log file.
83
- logPath := filepath .Join (* outPath , fmt .Sprintf ("%s_benchi.log" , now .Format ("20060102150405" )))
86
+ logPath := filepath .Join (* outPathFlag , fmt .Sprintf ("%s_benchi.log" , now .Format ("20060102150405" )))
84
87
logFile , err := os .Create (logPath )
85
88
if err != nil {
86
89
return nil , nil , nil , fmt .Errorf ("failed to create log file: %w" , err )
@@ -100,12 +103,14 @@ func prepareLogger(now time.Time) (io.Reader, io.Reader, func() error, error) {
100
103
)
101
104
slog .SetDefault (slog .New (logHandler ))
102
105
103
- return infoReader , errorReader , func () error {
106
+ return infoReader , errorReader , func () {
104
107
var errs []error
105
108
errs = append (errs , logFile .Close ())
106
109
errs = append (errs , infoWriter .Close ())
107
110
errs = append (errs , errorWriter .Close ())
108
- return errors .Join (errs ... )
111
+ if err := errors .Join (errs ... ); err != nil {
112
+ slog .Error ("Failed to close logger" , "error" , err )
113
+ }
109
114
}, nil
110
115
}
111
116
@@ -167,13 +172,13 @@ func (m mainModel) Init() tea.Cmd {
167
172
func (mainModel ) parseConfig (path string ) (config.Config , error ) {
168
173
f , err := os .Open (path )
169
174
if err != nil {
170
- return config.Config {}, err
175
+ return config.Config {}, fmt . Errorf ( "failed to open config file %s: %w" , path , err )
171
176
}
172
177
defer f .Close ()
173
178
var cfg config.Config
174
179
err = yaml .NewDecoder (f ).Decode (& cfg )
175
180
if err != nil {
176
- return config.Config {}, err
181
+ return config.Config {}, fmt . Errorf ( "failed to parse config file %s as YAML: %w" , path , err )
177
182
}
178
183
return cfg , nil
179
184
}
@@ -183,17 +188,10 @@ func (m mainModel) initCmd() tea.Cmd {
183
188
now := time .Now ()
184
189
185
190
// Resolve absolute paths.
186
- resultsDir , err := filepath .Abs (* outPath )
187
- if err != nil {
188
- return fmt .Errorf ("failed to get absolute path for output directory: %w" , err )
189
- }
190
- slog .Info ("Results directory" , "path" , resultsDir )
191
-
192
- configPath , err := filepath .Abs (* configPath )
191
+ resultsDir , configPath , err := m .initPaths ()
193
192
if err != nil {
194
- return fmt .Errorf ("failed to get absolute path for config file : %w" , err )
193
+ return fmt .Errorf ("failed to resolve paths : %w" , err )
195
194
}
196
- slog .Info ("Config file" , "path" , configPath )
197
195
198
196
// Parse config.
199
197
cfg , err := m .parseConfig (configPath )
@@ -210,24 +208,16 @@ func (m mainModel) initCmd() tea.Cmd {
210
208
}
211
209
212
210
// Create docker client and initialize network.
213
- dockerClient , err := client .NewClientWithOpts (client .FromEnv )
214
- if err != nil {
215
- return fmt .Errorf ("failed to create docker client: %w" , err )
216
- }
217
- defer dockerClient .Close ()
218
-
219
- slog .Info ("Creating docker network" , "network" , benchi .NetworkName )
220
- net , err := dockerutil .CreateNetworkIfNotExist (m .ctx , dockerClient , benchi .NetworkName )
211
+ dockerClient , err := m .initDocker ()
221
212
if err != nil {
222
- return fmt .Errorf ("failed to create docker network : %w" , err )
213
+ return fmt .Errorf ("failed to initialize docker: %w" , err )
223
214
}
224
- slog .Info ("Using network" , "network" , net .Name , "network-id" , net .ID )
225
215
226
216
testRunners , err := benchi .BuildTestRunners (cfg , benchi.TestRunnerOptions {
227
217
ResultsDir : resultsDir ,
228
218
StartedAt : now ,
229
- FilterTests : * tests ,
230
- FilterTools : * tools ,
219
+ FilterTests : * testsFlag ,
220
+ FilterTools : * toolsFlag ,
231
221
DockerClient : dockerClient ,
232
222
})
233
223
if err != nil {
@@ -244,6 +234,40 @@ func (m mainModel) initCmd() tea.Cmd {
244
234
}
245
235
}
246
236
237
+ func (mainModel ) initPaths () (resultsDir , configPath string , err error ) {
238
+ resultsDir , err = filepath .Abs (* outPathFlag )
239
+ if err != nil {
240
+ return "" , "" , fmt .Errorf ("failed to get absolute path for output directory: %w" , err )
241
+ }
242
+ slog .Info ("Results directory" , "path" , resultsDir )
243
+
244
+ configPath , err = filepath .Abs (* configPathFlag )
245
+ if err != nil {
246
+ return "" , "" , fmt .Errorf ("failed to get absolute path for config file: %w" , err )
247
+ }
248
+ slog .Info ("Config file" , "path" , configPath )
249
+
250
+ return resultsDir , configPath , nil
251
+ }
252
+
253
+ func (m mainModel ) initDocker () (client.APIClient , error ) {
254
+ slog .Info ("Creating docker client" )
255
+ dockerClient , err := client .NewClientWithOpts (client .FromEnv )
256
+ if err != nil {
257
+ return nil , fmt .Errorf ("failed to create docker client: %w" , err )
258
+ }
259
+ dockerClient .NegotiateAPIVersion (m .ctx )
260
+
261
+ slog .Info ("Creating docker network" , "network" , benchi .NetworkName )
262
+ net , err := dockerutil .CreateNetworkIfNotExist (m .ctx , dockerClient , benchi .NetworkName )
263
+ if err != nil {
264
+ return nil , fmt .Errorf ("failed to create docker network: %w" , err )
265
+ }
266
+ slog .Info ("Using network" , "network" , net .Name , "network-id" , net .ID )
267
+
268
+ return dockerClient , nil
269
+ }
270
+
247
271
func (mainModel ) runTestCmd (index int ) tea.Cmd {
248
272
return func () tea.Msg {
249
273
return mainModelMsgNextTest {testIndex : index }
@@ -257,12 +281,18 @@ func (m mainModel) quitCmd() tea.Cmd {
257
281
if err != nil {
258
282
slog .Error ("Failed to remove network" , "network" , benchi .NetworkName , "error" , err )
259
283
}
284
+ err = m .dockerClient .Close ()
285
+ if err != nil {
286
+ slog .Error ("Failed to close docker client" , "error" , err )
287
+ }
260
288
return tea .Quit ()
261
289
}
262
290
}
263
291
292
+ //nolint:funlen // This function is long because it manages messages for the whole application.
264
293
func (m mainModel ) Update (msg tea.Msg ) (tea.Model , tea.Cmd ) {
265
294
slog .Debug ("Received message" , "msg" , msg , "type" , fmt .Sprintf ("%T" , msg ))
295
+
266
296
switch msg := msg .(type ) {
267
297
case mainModelMsgInitDone :
268
298
m .config = msg .config
@@ -288,31 +318,33 @@ func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
288
318
}
289
319
m .currentTestIndex = msg .testIndex
290
320
return m , m .tests [m .currentTestIndex ].Init ()
321
+
291
322
case testModelMsgDone :
292
323
nextIndex := m .currentTestIndex + 1
293
324
if m .ctx .Err () != nil {
294
325
// Main context is cancelled, skip to the end.
295
326
nextIndex = len (m .tests )
296
327
}
297
328
return m , m .runTestCmd (nextIndex )
329
+
298
330
case tea.KeyMsg :
299
331
if msg .String () == "ctrl+c" {
300
- if m . ctx . Err () == nil {
301
- // First time, cancel the main context.
332
+ switch {
333
+ case m . ctx . Err () == nil :
302
334
m .ctxCancel ()
303
335
return m , nil
304
- } else if m .cleanupCtx .Err () == nil {
305
- // Second time, cancel the cleanup context.
336
+ case m .cleanupCtx .Err () == nil :
306
337
m .cleanupCtxCancel ()
307
338
return m , nil
308
- } else {
309
- // Third time, just quit.
339
+ default :
310
340
return m , tea .Quit
311
341
}
312
342
}
343
+
313
344
case error :
314
345
slog .Error ("Error message" , "error" , msg )
315
346
return m , nil
347
+
316
348
case internal.LogModelMsg :
317
349
var cmds []tea.Cmd
318
350
@@ -388,7 +420,7 @@ func newTestModel(ctx context.Context, cleanupCtx context.Context, client client
388
420
for _ , f := range runner .Infrastructure () {
389
421
infraFiles = append (infraFiles , f .DockerCompose )
390
422
}
391
- infraContainers , err := findContainerNames (infraFiles )
423
+ infraContainers , err := findContainerNames (ctx , infraFiles )
392
424
if err != nil {
393
425
return testModel {}, err
394
426
}
@@ -397,7 +429,7 @@ func newTestModel(ctx context.Context, cleanupCtx context.Context, client client
397
429
for _ , f := range runner .Tools () {
398
430
toolFiles = append (toolFiles , f .DockerCompose )
399
431
}
400
- toolContainers , err := findContainerNames (toolFiles )
432
+ toolContainers , err := findContainerNames (ctx , toolFiles )
401
433
if err != nil {
402
434
return testModel {}, err
403
435
}
@@ -421,10 +453,10 @@ func newTestModel(ctx context.Context, cleanupCtx context.Context, client client
421
453
}, nil
422
454
}
423
455
424
- func findContainerNames (files []string ) ([]string , error ) {
456
+ func findContainerNames (ctx context. Context , files []string ) ([]string , error ) {
425
457
var buf bytes.Buffer
426
458
err := dockerutil .ComposeConfig (
427
- context . Background () ,
459
+ ctx ,
428
460
dockerutil.ComposeOptions {
429
461
File : files ,
430
462
Stdout : & buf ,
0 commit comments