use std::fmt;
use crate::adapter::net::behavior::meshos::{DaemonRef, NodeId};
use super::claim_registry::ClaimRegistry;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClaimHeld(pub DaemonRef);
impl fmt::Display for ClaimHeld {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"daemon {:?} holds an exclusive claim and cannot be migrated; \
drain is release -> re-claim -> restart",
self.0
)
}
}
impl std::error::Error for ClaimHeld {}
#[derive(Debug, Clone)]
pub struct MigrationEligible(DaemonRef);
impl MigrationEligible {
pub fn check(daemon: DaemonRef, claims: &ClaimRegistry) -> Result<Self, ClaimHeld> {
if claims.holds_exclusive(&daemon) {
return Err(ClaimHeld(daemon));
}
Ok(MigrationEligible(daemon))
}
pub fn daemon(&self) -> &DaemonRef {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MigrationPlan {
pub daemon: DaemonRef,
pub target: NodeId,
}
pub fn migrate(eligible: MigrationEligible, target: NodeId) -> MigrationPlan {
MigrationPlan {
daemon: eligible.0,
target,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::adapter::net::cortex::workflow::ActiveClaim;
fn dref(id: u64) -> DaemonRef {
DaemonRef {
id,
name: format!("task/{id}"),
}
}
#[test]
fn a_claim_free_daemon_is_eligible_and_migrates() {
let claims = ClaimRegistry::new();
let d = dref(1);
let eligible =
MigrationEligible::check(d.clone(), &claims).expect("no claim held → eligible");
assert_eq!(eligible.daemon(), &d);
let plan = migrate(eligible, 42);
assert_eq!(
plan,
MigrationPlan {
daemon: d,
target: 42,
}
);
}
#[test]
fn a_claim_holder_is_vetoed() {
let mut claims = ClaimRegistry::new();
let d = dref(1);
claims.insert(d.clone(), ActiveClaim { island: 0xA0 });
let err =
MigrationEligible::check(d.clone(), &claims).expect_err("claim-holder must be vetoed");
assert_eq!(err, ClaimHeld(d));
}
#[test]
fn releasing_the_claim_makes_the_daemon_eligible_again() {
let mut claims = ClaimRegistry::new();
let d = dref(1);
claims.insert(d.clone(), ActiveClaim { island: 0xA0 });
assert!(MigrationEligible::check(d.clone(), &claims).is_err());
claims.remove(&d);
assert!(
MigrationEligible::check(d.clone(), &claims).is_ok(),
"after release the veto lifts",
);
}
#[test]
fn the_veto_is_per_daemon_not_global() {
let mut claims = ClaimRegistry::new();
let held = dref(1);
let free = dref(2);
claims.insert(held.clone(), ActiveClaim { island: 0xA0 });
assert!(MigrationEligible::check(held, &claims).is_err());
assert!(MigrationEligible::check(free, &claims).is_ok());
}
}