#![allow(missing_docs)]
#[allow(unused_imports)]
use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StuckVmObservation {
pub(crate) id: String,
heartbeat_age: Duration,
runtime_pid: Option<u32>,
}
impl StuckVmObservation {
#[must_use]
pub fn new(id: impl Into<String>, heartbeat_age: Duration) -> Self {
Self {
id: id.into(),
heartbeat_age,
runtime_pid: None,
}
}
#[must_use]
pub fn with_runtime_pid(
id: impl Into<String>,
heartbeat_age: Duration,
runtime_pid: u32,
) -> Self {
Self {
id: id.into(),
heartbeat_age,
runtime_pid: Some(runtime_pid),
}
}
#[must_use]
pub fn id(&self) -> &str {
&self.id
}
#[must_use]
pub const fn heartbeat_age(&self) -> Duration {
self.heartbeat_age
}
#[must_use]
pub const fn runtime_pid(&self) -> Option<u32> {
self.runtime_pid
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StuckVmCleanupDecision {
Preserve,
Cleanup,
Quarantine,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StuckVmCleanupPlanEntry {
observation: StuckVmObservation,
#[allow(missing_docs)]
pub decision: StuckVmCleanupDecision,
}
impl StuckVmCleanupPlanEntry {
#[must_use]
pub const fn new(observation: StuckVmObservation, decision: StuckVmCleanupDecision) -> Self {
Self {
observation,
decision,
}
}
#[must_use]
pub const fn observation(&self) -> &StuckVmObservation {
&self.observation
}
#[must_use]
pub const fn decision(&self) -> StuckVmCleanupDecision {
self.decision
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct StuckVmCleanupPlan {
pub(crate) decisions: Vec<StuckVmCleanupPlanEntry>,
}
impl StuckVmCleanupPlan {
#[must_use]
pub fn from_observations(
observations: impl IntoIterator<Item = StuckVmObservation>,
heartbeat_timeout: Duration,
) -> Self {
let decisions = observations
.into_iter()
.map(|observation| {
let decision = if observation.id().is_empty() {
StuckVmCleanupDecision::Quarantine
} else if observation.heartbeat_age() >= heartbeat_timeout {
StuckVmCleanupDecision::Cleanup
} else {
StuckVmCleanupDecision::Preserve
};
StuckVmCleanupPlanEntry::new(observation, decision)
})
.collect();
Self { decisions }
}
#[must_use]
pub fn decisions(&self) -> &[StuckVmCleanupPlanEntry] {
&self.decisions
}
#[must_use]
pub fn decision_for(&self, id: &str) -> Option<StuckVmCleanupDecision> {
self.decisions
.iter()
.find(|entry| entry.observation().id() == id)
.map(StuckVmCleanupPlanEntry::decision)
}
#[must_use]
pub fn cleanup_ids(&self) -> Vec<&str> {
self.decisions
.iter()
.filter(|entry| entry.decision() == StuckVmCleanupDecision::Cleanup)
.map(|entry| entry.observation().id())
.collect()
}
#[must_use]
pub fn quarantine_ids(&self) -> Vec<&str> {
self.decisions
.iter()
.filter(|entry| entry.decision() == StuckVmCleanupDecision::Quarantine)
.map(|entry| entry.observation().id())
.collect()
}
}