in common/build.go [1452:1502]
func (b *Build) getStageTimeoutContexts(parent context.Context, timeouts ...stageTimeout) map[string]func() (context.Context, func()) {
stack := make([]time.Duration, len(timeouts))
deadline, hasDeadline := parent.Deadline()
jobTimeout := time.Until(deadline)
for idx, timeout := range timeouts {
stack[idx] = timeout.defaultTimeout
rawTimeout := b.GetAllVariables().Value(timeout.configName)
duration, parseErr := time.ParseDuration(rawTimeout)
switch {
case strings.TrimSpace(rawTimeout) == "":
// no-op
case parseErr != nil:
b.logger.Warningln(fmt.Sprintf("Ignoring malformed %s timeout: %v", timeout.configName, rawTimeout))
case duration < 0:
// no relative durations for now...
b.logger.Warningln(fmt.Sprintf("Ignoring relative %s timeout: %v", timeout.configName, rawTimeout))
case hasDeadline && duration > jobTimeout:
// clamping timeouts to the job timeout happens automatically in `context.WithParent()`, mention it here
b.logger.Warningln(fmt.Sprintf("%s timeout: %v is longer than job timeout. Setting to job timeout", timeout.configName, rawTimeout))
case duration != 0:
stack[idx] = duration
}
}
results := make(map[string]func() (context.Context, func()))
for idx, timeout := range timeouts {
switch {
case stack[idx] == 0:
results[timeout.configName] = func() (context.Context, func()) {
// no timeout
return context.WithCancel(parent)
}
case stack[idx] > 0:
duration := stack[idx]
results[timeout.configName] = func() (context.Context, func()) {
// absolute timeout
return context.WithTimeout(parent, duration)
}
}
}
return results
}