Skip to content

Commit b29aa3f

Browse files
Merge pull request #315 from form3tech-oss/mb-wait-for-completion-arg
chore: make 'waitForCompletionTimeout' configurable with a flag
2 parents 181eb00 + df29847 commit b29aa3f

File tree

9 files changed

+164
-116
lines changed

9 files changed

+164
-116
lines changed

internal/options/run_options.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import (
55
)
66

77
type RunOptions struct {
8-
Scenario string
9-
MaxDuration time.Duration
10-
Concurrency int
11-
MaxIterations uint64
12-
MaxFailures uint64
13-
MaxFailuresRate int
14-
Verbose bool
15-
IgnoreDropped bool
8+
Scenario string
9+
MaxDuration time.Duration
10+
Concurrency int
11+
MaxIterations uint64
12+
MaxFailures uint64
13+
MaxFailuresRate int
14+
Verbose bool
15+
IgnoreDropped bool
16+
WaitForCompletionTimeout time.Duration
1617
}
1718

1819
func (o *RunOptions) LogToFile() bool {

internal/run/run_cmd.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ import (
1616
"github.com/form3tech-oss/f1/v2/pkg/f1/scenarios"
1717
)
1818

19-
const waitForCompletionTimeout = 10 * time.Second
20-
2119
func Cmd(
2220
s *scenarios.Scenarios,
2321
builders []api.Builder,
@@ -55,6 +53,8 @@ func Cmd(
5553
"--max-failures 10 (load test will fail if more than 10 errors occurred, default is 0)")
5654
triggerCmd.Flags().Int(triggerflags.FlagMaxFailuresRate, 0,
5755
"--max-failures-rate 5 (load test will fail if more than 5\\% requests failed, default is 0)")
56+
triggerCmd.Flags().Duration(triggerflags.FlagWaitForCompletionTimeout, 10*time.Second,
57+
"--wait-for-completion-timeout 10s (wait for completion for 10 seconds)")
5858
}
5959

6060
triggerCmd.Flags().AddFlagSet(t.Flags)
@@ -87,6 +87,7 @@ func runCmdExecute(
8787
var maxFailures uint64
8888
var maxFailuresRate int
8989
var ignoreDropped bool
90+
var waitForCompletionTimeout time.Duration
9091
if t.IgnoreCommonFlags {
9192
scenarioName = trig.Options.Scenario
9293
duration = trig.Options.MaxDuration
@@ -95,6 +96,7 @@ func runCmdExecute(
9596
maxFailures = trig.Options.MaxFailures
9697
maxFailuresRate = trig.Options.MaxFailuresRate
9798
ignoreDropped = trig.Options.IgnoreDropped
99+
waitForCompletionTimeout = trig.Options.WaitForCompletionTimeout
98100
} else {
99101
scenarioName = args[0]
100102
duration, err = cmd.Flags().GetDuration(triggerflags.FlagMaxDuration)
@@ -125,6 +127,10 @@ func runCmdExecute(
125127
if err != nil {
126128
return fmt.Errorf("getting flag: %w", err)
127129
}
130+
waitForCompletionTimeout, err = cmd.Flags().GetDuration(triggerflags.FlagWaitForCompletionTimeout)
131+
if err != nil {
132+
return fmt.Errorf("getting flag: %w", err)
133+
}
128134
}
129135

130136
verbose, err := cmd.Flags().GetBool(triggerflags.FlagVerbose)
@@ -151,15 +157,16 @@ func runCmdExecute(
151157
}
152158

153159
run, err := NewRun(options.RunOptions{
154-
Scenario: scenarioName,
155-
MaxDuration: duration,
156-
Concurrency: concurrency,
157-
Verbose: verbose,
158-
MaxIterations: maxIterations,
159-
MaxFailures: maxFailures,
160-
MaxFailuresRate: maxFailuresRate,
161-
IgnoreDropped: ignoreDropped,
162-
}, s, trig, waitForCompletionTimeout, settings, metricsInstance, output)
160+
Scenario: scenarioName,
161+
MaxDuration: duration,
162+
Concurrency: concurrency,
163+
Verbose: verbose,
164+
MaxIterations: maxIterations,
165+
MaxFailures: maxFailures,
166+
MaxFailuresRate: maxFailuresRate,
167+
IgnoreDropped: ignoreDropped,
168+
WaitForCompletionTimeout: waitForCompletionTimeout,
169+
}, s, trig, settings, metricsInstance, output)
163170
if err != nil {
164171
return fmt.Errorf("new run: %w", err)
165172
}

internal/run/run_stage_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,15 @@ func (s *RunTestStage) setupRun() {
198198
outputer := ui.NewOutput(logger, printer, s.interactive, false)
199199

200200
r, err := run.NewRun(options.RunOptions{
201-
Scenario: s.scenario,
202-
MaxDuration: s.duration,
203-
Concurrency: s.concurrency,
204-
MaxIterations: s.maxIterations,
205-
MaxFailures: s.maxFailures,
206-
MaxFailuresRate: s.maxFailuresRate,
207-
Verbose: s.verbose,
208-
}, s.f1.GetScenarios(), s.build_trigger(), s.waitForCompletionTimeout, s.settings, s.metrics, outputer)
201+
Scenario: s.scenario,
202+
MaxDuration: s.duration,
203+
Concurrency: s.concurrency,
204+
MaxIterations: s.maxIterations,
205+
MaxFailures: s.maxFailures,
206+
MaxFailuresRate: s.maxFailuresRate,
207+
Verbose: s.verbose,
208+
WaitForCompletionTimeout: s.waitForCompletionTimeout,
209+
}, s.f1.GetScenarios(), s.build_trigger(), s.settings, s.metrics, outputer)
209210

210211
s.require.NoError(err)
211212
s.runInstance = r

internal/run/test_runner.go

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,22 @@ const (
3030
)
3131

3232
type Run struct {
33-
pusher *push.Pusher
34-
progressRunner *raterun.Runner
35-
metrics *metrics.Metrics
36-
views *views.Views
37-
activeScenario *workers.ActiveScenario
38-
trigger *api.Trigger
39-
output *ui.Output
40-
scenarioLogger *ScenarioLogger
41-
result *Result
42-
options options.RunOptions
43-
waitForCompletionTimeout time.Duration
33+
pusher *push.Pusher
34+
progressRunner *raterun.Runner
35+
metrics *metrics.Metrics
36+
views *views.Views
37+
activeScenario *workers.ActiveScenario
38+
trigger *api.Trigger
39+
output *ui.Output
40+
scenarioLogger *ScenarioLogger
41+
result *Result
42+
options options.RunOptions
4443
}
4544

4645
func NewRun(
4746
options options.RunOptions,
4847
scenarios *scenarios.Scenarios,
4948
trigger *api.Trigger,
50-
waitForCompletionTimeout time.Duration,
5149
settings envsettings.Settings,
5250
metricsInstance *metrics.Metrics,
5351
parentOutput *ui.Output,
@@ -93,17 +91,16 @@ func NewRun(
9391
pusher := newMetricsPusher(settings, scenario.Name, metricsInstance)
9492

9593
return &Run{
96-
options: options,
97-
trigger: trigger,
98-
metrics: metricsInstance,
99-
views: viewsInstance,
100-
result: result,
101-
pusher: pusher,
102-
output: outputer,
103-
progressRunner: progressRunner,
104-
activeScenario: activeScenario,
105-
scenarioLogger: scenarioLogger,
106-
waitForCompletionTimeout: waitForCompletionTimeout,
94+
options: options,
95+
trigger: trigger,
96+
metrics: metricsInstance,
97+
views: viewsInstance,
98+
result: result,
99+
pusher: pusher,
100+
output: outputer,
101+
progressRunner: progressRunner,
102+
activeScenario: activeScenario,
103+
scenarioLogger: scenarioLogger,
107104
}, nil
108105
}
109106

@@ -259,9 +256,10 @@ func (r *Run) run(ctx context.Context) {
259256
r.progressRunner.Restart()
260257
select {
261258
case <-poolManager.WaitForCompletion():
262-
case <-time.After(r.waitForCompletionTimeout):
259+
case <-time.After(r.options.WaitForCompletionTimeout):
263260
r.output.Display(ui.WarningMessage{
264-
Message: fmt.Sprintf("Active tests not completed after %s. Stopping...", r.waitForCompletionTimeout.String()),
261+
Message: fmt.Sprintf("Active tests not completed after %s. Stopping...",
262+
r.options.WaitForCompletionTimeout.String()),
265263
})
266264
}
267265

@@ -273,9 +271,10 @@ func (r *Run) run(ctx context.Context) {
273271
}
274272
select {
275273
case <-poolManager.WaitForCompletion():
276-
case <-time.After(r.waitForCompletionTimeout):
274+
case <-time.After(r.options.WaitForCompletionTimeout):
277275
r.output.Display(ui.WarningMessage{
278-
Message: fmt.Sprintf("Active tests not completed after %s. Stopping...", r.waitForCompletionTimeout.String()),
276+
Message: fmt.Sprintf("Active tests not completed after %s. Stopping...",
277+
r.options.WaitForCompletionTimeout.String()),
279278
})
280279
}
281280
case <-poolManager.WaitForCompletion():

internal/trigger/api/api.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,16 @@ type Trigger struct {
4242
}
4343

4444
type Options struct {
45-
Scenario string
46-
MaxDuration time.Duration
47-
Concurrency int
48-
MaxIterations uint64
49-
MaxFailures uint64
50-
MaxFailuresRate int
51-
Verbose bool
52-
VerboseFail bool
53-
IgnoreDropped bool
45+
Scenario string
46+
MaxDuration time.Duration
47+
Concurrency int
48+
MaxIterations uint64
49+
MaxFailures uint64
50+
MaxFailuresRate int
51+
Verbose bool
52+
VerboseFail bool
53+
IgnoreDropped bool
54+
WaitForCompletionTimeout time.Duration
5455
}
5556

5657
type Rates struct {

internal/trigger/file/file_parser.go

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ type Schedule struct {
2626
}
2727

2828
type Limits struct {
29-
MaxDuration *time.Duration `yaml:"max-duration"`
30-
Concurrency *int `yaml:"concurrency"`
31-
MaxIterations *uint64 `yaml:"max-iterations"`
32-
MaxFailures *uint64 `yaml:"max-failures"`
33-
MaxFailuresRate *int `yaml:"max-failures-rate"`
34-
IgnoreDropped *bool `yaml:"ignore-dropped"`
29+
MaxDuration *time.Duration `yaml:"max-duration"`
30+
Concurrency *int `yaml:"concurrency"`
31+
MaxIterations *uint64 `yaml:"max-iterations"`
32+
MaxFailures *uint64 `yaml:"max-failures"`
33+
MaxFailuresRate *int `yaml:"max-failures-rate"`
34+
IgnoreDropped *bool `yaml:"ignore-dropped"`
35+
WaitForCompletionTimeout *time.Duration `yaml:"wait-for-completion-timeout"`
3536
}
3637

3738
type Stage struct {
@@ -84,15 +85,16 @@ func ParseConfigFile(fileContent []byte, now time.Time) (*RunnableStages, error)
8485
}
8586

8687
return &RunnableStages{
87-
Scenario: *validatedConfigFile.Scenario,
88-
Stages: stages,
89-
stagesTotalDuration: stagesTotalDuration,
90-
MaxDuration: *validatedConfigFile.Limits.MaxDuration,
91-
Concurrency: *validatedConfigFile.Limits.Concurrency,
92-
MaxIterations: *validatedConfigFile.Limits.MaxIterations,
93-
maxFailures: *validatedConfigFile.Limits.MaxFailures,
94-
maxFailuresRate: *validatedConfigFile.Limits.MaxFailuresRate,
95-
IgnoreDropped: *validatedConfigFile.Limits.IgnoreDropped,
88+
Scenario: *validatedConfigFile.Scenario,
89+
Stages: stages,
90+
stagesTotalDuration: stagesTotalDuration,
91+
MaxDuration: *validatedConfigFile.Limits.MaxDuration,
92+
Concurrency: *validatedConfigFile.Limits.Concurrency,
93+
MaxIterations: *validatedConfigFile.Limits.MaxIterations,
94+
maxFailures: *validatedConfigFile.Limits.MaxFailures,
95+
maxFailuresRate: *validatedConfigFile.Limits.MaxFailuresRate,
96+
IgnoreDropped: *validatedConfigFile.Limits.IgnoreDropped,
97+
WaitForCompletionTimeout: *validatedConfigFile.Limits.WaitForCompletionTimeout,
9698
}, nil
9799
}
98100

@@ -225,6 +227,10 @@ func (c *ConfigFile) validateCommonFields() (*ConfigFile, error) {
225227
maxFailuresRate := 0
226228
c.Limits.MaxFailuresRate = &maxFailuresRate
227229
}
230+
if c.Limits.WaitForCompletionTimeout == nil {
231+
waitForCompletionTimeout := 10 * time.Second
232+
c.Limits.WaitForCompletionTimeout = &waitForCompletionTimeout
233+
}
228234
if c.Default.Concurrency == nil {
229235
c.Default.Concurrency = c.Limits.Concurrency
230236
}

internal/trigger/file/file_parser_test.go

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,35 @@ stages:
382382
expectedRates: []int{6, 6, 6, 6, 6, 6},
383383
expectedParameters: map[string]string{"FOO": "bar"},
384384
},
385+
{
386+
testName: "Include wait for completion timeout",
387+
fileContent: `scenario: template
388+
limits:
389+
max-duration: 1m
390+
concurrency: 50
391+
max-iterations: 100
392+
ignore-dropped: true
393+
wait-for-completion-timeout: 5s
394+
stages:
395+
- duration: 5s
396+
mode: constant
397+
rate: 6/s
398+
jitter: 0
399+
distribution: none
400+
parameters:
401+
FOO: bar
402+
`,
403+
expectedScenario: "template",
404+
expectedMaxDuration: 1 * time.Minute,
405+
expectedConcurrency: 50,
406+
expectedMaxIterations: 100,
407+
expectedIgnoreDropped: true,
408+
expectedWaitForCompletionTimeout: 5 * time.Second,
409+
expectedTotalDuration: 5 * time.Second,
410+
expectedIterationDuration: 1 * time.Second,
411+
expectedRates: []int{6, 6, 6, 6, 6, 6},
412+
expectedParameters: map[string]string{"FOO": "bar"},
413+
},
385414
} {
386415
t.Run(test.testName, func(t *testing.T) {
387416
t.Parallel()
@@ -664,18 +693,19 @@ invalid file content
664693
}
665694

666695
type testData struct {
667-
testName string
668-
fileContent string
669-
expectedScenario string
670-
expectedTotalDuration time.Duration
671-
expectedIterationDuration time.Duration
672-
expectedMaxDuration time.Duration
673-
expectedIgnoreDropped bool
674-
expectedMaxIterations uint64
675-
expectedMaxFailures int
676-
expectedMaxFailuresRate int
677-
expectedConcurrency int
678-
expectedUsersConcurrency int
679-
expectedRates []int
680-
expectedParameters map[string]string
696+
testName string
697+
fileContent string
698+
expectedScenario string
699+
expectedTotalDuration time.Duration
700+
expectedIterationDuration time.Duration
701+
expectedMaxDuration time.Duration
702+
expectedIgnoreDropped bool
703+
expectedWaitForCompletionTimeout time.Duration
704+
expectedMaxIterations uint64
705+
expectedMaxFailures int
706+
expectedMaxFailuresRate int
707+
expectedConcurrency int
708+
expectedUsersConcurrency int
709+
expectedRates []int
710+
expectedParameters map[string]string
681711
}

0 commit comments

Comments
 (0)