@@ -40,6 +40,7 @@ const (
40
40
StateCrashed = "CRASHED"
41
41
StatePrune = "PRUNE"
42
42
StateToRetry = "TO_RETRY"
43
+ StateRetryNow = "RETRY_NOW"
43
44
StateAfterrunError = "AFTERRUN_ERROR"
44
45
45
46
// steps that carry a foreach list of arguments
@@ -50,12 +51,14 @@ const (
50
51
stepRefThis = utask .This
51
52
52
53
defaultMaxRetries = 10000
54
+
55
+ maxExecutionDelay = time .Duration (20 ) * time .Second
53
56
)
54
57
55
58
var (
56
- builtinStates = []string {StateTODO , StateRunning , StateDone , StateClientError , StateServerError , StateFatalError , StateCrashed , StatePrune , StateToRetry , StateAfterrunError , StateAny , StateExpanded }
57
- stepConditionValidStates = []string {StateDone , StatePrune , StateToRetry , StateFatalError , StateClientError }
58
- runnableStates = []string {StateTODO , StateServerError , StateClientError , StateFatalError , StateCrashed , StateToRetry , StateAfterrunError , StateExpanded } // everything but RUNNING, DONE, PRUNE
59
+ builtinStates = []string {StateTODO , StateRunning , StateDone , StateClientError , StateServerError , StateFatalError , StateCrashed , StatePrune , StateToRetry , StateRetryNow , StateAfterrunError , StateAny , StateExpanded }
60
+ stepConditionValidStates = []string {StateDone , StatePrune , StateToRetry , StateRetryNow , StateFatalError , StateClientError }
61
+ runnableStates = []string {StateTODO , StateServerError , StateClientError , StateFatalError , StateCrashed , StateToRetry , StateRetryNow , StateAfterrunError , StateExpanded } // everything but RUNNING, DONE, PRUNE
59
62
retriableStates = []string {StateServerError , StateToRetry , StateAfterrunError }
60
63
)
61
64
@@ -90,10 +93,11 @@ type Step struct {
90
93
State string `json:"state,omitempty"`
91
94
// hints about ETA latency, async, for retrier to define strategy
92
95
// how often VS how many times
93
- RetryPattern string `json:"retry_pattern,omitempty"` // seconds, minutes, hours
94
- TryCount int `json:"try_count,omitempty"`
95
- MaxRetries int `json:"max_retries,omitempty"`
96
- LastRun time.Time `json:"last_run,omitempty"`
96
+ RetryPattern string `json:"retry_pattern,omitempty"` // seconds, minutes, hours
97
+ TryCount int `json:"try_count,omitempty"`
98
+ MaxRetries int `json:"max_retries,omitempty"`
99
+ LastRun time.Time `json:"last_run,omitempty"`
100
+ ExecutionDelay time.Duration `json:"execution_delay,omitempty"`
97
101
98
102
// flow control
99
103
Dependencies []string `json:"dependencies,omitempty"`
@@ -321,7 +325,10 @@ func Run(st *Step, baseConfig map[string]json.RawMessage, stepValues *values.Val
321
325
if st .MaxRetries == 0 {
322
326
st .MaxRetries = defaultMaxRetries
323
327
}
324
- if st .TryCount > st .MaxRetries {
328
+
329
+ // we can set "max_retries" to a negative number to have full control
330
+ // over the repetition of a step with a check condition using RETRY_NOW
331
+ if st .MaxRetries > 0 && st .TryCount > st .MaxRetries {
325
332
st .State = StateFatalError
326
333
st .Error = fmt .Sprintf ("Step reached max retries %d: %s" , st .MaxRetries , st .Error )
327
334
go noopStep (st , stepChan )
@@ -372,6 +379,8 @@ func Run(st *Step, baseConfig map[string]json.RawMessage, stepValues *values.Val
372
379
go func () {
373
380
defer wg .Done ()
374
381
382
+ time .Sleep (st .ExecutionDelay )
383
+
375
384
// Wait the prehook execution is done
376
385
preHookWg .Wait ()
377
386
@@ -533,6 +542,13 @@ func (st *Step) ValidAndNormalize(name string, baseConfigs map[string]json.RawMe
533
542
}
534
543
}
535
544
545
+ // valid execution delay
546
+ if st .ExecutionDelay < 0 || st .ExecutionDelay > maxExecutionDelay {
547
+ return errors .NewNotValid (nil ,
548
+ fmt .Sprintf ("execution_delay: expected %s to be a duration between 0s and %s" ,
549
+ st .ExecutionDelay , maxExecutionDelay ))
550
+ }
551
+
536
552
// valid retry pattern, accept empty
537
553
switch st .RetryPattern {
538
554
case "" , RetrySeconds , RetryMinutes , RetryHours :
0 commit comments