1package exec 2 3import ( 4 "context" 5 "errors" 6 "time" 7) 8 9// TimeoutStep applies a fixed timeout to a step's Run. 10type TimeoutStep struct { 11 step Step 12 duration string 13 timedOut bool 14} 15 16// Timeout constructs a TimeoutStep factory. 17func Timeout(step Step, duration string) *TimeoutStep { 18 return &TimeoutStep{ 19 step: step, 20 duration: duration, 21 timedOut: false, 22 } 23} 24 25// Run parses the timeout duration and invokes the nested step. 26// 27// If the nested step takes longer than the duration, it is sent the Interrupt 28// signal, and the TimeoutStep returns nil once the nested step exits (ignoring 29// the nested step's error). 30// 31// The result of the nested step's Run is returned. 32func (ts *TimeoutStep) Run(ctx context.Context, state RunState) error { 33 parsedDuration, err := time.ParseDuration(ts.duration) 34 if err != nil { 35 return err 36 } 37 38 timeoutCtx, cancel := context.WithTimeout(ctx, parsedDuration) 39 defer cancel() 40 41 err = ts.step.Run(timeoutCtx, state) 42 if errors.Is(err, context.DeadlineExceeded) { 43 ts.timedOut = true 44 return nil 45 } 46 47 return err 48} 49 50// Succeeded is true if the nested step completed successfully 51// and did not time out. 52func (ts *TimeoutStep) Succeeded() bool { 53 return !ts.timedOut && ts.step.Succeeded() 54} 55