use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use super::{AlertLevel, AlertRuleDefinition, AlertState};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AlertInstance {
pub id: String,
pub rule_id: String,
pub state: AlertState,
pub level: AlertLevel,
pub summary: String,
pub description: String,
pub labels: HashMap<String, String>,
pub annotations: HashMap<String, String>,
pub pending_at: Option<DateTime<Utc>>,
pub firing_at: Option<DateTime<Utc>>,
pub resolved_at: Option<DateTime<Utc>>,
pub updated_at: DateTime<Utc>,
pub fire_count: u64,
pub fingerprint: String,
pub generator_url: Option<String>,
}
impl AlertInstance {
pub fn from_rule(rule: &AlertRuleDefinition) -> Self {
let now = Utc::now();
let id = uuid::Uuid::new_v4().to_string();
let fingerprint = Self::compute_fingerprint(&rule.id, &rule.labels);
Self {
id,
rule_id: rule.id.clone(),
state: AlertState::Inactive,
level: rule.level,
summary: rule.name.clone(),
description: rule.description.clone(),
labels: rule.labels.clone(),
annotations: rule.annotations.clone(),
pending_at: None,
firing_at: None,
resolved_at: None,
updated_at: now,
fire_count: 0,
fingerprint,
generator_url: rule.dashboard_url.clone(),
}
}
fn compute_fingerprint(rule_id: &str, labels: &HashMap<String, String>) -> String {
let mut sorted_labels: Vec<_> = labels.iter().collect();
sorted_labels.sort_by_key(|(k, _)| *k);
let label_str: String = sorted_labels
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<_>>()
.join(",");
format!("{}:{}", rule_id, label_str)
}
pub fn transition_to_pending(&mut self) {
if self.state == AlertState::Inactive {
self.state = AlertState::Pending;
self.pending_at = Some(Utc::now());
self.updated_at = Utc::now();
}
}
pub fn transition_to_firing(&mut self) {
if matches!(self.state, AlertState::Pending | AlertState::Inactive) {
self.state = AlertState::Firing;
self.firing_at = Some(Utc::now());
self.fire_count += 1;
self.updated_at = Utc::now();
}
}
pub fn transition_to_resolved(&mut self) {
if self.state.is_active() {
self.state = AlertState::Resolved;
self.resolved_at = Some(Utc::now());
self.updated_at = Utc::now();
}
}
pub fn transition_to_silenced(&mut self) {
self.state = AlertState::Silenced;
self.updated_at = Utc::now();
}
pub fn acknowledge(&mut self) {
if self.state == AlertState::Firing {
self.state = AlertState::Acknowledged;
self.updated_at = Utc::now();
}
}
pub fn reset(&mut self) {
self.state = AlertState::Inactive;
self.pending_at = None;
self.firing_at = None;
self.resolved_at = None;
self.updated_at = Utc::now();
}
}