1use std::collections::{BTreeMap, HashSet};
2use std::sync::Arc;
3
4use fakecloud_aws::arn::Arn;
5use parking_lot::RwLock;
6
7pub type SharedElastiCacheState =
8 Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<ElastiCacheState>>>;
9
10impl fakecloud_core::multi_account::AccountState for ElastiCacheState {
11 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
12 Self::new(account_id, region)
13 }
14}
15
16#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
17pub struct CacheEngineVersion {
18 pub engine: String,
19 pub engine_version: String,
20 pub cache_parameter_group_family: String,
21 pub cache_engine_description: String,
22 pub cache_engine_version_description: String,
23}
24
25#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
26pub struct CacheParameterGroup {
27 pub cache_parameter_group_name: String,
28 pub cache_parameter_group_family: String,
29 pub description: String,
30 pub is_global: bool,
31 pub arn: String,
32}
33
34#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
35pub struct EngineDefaultParameter {
36 pub parameter_name: String,
37 pub parameter_value: String,
38 pub description: String,
39 pub source: String,
40 pub data_type: String,
41 pub allowed_values: String,
42 pub is_modifiable: bool,
43 pub minimum_engine_version: String,
44}
45
46#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
47pub struct CacheSubnetGroup {
48 pub cache_subnet_group_name: String,
49 pub cache_subnet_group_description: String,
50 pub vpc_id: String,
51 pub subnet_ids: Vec<String>,
52 pub arn: String,
53}
54
55#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
56pub struct RecurringCharge {
57 pub recurring_charge_amount: f64,
58 pub recurring_charge_frequency: String,
59}
60
61#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
62pub struct ReservedCacheNode {
63 pub reserved_cache_node_id: String,
64 pub reserved_cache_nodes_offering_id: String,
65 pub cache_node_type: String,
66 pub start_time: String,
67 pub duration: i32,
68 pub fixed_price: f64,
69 pub usage_price: f64,
70 pub cache_node_count: i32,
71 pub product_description: String,
72 pub offering_type: String,
73 pub state: String,
74 pub recurring_charges: Vec<RecurringCharge>,
75 pub reservation_arn: String,
76}
77
78#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
79pub struct ReservedCacheNodesOffering {
80 pub reserved_cache_nodes_offering_id: String,
81 pub cache_node_type: String,
82 pub duration: i32,
83 pub fixed_price: f64,
84 pub usage_price: f64,
85 pub product_description: String,
86 pub offering_type: String,
87 pub recurring_charges: Vec<RecurringCharge>,
88}
89
90#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
91pub struct CacheCluster {
92 pub cache_cluster_id: String,
93 pub cache_node_type: String,
94 pub engine: String,
95 pub engine_version: String,
96 pub cache_cluster_status: String,
97 pub num_cache_nodes: i32,
98 pub preferred_availability_zone: String,
99 pub cache_subnet_group_name: Option<String>,
100 pub auto_minor_version_upgrade: bool,
101 pub arn: String,
102 pub created_at: String,
103 pub endpoint_address: String,
104 pub endpoint_port: u16,
105 pub container_id: String,
106 pub host_port: u16,
107 pub replication_group_id: Option<String>,
108 #[serde(default)]
112 pub cache_parameter_group_name: Option<String>,
113 #[serde(default)]
116 pub security_group_ids: Vec<String>,
117 #[serde(default)]
120 pub log_delivery_configurations: Vec<LogDeliveryConfiguration>,
121 #[serde(default)]
124 pub transit_encryption_enabled: bool,
125 #[serde(default)]
127 pub at_rest_encryption_enabled: bool,
128 #[serde(default)]
130 pub auth_token_enabled: bool,
131 #[serde(default)]
136 pub port: u16,
137 #[serde(default)]
139 pub preferred_maintenance_window: Option<String>,
140 #[serde(default)]
143 pub preferred_availability_zones: Vec<String>,
144 #[serde(default)]
146 pub notification_topic_arn: Option<String>,
147 #[serde(default)]
149 pub cache_security_group_names: Vec<String>,
150 #[serde(default)]
152 pub snapshot_arns: Vec<String>,
153 #[serde(default)]
155 pub snapshot_name: Option<String>,
156 #[serde(default)]
158 pub snapshot_retention_limit: i32,
159 #[serde(default)]
161 pub snapshot_window: Option<String>,
162 #[serde(default)]
164 pub outpost_mode: Option<String>,
165 #[serde(default)]
167 pub preferred_outpost_arn: Option<String>,
168 #[serde(default)]
170 pub network_type: Option<String>,
171 #[serde(default)]
173 pub ip_discovery: Option<String>,
174 #[serde(default)]
176 pub az_mode: Option<String>,
177 #[serde(default)]
180 pub auth_token: Option<String>,
181 #[serde(default)]
186 pub kms_key_id: Option<String>,
187 #[serde(default)]
190 pub transit_encryption_mode: Option<String>,
191 #[serde(default)]
194 pub data_tiering_enabled: Option<bool>,
195 #[serde(default)]
199 pub cluster_mode: Option<String>,
200 #[serde(default)]
204 pub preferred_outpost_arns: Vec<String>,
205}
206
207#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
208pub struct ReplicationGroup {
209 pub replication_group_id: String,
210 pub description: String,
211 pub global_replication_group_id: Option<String>,
212 pub global_replication_group_role: Option<String>,
213 pub status: String,
214 pub cache_node_type: String,
215 pub engine: String,
216 pub engine_version: String,
217 pub num_cache_clusters: i32,
218 pub automatic_failover_enabled: bool,
219 pub endpoint_address: String,
220 pub endpoint_port: u16,
221 pub arn: String,
222 pub created_at: String,
223 pub container_id: String,
224 pub host_port: u16,
225 pub member_clusters: Vec<String>,
226 pub snapshot_retention_limit: i32,
227 pub snapshot_window: String,
228 #[serde(default)]
233 pub transit_encryption_enabled: bool,
234 #[serde(default)]
235 pub at_rest_encryption_enabled: bool,
236 #[serde(default)]
237 pub cluster_enabled: bool,
238 #[serde(default)]
239 pub kms_key_id: Option<String>,
240 #[serde(default)]
241 pub auth_token_enabled: bool,
242 #[serde(default)]
243 pub user_group_ids: Vec<String>,
244 #[serde(default)]
245 pub multi_az_enabled: bool,
246 #[serde(default)]
247 pub log_delivery_configurations: Vec<LogDeliveryConfiguration>,
248 #[serde(default)]
249 pub data_tiering: Option<String>,
250 #[serde(default)]
251 pub ip_discovery: Option<String>,
252 #[serde(default)]
253 pub network_type: Option<String>,
254 #[serde(default)]
255 pub transit_encryption_mode: Option<String>,
256 #[serde(default)]
257 pub num_node_groups: i32,
258 #[serde(default)]
259 pub configuration_endpoint_address: Option<String>,
260 #[serde(default)]
261 pub configuration_endpoint_port: Option<u16>,
262 #[serde(default)]
263 pub replicas_per_node_group: Option<i32>,
264 #[serde(default)]
267 pub auth_token: Option<String>,
268 #[serde(default)]
273 pub port: u16,
274 #[serde(default)]
276 pub notification_topic_arn: Option<String>,
277 #[serde(default)]
280 pub cluster_mode: Option<String>,
281 #[serde(default)]
285 pub data_tiering_enabled: Option<bool>,
286 #[serde(default)]
289 pub notification_topic_status: Option<String>,
290 #[serde(default)]
293 pub cache_parameter_group_name: Option<String>,
294 #[serde(default)]
298 pub cache_subnet_group_name: Option<String>,
299 #[serde(default)]
303 pub security_group_ids: Vec<String>,
304 #[serde(default)]
308 pub preferred_maintenance_window: Option<String>,
309 #[serde(default)]
313 pub snapshot_name: Option<String>,
314 #[serde(default)]
316 pub snapshot_arns: Vec<String>,
317 #[serde(default = "default_auto_minor_version_upgrade")]
321 pub auto_minor_version_upgrade: bool,
322}
323
324fn default_auto_minor_version_upgrade() -> bool {
325 true
326}
327
328#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
332pub struct LogDeliveryConfiguration {
333 pub log_type: String,
334 pub destination_type: String,
335 pub destination_details: Option<String>,
336 pub log_format: String,
337 pub status: String,
338}
339
340#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
341pub struct GlobalReplicationGroupMember {
342 pub replication_group_id: String,
343 pub replication_group_region: String,
344 pub role: String,
345 pub automatic_failover: bool,
346 pub status: String,
347}
348
349#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
350pub struct GlobalReplicationGroup {
351 pub global_replication_group_id: String,
352 pub global_replication_group_description: String,
353 pub status: String,
354 pub cache_node_type: String,
355 pub engine: String,
356 pub engine_version: String,
357 pub members: Vec<GlobalReplicationGroupMember>,
358 pub cluster_enabled: bool,
359 pub arn: String,
360 #[serde(default = "default_global_node_group_count")]
364 pub num_node_groups: i32,
365}
366
367fn default_global_node_group_count() -> i32 {
368 1
369}
370
371#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
372pub struct ElastiCacheUser {
373 pub user_id: String,
374 pub user_name: String,
375 pub engine: String,
376 pub access_string: String,
377 pub status: String,
378 pub authentication_type: String,
379 pub password_count: i32,
380 pub arn: String,
381 pub minimum_engine_version: String,
382 pub user_group_ids: Vec<String>,
383}
384
385#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
386pub struct ElastiCacheUserGroup {
387 pub user_group_id: String,
388 pub engine: String,
389 pub status: String,
390 pub user_ids: Vec<String>,
391 pub arn: String,
392 pub minimum_engine_version: String,
393 pub pending_changes: Option<UserGroupPendingChanges>,
394 pub replication_groups: Vec<String>,
395}
396
397#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
398pub struct UserGroupPendingChanges {
399 pub user_ids_to_add: Vec<String>,
400 pub user_ids_to_remove: Vec<String>,
401}
402
403#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
404pub struct CacheSnapshot {
405 pub snapshot_name: String,
406 pub replication_group_id: String,
407 pub replication_group_description: String,
408 pub snapshot_status: String,
409 pub cache_node_type: String,
410 pub engine: String,
411 pub engine_version: String,
412 pub num_cache_clusters: i32,
413 pub arn: String,
414 pub created_at: String,
415 pub snapshot_source: String,
416 pub rdb_path: Option<String>,
419}
420
421#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
422pub struct ServerlessCacheUsageLimits {
423 pub data_storage: Option<ServerlessCacheDataStorage>,
424 pub ecpu_per_second: Option<ServerlessCacheEcpuPerSecond>,
425}
426
427#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
428pub struct ServerlessCacheDataStorage {
429 pub maximum: Option<i32>,
430 pub minimum: Option<i32>,
431 pub unit: Option<String>,
432}
433
434#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
435pub struct ServerlessCacheEcpuPerSecond {
436 pub maximum: Option<i32>,
437 pub minimum: Option<i32>,
438}
439
440#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
441pub struct ServerlessCacheEndpoint {
442 pub address: String,
443 pub port: u16,
444}
445
446#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
447pub struct ServerlessCache {
448 pub serverless_cache_name: String,
449 pub description: String,
450 pub engine: String,
451 pub major_engine_version: String,
452 pub full_engine_version: String,
453 pub status: String,
454 pub endpoint: ServerlessCacheEndpoint,
455 pub reader_endpoint: ServerlessCacheEndpoint,
456 pub arn: String,
457 pub created_at: String,
458 pub cache_usage_limits: Option<ServerlessCacheUsageLimits>,
459 pub security_group_ids: Vec<String>,
460 pub subnet_ids: Vec<String>,
461 pub kms_key_id: Option<String>,
462 pub user_group_id: Option<String>,
463 pub snapshot_retention_limit: Option<i32>,
464 pub daily_snapshot_time: Option<String>,
465 pub container_id: String,
466 pub host_port: u16,
467}
468
469#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
470pub struct ServerlessCacheSnapshot {
471 pub serverless_cache_snapshot_name: String,
472 pub arn: String,
473 pub kms_key_id: Option<String>,
474 pub snapshot_type: String,
475 pub status: String,
476 pub create_time: String,
477 pub expiry_time: Option<String>,
478 pub bytes_used_for_cache: Option<String>,
479 pub serverless_cache_name: String,
480 pub engine: String,
481 pub major_engine_version: String,
482}
483
484#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
485pub struct CacheSecurityGroup {
486 pub cache_security_group_name: String,
487 pub description: String,
488 pub owner_id: String,
489 pub arn: String,
490 pub ec2_security_groups: Vec<Ec2SecurityGroupAuth>,
491}
492
493#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
494pub struct Ec2SecurityGroupAuth {
495 pub status: String,
496 pub ec2_security_group_name: String,
497 pub ec2_security_group_owner_id: String,
498}
499
500#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
501pub struct CacheParameter {
502 pub parameter_name: String,
503 pub parameter_value: String,
504 pub description: String,
505 pub source: String,
506 pub data_type: String,
507 pub allowed_values: String,
508 pub is_modifiable: bool,
509 pub minimum_engine_version: String,
510}
511
512#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
513pub struct CacheEvent {
514 pub source_identifier: String,
515 pub source_type: String,
516 pub message: String,
517 pub date: String,
518}
519
520#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
521pub struct ServiceUpdate {
522 pub service_update_name: String,
523 pub service_update_release_date: String,
524 pub service_update_end_date: String,
525 pub service_update_severity: String,
526 pub service_update_status: String,
527 pub service_update_recommended_apply_by_date: String,
528 pub service_update_type: String,
529 pub engine: String,
530 pub engine_version: String,
531 pub auto_update_after_recommended_apply_by_date: bool,
532 pub estimated_update_time: String,
533 pub service_update_description: String,
534}
535
536#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
537pub struct UpdateAction {
538 pub replication_group_id: Option<String>,
539 pub cache_cluster_id: Option<String>,
540 pub service_update_name: String,
541 pub service_update_release_date: String,
542 pub service_update_severity: String,
543 pub service_update_status: String,
544 pub service_update_recommended_apply_by_date: String,
545 pub service_update_type: String,
546 pub update_action_available_date: String,
547 pub update_action_status: String,
548 pub nodes_updated: String,
549 pub update_action_status_modified_date: String,
550 pub sla_met: String,
551 pub estimated_update_time: String,
552 pub engine: String,
553}
554
555#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
556pub struct Migration {
557 pub replication_group_id: String,
558 pub customer_node_endpoint_address: String,
559 pub customer_node_endpoint_port: i32,
560 pub status: String,
561 pub started_at: String,
562}
563
564#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
565pub struct ElastiCacheState {
566 pub account_id: String,
567 pub region: String,
568 pub parameter_groups: Vec<CacheParameterGroup>,
569 pub subnet_groups: BTreeMap<String, CacheSubnetGroup>,
570 pub reserved_cache_nodes: BTreeMap<String, ReservedCacheNode>,
571 pub reserved_cache_nodes_offerings: Vec<ReservedCacheNodesOffering>,
572 pub cache_clusters: BTreeMap<String, CacheCluster>,
573 pub replication_groups: BTreeMap<String, ReplicationGroup>,
574 pub global_replication_groups: BTreeMap<String, GlobalReplicationGroup>,
575 pub users: BTreeMap<String, ElastiCacheUser>,
576 pub user_groups: BTreeMap<String, ElastiCacheUserGroup>,
577 pub snapshots: BTreeMap<String, CacheSnapshot>,
578 pub serverless_caches: BTreeMap<String, ServerlessCache>,
579 pub serverless_cache_snapshots: BTreeMap<String, ServerlessCacheSnapshot>,
580 pub tags: BTreeMap<String, Vec<(String, String)>>,
581 in_progress_cache_cluster_ids: HashSet<String>,
582 #[serde(default)]
588 delete_requested_cache_clusters: HashSet<String>,
589 in_progress_replication_group_ids: HashSet<String>,
590 in_progress_serverless_cache_names: HashSet<String>,
591 #[serde(default)]
592 pub security_groups: BTreeMap<String, CacheSecurityGroup>,
593 #[serde(default)]
594 pub parameter_group_parameters: BTreeMap<String, Vec<CacheParameter>>,
595 #[serde(default)]
596 pub events: Vec<CacheEvent>,
597 #[serde(default)]
599 pub migrations: BTreeMap<String, Migration>,
600 #[serde(default)]
603 pub service_updates: BTreeMap<String, ServiceUpdate>,
604 #[serde(default)]
608 pub update_actions: BTreeMap<String, UpdateAction>,
609}
610
611impl ElastiCacheState {
612 pub fn new(account_id: &str, region: &str) -> Self {
613 let parameter_groups = default_parameter_groups(account_id, region);
614 let subnet_groups = default_subnet_groups(account_id, region);
615 let users = default_users(account_id, region);
616 let mut tags: BTreeMap<String, Vec<(String, String)>> = subnet_groups
617 .values()
618 .map(|g| (g.arn.clone(), Vec::new()))
619 .collect();
620 for user in users.values() {
621 tags.insert(user.arn.clone(), Vec::new());
622 }
623 Self {
624 account_id: account_id.to_string(),
625 region: region.to_string(),
626 parameter_groups,
627 subnet_groups,
628 reserved_cache_nodes: BTreeMap::new(),
629 reserved_cache_nodes_offerings: default_reserved_cache_nodes_offerings(),
630 cache_clusters: BTreeMap::new(),
631 replication_groups: BTreeMap::new(),
632 global_replication_groups: BTreeMap::new(),
633 users,
634 user_groups: BTreeMap::new(),
635 snapshots: BTreeMap::new(),
636 serverless_caches: BTreeMap::new(),
637 serverless_cache_snapshots: BTreeMap::new(),
638 tags,
639 in_progress_cache_cluster_ids: HashSet::new(),
640 delete_requested_cache_clusters: HashSet::new(),
641 in_progress_replication_group_ids: HashSet::new(),
642 in_progress_serverless_cache_names: HashSet::new(),
643 security_groups: BTreeMap::new(),
644 parameter_group_parameters: BTreeMap::new(),
645 events: Vec::new(),
646 migrations: BTreeMap::new(),
647 service_updates: default_service_updates(),
648 update_actions: BTreeMap::new(),
649 }
650 }
651
652 pub fn reset(&mut self) {
653 self.parameter_groups = default_parameter_groups(&self.account_id, &self.region);
654 self.subnet_groups = default_subnet_groups(&self.account_id, &self.region);
655 self.reserved_cache_nodes.clear();
656 self.reserved_cache_nodes_offerings = default_reserved_cache_nodes_offerings();
657 self.cache_clusters.clear();
658 self.replication_groups.clear();
659 self.global_replication_groups.clear();
660 self.users = default_users(&self.account_id, &self.region);
661 self.user_groups.clear();
662 self.snapshots.clear();
663 self.serverless_caches.clear();
664 self.serverless_cache_snapshots.clear();
665 self.tags.clear();
666 for g in self.subnet_groups.values() {
667 self.tags.insert(g.arn.clone(), Vec::new());
668 }
669 for user in self.users.values() {
670 self.tags.insert(user.arn.clone(), Vec::new());
671 }
672 self.in_progress_cache_cluster_ids.clear();
673 self.in_progress_replication_group_ids.clear();
674 self.in_progress_serverless_cache_names.clear();
675 self.security_groups.clear();
676 self.parameter_group_parameters.clear();
677 self.events.clear();
678 self.migrations.clear();
679 self.service_updates = default_service_updates();
680 self.update_actions.clear();
681 }
682
683 pub fn ensure_update_actions(&mut self) {
688 let cluster_ids: Vec<String> = self.cache_clusters.keys().cloned().collect();
689 let group_ids: Vec<String> = self.replication_groups.keys().cloned().collect();
690 self.update_actions.retain(|_, ua| {
694 let su_exists = self.service_updates.contains_key(&ua.service_update_name);
695 let target_exists = match (&ua.cache_cluster_id, &ua.replication_group_id) {
696 (Some(cc), _) => cluster_ids.contains(cc),
697 (None, Some(rg)) => group_ids.contains(rg),
698 (None, None) => false,
699 };
700 su_exists && target_exists
701 });
702 for su in self.service_updates.values() {
703 for cc in &cluster_ids {
704 let key = format!("{}#cc#{}", su.service_update_name, cc);
705 self.update_actions
706 .entry(key)
707 .or_insert_with(|| new_update_action(su, None, Some(cc.clone())));
708 }
709 for rg in &group_ids {
710 let key = format!("{}#rg#{}", su.service_update_name, rg);
711 self.update_actions
712 .entry(key)
713 .or_insert_with(|| new_update_action(su, Some(rg.clone()), None));
714 }
715 }
716 }
717
718 pub fn begin_cache_cluster_creation(&mut self, cache_cluster_id: &str) -> bool {
719 if self.cache_clusters.contains_key(cache_cluster_id)
720 || self
721 .in_progress_cache_cluster_ids
722 .contains(cache_cluster_id)
723 {
724 return false;
725 }
726 self.in_progress_cache_cluster_ids
727 .insert(cache_cluster_id.to_string());
728 true
729 }
730
731 pub fn finish_cache_cluster_creation(&mut self, cluster: CacheCluster) {
732 self.in_progress_cache_cluster_ids
733 .remove(&cluster.cache_cluster_id);
734 self.tags.insert(cluster.arn.clone(), Vec::new());
735 self.cache_clusters
736 .insert(cluster.cache_cluster_id.clone(), cluster);
737 }
738
739 pub fn cancel_cache_cluster_creation(&mut self, cache_cluster_id: &str) {
740 self.in_progress_cache_cluster_ids.remove(cache_cluster_id);
741 }
742
743 pub fn cache_cluster_creation_in_progress(&self, cache_cluster_id: &str) -> bool {
746 self.in_progress_cache_cluster_ids
747 .contains(cache_cluster_id)
748 }
749
750 pub fn request_cache_cluster_delete_during_creation(&mut self, cache_cluster_id: &str) {
756 self.delete_requested_cache_clusters
757 .insert(cache_cluster_id.to_string());
758 }
759
760 pub fn take_cache_cluster_delete_request(&mut self, cache_cluster_id: &str) -> bool {
765 self.delete_requested_cache_clusters
766 .remove(cache_cluster_id)
767 }
768
769 pub fn begin_replication_group_creation(&mut self, replication_group_id: &str) -> bool {
770 if self.replication_groups.contains_key(replication_group_id)
771 || self
772 .in_progress_replication_group_ids
773 .contains(replication_group_id)
774 {
775 return false;
776 }
777 self.in_progress_replication_group_ids
778 .insert(replication_group_id.to_string());
779 true
780 }
781
782 pub fn finish_replication_group_creation(&mut self, group: ReplicationGroup) {
783 self.in_progress_replication_group_ids
784 .remove(&group.replication_group_id);
785 self.tags.insert(group.arn.clone(), Vec::new());
786 self.replication_groups
787 .insert(group.replication_group_id.clone(), group);
788 }
789
790 pub fn cancel_replication_group_creation(&mut self, replication_group_id: &str) {
791 self.in_progress_replication_group_ids
792 .remove(replication_group_id);
793 }
794
795 pub fn begin_serverless_cache_creation(&mut self, serverless_cache_name: &str) -> bool {
796 if self.serverless_caches.contains_key(serverless_cache_name)
797 || self
798 .in_progress_serverless_cache_names
799 .contains(serverless_cache_name)
800 {
801 return false;
802 }
803 self.in_progress_serverless_cache_names
804 .insert(serverless_cache_name.to_string());
805 true
806 }
807
808 pub fn finish_serverless_cache_creation(&mut self, cache: ServerlessCache) {
809 self.in_progress_serverless_cache_names
810 .remove(&cache.serverless_cache_name);
811 self.tags.insert(cache.arn.clone(), Vec::new());
812 self.serverless_caches
813 .insert(cache.serverless_cache_name.clone(), cache);
814 }
815
816 pub fn cancel_serverless_cache_creation(&mut self, serverless_cache_name: &str) {
817 self.in_progress_serverless_cache_names
818 .remove(serverless_cache_name);
819 }
820
821 pub fn register_arn(&mut self, arn: &str) {
822 self.tags.entry(arn.to_string()).or_default();
823 }
824
825 pub fn has_arn(&self, arn: &str) -> bool {
826 self.tags.contains_key(arn)
827 }
828}
829
830fn default_service_updates() -> BTreeMap<String, ServiceUpdate> {
834 let mut m = BTreeMap::new();
835 for (name, release, end, apply_by, severity, engine, desc) in [
836 (
837 "elasticache-20240301-redis-security",
838 "2024-03-01T00:00:00Z",
839 "2024-09-01T00:00:00Z",
840 "2024-04-01T00:00:00Z",
841 "important",
842 "redis",
843 "Security update for ElastiCache for Redis",
844 ),
845 (
846 "elasticache-20240601-memcached-security",
847 "2024-06-01T00:00:00Z",
848 "2024-12-01T00:00:00Z",
849 "2024-07-01T00:00:00Z",
850 "medium",
851 "memcached",
852 "Security update for ElastiCache for Memcached",
853 ),
854 ] {
855 m.insert(
856 name.to_string(),
857 ServiceUpdate {
858 service_update_name: name.to_string(),
859 service_update_release_date: release.to_string(),
860 service_update_end_date: end.to_string(),
861 service_update_severity: severity.to_string(),
862 service_update_status: "available".to_string(),
863 service_update_recommended_apply_by_date: apply_by.to_string(),
864 service_update_type: "security-update".to_string(),
865 engine: engine.to_string(),
866 engine_version: String::new(),
867 auto_update_after_recommended_apply_by_date: true,
868 estimated_update_time: "30 minutes".to_string(),
869 service_update_description: desc.to_string(),
870 },
871 );
872 }
873 m
874}
875
876fn new_update_action(
879 su: &ServiceUpdate,
880 replication_group_id: Option<String>,
881 cache_cluster_id: Option<String>,
882) -> UpdateAction {
883 UpdateAction {
884 replication_group_id,
885 cache_cluster_id,
886 service_update_name: su.service_update_name.clone(),
887 service_update_release_date: su.service_update_release_date.clone(),
888 service_update_severity: su.service_update_severity.clone(),
889 service_update_status: su.service_update_status.clone(),
890 service_update_recommended_apply_by_date: su
891 .service_update_recommended_apply_by_date
892 .clone(),
893 service_update_type: su.service_update_type.clone(),
894 update_action_available_date: su.service_update_release_date.clone(),
895 update_action_status: "not-applied".to_string(),
896 nodes_updated: "0/0".to_string(),
897 update_action_status_modified_date: su.service_update_release_date.clone(),
898 sla_met: "n/a".to_string(),
899 estimated_update_time: su.estimated_update_time.clone(),
900 engine: su.engine.clone(),
901 }
902}
903
904fn default_reserved_cache_nodes_offerings() -> Vec<ReservedCacheNodesOffering> {
905 vec![
906 ReservedCacheNodesOffering {
907 reserved_cache_nodes_offering_id: "off-cache-t3-micro-redis-1yr-no-upfront".to_string(),
908 cache_node_type: "cache.t3.micro".to_string(),
909 duration: 31_536_000,
910 fixed_price: 0.0,
911 usage_price: 0.011,
912 product_description: "redis".to_string(),
913 offering_type: "No Upfront".to_string(),
914 recurring_charges: Vec::new(),
915 },
916 ReservedCacheNodesOffering {
917 reserved_cache_nodes_offering_id: "off-cache-t3-small-redis-1yr-partial-upfront"
918 .to_string(),
919 cache_node_type: "cache.t3.small".to_string(),
920 duration: 31_536_000,
921 fixed_price: 120.0,
922 usage_price: 0.007,
923 product_description: "redis".to_string(),
924 offering_type: "Partial Upfront".to_string(),
925 recurring_charges: Vec::new(),
926 },
927 ReservedCacheNodesOffering {
928 reserved_cache_nodes_offering_id: "off-cache-m5-large-memcached-3yr-no-upfront"
929 .to_string(),
930 cache_node_type: "cache.m5.large".to_string(),
931 duration: 94_608_000,
932 fixed_price: 0.0,
933 usage_price: 0.033,
934 product_description: "memcached".to_string(),
935 offering_type: "No Upfront".to_string(),
936 recurring_charges: Vec::new(),
937 },
938 ReservedCacheNodesOffering {
939 reserved_cache_nodes_offering_id: "off-cache-r6g-large-redis-3yr-all-upfront"
940 .to_string(),
941 cache_node_type: "cache.r6g.large".to_string(),
942 duration: 94_608_000,
943 fixed_price: 1_550.0,
944 usage_price: 0.0,
945 product_description: "redis".to_string(),
946 offering_type: "All Upfront".to_string(),
947 recurring_charges: vec![RecurringCharge {
948 recurring_charge_amount: 0.0,
949 recurring_charge_frequency: "Hourly".to_string(),
950 }],
951 },
952 ]
953}
954
955pub fn default_engine_versions() -> Vec<CacheEngineVersion> {
956 vec![
957 CacheEngineVersion {
958 engine: "redis".to_string(),
959 engine_version: "7.1".to_string(),
960 cache_parameter_group_family: "redis7".to_string(),
961 cache_engine_description: "Redis".to_string(),
962 cache_engine_version_description: "Redis 7.1".to_string(),
963 },
964 CacheEngineVersion {
965 engine: "valkey".to_string(),
966 engine_version: "8.0".to_string(),
967 cache_parameter_group_family: "valkey8".to_string(),
968 cache_engine_description: "Valkey".to_string(),
969 cache_engine_version_description: "Valkey 8.0".to_string(),
970 },
971 CacheEngineVersion {
972 engine: "memcached".to_string(),
973 engine_version: "1.6.22".to_string(),
974 cache_parameter_group_family: "memcached1.6".to_string(),
975 cache_engine_description: "Memcached".to_string(),
976 cache_engine_version_description: "Memcached 1.6.22".to_string(),
977 },
978 ]
979}
980
981fn default_parameter_groups(account_id: &str, region: &str) -> Vec<CacheParameterGroup> {
982 vec![
983 CacheParameterGroup {
984 cache_parameter_group_name: "default.redis7".to_string(),
985 cache_parameter_group_family: "redis7".to_string(),
986 description: "Default parameter group for redis7".to_string(),
987 is_global: false,
988 arn: Arn::new(
989 "elasticache",
990 region,
991 account_id,
992 "parametergroup:default.redis7",
993 )
994 .to_string(),
995 },
996 CacheParameterGroup {
997 cache_parameter_group_name: "default.valkey8".to_string(),
998 cache_parameter_group_family: "valkey8".to_string(),
999 description: "Default parameter group for valkey8".to_string(),
1000 is_global: false,
1001 arn: Arn::new(
1002 "elasticache",
1003 region,
1004 account_id,
1005 "parametergroup:default.valkey8",
1006 )
1007 .to_string(),
1008 },
1009 CacheParameterGroup {
1010 cache_parameter_group_name: "default.memcached1.6".to_string(),
1011 cache_parameter_group_family: "memcached1.6".to_string(),
1012 description: "Default parameter group for memcached1.6".to_string(),
1013 is_global: false,
1014 arn: Arn::new(
1015 "elasticache",
1016 region,
1017 account_id,
1018 "parametergroup:default.memcached1.6",
1019 )
1020 .to_string(),
1021 },
1022 ]
1023}
1024
1025fn default_subnet_groups(account_id: &str, region: &str) -> BTreeMap<String, CacheSubnetGroup> {
1026 let default_group = CacheSubnetGroup {
1027 cache_subnet_group_name: "default".to_string(),
1028 cache_subnet_group_description: "Default CacheSubnetGroup".to_string(),
1029 vpc_id: "vpc-00000000".to_string(),
1030 subnet_ids: vec!["subnet-00000000".to_string()],
1031 arn: Arn::new("elasticache", region, account_id, "subnetgroup:default").to_string(),
1032 };
1033 let mut map = BTreeMap::new();
1034 map.insert("default".to_string(), default_group);
1035 map
1036}
1037
1038pub fn default_parameters_for_family(family: &str) -> Vec<EngineDefaultParameter> {
1039 match family {
1040 "redis7" => vec![
1041 EngineDefaultParameter {
1042 parameter_name: "maxmemory-policy".to_string(),
1043 parameter_value: "volatile-lru".to_string(),
1044 description: "Max memory policy".to_string(),
1045 source: "system".to_string(),
1046 data_type: "string".to_string(),
1047 allowed_values: "volatile-lru,allkeys-lru,volatile-lfu,allkeys-lfu,volatile-random,allkeys-random,volatile-ttl,noeviction".to_string(),
1048 is_modifiable: true,
1049 minimum_engine_version: "7.0.0".to_string(),
1050 },
1051 EngineDefaultParameter {
1052 parameter_name: "cluster-enabled".to_string(),
1053 parameter_value: "no".to_string(),
1054 description: "Enable or disable Redis Cluster mode".to_string(),
1055 source: "system".to_string(),
1056 data_type: "string".to_string(),
1057 allowed_values: "yes,no".to_string(),
1058 is_modifiable: false,
1059 minimum_engine_version: "7.0.0".to_string(),
1060 },
1061 EngineDefaultParameter {
1062 parameter_name: "activedefrag".to_string(),
1063 parameter_value: "no".to_string(),
1064 description: "Enable active defragmentation".to_string(),
1065 source: "system".to_string(),
1066 data_type: "string".to_string(),
1067 allowed_values: "yes,no".to_string(),
1068 is_modifiable: true,
1069 minimum_engine_version: "7.0.0".to_string(),
1070 },
1071 ],
1072 "valkey8" => vec![
1073 EngineDefaultParameter {
1074 parameter_name: "maxmemory-policy".to_string(),
1075 parameter_value: "volatile-lru".to_string(),
1076 description: "Max memory policy".to_string(),
1077 source: "system".to_string(),
1078 data_type: "string".to_string(),
1079 allowed_values: "volatile-lru,allkeys-lru,volatile-lfu,allkeys-lfu,volatile-random,allkeys-random,volatile-ttl,noeviction".to_string(),
1080 is_modifiable: true,
1081 minimum_engine_version: "8.0.0".to_string(),
1082 },
1083 EngineDefaultParameter {
1084 parameter_name: "cluster-enabled".to_string(),
1085 parameter_value: "no".to_string(),
1086 description: "Enable or disable cluster mode".to_string(),
1087 source: "system".to_string(),
1088 data_type: "string".to_string(),
1089 allowed_values: "yes,no".to_string(),
1090 is_modifiable: false,
1091 minimum_engine_version: "8.0.0".to_string(),
1092 },
1093 EngineDefaultParameter {
1094 parameter_name: "activedefrag".to_string(),
1095 parameter_value: "no".to_string(),
1096 description: "Enable active defragmentation".to_string(),
1097 source: "system".to_string(),
1098 data_type: "string".to_string(),
1099 allowed_values: "yes,no".to_string(),
1100 is_modifiable: true,
1101 minimum_engine_version: "8.0.0".to_string(),
1102 },
1103 ],
1104 "memcached1.6" => vec![
1105 EngineDefaultParameter {
1106 parameter_name: "max_item_size".to_string(),
1107 parameter_value: "1048576".to_string(),
1108 description: "Maximum item size".to_string(),
1109 source: "system".to_string(),
1110 data_type: "integer".to_string(),
1111 allowed_values: "1048576-1073741824".to_string(),
1112 is_modifiable: true,
1113 minimum_engine_version: "1.4.5".to_string(),
1114 },
1115 EngineDefaultParameter {
1116 parameter_name: "max_simultaneous_connections".to_string(),
1117 parameter_value: "65000".to_string(),
1118 description: "Maximum number of concurrent connections".to_string(),
1119 source: "system".to_string(),
1120 data_type: "integer".to_string(),
1121 allowed_values: "1-65000".to_string(),
1122 is_modifiable: false,
1123 minimum_engine_version: "1.4.5".to_string(),
1124 },
1125 ],
1126 _ => Vec::new(),
1127 }
1128}
1129
1130fn default_users(account_id: &str, region: &str) -> BTreeMap<String, ElastiCacheUser> {
1131 let mut map = BTreeMap::new();
1132 map.insert(
1133 "default".to_string(),
1134 ElastiCacheUser {
1135 user_id: "default".to_string(),
1136 user_name: "default".to_string(),
1137 engine: "redis".to_string(),
1138 access_string: "on ~* +@all".to_string(),
1139 status: "active".to_string(),
1140 authentication_type: "no-password".to_string(),
1141 password_count: 0,
1142 arn: Arn::new("elasticache", region, account_id, "user:default").to_string(),
1143 minimum_engine_version: "6.0".to_string(),
1144 user_group_ids: Vec::new(),
1145 },
1146 );
1147 map
1148}
1149
1150pub const ELASTICACHE_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
1151
1152#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1153pub struct ElastiCacheSnapshot {
1154 pub schema_version: u32,
1155 #[serde(default)]
1156 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<ElastiCacheState>>,
1157 #[serde(default)]
1158 pub state: Option<ElastiCacheState>,
1159}
1160
1161#[cfg(test)]
1162mod tests {
1163 use super::*;
1164
1165 #[test]
1166 fn default_engine_versions_contains_redis_valkey_memcached() {
1167 let versions = default_engine_versions();
1168 assert_eq!(versions.len(), 3);
1169 assert_eq!(versions[0].engine, "redis");
1170 assert_eq!(versions[0].engine_version, "7.1");
1171 assert_eq!(versions[1].engine, "valkey");
1172 assert_eq!(versions[1].engine_version, "8.0");
1173 assert_eq!(versions[2].engine, "memcached");
1174 assert_eq!(versions[2].engine_version, "1.6.22");
1175 }
1176
1177 #[test]
1178 fn state_new_creates_default_parameter_groups() {
1179 let state = ElastiCacheState::new("123456789012", "us-east-1");
1180 assert_eq!(state.parameter_groups.len(), 3);
1181 assert_eq!(
1182 state.parameter_groups[0].cache_parameter_group_name,
1183 "default.redis7"
1184 );
1185 assert_eq!(
1186 state.parameter_groups[1].cache_parameter_group_name,
1187 "default.valkey8"
1188 );
1189 assert_eq!(
1190 state.parameter_groups[2].cache_parameter_group_name,
1191 "default.memcached1.6"
1192 );
1193 }
1194
1195 #[test]
1196 fn state_new_creates_default_subnet_group() {
1197 let state = ElastiCacheState::new("123456789012", "us-east-1");
1198 assert_eq!(state.subnet_groups.len(), 1);
1199 let default = state.subnet_groups.get("default").unwrap();
1200 assert_eq!(default.cache_subnet_group_name, "default");
1201 assert_eq!(
1202 default.cache_subnet_group_description,
1203 "Default CacheSubnetGroup"
1204 );
1205 assert_eq!(default.vpc_id, "vpc-00000000");
1206 assert!(!default.subnet_ids.is_empty());
1207 assert!(default.arn.contains("subnetgroup:default"));
1208 }
1209
1210 #[test]
1211 fn reset_restores_default_parameter_groups() {
1212 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1213 state.parameter_groups.clear();
1214 assert!(state.parameter_groups.is_empty());
1215 state.reset();
1216 assert_eq!(state.parameter_groups.len(), 3);
1217 }
1218
1219 #[test]
1220 fn reset_restores_default_subnet_groups() {
1221 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1222 state.subnet_groups.clear();
1223 assert!(state.subnet_groups.is_empty());
1224 state.reset();
1225 assert_eq!(state.subnet_groups.len(), 1);
1226 assert!(state.subnet_groups.contains_key("default"));
1227 }
1228
1229 #[test]
1230 fn default_parameters_for_redis7_returns_parameters() {
1231 let params = default_parameters_for_family("redis7");
1232 assert_eq!(params.len(), 3);
1233 assert_eq!(params[0].parameter_name, "maxmemory-policy");
1234 }
1235
1236 #[test]
1237 fn default_parameters_for_unknown_family_returns_empty() {
1238 let params = default_parameters_for_family("unknown");
1239 assert!(params.is_empty());
1240 }
1241
1242 #[test]
1243 fn state_new_has_empty_replication_groups() {
1244 let state = ElastiCacheState::new("123456789012", "us-east-1");
1245 assert!(state.replication_groups.is_empty());
1246 }
1247
1248 #[test]
1249 fn state_new_has_empty_global_replication_groups() {
1250 let state = ElastiCacheState::new("123456789012", "us-east-1");
1251 assert!(state.global_replication_groups.is_empty());
1252 }
1253
1254 #[test]
1255 fn state_new_has_empty_cache_clusters() {
1256 let state = ElastiCacheState::new("123456789012", "us-east-1");
1257 assert!(state.cache_clusters.is_empty());
1258 }
1259
1260 #[test]
1261 fn state_new_has_empty_serverless_caches() {
1262 let state = ElastiCacheState::new("123456789012", "us-east-1");
1263 assert!(state.serverless_caches.is_empty());
1264 assert!(state.serverless_cache_snapshots.is_empty());
1265 }
1266
1267 #[test]
1268 fn begin_cache_cluster_creation_rejects_duplicate_ids() {
1269 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1270
1271 assert!(state.begin_cache_cluster_creation("cluster-1"));
1272 assert!(!state.begin_cache_cluster_creation("cluster-1"));
1273
1274 state.cancel_cache_cluster_creation("cluster-1");
1275 assert!(state.begin_cache_cluster_creation("cluster-1"));
1276 }
1277
1278 #[test]
1279 fn begin_replication_group_creation_rejects_duplicate_ids() {
1280 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1281
1282 assert!(state.begin_replication_group_creation("rg-1"));
1283 assert!(!state.begin_replication_group_creation("rg-1"));
1284
1285 state.cancel_replication_group_creation("rg-1");
1286 assert!(state.begin_replication_group_creation("rg-1"));
1287 }
1288
1289 #[test]
1290 fn begin_serverless_cache_creation_rejects_duplicate_names() {
1291 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1292
1293 assert!(state.begin_serverless_cache_creation("cache-1"));
1294 assert!(!state.begin_serverless_cache_creation("cache-1"));
1295
1296 state.cancel_serverless_cache_creation("cache-1");
1297 assert!(state.begin_serverless_cache_creation("cache-1"));
1298 }
1299
1300 #[test]
1301 fn finish_serverless_cache_creation_registers_cache_and_tags() {
1302 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1303 assert!(state.begin_serverless_cache_creation("cache-1"));
1304
1305 let cache = ServerlessCache {
1306 serverless_cache_name: "cache-1".to_string(),
1307 description: "test".to_string(),
1308 engine: "redis".to_string(),
1309 major_engine_version: "7.1".to_string(),
1310 full_engine_version: "7.1".to_string(),
1311 status: "available".to_string(),
1312 endpoint: ServerlessCacheEndpoint {
1313 address: "127.0.0.1".to_string(),
1314 port: 6379,
1315 },
1316 reader_endpoint: ServerlessCacheEndpoint {
1317 address: "127.0.0.1".to_string(),
1318 port: 6379,
1319 },
1320 arn: "arn:aws:elasticache:us-east-1:123456789012:serverlesscache:cache-1".to_string(),
1321 created_at: "2024-01-01T00:00:00Z".to_string(),
1322 cache_usage_limits: None,
1323 security_group_ids: Vec::new(),
1324 subnet_ids: Vec::new(),
1325 kms_key_id: None,
1326 user_group_id: None,
1327 snapshot_retention_limit: None,
1328 daily_snapshot_time: None,
1329 container_id: "cid".to_string(),
1330 host_port: 6379,
1331 };
1332
1333 state.finish_serverless_cache_creation(cache.clone());
1334
1335 assert!(state.serverless_caches.contains_key("cache-1"));
1336 assert!(state.tags.contains_key(&cache.arn));
1337 }
1338
1339 #[test]
1340 fn state_new_creates_default_user() {
1341 let state = ElastiCacheState::new("123456789012", "us-east-1");
1342 assert_eq!(state.users.len(), 1);
1343 let default = state.users.get("default").unwrap();
1344 assert_eq!(default.user_id, "default");
1345 assert_eq!(default.user_name, "default");
1346 assert_eq!(default.engine, "redis");
1347 assert_eq!(default.access_string, "on ~* +@all");
1348 assert_eq!(default.status, "active");
1349 assert_eq!(default.authentication_type, "no-password");
1350 assert_eq!(default.password_count, 0);
1351 assert!(default.arn.contains("user:default"));
1352 }
1353
1354 #[test]
1355 fn state_new_has_empty_user_groups() {
1356 let state = ElastiCacheState::new("123456789012", "us-east-1");
1357 assert!(state.user_groups.is_empty());
1358 }
1359
1360 #[test]
1361 fn reset_restores_default_user() {
1362 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1363 state.users.clear();
1364 assert!(state.users.is_empty());
1365 state.reset();
1366 assert_eq!(state.users.len(), 1);
1367 assert!(state.users.contains_key("default"));
1368 }
1369
1370 #[test]
1371 fn reset_clears_user_groups() {
1372 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1373 state.user_groups.insert(
1374 "my-group".to_string(),
1375 ElastiCacheUserGroup {
1376 user_group_id: "my-group".to_string(),
1377 engine: "redis".to_string(),
1378 status: "active".to_string(),
1379 user_ids: vec!["default".to_string()],
1380 arn: "arn:aws:elasticache:us-east-1:123456789012:usergroup:my-group".to_string(),
1381 minimum_engine_version: "6.0".to_string(),
1382 pending_changes: None,
1383 replication_groups: Vec::new(),
1384 },
1385 );
1386 assert_eq!(state.user_groups.len(), 1);
1387 state.reset();
1388 assert!(state.user_groups.is_empty());
1389 }
1390
1391 #[test]
1392 fn state_new_has_empty_snapshots() {
1393 let state = ElastiCacheState::new("123456789012", "us-east-1");
1394 assert!(state.snapshots.is_empty());
1395 }
1396
1397 #[test]
1398 fn reset_clears_snapshots() {
1399 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1400 state.snapshots.insert(
1401 "my-snapshot".to_string(),
1402 CacheSnapshot {
1403 snapshot_name: "my-snapshot".to_string(),
1404 replication_group_id: "rg-1".to_string(),
1405 replication_group_description: "test".to_string(),
1406 snapshot_status: "available".to_string(),
1407 cache_node_type: "cache.t3.micro".to_string(),
1408 engine: "redis".to_string(),
1409 engine_version: "7.1".to_string(),
1410 num_cache_clusters: 1,
1411 arn: "arn:aws:elasticache:us-east-1:123456789012:snapshot:my-snapshot".to_string(),
1412 created_at: "2024-01-01T00:00:00Z".to_string(),
1413 snapshot_source: "manual".to_string(),
1414 rdb_path: None,
1415 },
1416 );
1417 assert_eq!(state.snapshots.len(), 1);
1418 state.reset();
1419 assert!(state.snapshots.is_empty());
1420 }
1421
1422 #[test]
1423 fn reset_clears_replication_groups() {
1424 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1425 state.replication_groups.insert(
1426 "my-group".to_string(),
1427 ReplicationGroup {
1428 replication_group_id: "my-group".to_string(),
1429 description: "test".to_string(),
1430 global_replication_group_id: None,
1431 global_replication_group_role: None,
1432 status: "available".to_string(),
1433 cache_node_type: "cache.t3.micro".to_string(),
1434 engine: "redis".to_string(),
1435 engine_version: "7.1".to_string(),
1436 num_cache_clusters: 1,
1437 automatic_failover_enabled: false,
1438 endpoint_address: "127.0.0.1".to_string(),
1439 endpoint_port: 6379,
1440 arn: "arn:aws:elasticache:us-east-1:123456789012:replicationgroup:my-group"
1441 .to_string(),
1442 created_at: "2024-01-01T00:00:00Z".to_string(),
1443 container_id: "abc123".to_string(),
1444 host_port: 12345,
1445 member_clusters: vec!["my-group-001".to_string()],
1446 snapshot_retention_limit: 0,
1447 snapshot_window: "05:00-09:00".to_string(),
1448 transit_encryption_enabled: false,
1449 at_rest_encryption_enabled: false,
1450 cluster_enabled: false,
1451 kms_key_id: None,
1452 auth_token_enabled: false,
1453 user_group_ids: Vec::new(),
1454 multi_az_enabled: false,
1455 log_delivery_configurations: Vec::new(),
1456 data_tiering: None,
1457 ip_discovery: None,
1458 network_type: None,
1459 transit_encryption_mode: None,
1460 num_node_groups: 1,
1461 configuration_endpoint_address: None,
1462 configuration_endpoint_port: None,
1463 replicas_per_node_group: None,
1464 auth_token: None,
1465 port: 6379,
1466 notification_topic_arn: None,
1467 cluster_mode: None,
1468 data_tiering_enabled: None,
1469 notification_topic_status: None,
1470 cache_parameter_group_name: None,
1471 cache_subnet_group_name: None,
1472 security_group_ids: Vec::new(),
1473 preferred_maintenance_window: None,
1474 snapshot_name: None,
1475 snapshot_arns: Vec::new(),
1476 auto_minor_version_upgrade: true,
1477 },
1478 );
1479 assert_eq!(state.replication_groups.len(), 1);
1480 state.reset();
1481 assert!(state.replication_groups.is_empty());
1482 }
1483
1484 #[test]
1485 fn reset_clears_global_replication_groups() {
1486 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1487 state.global_replication_groups.insert(
1488 "global-rg".to_string(),
1489 GlobalReplicationGroup {
1490 global_replication_group_id: "global-rg".to_string(),
1491 global_replication_group_description: "test".to_string(),
1492 status: "available".to_string(),
1493 cache_node_type: "cache.t3.micro".to_string(),
1494 engine: "redis".to_string(),
1495 engine_version: "7.1".to_string(),
1496 members: vec![GlobalReplicationGroupMember {
1497 replication_group_id: "rg-1".to_string(),
1498 replication_group_region: "us-east-1".to_string(),
1499 role: "primary".to_string(),
1500 automatic_failover: false,
1501 status: "associated".to_string(),
1502 }],
1503 cluster_enabled: false,
1504 arn: "arn:aws:elasticache:us-east-1:123456789012:globalreplicationgroup:global-rg"
1505 .to_string(),
1506 num_node_groups: 1,
1507 },
1508 );
1509 assert_eq!(state.global_replication_groups.len(), 1);
1510 state.reset();
1511 assert!(state.global_replication_groups.is_empty());
1512 }
1513
1514 #[test]
1515 fn reset_clears_cache_clusters() {
1516 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1517 state.cache_clusters.insert(
1518 "classic-cluster".to_string(),
1519 CacheCluster {
1520 cache_cluster_id: "classic-cluster".to_string(),
1521 cache_node_type: "cache.t3.micro".to_string(),
1522 engine: "redis".to_string(),
1523 engine_version: "7.1".to_string(),
1524 cache_cluster_status: "available".to_string(),
1525 num_cache_nodes: 1,
1526 preferred_availability_zone: "us-east-1a".to_string(),
1527 cache_subnet_group_name: Some("default".to_string()),
1528 auto_minor_version_upgrade: true,
1529 arn: "arn:aws:elasticache:us-east-1:123456789012:cluster:classic-cluster"
1530 .to_string(),
1531 created_at: "2024-01-01T00:00:00Z".to_string(),
1532 endpoint_address: "127.0.0.1".to_string(),
1533 endpoint_port: 6379,
1534 container_id: "abc123".to_string(),
1535 host_port: 12345,
1536 replication_group_id: None,
1537 cache_parameter_group_name: None,
1538 security_group_ids: Vec::new(),
1539 log_delivery_configurations: Vec::new(),
1540 transit_encryption_enabled: false,
1541 at_rest_encryption_enabled: false,
1542 auth_token_enabled: false,
1543 port: 6379,
1544 preferred_maintenance_window: None,
1545 preferred_availability_zones: Vec::new(),
1546 notification_topic_arn: None,
1547 cache_security_group_names: Vec::new(),
1548 snapshot_arns: Vec::new(),
1549 snapshot_name: None,
1550 snapshot_retention_limit: 0,
1551 snapshot_window: None,
1552 outpost_mode: None,
1553 preferred_outpost_arn: None,
1554 network_type: None,
1555 ip_discovery: None,
1556 az_mode: None,
1557 auth_token: None,
1558 kms_key_id: None,
1559 transit_encryption_mode: None,
1560 data_tiering_enabled: None,
1561 cluster_mode: None,
1562 preferred_outpost_arns: Vec::new(),
1563 },
1564 );
1565 assert_eq!(state.cache_clusters.len(), 1);
1566 state.reset();
1567 assert!(state.cache_clusters.is_empty());
1568 }
1569
1570 #[test]
1571 fn reset_restores_reserved_cache_node_metadata() {
1572 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1573 state.reserved_cache_nodes.insert(
1574 "rcn-a".to_string(),
1575 ReservedCacheNode {
1576 reserved_cache_node_id: "rcn-a".to_string(),
1577 reserved_cache_nodes_offering_id: "offering-a".to_string(),
1578 cache_node_type: "cache.t3.micro".to_string(),
1579 start_time: "2024-01-01T00:00:00Z".to_string(),
1580 duration: 31_536_000,
1581 fixed_price: 0.0,
1582 usage_price: 0.011,
1583 cache_node_count: 1,
1584 product_description: "redis".to_string(),
1585 offering_type: "No Upfront".to_string(),
1586 state: "payment-pending".to_string(),
1587 recurring_charges: Vec::new(),
1588 reservation_arn:
1589 "arn:aws:elasticache:us-east-1:123456789012:reserved-instance:test".to_string(),
1590 },
1591 );
1592 state.reserved_cache_nodes_offerings.clear();
1593
1594 state.reset();
1595
1596 assert!(state.reserved_cache_nodes.is_empty());
1597 assert!(!state.reserved_cache_nodes_offerings.is_empty());
1598 }
1599
1600 #[test]
1601 fn reset_clears_serverless_cache_state() {
1602 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1603 state.serverless_caches.insert(
1604 "serverless".to_string(),
1605 ServerlessCache {
1606 serverless_cache_name: "serverless".to_string(),
1607 description: "test".to_string(),
1608 engine: "redis".to_string(),
1609 major_engine_version: "7.1".to_string(),
1610 full_engine_version: "7.1".to_string(),
1611 status: "available".to_string(),
1612 endpoint: ServerlessCacheEndpoint {
1613 address: "127.0.0.1".to_string(),
1614 port: 6379,
1615 },
1616 reader_endpoint: ServerlessCacheEndpoint {
1617 address: "127.0.0.1".to_string(),
1618 port: 6379,
1619 },
1620 arn: "arn:aws:elasticache:us-east-1:123456789012:serverlesscache:serverless"
1621 .to_string(),
1622 created_at: "2024-01-01T00:00:00Z".to_string(),
1623 cache_usage_limits: None,
1624 security_group_ids: Vec::new(),
1625 subnet_ids: Vec::new(),
1626 kms_key_id: None,
1627 user_group_id: None,
1628 snapshot_retention_limit: None,
1629 daily_snapshot_time: None,
1630 container_id: "cid".to_string(),
1631 host_port: 6379,
1632 },
1633 );
1634 state.serverless_cache_snapshots.insert(
1635 "snap-1".to_string(),
1636 ServerlessCacheSnapshot {
1637 serverless_cache_snapshot_name: "snap-1".to_string(),
1638 arn: "arn:aws:elasticache:us-east-1:123456789012:serverlesssnapshot:snap-1"
1639 .to_string(),
1640 kms_key_id: None,
1641 snapshot_type: "manual".to_string(),
1642 status: "available".to_string(),
1643 create_time: "2024-01-01T00:00:00Z".to_string(),
1644 expiry_time: None,
1645 bytes_used_for_cache: None,
1646 serverless_cache_name: "serverless".to_string(),
1647 engine: "redis".to_string(),
1648 major_engine_version: "7.1".to_string(),
1649 },
1650 );
1651
1652 state.reset();
1653
1654 assert!(state.serverless_caches.is_empty());
1655 assert!(state.serverless_cache_snapshots.is_empty());
1656 }
1657}