feat(supervisor): restart-policy decision logic
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
//! Process-supervision primitives for the xy daemon.
|
//! Process-supervision primitives for the xy daemon.
|
||||||
|
|
||||||
|
pub mod backoff;
|
||||||
pub mod child;
|
pub mod child;
|
||||||
|
pub mod policy;
|
||||||
|
pub mod retry_window;
|
||||||
|
|
||||||
|
pub use backoff::Backoff;
|
||||||
pub use child::{ChildHandle, MockChild, MockChildController};
|
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