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)]
59 pub composite_alarms: BTreeMap<String, BTreeMap<String, CompositeAlarm>>,
60 #[serde(default)]
63 pub dashboards: BTreeMap<String, Dashboard>,
64 #[serde(default)]
66 pub anomaly_detectors: BTreeMap<String, BTreeMap<String, AnomalyDetector>>,
67 #[serde(default)]
69 pub insight_rules: BTreeMap<String, BTreeMap<String, InsightRule>>,
70 #[serde(default)]
72 pub managed_rules: BTreeMap<String, BTreeMap<String, Vec<ManagedRule>>>,
73 #[serde(default)]
75 pub metric_streams: BTreeMap<String, BTreeMap<String, MetricStream>>,
76 #[serde(default)]
78 pub mute_rules: BTreeMap<String, BTreeMap<String, AlarmMuteRule>>,
79 #[serde(default)]
81 pub datasets: BTreeMap<String, BTreeMap<String, Dataset>>,
82 #[serde(default)]
84 pub tags: BTreeMap<String, BTreeMap<String, String>>,
85 #[serde(default)]
87 pub otel_enrichment_running: bool,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct Dashboard {
92 pub name: String,
93 pub arn: String,
94 pub body: String,
95 pub last_modified: DateTime<Utc>,
96 pub size_bytes: i64,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct Dataset {
104 pub id: String,
105 pub arn: String,
106 pub kms_key_arn: Option<String>,
107}
108
109impl CloudWatchState {
110 pub fn new(account_id: &str) -> Self {
111 Self {
112 account_id: account_id.to_string(),
113 ..Default::default()
114 }
115 }
116
117 pub fn metrics_in(&self, region: &str) -> Option<&BTreeMap<String, Vec<MetricDatum>>> {
118 self.metrics.get(region)
119 }
120
121 pub fn metrics_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, Vec<MetricDatum>> {
122 self.metrics.entry(region.to_string()).or_default()
123 }
124
125 pub fn alarms_in(&self, region: &str) -> Option<&BTreeMap<String, MetricAlarm>> {
126 self.alarms.get(region)
127 }
128
129 pub fn alarms_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, MetricAlarm> {
130 self.alarms.entry(region.to_string()).or_default()
131 }
132
133 pub fn composite_alarms_in(&self, region: &str) -> Option<&BTreeMap<String, CompositeAlarm>> {
134 self.composite_alarms.get(region)
135 }
136
137 pub fn composite_alarms_in_mut(
138 &mut self,
139 region: &str,
140 ) -> &mut BTreeMap<String, CompositeAlarm> {
141 self.composite_alarms.entry(region.to_string()).or_default()
142 }
143
144 pub fn anomaly_detectors_in(&self, region: &str) -> Option<&BTreeMap<String, AnomalyDetector>> {
145 self.anomaly_detectors.get(region)
146 }
147
148 pub fn anomaly_detectors_in_mut(
149 &mut self,
150 region: &str,
151 ) -> &mut BTreeMap<String, AnomalyDetector> {
152 self.anomaly_detectors
153 .entry(region.to_string())
154 .or_default()
155 }
156
157 pub fn insight_rules_in(&self, region: &str) -> Option<&BTreeMap<String, InsightRule>> {
158 self.insight_rules.get(region)
159 }
160
161 pub fn insight_rules_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, InsightRule> {
162 self.insight_rules.entry(region.to_string()).or_default()
163 }
164
165 pub fn managed_rules_in(&self, region: &str) -> Option<&BTreeMap<String, Vec<ManagedRule>>> {
166 self.managed_rules.get(region)
167 }
168
169 pub fn managed_rules_in_mut(
170 &mut self,
171 region: &str,
172 ) -> &mut BTreeMap<String, Vec<ManagedRule>> {
173 self.managed_rules.entry(region.to_string()).or_default()
174 }
175
176 pub fn metric_streams_in(&self, region: &str) -> Option<&BTreeMap<String, MetricStream>> {
177 self.metric_streams.get(region)
178 }
179
180 pub fn metric_streams_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, MetricStream> {
181 self.metric_streams.entry(region.to_string()).or_default()
182 }
183
184 pub fn mute_rules_in(&self, region: &str) -> Option<&BTreeMap<String, AlarmMuteRule>> {
185 self.mute_rules.get(region)
186 }
187
188 pub fn mute_rules_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, AlarmMuteRule> {
189 self.mute_rules.entry(region.to_string()).or_default()
190 }
191
192 pub fn datasets_in(&self, region: &str) -> Option<&BTreeMap<String, Dataset>> {
193 self.datasets.get(region)
194 }
195
196 pub fn datasets_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, Dataset> {
197 self.datasets.entry(region.to_string()).or_default()
198 }
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct MetricDatum {
203 pub metric_name: String,
204 pub dimensions: BTreeMap<String, String>,
205 pub timestamp: DateTime<Utc>,
206 pub value: Option<f64>,
207 pub statistic_values: Option<StatisticSet>,
208 pub unit: Option<String>,
209 pub storage_resolution: Option<i64>,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct StatisticSet {
214 pub sample_count: f64,
215 pub sum: f64,
216 pub minimum: f64,
217 pub maximum: f64,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct MetricAlarm {
222 pub alarm_name: String,
223 pub alarm_arn: String,
224 pub alarm_description: Option<String>,
225 pub actions_enabled: bool,
226 pub ok_actions: Vec<String>,
227 pub alarm_actions: Vec<String>,
228 pub insufficient_data_actions: Vec<String>,
229 pub state_value: AlarmState,
230 pub state_reason: String,
231 pub state_updated_timestamp: DateTime<Utc>,
232 pub metric_name: Option<String>,
233 pub namespace: Option<String>,
234 pub statistic: Option<String>,
235 pub extended_statistic: Option<String>,
236 pub dimensions: BTreeMap<String, String>,
237 pub period: Option<i64>,
238 pub unit: Option<String>,
239 pub evaluation_periods: i64,
240 pub datapoints_to_alarm: Option<i64>,
241 pub threshold: Option<f64>,
242 pub comparison_operator: String,
243 pub treat_missing_data: Option<String>,
244 pub evaluate_low_sample_count_percentile: Option<String>,
245 #[serde(default)]
249 pub threshold_metric_id: Option<String>,
250 pub configuration_updated_timestamp: DateTime<Utc>,
251 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
252}
253
254#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
255pub enum AlarmState {
256 Ok,
257 Alarm,
258 InsufficientData,
259}
260
261impl AlarmState {
262 pub fn as_str(&self) -> &'static str {
263 match self {
264 AlarmState::Ok => "OK",
265 AlarmState::Alarm => "ALARM",
266 AlarmState::InsufficientData => "INSUFFICIENT_DATA",
267 }
268 }
269
270 pub fn parse(s: &str) -> Option<Self> {
271 match s {
272 "OK" => Some(AlarmState::Ok),
273 "ALARM" => Some(AlarmState::Alarm),
274 "INSUFFICIENT_DATA" => Some(AlarmState::InsufficientData),
275 _ => None,
276 }
277 }
278}
279
280#[derive(Debug, Clone, Serialize, Deserialize)]
282pub struct CompositeAlarm {
283 pub alarm_name: String,
284 pub alarm_arn: String,
285 pub alarm_description: Option<String>,
286 pub alarm_rule: String,
287 pub actions_enabled: bool,
288 pub ok_actions: Vec<String>,
289 pub alarm_actions: Vec<String>,
290 pub insufficient_data_actions: Vec<String>,
291 pub actions_suppressor: Option<String>,
292 pub actions_suppressor_wait_period: Option<i64>,
293 pub actions_suppressor_extension_period: Option<i64>,
294 pub state_value: AlarmState,
295 pub state_reason: String,
296 pub state_updated_timestamp: DateTime<Utc>,
297 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct AnomalyDetector {
303 pub key: String,
306 pub namespace: Option<String>,
307 pub metric_name: Option<String>,
308 pub stat: Option<String>,
309 pub dimensions: BTreeMap<String, String>,
310 pub metric_math: bool,
311 pub state_value: String,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct InsightRule {
317 pub name: String,
318 pub state: String,
319 pub schema: String,
320 pub definition: String,
321 pub managed: bool,
322 pub apply_on_transformed_logs: bool,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
327pub struct ManagedRule {
328 pub template_name: String,
329 pub resource_arn: String,
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct MetricStream {
335 pub name: String,
336 pub arn: String,
337 pub firehose_arn: String,
338 pub role_arn: String,
339 pub output_format: String,
340 pub state: String,
342 pub include_filters: Vec<MetricStreamFilter>,
343 pub exclude_filters: Vec<MetricStreamFilter>,
344 pub include_linked_accounts_metrics: bool,
345 pub creation_date: DateTime<Utc>,
346 pub last_update_date: DateTime<Utc>,
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct MetricStreamFilter {
351 pub namespace: Option<String>,
352 pub metric_names: Vec<String>,
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct AlarmMuteRule {
358 pub name: String,
359 pub arn: String,
360 pub description: Option<String>,
361 pub schedule_expression: Option<String>,
362 pub schedule_duration: Option<String>,
363 pub schedule_timezone: Option<String>,
364 pub mute_target_alarm_names: Vec<String>,
365 pub start_date: Option<DateTime<Utc>>,
366 pub expire_date: Option<DateTime<Utc>>,
367 pub last_updated_timestamp: DateTime<Utc>,
368}