1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use serde::{Deserialize, Serialize};
7
8pub type SharedCloudWatchState = Arc<RwLock<CloudWatchAccounts>>;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct CloudWatchSnapshot {
13 pub schema_version: u32,
14 pub accounts: CloudWatchAccounts,
15}
16
17pub const CLOUDWATCH_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
18
19#[derive(Debug, Default, Clone, Serialize, Deserialize)]
20pub struct CloudWatchAccounts {
21 pub accounts: BTreeMap<String, CloudWatchState>,
22}
23
24impl CloudWatchAccounts {
25 pub fn new() -> Self {
26 Self::default()
27 }
28
29 pub fn clone_for_snapshot(&self) -> CloudWatchAccounts {
34 CloudWatchAccounts {
35 accounts: self.accounts.clone(),
36 }
37 }
38
39 pub fn get_or_create(&mut self, account_id: &str) -> &mut CloudWatchState {
40 self.accounts
41 .entry(account_id.to_string())
42 .or_insert_with(|| CloudWatchState::new(account_id))
43 }
44
45 pub fn get(&self, account_id: &str) -> Option<&CloudWatchState> {
46 self.accounts.get(account_id)
47 }
48}
49
50#[derive(Debug, Default, Clone, Serialize, Deserialize)]
51pub struct CloudWatchState {
52 pub account_id: String,
53 pub metrics: BTreeMap<String, BTreeMap<String, Vec<MetricDatum>>>,
55 pub alarms: BTreeMap<String, BTreeMap<String, MetricAlarm>>,
57 #[serde(default)]
60 pub dashboards: BTreeMap<String, Dashboard>,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Dashboard {
65 pub name: String,
66 pub arn: String,
67 pub body: String,
68 pub last_modified: DateTime<Utc>,
69 pub size_bytes: i64,
70}
71
72impl CloudWatchState {
73 pub fn new(account_id: &str) -> Self {
74 Self {
75 account_id: account_id.to_string(),
76 metrics: BTreeMap::new(),
77 alarms: BTreeMap::new(),
78 dashboards: BTreeMap::new(),
79 }
80 }
81
82 pub fn metrics_in(&self, region: &str) -> Option<&BTreeMap<String, Vec<MetricDatum>>> {
83 self.metrics.get(region)
84 }
85
86 pub fn metrics_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, Vec<MetricDatum>> {
87 self.metrics.entry(region.to_string()).or_default()
88 }
89
90 pub fn alarms_in(&self, region: &str) -> Option<&BTreeMap<String, MetricAlarm>> {
91 self.alarms.get(region)
92 }
93
94 pub fn alarms_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, MetricAlarm> {
95 self.alarms.entry(region.to_string()).or_default()
96 }
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct MetricDatum {
101 pub metric_name: String,
102 pub dimensions: BTreeMap<String, String>,
103 pub timestamp: DateTime<Utc>,
104 pub value: Option<f64>,
105 pub statistic_values: Option<StatisticSet>,
106 pub unit: Option<String>,
107 pub storage_resolution: Option<i64>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct StatisticSet {
112 pub sample_count: f64,
113 pub sum: f64,
114 pub minimum: f64,
115 pub maximum: f64,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct MetricAlarm {
120 pub alarm_name: String,
121 pub alarm_arn: String,
122 pub alarm_description: Option<String>,
123 pub actions_enabled: bool,
124 pub ok_actions: Vec<String>,
125 pub alarm_actions: Vec<String>,
126 pub insufficient_data_actions: Vec<String>,
127 pub state_value: AlarmState,
128 pub state_reason: String,
129 pub state_updated_timestamp: DateTime<Utc>,
130 pub metric_name: Option<String>,
131 pub namespace: Option<String>,
132 pub statistic: Option<String>,
133 pub extended_statistic: Option<String>,
134 pub dimensions: BTreeMap<String, String>,
135 pub period: Option<i64>,
136 pub unit: Option<String>,
137 pub evaluation_periods: i64,
138 pub datapoints_to_alarm: Option<i64>,
139 pub threshold: Option<f64>,
140 pub comparison_operator: String,
141 pub treat_missing_data: Option<String>,
142 pub evaluate_low_sample_count_percentile: Option<String>,
143 pub configuration_updated_timestamp: DateTime<Utc>,
144 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
145}
146
147#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
148pub enum AlarmState {
149 Ok,
150 Alarm,
151 InsufficientData,
152}
153
154impl AlarmState {
155 pub fn as_str(&self) -> &'static str {
156 match self {
157 AlarmState::Ok => "OK",
158 AlarmState::Alarm => "ALARM",
159 AlarmState::InsufficientData => "INSUFFICIENT_DATA",
160 }
161 }
162
163 pub fn parse(s: &str) -> Option<Self> {
164 match s {
165 "OK" => Some(AlarmState::Ok),
166 "ALARM" => Some(AlarmState::Alarm),
167 "INSUFFICIENT_DATA" => Some(AlarmState::InsufficientData),
168 _ => None,
169 }
170 }
171}