package activator import ( "context" "errors" "time" ) // swapSettle is the canary's settle (spec §6.2, §7.5 step 4). A successor // that passed its health check must then stay up this long before the // switch. A var so tests can shrink it, like spawnHealthTimeout. var swapSettle = 10 * time.Second var errSettleExit = errors.New("swap: successor exited during its settle") type settleResult int const ( settleOK settleResult = iota // stayed up for the whole window settleExited // died on its own: a failure of the build settleCancelled // an intentional stop ended the observation ) // observeSettle watches p for window after it became healthy (spec §6.2, // §6.4). Cancellation wins over an exit seen at the same moment. The // barrier cancels an attempt BEFORE anything is stopped, so a process that // dies once a stop has begun died of the stop, not of its build. func observeSettle(ctx context.Context, p Proc, window time.Duration) settleResult { verdict := func() (settleResult, bool) { if ctx.Err() != nil { return settleCancelled, true } if p.Exited() { return settleExited, true } return settleOK, false } if r, over := verdict(); over || window <= 0 { return r } deadline := time.NewTimer(window) defer deadline.Stop() tick := time.NewTicker(settlePoll(window)) defer tick.Stop() for { select { case <-ctx.Done(): return settleCancelled case <-tick.C: if r, over := verdict(); over { return r } case <-deadline.C: r, _ := verdict() return r } } }