feat(supervisor): restart-policy decision logic
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
//! Process-supervision primitives for the xy daemon.
|
||||
|
||||
pub mod backoff;
|
||||
pub mod child;
|
||||
pub mod policy;
|
||||
pub mod retry_window;
|
||||
|
||||
pub use backoff::Backoff;
|
||||
pub use child::{ChildHandle, MockChild, MockChildController};
|
||||
pub use policy::{RestartDecision, decide};
|
||||
pub use retry_window::RetryWindow;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
use xy_protocol::RestartPolicy;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum RestartDecision {
|
||||
Restart,
|
||||
StayStopped,
|
||||
MarkFailed,
|
||||
}
|
||||
|
||||
pub fn decide(
|
||||
policy: RestartPolicy,
|
||||
exit_code: Option<i32>,
|
||||
retry_cap_reached: bool,
|
||||
) -> RestartDecision {
|
||||
let clean = matches!(exit_code, Some(0));
|
||||
let want = match policy {
|
||||
RestartPolicy::Never => false,
|
||||
RestartPolicy::OnFailure => !clean,
|
||||
RestartPolicy::Always => true,
|
||||
};
|
||||
if !want {
|
||||
return RestartDecision::StayStopped;
|
||||
}
|
||||
if retry_cap_reached {
|
||||
RestartDecision::MarkFailed
|
||||
} else {
|
||||
RestartDecision::Restart
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn never_never_restarts() {
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::Never, Some(0), false),
|
||||
RestartDecision::StayStopped
|
||||
);
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::Never, Some(1), false),
|
||||
RestartDecision::StayStopped
|
||||
);
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::Never, None, false),
|
||||
RestartDecision::StayStopped
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_failure_skips_clean() {
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::OnFailure, Some(0), false),
|
||||
RestartDecision::StayStopped
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_failure_restarts_nonzero() {
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::OnFailure, Some(1), false),
|
||||
RestartDecision::Restart
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_failure_restarts_signal() {
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::OnFailure, None, false),
|
||||
RestartDecision::Restart
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn always_restarts_on_clean() {
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::Always, Some(0), false),
|
||||
RestartDecision::Restart
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cap_reached_marks_failed() {
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::Always, Some(0), true),
|
||||
RestartDecision::MarkFailed
|
||||
);
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::OnFailure, Some(1), true),
|
||||
RestartDecision::MarkFailed
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cap_reached_never_still_stopped() {
|
||||
assert_eq!(
|
||||
decide(RestartPolicy::Never, Some(1), true),
|
||||
RestartDecision::StayStopped
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user