fakecloud_resource_groups/
state.rs1use std::collections::{BTreeMap, BTreeSet};
4use std::sync::Arc;
5
6use chrono::{DateTime, Utc};
7use parking_lot::RwLock;
8use serde::{Deserialize, Serialize};
9
10use fakecloud_core::multi_account::{AccountState, MultiAccountState};
11
12pub const RESOURCE_GROUPS_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ResourceQuery {
19 pub type_: String,
21 pub query: String,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ResourceGroup {
29 pub name: String,
30 pub arn: String,
31 pub description: Option<String>,
32 pub query: Option<ResourceQuery>,
33 pub configuration: Vec<serde_json::Value>,
35 pub tags: BTreeMap<String, String>,
36 pub criticality: Option<i32>,
37 pub owner: Option<String>,
38 pub display_name: Option<String>,
39 pub application_tag: BTreeMap<String, String>,
40 pub resources: BTreeSet<String>,
42 pub created_at: DateTime<Utc>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct TagSyncTask {
49 pub task_arn: String,
50 pub group_arn: String,
51 pub group_name: String,
52 pub tag_key: Option<String>,
53 pub tag_value: Option<String>,
54 pub resource_query: Option<ResourceQuery>,
55 pub role_arn: String,
56 pub status: String,
57 pub created_at: DateTime<Utc>,
58}
59
60#[derive(Debug, Clone, Default, Serialize, Deserialize)]
61pub struct AccountSettings {
62 pub desired_status: Option<String>,
64}
65
66#[derive(Debug, Clone, Default, Serialize, Deserialize)]
67pub struct ResourceGroupsState {
68 pub groups: BTreeMap<String, ResourceGroup>,
70 pub tag_sync_tasks: BTreeMap<String, TagSyncTask>,
72 pub account_settings: AccountSettings,
73}
74
75impl ResourceGroupsState {
76 pub fn resolve_name<'a>(&'a self, name_or_arn: &'a str) -> Option<&'a str> {
78 if self.groups.contains_key(name_or_arn) {
79 return Some(name_or_arn);
80 }
81 if let Some(g) = self.groups.values().find(|g| g.arn == name_or_arn) {
83 return Some(g.name.as_str());
84 }
85 if let Some(resource) = name_or_arn
89 .starts_with("arn:")
90 .then(|| name_or_arn.split(":group/").nth(1))
91 .flatten()
92 {
93 let group_name = resource.split('/').next().unwrap_or(resource);
94 if let Some((stored, _)) = self.groups.get_key_value(group_name) {
95 return Some(stored.as_str());
96 }
97 }
98 None
99 }
100}
101
102impl AccountState for ResourceGroupsState {
103 fn new_for_account(_account_id: &str, _region: &str, _endpoint: &str) -> Self {
104 Self::default()
105 }
106}
107
108pub type SharedResourceGroupsState = Arc<RwLock<MultiAccountState<ResourceGroupsState>>>;
109
110#[derive(Debug, Serialize, Deserialize)]
111pub struct ResourceGroupsSnapshot {
112 pub schema_version: u32,
113 pub accounts: MultiAccountState<ResourceGroupsState>,
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn resolve_by_name_and_arn() {
122 let mut st = ResourceGroupsState::default();
123 st.groups.insert(
124 "g1".into(),
125 ResourceGroup {
126 name: "g1".into(),
127 arn: "arn:aws:resource-groups:us-east-1:123456789012:group/g1/abcd".into(),
128 description: None,
129 query: None,
130 configuration: vec![],
131 tags: BTreeMap::new(),
132 criticality: None,
133 owner: None,
134 display_name: None,
135 application_tag: BTreeMap::new(),
136 resources: BTreeSet::new(),
137 created_at: Utc::now(),
138 },
139 );
140 assert_eq!(st.resolve_name("g1"), Some("g1"));
141 assert_eq!(
142 st.resolve_name("arn:aws:resource-groups:us-east-1:123456789012:group/g1/abcd"),
143 Some("g1")
144 );
145 assert_eq!(
147 st.resolve_name("arn:aws:resource-groups:us-east-1:123456789012:group/g1"),
148 Some("g1")
149 );
150 assert_eq!(
152 st.resolve_name("arn:aws:resource-groups:us-west-2:123456789012:group/g1/zzzz"),
153 Some("g1")
154 );
155 assert_eq!(st.resolve_name("missing"), None);
156 assert_eq!(
157 st.resolve_name("arn:aws:resource-groups:us-east-1:123456789012:group/missing"),
158 None
159 );
160 }
161}