Skip to main content

fakecloud_memorydb/
state.rs

1//! Account-partitioned, serializable state for AWS MemoryDB.
2//!
3//! MemoryDB is a Redis/Valkey-compatible in-memory database. This models the
4//! full control plane — clusters (with shards/nodes), ACLs, users, parameter
5//! groups, subnet groups, and snapshots — as typed, serializable state keyed
6//! by name within each account. Cluster data-plane backing (a real Redis
7//! container) is layered on top of this control plane, mirroring ElastiCache.
8
9use std::collections::BTreeMap;
10use std::sync::Arc;
11
12use chrono::{DateTime, Utc};
13use parking_lot::RwLock;
14use serde::{Deserialize, Serialize};
15
16use fakecloud_core::multi_account::{AccountState, MultiAccountState};
17
18pub const MEMORYDB_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
19
20/// Tags on a resource, stored by ARN so `ListTags`/`TagResource` work
21/// uniformly across every MemoryDB resource type.
22pub type TagMap = BTreeMap<String, String>;
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct Endpoint {
26    pub address: String,
27    pub port: i32,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Node {
32    pub name: String,
33    pub status: String,
34    pub availability_zone: String,
35    pub create_time: DateTime<Utc>,
36    pub endpoint: Endpoint,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Shard {
41    pub name: String,
42    pub status: String,
43    pub slots: String,
44    pub nodes: Vec<Node>,
45    pub number_of_nodes: i32,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct Cluster {
50    pub name: String,
51    pub description: Option<String>,
52    pub status: String,
53    pub number_of_shards: i32,
54    pub shards: Vec<Shard>,
55    pub node_type: String,
56    pub engine: String,
57    pub engine_version: String,
58    pub engine_patch_version: String,
59    pub parameter_group_name: String,
60    pub parameter_group_status: String,
61    pub security_group_ids: Vec<String>,
62    pub subnet_group_name: String,
63    pub tls_enabled: bool,
64    pub kms_key_id: Option<String>,
65    pub arn: String,
66    pub sns_topic_arn: Option<String>,
67    pub snapshot_retention_limit: i32,
68    pub maintenance_window: String,
69    pub snapshot_window: String,
70    pub acl_name: String,
71    pub auto_minor_version_upgrade: bool,
72    pub data_tiering: String,
73    pub availability_mode: String,
74    pub cluster_endpoint: Endpoint,
75    pub network_type: String,
76    pub ip_discovery: String,
77    /// The port the cluster listens on (echoed into endpoints).
78    pub port: i32,
79    /// Backing Redis container id, when a data-plane container is running.
80    pub container_id: Option<String>,
81    pub created_at: DateTime<Utc>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct Acl {
86    pub name: String,
87    pub status: String,
88    pub user_names: Vec<String>,
89    pub minimum_engine_version: String,
90    pub clusters: Vec<String>,
91    pub arn: String,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct UserAuthentication {
96    pub type_: String,
97    pub password_count: i32,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct User {
102    pub name: String,
103    pub status: String,
104    pub access_string: String,
105    pub acl_names: Vec<String>,
106    pub minimum_engine_version: String,
107    pub authentication: UserAuthentication,
108    pub arn: String,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ParameterGroup {
113    pub name: String,
114    pub family: String,
115    pub description: String,
116    pub arn: String,
117    /// Explicitly set (name -> value) parameter overrides.
118    pub parameters: BTreeMap<String, String>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct SubnetGroup {
123    pub name: String,
124    pub description: String,
125    pub vpc_id: String,
126    pub subnet_ids: Vec<String>,
127    pub arn: String,
128    pub supported_network_types: Vec<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct Snapshot {
133    pub name: String,
134    pub status: String,
135    pub source: String,
136    pub kms_key_id: Option<String>,
137    pub arn: String,
138    pub data_tiering: String,
139    /// Snapshotted cluster configuration, stored as the response JSON so the
140    /// full `ClusterConfiguration` shape round-trips faithfully.
141    pub cluster_configuration: serde_json::Value,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct MultiRegionCluster {
146    pub name: String,
147    pub description: Option<String>,
148    pub status: String,
149    pub node_type: String,
150    pub engine: String,
151    pub engine_version: String,
152    pub number_of_shards: i32,
153    pub multi_region_parameter_group_name: Option<String>,
154    pub tls_enabled: bool,
155    pub arn: String,
156    /// Member clusters: (cluster_name, region, status, arn) JSON objects.
157    pub clusters: serde_json::Value,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct ReservedNode {
162    pub reservation_id: String,
163    pub reserved_nodes_offering_id: String,
164    pub node_type: String,
165    pub start_time: DateTime<Utc>,
166    pub duration: i32,
167    pub fixed_price: f64,
168    pub node_count: i32,
169    pub offering_type: String,
170    pub state: String,
171    pub arn: String,
172}
173
174#[derive(Debug, Clone, Default, Serialize, Deserialize)]
175pub struct MemoryDbState {
176    pub clusters: BTreeMap<String, Cluster>,
177    pub acls: BTreeMap<String, Acl>,
178    pub users: BTreeMap<String, User>,
179    pub parameter_groups: BTreeMap<String, ParameterGroup>,
180    pub subnet_groups: BTreeMap<String, SubnetGroup>,
181    pub snapshots: BTreeMap<String, Snapshot>,
182    pub multi_region_clusters: BTreeMap<String, MultiRegionCluster>,
183    pub reserved_nodes: BTreeMap<String, ReservedNode>,
184    /// Tags keyed by resource ARN.
185    pub tags: BTreeMap<String, TagMap>,
186}
187
188impl AccountState for MemoryDbState {
189    fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
190        // AWS seeds every account with a default ACL and default user.
191        let mut s = Self::default();
192        s.users.insert(
193            "default".to_string(),
194            User {
195                name: "default".to_string(),
196                status: "active".to_string(),
197                access_string: "on ~* &* +@all".to_string(),
198                acl_names: vec!["open-access".to_string()],
199                minimum_engine_version: "6.2".to_string(),
200                authentication: UserAuthentication {
201                    type_: "no-password".to_string(),
202                    password_count: 0,
203                },
204                arn: format!("arn:aws:memorydb:{region}:{account_id}:user/default"),
205            },
206        );
207        s.acls.insert(
208            "open-access".to_string(),
209            Acl {
210                name: "open-access".to_string(),
211                status: "active".to_string(),
212                user_names: vec!["default".to_string()],
213                minimum_engine_version: "6.2".to_string(),
214                clusters: vec![],
215                arn: format!("arn:aws:memorydb:{region}:{account_id}:acl/open-access"),
216            },
217        );
218        // AWS provides a default parameter group per supported engine family.
219        // The Terraform provider reads e.g. `default.memorydb-redis7` when
220        // managing a parameter group, so these must exist out of the box.
221        for (pg_name, family) in [
222            ("default.memorydb-redis7", "memorydb_redis7"),
223            ("default.memorydb-redis6", "memorydb_redis6"),
224            ("default.memorydb-valkey7", "memorydb_valkey7"),
225        ] {
226            s.parameter_groups.insert(
227                pg_name.to_string(),
228                ParameterGroup {
229                    name: pg_name.to_string(),
230                    family: family.to_string(),
231                    description: format!("Default parameter group for {family}"),
232                    arn: format!("arn:aws:memorydb:{region}:{account_id}:parametergroup/{pg_name}"),
233                    parameters: BTreeMap::new(),
234                },
235            );
236        }
237        s
238    }
239}
240
241pub type SharedMemoryDbState = Arc<RwLock<MultiAccountState<MemoryDbState>>>;
242
243#[derive(Debug, Serialize, Deserialize)]
244pub struct MemoryDbSnapshot {
245    pub schema_version: u32,
246    pub accounts: MultiAccountState<MemoryDbState>,
247}