forked from vmware-archive/atc
/
timeout_step.go
101 lines (85 loc) · 1.96 KB
/
timeout_step.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
package exec
import (
"os"
"time"
"code.cloudfoundry.org/clock"
"github.com/concourse/atc/worker"
"github.com/tedsuo/ifrit"
)
// TimeoutStep applies a fixed timeout to a step's Run.
type TimeoutStep struct {
step StepFactory
runStep Step
duration string
clock clock.Clock
timedOut bool
}
// Timeout constructs a TimeoutStep factory.
func Timeout(
step StepFactory,
duration string,
clock clock.Clock,
) TimeoutStep {
return TimeoutStep{
step: step,
duration: duration,
clock: clock,
}
}
// Using constructs a *TimeoutStep.
func (ts TimeoutStep) Using(prev Step, repo *worker.ArtifactRepository) Step {
ts.runStep = ts.step.Using(prev, repo)
return &ts
}
// Run parses the timeout duration and invokes the nested step.
//
// If the nested step takes longer than the duration, it is sent the Interrupt
// signal, and the TimeoutStep returns nil once the nested step exits (ignoring
// the nested step's error).
//
// The result of the nested step's Run is returned.
func (ts *TimeoutStep) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
parsedDuration, err := time.ParseDuration(ts.duration)
if err != nil {
return err
}
timer := ts.clock.NewTimer(parsedDuration)
runProcess := ifrit.Invoke(ts.runStep)
close(ready)
var runErr error
var sig os.Signal
dance:
for {
select {
case runErr = <-runProcess.Wait():
break dance
case <-timer.C():
ts.timedOut = true
runProcess.Signal(os.Interrupt)
case sig = <-signals:
runProcess.Signal(sig)
}
}
if ts.timedOut {
// swallow interrupted error
return nil
}
if runErr != nil {
return runErr
}
return nil
}
// Result indicates Success as true if the nested step completed successfully
// and did not time out.
//
// Any other type is ignored.
func (ts *TimeoutStep) Result(x interface{}) bool {
switch v := x.(type) {
case *Success:
var success Success
ts.runStep.Result(&success)
*v = success && !Success(ts.timedOut)
return true
}
return false
}