patches on sxsw ai working api key working container hardened plus many more
This commit is contained in:
@@ -2,11 +2,14 @@ pub mod collector;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::VecDeque;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
const ALERT_RULES_FILE: &str = "alert-rules.json";
|
||||
|
||||
/// Maximum entries at 1-minute resolution (24 hours = 1440 minutes)
|
||||
const MAX_1MIN_ENTRIES: usize = 1440;
|
||||
|
||||
@@ -132,6 +135,7 @@ pub struct MetricsStore {
|
||||
ws_connections: AtomicU32,
|
||||
alert_rules: RwLock<Vec<AlertRule>>,
|
||||
fired_alerts: RwLock<VecDeque<FiredAlert>>,
|
||||
data_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl MetricsStore {
|
||||
@@ -144,6 +148,22 @@ impl MetricsStore {
|
||||
ws_connections: AtomicU32::new(0),
|
||||
alert_rules: RwLock::new(AlertRule::default_rules()),
|
||||
fired_alerts: RwLock::new(VecDeque::with_capacity(MAX_ALERT_HISTORY)),
|
||||
data_dir: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a MetricsStore that persists alert rules to disk.
|
||||
pub fn with_data_dir(data_dir: PathBuf) -> Self {
|
||||
let rules = load_alert_rules_sync(&data_dir);
|
||||
Self {
|
||||
minute_data: RwLock::new(VecDeque::with_capacity(MAX_1MIN_ENTRIES)),
|
||||
quarter_hour_data: RwLock::new(VecDeque::with_capacity(MAX_15MIN_ENTRIES)),
|
||||
minute_count: RwLock::new(0),
|
||||
rpc_latency: RwLock::new((0.0, 0)),
|
||||
ws_connections: AtomicU32::new(0),
|
||||
alert_rules: RwLock::new(rules),
|
||||
fired_alerts: RwLock::new(VecDeque::with_capacity(MAX_ALERT_HISTORY)),
|
||||
data_dir: Some(data_dir),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +249,7 @@ impl MetricsStore {
|
||||
self.alert_rules.read().await.clone()
|
||||
}
|
||||
|
||||
/// Update an alert rule by kind.
|
||||
/// Update an alert rule by kind and persist to disk.
|
||||
pub async fn update_alert_rule(&self, kind: &AlertRuleKind, enabled: Option<bool>, threshold: Option<f64>) {
|
||||
let mut rules = self.alert_rules.write().await;
|
||||
if let Some(rule) = rules.iter_mut().find(|r| &r.kind == kind) {
|
||||
@@ -240,6 +260,12 @@ impl MetricsStore {
|
||||
rule.threshold = t;
|
||||
}
|
||||
}
|
||||
// Persist to disk so changes survive restarts
|
||||
if let Some(ref dir) = self.data_dir {
|
||||
if let Err(e) = save_alert_rules(dir, &rules).await {
|
||||
warn!("Failed to persist alert rules: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get fired alert history.
|
||||
@@ -409,6 +435,47 @@ impl MetricsStore {
|
||||
}
|
||||
}
|
||||
|
||||
/// Load alert rules from disk, falling back to defaults if file missing or corrupt.
|
||||
fn load_alert_rules_sync(data_dir: &std::path::Path) -> Vec<AlertRule> {
|
||||
let path = data_dir.join(ALERT_RULES_FILE);
|
||||
match std::fs::read_to_string(&path) {
|
||||
Ok(content) => match serde_json::from_str::<Vec<AlertRule>>(&content) {
|
||||
Ok(saved) => {
|
||||
// Merge with defaults: use saved enabled/threshold, add any new rule kinds
|
||||
let defaults = AlertRule::default_rules();
|
||||
let mut merged = Vec::new();
|
||||
for default in &defaults {
|
||||
if let Some(saved_rule) = saved.iter().find(|r| r.kind == default.kind) {
|
||||
merged.push(AlertRule {
|
||||
kind: default.kind.clone(),
|
||||
threshold: saved_rule.threshold,
|
||||
enabled: saved_rule.enabled,
|
||||
description: default.description.clone(),
|
||||
});
|
||||
} else {
|
||||
merged.push(default.clone());
|
||||
}
|
||||
}
|
||||
info!("Loaded alert rules from {}", path.display());
|
||||
merged
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to parse alert rules ({}), using defaults", e);
|
||||
AlertRule::default_rules()
|
||||
}
|
||||
},
|
||||
Err(_) => AlertRule::default_rules(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Save alert rules to disk.
|
||||
async fn save_alert_rules(data_dir: &std::path::Path, rules: &[AlertRule]) -> anyhow::Result<()> {
|
||||
tokio::fs::create_dir_all(data_dir).await?;
|
||||
let content = serde_json::to_string_pretty(rules)?;
|
||||
tokio::fs::write(data_dir.join(ALERT_RULES_FILE), content).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Spawn the background metrics collector (runs every 60 seconds).
|
||||
/// Also evaluates alert rules on each snapshot and pushes notifications.
|
||||
pub fn spawn_metrics_collector(
|
||||
|
||||
Reference in New Issue
Block a user