use super::super::crd::{ClusterPhase, Condition, ConditionStatus, ConditionType, WorkloadPhase};
use std::collections::{BTreeMap, HashMap};
use std::time::SystemTime;
use tracing::{debug, error, info, warn};
pub struct ClusterLifecycle {
max_failure_count: u32,
failure_count: u32,
}
impl ClusterLifecycle {
pub fn new(max_failure_count: u32) -> Self {
Self {
max_failure_count,
failure_count: 0,
}
}
pub fn handle_initialization(&mut self) -> Result<(), LifecycleError> {
info!("Initializing cluster");
self.failure_count = 0;
Ok(())
}
pub fn handle_creation(&mut self) -> Result<ClusterPhase, LifecycleError> {
info!("Creating cluster resources");
self.failure_count = 0;
Ok(ClusterPhase::Creating)
}
pub fn handle_startup(&mut self) -> Result<ClusterPhase, LifecycleError> {
info!("Starting cluster");
self.failure_count = 0;
Ok(ClusterPhase::Running)
}
pub fn handle_update(&mut self) -> Result<ClusterPhase, LifecycleError> {
info!("Updating cluster");
self.failure_count = 0;
Ok(ClusterPhase::Updating)
}
pub fn handle_shutdown(&mut self) -> Result<(), LifecycleError> {
info!("Shutting down cluster");
self.failure_count = 0;
Ok(())
}
pub fn handle_failure(&mut self, error: &str) -> Result<ClusterPhase, LifecycleError> {
warn!("Cluster failure: {}", error);
self.failure_count += 1;
if self.failure_count >= self.max_failure_count {
error!("Cluster has failed {} times, giving up", self.failure_count);
return Ok(ClusterPhase::Failed);
}
info!(
"Attempting to recover cluster (failure count: {})",
self.failure_count
);
Ok(ClusterPhase::Running)
}
pub fn handle_recovery(&mut self) -> Result<ClusterPhase, LifecycleError> {
info!("Recovering cluster");
self.failure_count = 0;
Ok(ClusterPhase::Running)
}
pub fn failure_count(&self) -> u32 {
self.failure_count
}
pub fn reset_failure_count(&mut self) {
self.failure_count = 0;
}
pub fn generate_readiness_condition(&self) -> Condition {
Condition {
condition_type: ConditionType::Ready,
status: if self.failure_count == 0 {
ConditionStatus::True
} else {
ConditionStatus::False
},
last_probe_time: Some(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string(),
),
last_transition_time: Some(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string(),
),
reason: if self.failure_count == 0 {
Some("ClusterReady".to_string())
} else {
Some("ClusterNotReady".to_string())
},
message: if self.failure_count == 0 {
Some("Cluster is ready".to_string())
} else {
Some(format!(
"Cluster is not ready ({} failures)",
self.failure_count
))
},
}
}
}
pub struct WorkloadLifecycle {
max_failure_count: u32,
failure_count: u32,
execution_count: u64,
}
impl WorkloadLifecycle {
pub fn new(max_failure_count: u32) -> Self {
Self {
max_failure_count,
failure_count: 0,
execution_count: 0,
}
}
pub fn handle_initialization(&mut self) -> Result<(), LifecycleError> {
info!("Initializing workload");
self.failure_count = 0;
self.execution_count = 0;
Ok(())
}
pub fn handle_creation(&mut self) -> Result<WorkloadPhase, LifecycleError> {
info!("Creating workload");
self.failure_count = 0;
Ok(WorkloadPhase::Creating)
}
pub fn handle_startup(&mut self) -> Result<WorkloadPhase, LifecycleError> {
info!("Starting workload");
self.failure_count = 0;
Ok(WorkloadPhase::Running)
}
pub fn handle_execution(&mut self) -> Result<(), LifecycleError> {
info!("Executing workload");
self.execution_count += 1;
Ok(())
}
pub fn handle_update(&mut self) -> Result<WorkloadPhase, LifecycleError> {
info!("Updating workload");
self.failure_count = 0;
Ok(WorkloadPhase::Updating)
}
pub fn handle_failure(&mut self, error: &str) -> Result<WorkloadPhase, LifecycleError> {
warn!("Workload failure: {}", error);
self.failure_count += 1;
if self.failure_count >= self.max_failure_count {
error!(
"Workload has failed {} times, giving up",
self.failure_count
);
return Ok(WorkloadPhase::Failed);
}
info!(
"Attempting to recover workload (failure count: {})",
self.failure_count
);
Ok(WorkloadPhase::Running)
}
pub fn handle_recovery(&mut self) -> Result<WorkloadPhase, LifecycleError> {
info!("Recovering workload");
self.failure_count = 0;
Ok(WorkloadPhase::Running)
}
pub fn execution_count(&self) -> u64 {
self.execution_count
}
pub fn generate_readiness_condition(&self) -> Condition {
Condition {
condition_type: ConditionType::Ready,
status: if self.failure_count == 0 {
ConditionStatus::True
} else {
ConditionStatus::False
},
last_probe_time: Some(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string(),
),
last_transition_time: Some(
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string(),
),
reason: if self.failure_count == 0 {
Some("WorkloadReady".to_string())
} else {
Some("WorkloadNotReady".to_string())
},
message: if self.failure_count == 0 {
Some("Workload is ready".to_string())
} else {
Some(format!(
"Workload is not ready ({} failures)",
self.failure_count
))
},
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum LifecycleError {
#[error("Initialization failed: {0}")]
InitializationFailed(String),
#[error("Creation failed: {0}")]
CreationFailed(String),
#[error("Update failed: {0}")]
UpdateFailed(String),
#[error("Execution failed: {0}")]
ExecutionFailed(String),
#[error("Recovery failed: {0}")]
RecoveryFailed(String),
#[error("Other error: {0}")]
Other(String),
}
#[cfg(test)]
mod tests {
#[test]
fn test_cluster_lifecycle_initialization() {
let mut lifecycle = ClusterLifecycle::new(3);
let result: _ = lifecycle.handle_initialization();
assert!(result.is_ok());
assert_eq!(lifecycle.failure_count(), 0);
}
#[test]
fn test_cluster_lifecycle_failure_recovery() {
let mut lifecycle = ClusterLifecycle::new(3);
let result: _ = lifecycle.handle_failure("Test failure");
assert!(result.is_ok());
assert_eq!(lifecycle.failure_count(), 1);
assert_eq!(result.unwrap(), ClusterPhase::Running);
let result: _ = lifecycle.handle_recovery();
assert!(result.is_ok());
assert_eq!(lifecycle.failure_count(), 0);
}
#[test]
fn test_cluster_lifecycle_max_failures() {
let mut lifecycle = ClusterLifecycle::new(2);
let _: _ = lifecycle.handle_failure("Test failure 1");
let _: _ = lifecycle.handle_failure("Test failure 2");
assert_eq!(lifecycle.failure_count(), 2);
}
#[test]
fn test_workload_lifecycle_execution() {
let mut lifecycle = WorkloadLifecycle::new(3);
let result: _ = lifecycle.handle_execution();
assert!(result.is_ok());
assert_eq!(lifecycle.execution_count(), 1);
let result: _ = lifecycle.handle_execution();
assert!(result.is_ok());
assert_eq!(lifecycle.execution_count(), 2);
}
#[test]
fn test_generating_conditions() {
let lifecycle: _ = ClusterLifecycle::new(3);
let condition: _ = lifecycle.generate_readiness_condition();
assert_eq!(condition.condition_type, ConditionType::Ready);
assert_eq!(condition.status, ConditionStatus::True);
assert!(condition.reason.is_some());
assert!(condition.message.is_some());
}
}