use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use tracing::warn;
use super::host::HostRef;
fn unknown_phase(kind: &str, word: &str) -> String {
warn!(
phase = kind,
input = word,
"phase word is unknown to the protocol; the caller answers with its fallback"
);
format!("unknown {kind} phase {word:?}")
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
JsonSchema,
Default,
)]
#[serde(rename_all = "snake_case")]
pub enum AgentPhase {
#[default]
Booting,
Ready,
Running,
Idle,
Gone,
}
impl AgentPhase {
pub const VARIANT_COUNT: usize = 5;
}
impl std::fmt::Display for AgentPhase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
AgentPhase::Booting => "booting",
AgentPhase::Ready => "ready",
AgentPhase::Running => "running",
AgentPhase::Idle => "idle",
AgentPhase::Gone => "gone",
})
}
}
impl std::str::FromStr for AgentPhase {
type Err = String;
fn from_str(name: &str) -> Result<Self, Self::Err> {
Ok(match name {
"booting" => AgentPhase::Booting,
"ready" => AgentPhase::Ready,
"running" => AgentPhase::Running,
"idle" => AgentPhase::Idle,
"gone" => AgentPhase::Gone,
other => return Err(unknown_phase("agent", other)),
})
}
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
JsonSchema,
Default,
)]
#[serde(rename_all = "snake_case")]
pub enum DeliveryPhase {
#[serde(rename = "none")]
#[default]
NoIntent,
Pending,
Retrying,
Accepted,
Exhausted,
}
impl DeliveryPhase {
pub const VARIANT_COUNT: usize = 5;
}
impl std::fmt::Display for DeliveryPhase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
DeliveryPhase::NoIntent => "none",
DeliveryPhase::Pending => "pending",
DeliveryPhase::Retrying => "retrying",
DeliveryPhase::Accepted => "accepted",
DeliveryPhase::Exhausted => "exhausted",
})
}
}
impl std::str::FromStr for DeliveryPhase {
type Err = String;
fn from_str(name: &str) -> Result<Self, Self::Err> {
Ok(match name {
"none" => DeliveryPhase::NoIntent,
"pending" => DeliveryPhase::Pending,
"retrying" => DeliveryPhase::Retrying,
"accepted" => DeliveryPhase::Accepted,
"exhausted" => DeliveryPhase::Exhausted,
other => return Err(unknown_phase("delivery", other)),
})
}
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
JsonSchema,
Default,
)]
#[serde(rename_all = "snake_case")]
pub enum ResourcePhase {
#[default]
Detached,
Attached,
Closing,
Closed,
}
impl ResourcePhase {
pub const VARIANT_COUNT: usize = 4;
}
impl std::fmt::Display for ResourcePhase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
ResourcePhase::Detached => "detached",
ResourcePhase::Attached => "attached",
ResourcePhase::Closing => "closing",
ResourcePhase::Closed => "closed",
})
}
}
impl std::str::FromStr for ResourcePhase {
type Err = String;
fn from_str(name: &str) -> Result<Self, Self::Err> {
Ok(match name {
"detached" => ResourcePhase::Detached,
"attached" => ResourcePhase::Attached,
"closing" => ResourcePhase::Closing,
"closed" => ResourcePhase::Closed,
other => return Err(unknown_phase("resource", other)),
})
}
}
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
JsonSchema,
Default,
)]
#[serde(rename_all = "snake_case")]
pub enum RecoveryPhase {
#[serde(rename = "none")]
#[default]
NoRecovery,
IdleWaiting,
IdleFault,
Draining,
}
impl RecoveryPhase {
pub const VARIANT_COUNT: usize = 4;
}
impl std::fmt::Display for RecoveryPhase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
RecoveryPhase::NoRecovery => "none",
RecoveryPhase::IdleWaiting => "idle_waiting",
RecoveryPhase::IdleFault => "idle_fault",
RecoveryPhase::Draining => "draining",
})
}
}
impl std::str::FromStr for RecoveryPhase {
type Err = String;
fn from_str(name: &str) -> Result<Self, Self::Err> {
Ok(match name {
"none" => RecoveryPhase::NoRecovery,
"idle_waiting" => RecoveryPhase::IdleWaiting,
"idle_fault" => RecoveryPhase::IdleFault,
"draining" => RecoveryPhase::Draining,
other => return Err(unknown_phase("recovery", other)),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TaskState {
Pending,
Done,
Failed,
Cancelled,
Blocked,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Version {
pub generation: u64,
pub seq: u64,
}
impl Version {
pub fn new(generation: u64, seq: u64) -> Self {
Self { generation, seq }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Observation {
pub version: Version,
pub generation_live: bool,
pub isolate_after: u32,
pub terminate_after: u32,
pub mismatch_count: u32,
pub agent: AgentPhase,
pub delivery: DeliveryPhase,
pub resource: ResourcePhase,
pub recovery: RecoveryPhase,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub host: Option<HostRef>,
}
impl Observation {
pub fn initial(isolate_after: u32, terminate_after: u32) -> Self {
Self::build(
Version::new(1, 0),
true,
isolate_after,
terminate_after,
0,
AgentPhase::Booting,
DeliveryPhase::NoIntent,
ResourcePhase::Detached,
RecoveryPhase::NoRecovery,
)
}
#[allow(clippy::too_many_arguments)]
pub fn build(
version: Version,
generation_live: bool,
isolate_after: u32,
terminate_after: u32,
mismatch_count: u32,
agent: AgentPhase,
delivery: DeliveryPhase,
resource: ResourcePhase,
recovery: RecoveryPhase,
) -> Self {
Self {
version,
generation_live,
isolate_after,
terminate_after,
mismatch_count,
agent,
delivery,
resource,
recovery,
host: None,
}
}
pub fn with_host(mut self, host: Option<HostRef>) -> Self {
self.host = host;
self
}
pub(super) fn advanced(&self, version: Version) -> Self {
let mut next = self.clone();
next.version = version;
next
}
pub(super) fn adopted(&self, version: Version) -> Self {
let mut next = self.advanced(version);
next.generation_live = true;
next.mismatch_count = 0;
next
}
pub(super) fn with_mismatch(&self, count: u32) -> Self {
let mut next = self.clone();
next.mismatch_count = count;
next
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct Tuple {
agent: AgentPhase,
delivery: DeliveryPhase,
resource: ResourcePhase,
recovery: RecoveryPhase,
generation_live: bool,
host: Option<HostRef>,
}
impl Observation {
pub(super) fn tuple(&self) -> Tuple {
Tuple {
agent: self.agent,
delivery: self.delivery,
resource: self.resource,
recovery: self.recovery,
generation_live: self.generation_live,
host: self.host.clone(),
}
}
}