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)]
62 pub log_alarms: BTreeMap<String, BTreeMap<String, LogAlarm>>,
63 #[serde(default)]
67 pub alarm_history: BTreeMap<String, BTreeMap<String, Vec<AlarmHistoryItem>>>,
68 #[serde(default)]
71 pub dashboards: BTreeMap<String, Dashboard>,
72 #[serde(default)]
74 pub anomaly_detectors: BTreeMap<String, BTreeMap<String, AnomalyDetector>>,
75 #[serde(default)]
77 pub insight_rules: BTreeMap<String, BTreeMap<String, InsightRule>>,
78 #[serde(default)]
80 pub managed_rules: BTreeMap<String, BTreeMap<String, Vec<ManagedRule>>>,
81 #[serde(default)]
83 pub metric_streams: BTreeMap<String, BTreeMap<String, MetricStream>>,
84 #[serde(default)]
86 pub mute_rules: BTreeMap<String, BTreeMap<String, AlarmMuteRule>>,
87 #[serde(default)]
89 pub datasets: BTreeMap<String, BTreeMap<String, Dataset>>,
90 #[serde(default)]
92 pub tags: BTreeMap<String, BTreeMap<String, String>>,
93 #[serde(default)]
95 pub otel_enrichment_running: bool,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct Dashboard {
100 pub name: String,
101 pub arn: String,
102 pub body: String,
103 pub last_modified: DateTime<Utc>,
104 pub size_bytes: i64,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct Dataset {
112 pub id: String,
113 pub arn: String,
114 pub kms_key_arn: Option<String>,
115}
116
117impl CloudWatchState {
118 pub fn new(account_id: &str) -> Self {
119 Self {
120 account_id: account_id.to_string(),
121 ..Default::default()
122 }
123 }
124
125 pub fn metrics_in(&self, region: &str) -> Option<&BTreeMap<String, Vec<MetricDatum>>> {
126 self.metrics.get(region)
127 }
128
129 pub fn metrics_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, Vec<MetricDatum>> {
130 self.metrics.entry(region.to_string()).or_default()
131 }
132
133 pub fn alarms_in(&self, region: &str) -> Option<&BTreeMap<String, MetricAlarm>> {
134 self.alarms.get(region)
135 }
136
137 pub fn alarms_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, MetricAlarm> {
138 self.alarms.entry(region.to_string()).or_default()
139 }
140
141 pub fn composite_alarms_in(&self, region: &str) -> Option<&BTreeMap<String, CompositeAlarm>> {
142 self.composite_alarms.get(region)
143 }
144
145 pub fn composite_alarms_in_mut(
146 &mut self,
147 region: &str,
148 ) -> &mut BTreeMap<String, CompositeAlarm> {
149 self.composite_alarms.entry(region.to_string()).or_default()
150 }
151
152 pub fn log_alarms_in(&self, region: &str) -> Option<&BTreeMap<String, LogAlarm>> {
153 self.log_alarms.get(region)
154 }
155
156 pub fn log_alarms_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, LogAlarm> {
157 self.log_alarms.entry(region.to_string()).or_default()
158 }
159
160 pub fn alarm_history_in(
161 &self,
162 region: &str,
163 ) -> Option<&BTreeMap<String, Vec<AlarmHistoryItem>>> {
164 self.alarm_history.get(region)
165 }
166
167 pub fn alarm_history_in_mut(
168 &mut self,
169 region: &str,
170 ) -> &mut BTreeMap<String, Vec<AlarmHistoryItem>> {
171 self.alarm_history.entry(region.to_string()).or_default()
172 }
173
174 pub fn anomaly_detectors_in(&self, region: &str) -> Option<&BTreeMap<String, AnomalyDetector>> {
175 self.anomaly_detectors.get(region)
176 }
177
178 pub fn anomaly_detectors_in_mut(
179 &mut self,
180 region: &str,
181 ) -> &mut BTreeMap<String, AnomalyDetector> {
182 self.anomaly_detectors
183 .entry(region.to_string())
184 .or_default()
185 }
186
187 pub fn insight_rules_in(&self, region: &str) -> Option<&BTreeMap<String, InsightRule>> {
188 self.insight_rules.get(region)
189 }
190
191 pub fn insight_rules_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, InsightRule> {
192 self.insight_rules.entry(region.to_string()).or_default()
193 }
194
195 pub fn managed_rules_in(&self, region: &str) -> Option<&BTreeMap<String, Vec<ManagedRule>>> {
196 self.managed_rules.get(region)
197 }
198
199 pub fn managed_rules_in_mut(
200 &mut self,
201 region: &str,
202 ) -> &mut BTreeMap<String, Vec<ManagedRule>> {
203 self.managed_rules.entry(region.to_string()).or_default()
204 }
205
206 pub fn metric_streams_in(&self, region: &str) -> Option<&BTreeMap<String, MetricStream>> {
207 self.metric_streams.get(region)
208 }
209
210 pub fn metric_streams_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, MetricStream> {
211 self.metric_streams.entry(region.to_string()).or_default()
212 }
213
214 pub fn mute_rules_in(&self, region: &str) -> Option<&BTreeMap<String, AlarmMuteRule>> {
215 self.mute_rules.get(region)
216 }
217
218 pub fn mute_rules_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, AlarmMuteRule> {
219 self.mute_rules.entry(region.to_string()).or_default()
220 }
221
222 pub fn datasets_in(&self, region: &str) -> Option<&BTreeMap<String, Dataset>> {
223 self.datasets.get(region)
224 }
225
226 pub fn datasets_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, Dataset> {
227 self.datasets.entry(region.to_string()).or_default()
228 }
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct MetricDatum {
233 pub metric_name: String,
234 pub dimensions: BTreeMap<String, String>,
235 pub timestamp: DateTime<Utc>,
236 pub value: Option<f64>,
237 pub statistic_values: Option<StatisticSet>,
238 pub unit: Option<String>,
239 pub storage_resolution: Option<i64>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct StatisticSet {
244 pub sample_count: f64,
245 pub sum: f64,
246 pub minimum: f64,
247 pub maximum: f64,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct MetricAlarm {
252 pub alarm_name: String,
253 pub alarm_arn: String,
254 pub alarm_description: Option<String>,
255 pub actions_enabled: bool,
256 pub ok_actions: Vec<String>,
257 pub alarm_actions: Vec<String>,
258 pub insufficient_data_actions: Vec<String>,
259 pub state_value: AlarmState,
260 pub state_reason: String,
261 pub state_updated_timestamp: DateTime<Utc>,
262 pub metric_name: Option<String>,
263 pub namespace: Option<String>,
264 pub statistic: Option<String>,
265 pub extended_statistic: Option<String>,
266 pub dimensions: BTreeMap<String, String>,
267 pub period: Option<i64>,
268 pub unit: Option<String>,
269 pub evaluation_periods: i64,
270 pub datapoints_to_alarm: Option<i64>,
271 pub threshold: Option<f64>,
272 pub comparison_operator: String,
273 pub treat_missing_data: Option<String>,
274 pub evaluate_low_sample_count_percentile: Option<String>,
275 #[serde(default)]
279 pub threshold_metric_id: Option<String>,
280 pub configuration_updated_timestamp: DateTime<Utc>,
281 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
282 #[serde(default)]
286 pub metrics: Vec<AlarmMetricQuery>,
287 #[serde(default)]
294 pub state_manually_set: bool,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize, Default)]
299pub struct AlarmMetricQuery {
300 pub id: String,
301 #[serde(default)]
302 pub metric_stat: Option<AlarmMetricStat>,
303 #[serde(default)]
304 pub expression: Option<String>,
305 #[serde(default)]
306 pub label: Option<String>,
307 #[serde(default)]
308 pub return_data: Option<bool>,
309 #[serde(default)]
310 pub account_id: Option<String>,
311 #[serde(default)]
312 pub period: Option<i64>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize, Default)]
318pub struct AlarmMetricStat {
319 #[serde(default)]
320 pub namespace: Option<String>,
321 #[serde(default)]
322 pub metric_name: Option<String>,
323 #[serde(default)]
324 pub dimensions: BTreeMap<String, String>,
325 #[serde(default)]
326 pub period: Option<i64>,
327 #[serde(default)]
328 pub stat: Option<String>,
329 #[serde(default)]
330 pub unit: Option<String>,
331}
332
333#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
334pub enum AlarmState {
335 Ok,
336 Alarm,
337 InsufficientData,
338}
339
340impl AlarmState {
341 pub fn as_str(&self) -> &'static str {
342 match self {
343 AlarmState::Ok => "OK",
344 AlarmState::Alarm => "ALARM",
345 AlarmState::InsufficientData => "INSUFFICIENT_DATA",
346 }
347 }
348
349 pub fn parse(s: &str) -> Option<Self> {
350 match s {
351 "OK" => Some(AlarmState::Ok),
352 "ALARM" => Some(AlarmState::Alarm),
353 "INSUFFICIENT_DATA" => Some(AlarmState::InsufficientData),
354 _ => None,
355 }
356 }
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
361pub struct AlarmHistoryItem {
362 pub alarm_name: String,
363 pub alarm_type: String,
365 pub timestamp: DateTime<Utc>,
366 pub history_item_type: String,
369 pub history_summary: String,
370 pub history_data: String,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct CompositeAlarm {
377 pub alarm_name: String,
378 pub alarm_arn: String,
379 pub alarm_description: Option<String>,
380 pub alarm_rule: String,
381 pub actions_enabled: bool,
382 pub ok_actions: Vec<String>,
383 pub alarm_actions: Vec<String>,
384 pub insufficient_data_actions: Vec<String>,
385 pub actions_suppressor: Option<String>,
386 pub actions_suppressor_wait_period: Option<i64>,
387 pub actions_suppressor_extension_period: Option<i64>,
388 pub state_value: AlarmState,
389 pub state_reason: String,
390 pub state_updated_timestamp: DateTime<Utc>,
391 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize)]
398pub struct LogAlarm {
399 pub alarm_name: String,
400 pub alarm_arn: String,
401 pub alarm_description: Option<String>,
402 pub query_string: String,
404 pub scheduled_query_role_arn: String,
405 pub schedule_expression: Option<String>,
406 pub schedule_start_time_offset: Option<i64>,
407 pub schedule_end_time_offset: Option<i64>,
408 pub aggregation_expression: String,
409 pub log_group_identifiers: Vec<String>,
410 pub query_arn: Option<String>,
411 pub query_results_to_evaluate: i64,
412 pub query_results_to_alarm: i64,
413 pub threshold: f64,
414 pub comparison_operator: String,
415 pub treat_missing_data: Option<String>,
416 pub action_log_line_count: Option<i64>,
417 pub action_log_line_role_arn: Option<String>,
418 pub actions_enabled: bool,
419 pub ok_actions: Vec<String>,
420 pub alarm_actions: Vec<String>,
421 pub insufficient_data_actions: Vec<String>,
422 pub state_value: AlarmState,
423 pub state_reason: String,
424 pub state_updated_timestamp: DateTime<Utc>,
425 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct AnomalyDetector {
431 pub key: String,
434 pub namespace: Option<String>,
435 pub metric_name: Option<String>,
436 pub stat: Option<String>,
437 pub dimensions: BTreeMap<String, String>,
438 pub metric_math: bool,
439 pub state_value: String,
440 #[serde(default)]
443 pub configuration: Option<AnomalyDetectorConfiguration>,
444 #[serde(default)]
446 pub periodic_spikes: Option<bool>,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize, Default)]
451pub struct AnomalyDetectorConfiguration {
452 #[serde(default)]
453 pub excluded_time_ranges: Vec<ExcludedTimeRange>,
454 #[serde(default)]
455 pub metric_timezone: Option<String>,
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize, Default)]
460pub struct ExcludedTimeRange {
461 #[serde(default)]
462 pub start_time: Option<String>,
463 #[serde(default)]
464 pub end_time: Option<String>,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct InsightRule {
470 pub name: String,
471 pub state: String,
472 pub schema: String,
473 pub definition: String,
474 pub managed: bool,
475 pub apply_on_transformed_logs: bool,
476}
477
478#[derive(Debug, Clone, Serialize, Deserialize)]
480pub struct ManagedRule {
481 pub template_name: String,
482 pub resource_arn: String,
483}
484
485#[derive(Debug, Clone, Serialize, Deserialize)]
487pub struct MetricStream {
488 pub name: String,
489 pub arn: String,
490 pub firehose_arn: String,
491 pub role_arn: String,
492 pub output_format: String,
493 pub state: String,
495 pub include_filters: Vec<MetricStreamFilter>,
496 pub exclude_filters: Vec<MetricStreamFilter>,
497 pub include_linked_accounts_metrics: bool,
498 pub creation_date: DateTime<Utc>,
499 pub last_update_date: DateTime<Utc>,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct MetricStreamFilter {
504 pub namespace: Option<String>,
505 pub metric_names: Vec<String>,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct AlarmMuteRule {
511 pub name: String,
512 pub arn: String,
513 pub description: Option<String>,
514 pub schedule_expression: Option<String>,
515 pub schedule_duration: Option<String>,
516 pub schedule_timezone: Option<String>,
517 pub mute_target_alarm_names: Vec<String>,
518 pub start_date: Option<DateTime<Utc>>,
519 pub expire_date: Option<DateTime<Utc>>,
520 pub last_updated_timestamp: DateTime<Utc>,
521}