fakecloud_efs/state.rs
1//! Account-partitioned, serializable state for Amazon EFS's
2//! (`elasticfilesystem`) control plane.
3//!
4//! Each file system, mount target, and access point is stored as its
5//! already-output-valid `Description` JSON object (`serde_json::Value`) keyed by
6//! its identifier. Storing the wire shape directly keeps nested configuration
7//! (POSIX user, root directory, size breakdown) round-tripping verbatim and
8//! guarantees the `Describe` responses echo exactly what was persisted. The
9//! async `creating` -> `available` lifecycle mutates the stored object's
10//! `LifeCycleState` field on the next describe.
11
12use std::collections::BTreeMap;
13use std::sync::Arc;
14
15use parking_lot::RwLock;
16use serde::{Deserialize, Serialize};
17use serde_json::Value;
18
19use fakecloud_core::multi_account::{AccountState, MultiAccountState};
20
21pub const EFS_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
22
23/// Per-account EFS state.
24#[derive(Debug, Clone, Default, Serialize, Deserialize)]
25pub struct EfsData {
26 /// File systems keyed by `FileSystemId` (`fs-...`), stored as their
27 /// `FileSystemDescription` object.
28 #[serde(default)]
29 pub file_systems: BTreeMap<String, Value>,
30 /// Mount targets keyed by `MountTargetId` (`fsmt-...`), stored as their
31 /// `MountTargetDescription` object.
32 #[serde(default)]
33 pub mount_targets: BTreeMap<String, Value>,
34 /// Security groups per mount target, keyed by `MountTargetId`.
35 #[serde(default)]
36 pub mount_target_security_groups: BTreeMap<String, Vec<String>>,
37 /// Access points keyed by `AccessPointId` (`fsap-...`), stored as their
38 /// `AccessPointDescription` object.
39 #[serde(default)]
40 pub access_points: BTreeMap<String, Value>,
41 /// Lifecycle configurations keyed by `FileSystemId` -> `LifecyclePolicies`.
42 #[serde(default)]
43 pub lifecycle_configs: BTreeMap<String, Value>,
44 /// Backup policy status keyed by `FileSystemId` (`ENABLED`/`DISABLED`/...).
45 #[serde(default)]
46 pub backup_policies: BTreeMap<String, String>,
47 /// File-system resource policies keyed by `FileSystemId` -> policy JSON.
48 #[serde(default)]
49 pub file_system_policies: BTreeMap<String, String>,
50 /// Replication configurations keyed by source `FileSystemId`, stored as the
51 /// `ReplicationConfigurationDescription` object.
52 #[serde(default)]
53 pub replications: BTreeMap<String, Value>,
54 /// Resource tags keyed by resource id (`fs-...` or `fsap-...`) -> tag map.
55 #[serde(default)]
56 pub tags: BTreeMap<String, BTreeMap<String, String>>,
57 /// Account-level resource-id-type preference (`LONG_ID` / `SHORT_ID`).
58 #[serde(default)]
59 pub resource_id_preference: Option<String>,
60}
61
62impl EfsData {
63 /// Settle any in-flight lifecycle transition: a resource left in `creating`
64 /// becomes `available`. Used both on the next describe and on restart so an
65 /// interrupted transition never wedges. Returns `true` if anything changed.
66 pub fn reconcile_lifecycle(&mut self) -> bool {
67 let mut changed = false;
68 for map in [
69 &mut self.file_systems,
70 &mut self.mount_targets,
71 &mut self.access_points,
72 ] {
73 for v in map.values_mut() {
74 if let Some(obj) = v.as_object_mut() {
75 if obj.get("LifeCycleState").and_then(Value::as_str) == Some("creating") {
76 obj.insert("LifeCycleState".into(), Value::String("available".into()));
77 changed = true;
78 }
79 }
80 }
81 }
82 changed
83 }
84}
85
86impl AccountState for EfsData {
87 fn new_for_account(_account_id: &str, _region: &str, _endpoint: &str) -> Self {
88 Self::default()
89 }
90}
91
92pub type SharedEfsState = Arc<RwLock<MultiAccountState<EfsData>>>;
93
94#[derive(Debug, Serialize, Deserialize)]
95pub struct EfsSnapshot {
96 pub schema_version: u32,
97 pub accounts: MultiAccountState<EfsData>,
98}