-
Notifications
You must be signed in to change notification settings - Fork 0
/
go_steps.go
195 lines (156 loc) · 4.2 KB
/
go_steps.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package gosteps
import (
"time"
)
// Execute a branch with the context provided
func (branch *Branch) Execute(c GoStepsContext) {
if branch.Steps == nil {
return
}
branch.Steps.execute(c.getCtx())
}
// setProgress sets the run progress (runCount) of a step
func (step *Step) setProgress() {
step.stepRunProgress.runCount += 1
}
// setResult sets the result of the executed step
func (step *Step) setResult(stepResult *StepResult) *Step {
step.stepResult = stepResult
return step
}
// setDefaults sets the default values for the StepOpts
func (step *Step) setDefaults() {
if step.StepOpts.MaxRunAttempts == 0 {
step.StepOpts.MaxRunAttempts = 1
}
if step.StepOpts.RetryAllErrors {
step.StepOpts.ErrorsToRetry = nil
}
}
// sleep for the retry sleep duration of the step
func (step *Step) sleep() {
if step.StepOpts.RetrySleep > 0 {
time.Sleep(step.StepOpts.RetrySleep)
}
}
// Execute a step with the context provided
func (step *Step) execute(c *GoStepsCtx) {
// skip if the step function is nil
if step.Function == nil {
return
}
// set the current step in the context
c.SetCurrentStep(step.Name)
// set the data from the step args in the context
c.WithData(step.StepArgs)
// set the default values for the step options
step.setDefaults()
// execute the step function
stepResult := step.Function(*c)
// set the result of the executed step
step.setResult(&stepResult)
// set the step result data in the context
c.WithData(stepResult.StepData)
// set the progress of the executed step in the context
c.SetProgress(step.Name, stepResult)
// set the progress of the executed step in the step
step.setProgress()
// log the step, if logger is provided
if c.logger.config.StepLoggingEnabled {
c.log(step)
}
}
// Execute a chain of steps with the context provided
func (steps *Steps) execute(c GoStepsCtx) {
s := *steps
if len(s) == 0 {
return
}
currentStepCounter := 0
var currentStep *Step = &s[currentStepCounter]
for currentStep != nil {
if currentStepCounter >= len(s) {
break
}
currentStep = &s[currentStepCounter]
currentStep.execute(&c)
if currentStep.shouldRetry() {
currentStep.sleep()
continue
}
if currentStep.shouldExit() {
break
}
branches := currentStep.Branches
if branches != nil {
branchName := branches.Resolver(c)
branch := branches.getExecutableBranch(branchName)
if branch != nil {
branch.Execute(&c)
}
}
currentStepCounter += 1
}
}
// getExecutableBranch returns the branch to execute based on the resolver result
func (branches *Branches) getExecutableBranch(branchName BranchName) *Branch {
for _, branch := range branches.Branches {
if branch.BranchName == branchName {
return &branch
}
}
return nil
}
// shouldRetry checks if the step should be retried
// retry steps, if:
// - step state is pending
// - step state is error and RetryAllErrors is true
// - step state is error and error is in ErrorsToRetry
// - step run count is less than MaxRunAttempts
//
// skip retry if:
// - step state is failed, complete or skipped
// - step run count is equal to MaxRunAttempts
func (step *Step) shouldRetry() bool {
if step.StepOpts.MaxRunAttempts == step.stepRunProgress.runCount {
return false
}
if step.stepResult == nil {
return false
}
if step.stepResult.StepState == StepStateFailed {
return false
}
if step.stepResult.StepState == StepStatePending {
return true
}
if step.stepResult.StepState == StepStateError && step.StepOpts.RetryAllErrors {
return true
}
if step.stepResult.StepState == StepStateError && step.stepResult.StepError != nil {
for _, errorToRetry := range step.StepOpts.ErrorsToRetry {
if errorToRetry == step.stepResult.StepError {
return true
}
}
for _, re := range step.StepOpts.ErrorPatternsToRetry {
if re.MatchString(step.stepResult.StepError.Error()) {
return true
}
}
}
return false
}
// shouldExit checks if the step should exists
// and step-chain execution should be stopped
func (step *Step) shouldExit() bool {
if step.stepResult == nil {
return false
}
switch step.stepResult.StepState {
case StepStateComplete, StepStateSkipped:
return false
default: // StepStateError, StepStatePending, StepStateFailed
return true
}
}