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 #[serde(default)]
98 pub otel_enrichment: OTelEnrichmentConfig,
99 #[serde(default)]
101 pub resource_metrics_configurations:
102 BTreeMap<String, BTreeMap<String, ResourceMetricsConfiguration>>,
103}
104
105#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
108pub struct OTelMetricSelector {
109 pub namespace: String,
110 pub metric_names: Vec<String>,
111}
112
113#[derive(Debug, Clone, Default, Serialize, Deserialize)]
116pub struct OTelEnrichmentConfig {
117 #[serde(default)]
118 pub include_filters: Vec<OTelMetricSelector>,
119 #[serde(default)]
120 pub exclude_filters: Vec<OTelMetricSelector>,
121 #[serde(default)]
122 pub created_at: Option<DateTime<Utc>>,
123 #[serde(default)]
124 pub updated_at: Option<DateTime<Utc>>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct ResourceMetricsConfiguration {
131 pub resource_arn: String,
132 pub include_metrics: Option<Vec<String>>,
133 pub created_at: DateTime<Utc>,
134 pub updated_at: DateTime<Utc>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct Dashboard {
139 pub name: String,
140 pub arn: String,
141 pub body: String,
142 pub last_modified: DateTime<Utc>,
143 pub size_bytes: i64,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct Dataset {
151 pub id: String,
152 pub arn: String,
153 pub kms_key_arn: Option<String>,
154}
155
156impl CloudWatchState {
157 pub fn new(account_id: &str) -> Self {
158 Self {
159 account_id: account_id.to_string(),
160 ..Default::default()
161 }
162 }
163
164 pub fn metrics_in(&self, region: &str) -> Option<&BTreeMap<String, Vec<MetricDatum>>> {
165 self.metrics.get(region)
166 }
167
168 pub fn metrics_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, Vec<MetricDatum>> {
169 self.metrics.entry(region.to_string()).or_default()
170 }
171
172 pub fn alarms_in(&self, region: &str) -> Option<&BTreeMap<String, MetricAlarm>> {
173 self.alarms.get(region)
174 }
175
176 pub fn alarms_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, MetricAlarm> {
177 self.alarms.entry(region.to_string()).or_default()
178 }
179
180 pub fn composite_alarms_in(&self, region: &str) -> Option<&BTreeMap<String, CompositeAlarm>> {
181 self.composite_alarms.get(region)
182 }
183
184 pub fn composite_alarms_in_mut(
185 &mut self,
186 region: &str,
187 ) -> &mut BTreeMap<String, CompositeAlarm> {
188 self.composite_alarms.entry(region.to_string()).or_default()
189 }
190
191 pub fn log_alarms_in(&self, region: &str) -> Option<&BTreeMap<String, LogAlarm>> {
192 self.log_alarms.get(region)
193 }
194
195 pub fn log_alarms_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, LogAlarm> {
196 self.log_alarms.entry(region.to_string()).or_default()
197 }
198
199 pub fn alarm_history_in(
200 &self,
201 region: &str,
202 ) -> Option<&BTreeMap<String, Vec<AlarmHistoryItem>>> {
203 self.alarm_history.get(region)
204 }
205
206 pub fn alarm_history_in_mut(
207 &mut self,
208 region: &str,
209 ) -> &mut BTreeMap<String, Vec<AlarmHistoryItem>> {
210 self.alarm_history.entry(region.to_string()).or_default()
211 }
212
213 pub fn anomaly_detectors_in(&self, region: &str) -> Option<&BTreeMap<String, AnomalyDetector>> {
214 self.anomaly_detectors.get(region)
215 }
216
217 pub fn anomaly_detectors_in_mut(
218 &mut self,
219 region: &str,
220 ) -> &mut BTreeMap<String, AnomalyDetector> {
221 self.anomaly_detectors
222 .entry(region.to_string())
223 .or_default()
224 }
225
226 pub fn insight_rules_in(&self, region: &str) -> Option<&BTreeMap<String, InsightRule>> {
227 self.insight_rules.get(region)
228 }
229
230 pub fn insight_rules_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, InsightRule> {
231 self.insight_rules.entry(region.to_string()).or_default()
232 }
233
234 pub fn managed_rules_in(&self, region: &str) -> Option<&BTreeMap<String, Vec<ManagedRule>>> {
235 self.managed_rules.get(region)
236 }
237
238 pub fn managed_rules_in_mut(
239 &mut self,
240 region: &str,
241 ) -> &mut BTreeMap<String, Vec<ManagedRule>> {
242 self.managed_rules.entry(region.to_string()).or_default()
243 }
244
245 pub fn metric_streams_in(&self, region: &str) -> Option<&BTreeMap<String, MetricStream>> {
246 self.metric_streams.get(region)
247 }
248
249 pub fn metric_streams_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, MetricStream> {
250 self.metric_streams.entry(region.to_string()).or_default()
251 }
252
253 pub fn mute_rules_in(&self, region: &str) -> Option<&BTreeMap<String, AlarmMuteRule>> {
254 self.mute_rules.get(region)
255 }
256
257 pub fn mute_rules_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, AlarmMuteRule> {
258 self.mute_rules.entry(region.to_string()).or_default()
259 }
260
261 pub fn resource_metrics_in(
262 &self,
263 region: &str,
264 ) -> Option<&BTreeMap<String, ResourceMetricsConfiguration>> {
265 self.resource_metrics_configurations.get(region)
266 }
267
268 pub fn resource_metrics_in_mut(
269 &mut self,
270 region: &str,
271 ) -> &mut BTreeMap<String, ResourceMetricsConfiguration> {
272 self.resource_metrics_configurations
273 .entry(region.to_string())
274 .or_default()
275 }
276
277 pub fn datasets_in(&self, region: &str) -> Option<&BTreeMap<String, Dataset>> {
278 self.datasets.get(region)
279 }
280
281 pub fn datasets_in_mut(&mut self, region: &str) -> &mut BTreeMap<String, Dataset> {
282 self.datasets.entry(region.to_string()).or_default()
283 }
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct MetricDatum {
288 pub metric_name: String,
289 pub dimensions: BTreeMap<String, String>,
290 pub timestamp: DateTime<Utc>,
291 pub value: Option<f64>,
292 pub statistic_values: Option<StatisticSet>,
293 pub unit: Option<String>,
294 pub storage_resolution: Option<i64>,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct StatisticSet {
299 pub sample_count: f64,
300 pub sum: f64,
301 pub minimum: f64,
302 pub maximum: f64,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct MetricAlarm {
307 pub alarm_name: String,
308 pub alarm_arn: String,
309 pub alarm_description: Option<String>,
310 pub actions_enabled: bool,
311 pub ok_actions: Vec<String>,
312 pub alarm_actions: Vec<String>,
313 pub insufficient_data_actions: Vec<String>,
314 pub state_value: AlarmState,
315 pub state_reason: String,
316 pub state_updated_timestamp: DateTime<Utc>,
317 pub metric_name: Option<String>,
318 pub namespace: Option<String>,
319 pub statistic: Option<String>,
320 pub extended_statistic: Option<String>,
321 pub dimensions: BTreeMap<String, String>,
322 pub period: Option<i64>,
323 pub unit: Option<String>,
324 pub evaluation_periods: i64,
325 pub datapoints_to_alarm: Option<i64>,
326 pub threshold: Option<f64>,
327 pub comparison_operator: String,
328 pub treat_missing_data: Option<String>,
329 pub evaluate_low_sample_count_percentile: Option<String>,
330 #[serde(default)]
334 pub threshold_metric_id: Option<String>,
335 pub configuration_updated_timestamp: DateTime<Utc>,
336 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
337 #[serde(default)]
341 pub metrics: Vec<AlarmMetricQuery>,
342 #[serde(default)]
349 pub state_manually_set: bool,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize, Default)]
354pub struct AlarmMetricQuery {
355 pub id: String,
356 #[serde(default)]
357 pub metric_stat: Option<AlarmMetricStat>,
358 #[serde(default)]
359 pub expression: Option<String>,
360 #[serde(default)]
361 pub label: Option<String>,
362 #[serde(default)]
363 pub return_data: Option<bool>,
364 #[serde(default)]
365 pub account_id: Option<String>,
366 #[serde(default)]
367 pub period: Option<i64>,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize, Default)]
373pub struct AlarmMetricStat {
374 #[serde(default)]
375 pub namespace: Option<String>,
376 #[serde(default)]
377 pub metric_name: Option<String>,
378 #[serde(default)]
379 pub dimensions: BTreeMap<String, String>,
380 #[serde(default)]
381 pub period: Option<i64>,
382 #[serde(default)]
383 pub stat: Option<String>,
384 #[serde(default)]
385 pub unit: Option<String>,
386}
387
388#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
389pub enum AlarmState {
390 Ok,
391 Alarm,
392 InsufficientData,
393}
394
395impl AlarmState {
396 pub fn as_str(&self) -> &'static str {
397 match self {
398 AlarmState::Ok => "OK",
399 AlarmState::Alarm => "ALARM",
400 AlarmState::InsufficientData => "INSUFFICIENT_DATA",
401 }
402 }
403
404 pub fn parse(s: &str) -> Option<Self> {
405 match s {
406 "OK" => Some(AlarmState::Ok),
407 "ALARM" => Some(AlarmState::Alarm),
408 "INSUFFICIENT_DATA" => Some(AlarmState::InsufficientData),
409 _ => None,
410 }
411 }
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct AlarmHistoryItem {
417 pub alarm_name: String,
418 pub alarm_type: String,
420 pub timestamp: DateTime<Utc>,
421 pub history_item_type: String,
424 pub history_summary: String,
425 pub history_data: String,
427}
428
429pub fn alarm_arn(region: &str, account_id: &str, name: &str) -> String {
432 fakecloud_aws::arn::Arn::regional("cloudwatch", region, account_id, &format!("alarm:{name}"))
433 .to_string()
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
438pub struct CompositeAlarm {
439 pub alarm_name: String,
440 pub alarm_arn: String,
441 pub alarm_description: Option<String>,
442 pub alarm_rule: String,
443 pub actions_enabled: bool,
444 pub ok_actions: Vec<String>,
445 pub alarm_actions: Vec<String>,
446 pub insufficient_data_actions: Vec<String>,
447 pub actions_suppressor: Option<String>,
448 pub actions_suppressor_wait_period: Option<i64>,
449 pub actions_suppressor_extension_period: Option<i64>,
450 pub state_value: AlarmState,
451 pub state_reason: String,
452 pub state_updated_timestamp: DateTime<Utc>,
453 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct LogAlarm {
461 pub alarm_name: String,
462 pub alarm_arn: String,
463 pub alarm_description: Option<String>,
464 pub query_string: String,
466 pub scheduled_query_role_arn: String,
467 pub schedule_expression: Option<String>,
468 pub schedule_start_time_offset: Option<i64>,
469 pub schedule_end_time_offset: Option<i64>,
470 pub aggregation_expression: String,
471 pub log_group_identifiers: Vec<String>,
472 pub query_arn: Option<String>,
473 pub query_results_to_evaluate: i64,
474 pub query_results_to_alarm: i64,
475 pub threshold: f64,
476 pub comparison_operator: String,
477 pub treat_missing_data: Option<String>,
478 pub action_log_line_count: Option<i64>,
479 pub action_log_line_role_arn: Option<String>,
480 pub actions_enabled: bool,
481 pub ok_actions: Vec<String>,
482 pub alarm_actions: Vec<String>,
483 pub insufficient_data_actions: Vec<String>,
484 pub state_value: AlarmState,
485 pub state_reason: String,
486 pub state_updated_timestamp: DateTime<Utc>,
487 pub alarm_configuration_updated_timestamp: DateTime<Utc>,
488}
489
490#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct AnomalyDetector {
493 pub key: String,
496 pub namespace: Option<String>,
497 pub metric_name: Option<String>,
498 pub stat: Option<String>,
499 pub dimensions: BTreeMap<String, String>,
500 pub metric_math: bool,
501 pub state_value: String,
502 #[serde(default)]
505 pub configuration: Option<AnomalyDetectorConfiguration>,
506 #[serde(default)]
508 pub periodic_spikes: Option<bool>,
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize, Default)]
513pub struct AnomalyDetectorConfiguration {
514 #[serde(default)]
515 pub excluded_time_ranges: Vec<ExcludedTimeRange>,
516 #[serde(default)]
517 pub metric_timezone: Option<String>,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize, Default)]
522pub struct ExcludedTimeRange {
523 #[serde(default)]
524 pub start_time: Option<String>,
525 #[serde(default)]
526 pub end_time: Option<String>,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct InsightRule {
532 pub name: String,
533 pub state: String,
534 pub schema: String,
535 pub definition: String,
536 pub managed: bool,
537 pub apply_on_transformed_logs: bool,
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct ManagedRule {
543 pub template_name: String,
544 pub resource_arn: String,
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize)]
549pub struct MetricStream {
550 pub name: String,
551 pub arn: String,
552 pub firehose_arn: String,
553 pub role_arn: String,
554 pub output_format: String,
555 pub state: String,
557 pub include_filters: Vec<MetricStreamFilter>,
558 pub exclude_filters: Vec<MetricStreamFilter>,
559 pub include_linked_accounts_metrics: bool,
560 pub creation_date: DateTime<Utc>,
561 pub last_update_date: DateTime<Utc>,
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize)]
565pub struct MetricStreamFilter {
566 pub namespace: Option<String>,
567 pub metric_names: Vec<String>,
568}
569
570#[derive(Debug, Clone, Serialize, Deserialize)]
572pub struct AlarmMuteRule {
573 pub name: String,
574 pub arn: String,
575 pub description: Option<String>,
576 pub schedule_expression: Option<String>,
577 pub schedule_duration: Option<String>,
578 pub schedule_timezone: Option<String>,
579 pub mute_target_alarm_names: Vec<String>,
580 pub start_date: Option<DateTime<Utc>>,
581 pub expire_date: Option<DateTime<Utc>>,
582 pub last_updated_timestamp: DateTime<Utc>,
583}