fakecloud_cloudformation/
state.rs1use chrono::{DateTime, Utc};
2use parking_lot::RwLock;
3use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct StackResource {
9 pub logical_id: String,
10 pub physical_id: String,
11 pub resource_type: String,
12 pub status: String,
13 pub service_token: Option<String>,
15 #[serde(default)]
18 pub attributes: BTreeMap<String, String>,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct StackOutput {
23 pub key: String,
24 pub value: String,
25 pub description: Option<String>,
26 pub export_name: Option<String>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct StackExport {
35 pub value: String,
36 pub exporting_stack_id: String,
37 pub exporting_stack_name: String,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct Stack {
42 pub name: String,
43 pub stack_id: String,
44 pub template: String,
45 pub status: String,
46 pub resources: Vec<StackResource>,
47 pub parameters: BTreeMap<String, String>,
48 pub tags: BTreeMap<String, String>,
49 pub created_at: DateTime<Utc>,
50 pub updated_at: Option<DateTime<Utc>>,
51 pub description: Option<String>,
52 pub notification_arns: Vec<String>,
53 #[serde(default)]
54 pub outputs: Vec<StackOutput>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct CloudFormationState {
59 pub account_id: String,
60 pub region: String,
61 #[serde(default)]
62 pub stacks: BTreeMap<String, Stack>,
63 #[serde(default)]
68 pub extras: BTreeMap<String, BTreeMap<String, serde_json::Value>>,
69 #[serde(default)]
70 pub events: BTreeMap<String, Vec<serde_json::Value>>,
71 #[serde(default)]
72 pub stack_policies: BTreeMap<String, String>,
73 #[serde(default)]
74 pub termination_protection: BTreeMap<String, bool>,
75 #[serde(default)]
76 pub orgs_access_enabled: bool,
77 #[serde(default)]
80 pub exports: BTreeMap<String, StackExport>,
81 #[serde(default)]
85 pub imports: BTreeMap<String, Vec<String>>,
86}
87
88impl CloudFormationState {
89 pub fn new(account_id: &str, region: &str) -> Self {
90 Self {
91 account_id: account_id.to_string(),
92 region: region.to_string(),
93 stacks: BTreeMap::new(),
94 extras: BTreeMap::new(),
95 events: BTreeMap::new(),
96 stack_policies: BTreeMap::new(),
97 termination_protection: BTreeMap::new(),
98 orgs_access_enabled: false,
99 exports: BTreeMap::new(),
100 imports: BTreeMap::new(),
101 }
102 }
103
104 pub fn reset(&mut self) {
105 self.stacks.clear();
106 self.extras.clear();
107 self.events.clear();
108 self.stack_policies.clear();
109 self.termination_protection.clear();
110 self.orgs_access_enabled = false;
111 self.exports.clear();
112 self.imports.clear();
113 }
114}
115
116pub type SharedCloudFormationState =
117 Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<CloudFormationState>>>;
118
119impl fakecloud_core::multi_account::AccountState for CloudFormationState {
120 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
121 Self::new(account_id, region)
122 }
123}
124
125pub const CLOUDFORMATION_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
126
127#[derive(Debug, Serialize, Deserialize)]
128pub struct CloudFormationSnapshot {
129 pub schema_version: u32,
130 #[serde(default)]
131 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<CloudFormationState>>,
132 #[serde(default)]
133 pub state: Option<CloudFormationState>,
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn new_initializes_empty() {
142 let state = CloudFormationState::new("123456789012", "us-east-1");
143 assert_eq!(state.account_id, "123456789012");
144 assert_eq!(state.region, "us-east-1");
145 assert!(state.stacks.is_empty());
146 }
147
148 #[test]
149 fn reset_clears_stacks() {
150 let mut state = CloudFormationState::new("123456789012", "us-east-1");
151 state.stacks.insert(
152 "s1".to_string(),
153 Stack {
154 name: "s1".to_string(),
155 stack_id: "id".to_string(),
156 template: "{}".to_string(),
157 status: "CREATE_COMPLETE".to_string(),
158 resources: vec![],
159 parameters: BTreeMap::new(),
160 tags: BTreeMap::new(),
161 created_at: Utc::now(),
162 updated_at: None,
163 description: None,
164 notification_arns: vec![],
165 outputs: vec![],
166 },
167 );
168 state.reset();
169 assert!(state.stacks.is_empty());
170 }
171}