use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct FleetMarks {
pub added: Vec<String>,
pub removed: Vec<String>,
}
impl FleetMarks {
pub fn default_path() -> PathBuf {
crate::diagnostics::state_dir().join("fleet.toml")
}
pub fn load(path: &Path) -> Self {
std::fs::read_to_string(path)
.ok()
.and_then(|s| toml::from_str(&s).ok())
.unwrap_or_default()
}
pub fn save(&self, path: &Path) -> Result<(), String> {
if let Some(dir) = path.parent() {
std::fs::create_dir_all(dir).map_err(|e| e.to_string())?;
}
let text = toml::to_string(self).map_err(|e| e.to_string())?;
std::fs::write(path, text).map_err(|e| e.to_string())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FleetStatus {
Connecting,
Ok,
Error(String),
}
#[derive(Clone, Debug)]
pub struct FleetRow {
pub context: String,
pub status: FleetStatus,
pub version: String,
pub nodes_ready: usize,
pub nodes_total: usize,
pub pods_unhealthy: usize,
pub pods_total: usize,
pub flux_failed: Option<usize>,
pub readonly: bool,
}
impl FleetRow {
pub fn connecting(context: String, readonly: bool) -> Self {
FleetRow {
context,
status: FleetStatus::Connecting,
version: String::new(),
nodes_ready: 0,
nodes_total: 0,
pods_unhealthy: 0,
pods_total: 0,
flux_failed: None,
readonly,
}
}
pub fn is_healthy(&self) -> bool {
self.status == FleetStatus::Ok
&& self.nodes_ready == self.nodes_total
&& self.pods_unhealthy == 0
&& self.flux_failed.unwrap_or(0) == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ok_row() -> FleetRow {
FleetRow {
context: "prod".into(),
status: FleetStatus::Ok,
version: "v1.30.2".into(),
nodes_ready: 3,
nodes_total: 3,
pods_unhealthy: 0,
pods_total: 40,
flux_failed: Some(0),
readonly: true,
}
}
#[test]
fn connecting_row_is_not_healthy() {
let r = FleetRow::connecting("staging".into(), false);
assert_eq!(r.status, FleetStatus::Connecting);
assert!(!r.is_healthy());
}
#[test]
fn healthy_requires_ready_nodes_no_bad_pods_no_flux_failures() {
assert!(ok_row().is_healthy());
let mut r = ok_row();
r.nodes_ready = 2; assert!(!r.is_healthy());
let mut r = ok_row();
r.pods_unhealthy = 1;
assert!(!r.is_healthy());
let mut r = ok_row();
r.flux_failed = Some(2);
assert!(!r.is_healthy());
}
#[test]
fn errored_context_is_never_healthy() {
let mut r = ok_row();
r.status = FleetStatus::Error("deadline elapsed".into());
assert!(!r.is_healthy());
}
#[test]
fn no_flux_crds_does_not_count_as_a_failure() {
let mut r = ok_row();
r.flux_failed = None;
assert!(r.is_healthy());
}
}