1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use parking_lot::RwLock;
5
6pub type SharedLogsState = Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<LogsState>>>;
7
8impl fakecloud_core::multi_account::AccountState for LogsState {
9 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
10 Self::new(account_id, region)
11 }
12}
13
14mod account_policy_map_serde {
18 use super::AccountPolicy;
19 use serde::{Deserialize, Deserializer, Serialize, Serializer};
20 use std::collections::BTreeMap;
21
22 pub fn serialize<S: Serializer>(
23 map: &BTreeMap<(String, String), AccountPolicy>,
24 s: S,
25 ) -> Result<S::Ok, S::Error> {
26 let entries: Vec<(&String, &String, &AccountPolicy)> = map
27 .iter()
28 .map(|((name, kind), p)| (name, kind, p))
29 .collect();
30 entries.serialize(s)
31 }
32
33 pub fn deserialize<'de, D: Deserializer<'de>>(
34 d: D,
35 ) -> Result<BTreeMap<(String, String), AccountPolicy>, D::Error> {
36 let entries: Vec<(String, String, AccountPolicy)> = Vec::deserialize(d)?;
37 Ok(entries
38 .into_iter()
39 .map(|(name, kind, p)| ((name, kind), p))
40 .collect())
41 }
42}
43
44#[derive(Clone, serde::Serialize, serde::Deserialize)]
45pub struct LogsState {
46 pub account_id: String,
47 pub region: String,
48 pub log_groups: BTreeMap<String, LogGroup>,
49 pub metric_filters: Vec<MetricFilter>,
50 pub resource_policies: BTreeMap<String, ResourcePolicy>,
51 pub destinations: BTreeMap<String, Destination>,
52 pub queries: BTreeMap<String, QueryInfo>,
53 pub export_tasks: Vec<ExportTask>,
54 pub delivery_destinations: BTreeMap<String, DeliveryDestination>,
55 pub delivery_sources: BTreeMap<String, DeliverySource>,
56 pub deliveries: BTreeMap<String, Delivery>,
57 pub query_definitions: BTreeMap<String, QueryDefinition>,
58 #[serde(with = "account_policy_map_serde")]
60 pub account_policies: BTreeMap<(String, String), AccountPolicy>,
61 pub anomaly_detectors: BTreeMap<String, AnomalyDetector>,
63 pub import_tasks: BTreeMap<String, ImportTask>,
65 pub integrations: BTreeMap<String, Integration>,
67 pub lookup_tables: BTreeMap<String, LookupTable>,
69 pub scheduled_queries: BTreeMap<String, ScheduledQuery>,
71 pub s3_table_sources: BTreeMap<String, Vec<String>>,
73 pub bearer_token_auth: BTreeMap<String, bool>,
75 pub export_storage: BTreeMap<String, Vec<u8>>,
78 #[serde(default)]
82 pub anomalies: BTreeMap<String, LogAnomaly>,
83}
84
85#[derive(Clone, serde::Serialize, serde::Deserialize)]
86pub struct LogAnomaly {
87 pub anomaly_id: String,
88 pub anomaly_detector_arn: String,
89 pub log_group_arn_list: Vec<String>,
90 pub pattern_id: String,
91 pub pattern_string: String,
92 pub first_seen: i64,
93 pub last_seen: i64,
94 pub priority: String,
95 pub state: String,
96 pub suppressed: bool,
97}
98
99impl LogsState {
100 pub fn new(account_id: &str, region: &str) -> Self {
101 Self {
102 account_id: account_id.to_string(),
103 region: region.to_string(),
104 log_groups: BTreeMap::new(),
105 metric_filters: Vec::new(),
106 resource_policies: BTreeMap::new(),
107 destinations: BTreeMap::new(),
108 queries: BTreeMap::new(),
109 export_tasks: Vec::new(),
110 delivery_destinations: BTreeMap::new(),
111 delivery_sources: BTreeMap::new(),
112 deliveries: BTreeMap::new(),
113 query_definitions: BTreeMap::new(),
114 account_policies: BTreeMap::new(),
115 anomaly_detectors: BTreeMap::new(),
116 import_tasks: BTreeMap::new(),
117 integrations: BTreeMap::new(),
118 lookup_tables: BTreeMap::new(),
119 scheduled_queries: BTreeMap::new(),
120 s3_table_sources: BTreeMap::new(),
121 bearer_token_auth: BTreeMap::new(),
122 export_storage: BTreeMap::new(),
123 anomalies: BTreeMap::new(),
124 }
125 }
126
127 pub fn reset(&mut self) {
128 self.log_groups.clear();
129 self.metric_filters.clear();
130 self.resource_policies.clear();
131 self.destinations.clear();
132 self.queries.clear();
133 self.export_tasks.clear();
134 self.delivery_destinations.clear();
135 self.delivery_sources.clear();
136 self.deliveries.clear();
137 self.query_definitions.clear();
138 self.account_policies.clear();
139 self.anomaly_detectors.clear();
140 self.import_tasks.clear();
141 self.integrations.clear();
142 self.lookup_tables.clear();
143 self.scheduled_queries.clear();
144 self.s3_table_sources.clear();
145 self.bearer_token_auth.clear();
146 self.export_storage.clear();
147 self.anomalies.clear();
148 }
149}
150
151#[derive(Clone, serde::Serialize, serde::Deserialize)]
152pub struct LogGroup {
153 pub name: String,
154 pub arn: String,
155 pub creation_time: i64,
156 pub retention_in_days: Option<i32>,
157 pub kms_key_id: Option<String>,
158 pub tags: BTreeMap<String, String>,
159 pub log_streams: BTreeMap<String, LogStream>,
160 pub stored_bytes: i64,
161 pub subscription_filters: Vec<SubscriptionFilter>,
162 pub data_protection_policy: Option<DataProtectionPolicy>,
163 pub index_policies: Vec<IndexPolicy>,
164 pub transformer: Option<Transformer>,
165 pub deletion_protection: bool,
166 pub log_group_class: Option<String>,
170}
171
172#[derive(Clone, serde::Serialize, serde::Deserialize)]
173pub struct LogStream {
174 pub name: String,
175 pub arn: String,
176 pub creation_time: i64,
177 pub first_event_timestamp: Option<i64>,
178 pub last_event_timestamp: Option<i64>,
179 pub last_ingestion_time: Option<i64>,
180 pub upload_sequence_token: String,
181 pub events: Vec<LogEvent>,
182}
183
184#[derive(Clone, serde::Serialize, serde::Deserialize)]
185pub struct LogEvent {
186 pub timestamp: i64,
187 pub message: String,
188 pub ingestion_time: i64,
189}
190
191#[derive(Clone, serde::Serialize, serde::Deserialize)]
192pub struct SubscriptionFilter {
193 pub filter_name: String,
194 pub log_group_name: String,
195 pub filter_pattern: String,
196 pub destination_arn: String,
197 pub role_arn: Option<String>,
198 pub distribution: String,
199 pub creation_time: i64,
200}
201
202#[derive(Clone, serde::Serialize, serde::Deserialize)]
203pub struct MetricFilter {
204 pub filter_name: String,
205 pub filter_pattern: String,
206 pub log_group_name: String,
207 pub metric_transformations: Vec<MetricTransformation>,
208 pub creation_time: i64,
209}
210
211#[derive(Clone, serde::Serialize, serde::Deserialize)]
212pub struct MetricTransformation {
213 pub metric_name: String,
214 pub metric_namespace: String,
215 pub metric_value: String,
216 pub default_value: Option<f64>,
217 #[serde(default)]
221 pub unit: Option<String>,
222}
223
224#[derive(Clone, serde::Serialize, serde::Deserialize)]
225pub struct ResourcePolicy {
226 pub policy_name: String,
227 pub policy_document: String,
228 pub last_updated_time: i64,
229}
230
231#[derive(Clone, serde::Serialize, serde::Deserialize)]
232pub struct Destination {
233 pub destination_name: String,
234 pub target_arn: String,
235 pub role_arn: String,
236 pub arn: String,
237 pub access_policy: Option<String>,
238 pub creation_time: i64,
239 pub tags: BTreeMap<String, String>,
240}
241
242#[derive(Clone, serde::Serialize, serde::Deserialize)]
243pub struct QueryInfo {
244 pub query_id: String,
245 pub log_group_name: String,
246 #[serde(default)]
251 pub log_group_identifiers: Vec<String>,
252 pub query_string: String,
253 pub start_time: i64,
254 pub end_time: i64,
255 pub status: String,
256 pub create_time: i64,
257}
258
259#[derive(Clone, serde::Serialize, serde::Deserialize)]
260pub struct ExportTask {
261 pub task_id: String,
262 pub task_name: Option<String>,
263 pub log_group_name: String,
264 pub log_stream_name_prefix: Option<String>,
265 pub from_time: i64,
266 pub to_time: i64,
267 pub destination: String,
268 pub destination_prefix: String,
269 pub status_code: String,
270 pub status_message: String,
271 #[serde(default)]
272 pub creation_time: i64,
273 #[serde(default)]
274 pub completion_time: Option<i64>,
275}
276
277#[derive(Clone, serde::Serialize, serde::Deserialize)]
278pub struct DeliveryDestination {
279 pub name: String,
280 pub arn: String,
281 pub output_format: Option<String>,
282 pub delivery_destination_configuration: BTreeMap<String, String>,
283 pub tags: BTreeMap<String, String>,
284 pub delivery_destination_policy: Option<String>,
285}
286
287#[derive(Clone, serde::Serialize, serde::Deserialize)]
288pub struct DeliverySource {
289 pub name: String,
290 pub arn: String,
291 pub resource_arns: Vec<String>,
292 pub service: String,
293 pub log_type: String,
294 pub tags: BTreeMap<String, String>,
295 #[serde(default)]
296 pub created_at: i64,
297}
298
299#[derive(Clone, serde::Serialize, serde::Deserialize)]
300pub struct Delivery {
301 pub id: String,
302 pub delivery_source_name: String,
303 pub delivery_destination_arn: String,
304 pub delivery_destination_type: String,
305 pub arn: String,
306 pub tags: BTreeMap<String, String>,
307 #[serde(default)]
308 pub field_delimiter: Option<String>,
309 #[serde(default)]
310 pub record_fields: Vec<String>,
311 #[serde(default)]
312 pub s3_delivery_configuration: Option<serde_json::Value>,
313 #[serde(default)]
314 pub created_at: i64,
315}
316
317#[derive(Clone, serde::Serialize, serde::Deserialize)]
318pub struct QueryDefinition {
319 pub query_definition_id: String,
320 pub name: String,
321 pub query_string: String,
322 pub log_group_names: Vec<String>,
323 pub last_modified: i64,
324}
325
326#[derive(Clone, serde::Serialize, serde::Deserialize)]
327pub struct AccountPolicy {
328 pub policy_name: String,
329 pub policy_type: String,
330 pub policy_document: String,
331 pub scope: Option<String>,
332 pub selection_criteria: Option<String>,
333 pub account_id: String,
334 pub last_updated_time: i64,
335}
336
337#[derive(Clone, serde::Serialize, serde::Deserialize)]
338pub struct DataProtectionPolicy {
339 pub policy_document: String,
340 pub last_updated_time: i64,
341}
342
343#[derive(Clone, serde::Serialize, serde::Deserialize)]
344pub struct IndexPolicy {
345 pub policy_name: String,
346 pub policy_document: String,
347 pub last_updated_time: i64,
348}
349
350#[derive(Clone, serde::Serialize, serde::Deserialize)]
351pub struct Transformer {
352 pub transformer_config: serde_json::Value,
353 pub creation_time: i64,
354 pub last_modified_time: i64,
355}
356
357#[derive(Clone, serde::Serialize, serde::Deserialize)]
358pub struct AnomalyDetector {
359 pub detector_name: String,
360 pub arn: String,
361 pub log_group_arn_list: Vec<String>,
362 pub evaluation_frequency: Option<String>,
363 pub filter_pattern: Option<String>,
364 pub anomaly_visibility_time: Option<i64>,
365 pub creation_time: i64,
366 pub last_modified_time: i64,
367 pub enabled: bool,
368}
369
370#[derive(Clone, serde::Serialize, serde::Deserialize)]
371pub struct ImportTask {
372 pub import_id: String,
373 pub import_source_arn: String,
374 pub import_role_arn: String,
375 pub log_group_name: Option<String>,
376 pub status: String,
377 pub creation_time: i64,
378}
379
380#[derive(Clone, serde::Serialize, serde::Deserialize)]
381pub struct Integration {
382 pub integration_name: String,
383 pub integration_type: String,
384 pub resource_config: serde_json::Value,
385 pub status: String,
386 pub creation_time: i64,
387}
388
389#[derive(Clone, serde::Serialize, serde::Deserialize)]
390pub struct LookupTable {
391 pub lookup_table_name: String,
392 pub arn: String,
393 pub table_body: String,
394 pub creation_time: i64,
395 pub last_modified_time: i64,
396}
397
398#[derive(Clone, serde::Serialize, serde::Deserialize)]
399pub struct ScheduledQuery {
400 pub name: String,
401 pub arn: String,
402 pub query_string: String,
403 pub query_language: String,
404 pub schedule_expression: String,
405 pub execution_role_arn: String,
406 pub status: String,
407 pub creation_time: i64,
408 pub last_modified_time: i64,
409}
410
411#[derive(Clone, serde::Serialize, serde::Deserialize)]
414pub struct LogsSnapshot {
415 pub schema_version: u32,
416 #[serde(default)]
417 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<LogsState>>,
418 #[serde(default)]
419 pub state: Option<LogsState>,
420}
421
422pub const LOGS_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
423
424#[cfg(test)]
425mod tests {
426 use super::*;
427
428 #[test]
429 fn new_initializes_empty() {
430 let state = LogsState::new("123456789012", "us-east-1");
431 assert_eq!(state.account_id, "123456789012");
432 assert_eq!(state.region, "us-east-1");
433 assert!(state.log_groups.is_empty());
434 assert!(state.queries.is_empty());
435 }
436
437 #[test]
438 fn reset_clears_state() {
439 let mut state = LogsState::new("123456789012", "us-east-1");
440 state.bearer_token_auth.insert("g".to_string(), true);
441 state.reset();
442 assert!(state.bearer_token_auth.is_empty());
443 }
444}