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}
601
602impl ElastiCacheState {
603 pub fn new(account_id: &str, region: &str) -> Self {
604 let parameter_groups = default_parameter_groups(account_id, region);
605 let subnet_groups = default_subnet_groups(account_id, region);
606 let users = default_users(account_id, region);
607 let mut tags: BTreeMap<String, Vec<(String, String)>> = subnet_groups
608 .values()
609 .map(|g| (g.arn.clone(), Vec::new()))
610 .collect();
611 for user in users.values() {
612 tags.insert(user.arn.clone(), Vec::new());
613 }
614 Self {
615 account_id: account_id.to_string(),
616 region: region.to_string(),
617 parameter_groups,
618 subnet_groups,
619 reserved_cache_nodes: BTreeMap::new(),
620 reserved_cache_nodes_offerings: default_reserved_cache_nodes_offerings(),
621 cache_clusters: BTreeMap::new(),
622 replication_groups: BTreeMap::new(),
623 global_replication_groups: BTreeMap::new(),
624 users,
625 user_groups: BTreeMap::new(),
626 snapshots: BTreeMap::new(),
627 serverless_caches: BTreeMap::new(),
628 serverless_cache_snapshots: BTreeMap::new(),
629 tags,
630 in_progress_cache_cluster_ids: HashSet::new(),
631 delete_requested_cache_clusters: HashSet::new(),
632 in_progress_replication_group_ids: HashSet::new(),
633 in_progress_serverless_cache_names: HashSet::new(),
634 security_groups: BTreeMap::new(),
635 parameter_group_parameters: BTreeMap::new(),
636 events: Vec::new(),
637 migrations: BTreeMap::new(),
638 }
639 }
640
641 pub fn reset(&mut self) {
642 self.parameter_groups = default_parameter_groups(&self.account_id, &self.region);
643 self.subnet_groups = default_subnet_groups(&self.account_id, &self.region);
644 self.reserved_cache_nodes.clear();
645 self.reserved_cache_nodes_offerings = default_reserved_cache_nodes_offerings();
646 self.cache_clusters.clear();
647 self.replication_groups.clear();
648 self.global_replication_groups.clear();
649 self.users = default_users(&self.account_id, &self.region);
650 self.user_groups.clear();
651 self.snapshots.clear();
652 self.serverless_caches.clear();
653 self.serverless_cache_snapshots.clear();
654 self.tags.clear();
655 for g in self.subnet_groups.values() {
656 self.tags.insert(g.arn.clone(), Vec::new());
657 }
658 for user in self.users.values() {
659 self.tags.insert(user.arn.clone(), Vec::new());
660 }
661 self.in_progress_cache_cluster_ids.clear();
662 self.in_progress_replication_group_ids.clear();
663 self.in_progress_serverless_cache_names.clear();
664 self.security_groups.clear();
665 self.parameter_group_parameters.clear();
666 self.events.clear();
667 self.migrations.clear();
668 }
669
670 pub fn begin_cache_cluster_creation(&mut self, cache_cluster_id: &str) -> bool {
671 if self.cache_clusters.contains_key(cache_cluster_id)
672 || self
673 .in_progress_cache_cluster_ids
674 .contains(cache_cluster_id)
675 {
676 return false;
677 }
678 self.in_progress_cache_cluster_ids
679 .insert(cache_cluster_id.to_string());
680 true
681 }
682
683 pub fn finish_cache_cluster_creation(&mut self, cluster: CacheCluster) {
684 self.in_progress_cache_cluster_ids
685 .remove(&cluster.cache_cluster_id);
686 self.tags.insert(cluster.arn.clone(), Vec::new());
687 self.cache_clusters
688 .insert(cluster.cache_cluster_id.clone(), cluster);
689 }
690
691 pub fn cancel_cache_cluster_creation(&mut self, cache_cluster_id: &str) {
692 self.in_progress_cache_cluster_ids.remove(cache_cluster_id);
693 }
694
695 pub fn cache_cluster_creation_in_progress(&self, cache_cluster_id: &str) -> bool {
698 self.in_progress_cache_cluster_ids
699 .contains(cache_cluster_id)
700 }
701
702 pub fn request_cache_cluster_delete_during_creation(&mut self, cache_cluster_id: &str) {
708 self.delete_requested_cache_clusters
709 .insert(cache_cluster_id.to_string());
710 }
711
712 pub fn take_cache_cluster_delete_request(&mut self, cache_cluster_id: &str) -> bool {
717 self.delete_requested_cache_clusters
718 .remove(cache_cluster_id)
719 }
720
721 pub fn begin_replication_group_creation(&mut self, replication_group_id: &str) -> bool {
722 if self.replication_groups.contains_key(replication_group_id)
723 || self
724 .in_progress_replication_group_ids
725 .contains(replication_group_id)
726 {
727 return false;
728 }
729 self.in_progress_replication_group_ids
730 .insert(replication_group_id.to_string());
731 true
732 }
733
734 pub fn finish_replication_group_creation(&mut self, group: ReplicationGroup) {
735 self.in_progress_replication_group_ids
736 .remove(&group.replication_group_id);
737 self.tags.insert(group.arn.clone(), Vec::new());
738 self.replication_groups
739 .insert(group.replication_group_id.clone(), group);
740 }
741
742 pub fn cancel_replication_group_creation(&mut self, replication_group_id: &str) {
743 self.in_progress_replication_group_ids
744 .remove(replication_group_id);
745 }
746
747 pub fn begin_serverless_cache_creation(&mut self, serverless_cache_name: &str) -> bool {
748 if self.serverless_caches.contains_key(serverless_cache_name)
749 || self
750 .in_progress_serverless_cache_names
751 .contains(serverless_cache_name)
752 {
753 return false;
754 }
755 self.in_progress_serverless_cache_names
756 .insert(serverless_cache_name.to_string());
757 true
758 }
759
760 pub fn finish_serverless_cache_creation(&mut self, cache: ServerlessCache) {
761 self.in_progress_serverless_cache_names
762 .remove(&cache.serverless_cache_name);
763 self.tags.insert(cache.arn.clone(), Vec::new());
764 self.serverless_caches
765 .insert(cache.serverless_cache_name.clone(), cache);
766 }
767
768 pub fn cancel_serverless_cache_creation(&mut self, serverless_cache_name: &str) {
769 self.in_progress_serverless_cache_names
770 .remove(serverless_cache_name);
771 }
772
773 pub fn register_arn(&mut self, arn: &str) {
774 self.tags.entry(arn.to_string()).or_default();
775 }
776
777 pub fn has_arn(&self, arn: &str) -> bool {
778 self.tags.contains_key(arn)
779 }
780}
781
782fn default_reserved_cache_nodes_offerings() -> Vec<ReservedCacheNodesOffering> {
783 vec![
784 ReservedCacheNodesOffering {
785 reserved_cache_nodes_offering_id: "off-cache-t3-micro-redis-1yr-no-upfront".to_string(),
786 cache_node_type: "cache.t3.micro".to_string(),
787 duration: 31_536_000,
788 fixed_price: 0.0,
789 usage_price: 0.011,
790 product_description: "redis".to_string(),
791 offering_type: "No Upfront".to_string(),
792 recurring_charges: Vec::new(),
793 },
794 ReservedCacheNodesOffering {
795 reserved_cache_nodes_offering_id: "off-cache-t3-small-redis-1yr-partial-upfront"
796 .to_string(),
797 cache_node_type: "cache.t3.small".to_string(),
798 duration: 31_536_000,
799 fixed_price: 120.0,
800 usage_price: 0.007,
801 product_description: "redis".to_string(),
802 offering_type: "Partial Upfront".to_string(),
803 recurring_charges: Vec::new(),
804 },
805 ReservedCacheNodesOffering {
806 reserved_cache_nodes_offering_id: "off-cache-m5-large-memcached-3yr-no-upfront"
807 .to_string(),
808 cache_node_type: "cache.m5.large".to_string(),
809 duration: 94_608_000,
810 fixed_price: 0.0,
811 usage_price: 0.033,
812 product_description: "memcached".to_string(),
813 offering_type: "No Upfront".to_string(),
814 recurring_charges: Vec::new(),
815 },
816 ReservedCacheNodesOffering {
817 reserved_cache_nodes_offering_id: "off-cache-r6g-large-redis-3yr-all-upfront"
818 .to_string(),
819 cache_node_type: "cache.r6g.large".to_string(),
820 duration: 94_608_000,
821 fixed_price: 1_550.0,
822 usage_price: 0.0,
823 product_description: "redis".to_string(),
824 offering_type: "All Upfront".to_string(),
825 recurring_charges: vec![RecurringCharge {
826 recurring_charge_amount: 0.0,
827 recurring_charge_frequency: "Hourly".to_string(),
828 }],
829 },
830 ]
831}
832
833pub fn default_engine_versions() -> Vec<CacheEngineVersion> {
834 vec![
835 CacheEngineVersion {
836 engine: "redis".to_string(),
837 engine_version: "7.1".to_string(),
838 cache_parameter_group_family: "redis7".to_string(),
839 cache_engine_description: "Redis".to_string(),
840 cache_engine_version_description: "Redis 7.1".to_string(),
841 },
842 CacheEngineVersion {
843 engine: "valkey".to_string(),
844 engine_version: "8.0".to_string(),
845 cache_parameter_group_family: "valkey8".to_string(),
846 cache_engine_description: "Valkey".to_string(),
847 cache_engine_version_description: "Valkey 8.0".to_string(),
848 },
849 CacheEngineVersion {
850 engine: "memcached".to_string(),
851 engine_version: "1.6.22".to_string(),
852 cache_parameter_group_family: "memcached1.6".to_string(),
853 cache_engine_description: "Memcached".to_string(),
854 cache_engine_version_description: "Memcached 1.6.22".to_string(),
855 },
856 ]
857}
858
859fn default_parameter_groups(account_id: &str, region: &str) -> Vec<CacheParameterGroup> {
860 vec![
861 CacheParameterGroup {
862 cache_parameter_group_name: "default.redis7".to_string(),
863 cache_parameter_group_family: "redis7".to_string(),
864 description: "Default parameter group for redis7".to_string(),
865 is_global: false,
866 arn: Arn::new(
867 "elasticache",
868 region,
869 account_id,
870 "parametergroup:default.redis7",
871 )
872 .to_string(),
873 },
874 CacheParameterGroup {
875 cache_parameter_group_name: "default.valkey8".to_string(),
876 cache_parameter_group_family: "valkey8".to_string(),
877 description: "Default parameter group for valkey8".to_string(),
878 is_global: false,
879 arn: Arn::new(
880 "elasticache",
881 region,
882 account_id,
883 "parametergroup:default.valkey8",
884 )
885 .to_string(),
886 },
887 CacheParameterGroup {
888 cache_parameter_group_name: "default.memcached1.6".to_string(),
889 cache_parameter_group_family: "memcached1.6".to_string(),
890 description: "Default parameter group for memcached1.6".to_string(),
891 is_global: false,
892 arn: Arn::new(
893 "elasticache",
894 region,
895 account_id,
896 "parametergroup:default.memcached1.6",
897 )
898 .to_string(),
899 },
900 ]
901}
902
903fn default_subnet_groups(account_id: &str, region: &str) -> BTreeMap<String, CacheSubnetGroup> {
904 let default_group = CacheSubnetGroup {
905 cache_subnet_group_name: "default".to_string(),
906 cache_subnet_group_description: "Default CacheSubnetGroup".to_string(),
907 vpc_id: "vpc-00000000".to_string(),
908 subnet_ids: vec!["subnet-00000000".to_string()],
909 arn: Arn::new("elasticache", region, account_id, "subnetgroup:default").to_string(),
910 };
911 let mut map = BTreeMap::new();
912 map.insert("default".to_string(), default_group);
913 map
914}
915
916pub fn default_parameters_for_family(family: &str) -> Vec<EngineDefaultParameter> {
917 match family {
918 "redis7" => vec![
919 EngineDefaultParameter {
920 parameter_name: "maxmemory-policy".to_string(),
921 parameter_value: "volatile-lru".to_string(),
922 description: "Max memory policy".to_string(),
923 source: "system".to_string(),
924 data_type: "string".to_string(),
925 allowed_values: "volatile-lru,allkeys-lru,volatile-lfu,allkeys-lfu,volatile-random,allkeys-random,volatile-ttl,noeviction".to_string(),
926 is_modifiable: true,
927 minimum_engine_version: "7.0.0".to_string(),
928 },
929 EngineDefaultParameter {
930 parameter_name: "cluster-enabled".to_string(),
931 parameter_value: "no".to_string(),
932 description: "Enable or disable Redis Cluster mode".to_string(),
933 source: "system".to_string(),
934 data_type: "string".to_string(),
935 allowed_values: "yes,no".to_string(),
936 is_modifiable: false,
937 minimum_engine_version: "7.0.0".to_string(),
938 },
939 EngineDefaultParameter {
940 parameter_name: "activedefrag".to_string(),
941 parameter_value: "no".to_string(),
942 description: "Enable active defragmentation".to_string(),
943 source: "system".to_string(),
944 data_type: "string".to_string(),
945 allowed_values: "yes,no".to_string(),
946 is_modifiable: true,
947 minimum_engine_version: "7.0.0".to_string(),
948 },
949 ],
950 "valkey8" => vec![
951 EngineDefaultParameter {
952 parameter_name: "maxmemory-policy".to_string(),
953 parameter_value: "volatile-lru".to_string(),
954 description: "Max memory policy".to_string(),
955 source: "system".to_string(),
956 data_type: "string".to_string(),
957 allowed_values: "volatile-lru,allkeys-lru,volatile-lfu,allkeys-lfu,volatile-random,allkeys-random,volatile-ttl,noeviction".to_string(),
958 is_modifiable: true,
959 minimum_engine_version: "8.0.0".to_string(),
960 },
961 EngineDefaultParameter {
962 parameter_name: "cluster-enabled".to_string(),
963 parameter_value: "no".to_string(),
964 description: "Enable or disable cluster mode".to_string(),
965 source: "system".to_string(),
966 data_type: "string".to_string(),
967 allowed_values: "yes,no".to_string(),
968 is_modifiable: false,
969 minimum_engine_version: "8.0.0".to_string(),
970 },
971 EngineDefaultParameter {
972 parameter_name: "activedefrag".to_string(),
973 parameter_value: "no".to_string(),
974 description: "Enable active defragmentation".to_string(),
975 source: "system".to_string(),
976 data_type: "string".to_string(),
977 allowed_values: "yes,no".to_string(),
978 is_modifiable: true,
979 minimum_engine_version: "8.0.0".to_string(),
980 },
981 ],
982 "memcached1.6" => vec![
983 EngineDefaultParameter {
984 parameter_name: "max_item_size".to_string(),
985 parameter_value: "1048576".to_string(),
986 description: "Maximum item size".to_string(),
987 source: "system".to_string(),
988 data_type: "integer".to_string(),
989 allowed_values: "1048576-1073741824".to_string(),
990 is_modifiable: true,
991 minimum_engine_version: "1.4.5".to_string(),
992 },
993 EngineDefaultParameter {
994 parameter_name: "max_simultaneous_connections".to_string(),
995 parameter_value: "65000".to_string(),
996 description: "Maximum number of concurrent connections".to_string(),
997 source: "system".to_string(),
998 data_type: "integer".to_string(),
999 allowed_values: "1-65000".to_string(),
1000 is_modifiable: false,
1001 minimum_engine_version: "1.4.5".to_string(),
1002 },
1003 ],
1004 _ => Vec::new(),
1005 }
1006}
1007
1008fn default_users(account_id: &str, region: &str) -> BTreeMap<String, ElastiCacheUser> {
1009 let mut map = BTreeMap::new();
1010 map.insert(
1011 "default".to_string(),
1012 ElastiCacheUser {
1013 user_id: "default".to_string(),
1014 user_name: "default".to_string(),
1015 engine: "redis".to_string(),
1016 access_string: "on ~* +@all".to_string(),
1017 status: "active".to_string(),
1018 authentication_type: "no-password".to_string(),
1019 password_count: 0,
1020 arn: Arn::new("elasticache", region, account_id, "user:default").to_string(),
1021 minimum_engine_version: "6.0".to_string(),
1022 user_group_ids: Vec::new(),
1023 },
1024 );
1025 map
1026}
1027
1028pub const ELASTICACHE_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
1029
1030#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1031pub struct ElastiCacheSnapshot {
1032 pub schema_version: u32,
1033 #[serde(default)]
1034 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<ElastiCacheState>>,
1035 #[serde(default)]
1036 pub state: Option<ElastiCacheState>,
1037}
1038
1039#[cfg(test)]
1040mod tests {
1041 use super::*;
1042
1043 #[test]
1044 fn default_engine_versions_contains_redis_valkey_memcached() {
1045 let versions = default_engine_versions();
1046 assert_eq!(versions.len(), 3);
1047 assert_eq!(versions[0].engine, "redis");
1048 assert_eq!(versions[0].engine_version, "7.1");
1049 assert_eq!(versions[1].engine, "valkey");
1050 assert_eq!(versions[1].engine_version, "8.0");
1051 assert_eq!(versions[2].engine, "memcached");
1052 assert_eq!(versions[2].engine_version, "1.6.22");
1053 }
1054
1055 #[test]
1056 fn state_new_creates_default_parameter_groups() {
1057 let state = ElastiCacheState::new("123456789012", "us-east-1");
1058 assert_eq!(state.parameter_groups.len(), 3);
1059 assert_eq!(
1060 state.parameter_groups[0].cache_parameter_group_name,
1061 "default.redis7"
1062 );
1063 assert_eq!(
1064 state.parameter_groups[1].cache_parameter_group_name,
1065 "default.valkey8"
1066 );
1067 assert_eq!(
1068 state.parameter_groups[2].cache_parameter_group_name,
1069 "default.memcached1.6"
1070 );
1071 }
1072
1073 #[test]
1074 fn state_new_creates_default_subnet_group() {
1075 let state = ElastiCacheState::new("123456789012", "us-east-1");
1076 assert_eq!(state.subnet_groups.len(), 1);
1077 let default = state.subnet_groups.get("default").unwrap();
1078 assert_eq!(default.cache_subnet_group_name, "default");
1079 assert_eq!(
1080 default.cache_subnet_group_description,
1081 "Default CacheSubnetGroup"
1082 );
1083 assert_eq!(default.vpc_id, "vpc-00000000");
1084 assert!(!default.subnet_ids.is_empty());
1085 assert!(default.arn.contains("subnetgroup:default"));
1086 }
1087
1088 #[test]
1089 fn reset_restores_default_parameter_groups() {
1090 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1091 state.parameter_groups.clear();
1092 assert!(state.parameter_groups.is_empty());
1093 state.reset();
1094 assert_eq!(state.parameter_groups.len(), 3);
1095 }
1096
1097 #[test]
1098 fn reset_restores_default_subnet_groups() {
1099 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1100 state.subnet_groups.clear();
1101 assert!(state.subnet_groups.is_empty());
1102 state.reset();
1103 assert_eq!(state.subnet_groups.len(), 1);
1104 assert!(state.subnet_groups.contains_key("default"));
1105 }
1106
1107 #[test]
1108 fn default_parameters_for_redis7_returns_parameters() {
1109 let params = default_parameters_for_family("redis7");
1110 assert_eq!(params.len(), 3);
1111 assert_eq!(params[0].parameter_name, "maxmemory-policy");
1112 }
1113
1114 #[test]
1115 fn default_parameters_for_unknown_family_returns_empty() {
1116 let params = default_parameters_for_family("unknown");
1117 assert!(params.is_empty());
1118 }
1119
1120 #[test]
1121 fn state_new_has_empty_replication_groups() {
1122 let state = ElastiCacheState::new("123456789012", "us-east-1");
1123 assert!(state.replication_groups.is_empty());
1124 }
1125
1126 #[test]
1127 fn state_new_has_empty_global_replication_groups() {
1128 let state = ElastiCacheState::new("123456789012", "us-east-1");
1129 assert!(state.global_replication_groups.is_empty());
1130 }
1131
1132 #[test]
1133 fn state_new_has_empty_cache_clusters() {
1134 let state = ElastiCacheState::new("123456789012", "us-east-1");
1135 assert!(state.cache_clusters.is_empty());
1136 }
1137
1138 #[test]
1139 fn state_new_has_empty_serverless_caches() {
1140 let state = ElastiCacheState::new("123456789012", "us-east-1");
1141 assert!(state.serverless_caches.is_empty());
1142 assert!(state.serverless_cache_snapshots.is_empty());
1143 }
1144
1145 #[test]
1146 fn begin_cache_cluster_creation_rejects_duplicate_ids() {
1147 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1148
1149 assert!(state.begin_cache_cluster_creation("cluster-1"));
1150 assert!(!state.begin_cache_cluster_creation("cluster-1"));
1151
1152 state.cancel_cache_cluster_creation("cluster-1");
1153 assert!(state.begin_cache_cluster_creation("cluster-1"));
1154 }
1155
1156 #[test]
1157 fn begin_replication_group_creation_rejects_duplicate_ids() {
1158 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1159
1160 assert!(state.begin_replication_group_creation("rg-1"));
1161 assert!(!state.begin_replication_group_creation("rg-1"));
1162
1163 state.cancel_replication_group_creation("rg-1");
1164 assert!(state.begin_replication_group_creation("rg-1"));
1165 }
1166
1167 #[test]
1168 fn begin_serverless_cache_creation_rejects_duplicate_names() {
1169 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1170
1171 assert!(state.begin_serverless_cache_creation("cache-1"));
1172 assert!(!state.begin_serverless_cache_creation("cache-1"));
1173
1174 state.cancel_serverless_cache_creation("cache-1");
1175 assert!(state.begin_serverless_cache_creation("cache-1"));
1176 }
1177
1178 #[test]
1179 fn finish_serverless_cache_creation_registers_cache_and_tags() {
1180 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1181 assert!(state.begin_serverless_cache_creation("cache-1"));
1182
1183 let cache = ServerlessCache {
1184 serverless_cache_name: "cache-1".to_string(),
1185 description: "test".to_string(),
1186 engine: "redis".to_string(),
1187 major_engine_version: "7.1".to_string(),
1188 full_engine_version: "7.1".to_string(),
1189 status: "available".to_string(),
1190 endpoint: ServerlessCacheEndpoint {
1191 address: "127.0.0.1".to_string(),
1192 port: 6379,
1193 },
1194 reader_endpoint: ServerlessCacheEndpoint {
1195 address: "127.0.0.1".to_string(),
1196 port: 6379,
1197 },
1198 arn: "arn:aws:elasticache:us-east-1:123456789012:serverlesscache:cache-1".to_string(),
1199 created_at: "2024-01-01T00:00:00Z".to_string(),
1200 cache_usage_limits: None,
1201 security_group_ids: Vec::new(),
1202 subnet_ids: Vec::new(),
1203 kms_key_id: None,
1204 user_group_id: None,
1205 snapshot_retention_limit: None,
1206 daily_snapshot_time: None,
1207 container_id: "cid".to_string(),
1208 host_port: 6379,
1209 };
1210
1211 state.finish_serverless_cache_creation(cache.clone());
1212
1213 assert!(state.serverless_caches.contains_key("cache-1"));
1214 assert!(state.tags.contains_key(&cache.arn));
1215 }
1216
1217 #[test]
1218 fn state_new_creates_default_user() {
1219 let state = ElastiCacheState::new("123456789012", "us-east-1");
1220 assert_eq!(state.users.len(), 1);
1221 let default = state.users.get("default").unwrap();
1222 assert_eq!(default.user_id, "default");
1223 assert_eq!(default.user_name, "default");
1224 assert_eq!(default.engine, "redis");
1225 assert_eq!(default.access_string, "on ~* +@all");
1226 assert_eq!(default.status, "active");
1227 assert_eq!(default.authentication_type, "no-password");
1228 assert_eq!(default.password_count, 0);
1229 assert!(default.arn.contains("user:default"));
1230 }
1231
1232 #[test]
1233 fn state_new_has_empty_user_groups() {
1234 let state = ElastiCacheState::new("123456789012", "us-east-1");
1235 assert!(state.user_groups.is_empty());
1236 }
1237
1238 #[test]
1239 fn reset_restores_default_user() {
1240 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1241 state.users.clear();
1242 assert!(state.users.is_empty());
1243 state.reset();
1244 assert_eq!(state.users.len(), 1);
1245 assert!(state.users.contains_key("default"));
1246 }
1247
1248 #[test]
1249 fn reset_clears_user_groups() {
1250 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1251 state.user_groups.insert(
1252 "my-group".to_string(),
1253 ElastiCacheUserGroup {
1254 user_group_id: "my-group".to_string(),
1255 engine: "redis".to_string(),
1256 status: "active".to_string(),
1257 user_ids: vec!["default".to_string()],
1258 arn: "arn:aws:elasticache:us-east-1:123456789012:usergroup:my-group".to_string(),
1259 minimum_engine_version: "6.0".to_string(),
1260 pending_changes: None,
1261 replication_groups: Vec::new(),
1262 },
1263 );
1264 assert_eq!(state.user_groups.len(), 1);
1265 state.reset();
1266 assert!(state.user_groups.is_empty());
1267 }
1268
1269 #[test]
1270 fn state_new_has_empty_snapshots() {
1271 let state = ElastiCacheState::new("123456789012", "us-east-1");
1272 assert!(state.snapshots.is_empty());
1273 }
1274
1275 #[test]
1276 fn reset_clears_snapshots() {
1277 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1278 state.snapshots.insert(
1279 "my-snapshot".to_string(),
1280 CacheSnapshot {
1281 snapshot_name: "my-snapshot".to_string(),
1282 replication_group_id: "rg-1".to_string(),
1283 replication_group_description: "test".to_string(),
1284 snapshot_status: "available".to_string(),
1285 cache_node_type: "cache.t3.micro".to_string(),
1286 engine: "redis".to_string(),
1287 engine_version: "7.1".to_string(),
1288 num_cache_clusters: 1,
1289 arn: "arn:aws:elasticache:us-east-1:123456789012:snapshot:my-snapshot".to_string(),
1290 created_at: "2024-01-01T00:00:00Z".to_string(),
1291 snapshot_source: "manual".to_string(),
1292 rdb_path: None,
1293 },
1294 );
1295 assert_eq!(state.snapshots.len(), 1);
1296 state.reset();
1297 assert!(state.snapshots.is_empty());
1298 }
1299
1300 #[test]
1301 fn reset_clears_replication_groups() {
1302 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1303 state.replication_groups.insert(
1304 "my-group".to_string(),
1305 ReplicationGroup {
1306 replication_group_id: "my-group".to_string(),
1307 description: "test".to_string(),
1308 global_replication_group_id: None,
1309 global_replication_group_role: None,
1310 status: "available".to_string(),
1311 cache_node_type: "cache.t3.micro".to_string(),
1312 engine: "redis".to_string(),
1313 engine_version: "7.1".to_string(),
1314 num_cache_clusters: 1,
1315 automatic_failover_enabled: false,
1316 endpoint_address: "127.0.0.1".to_string(),
1317 endpoint_port: 6379,
1318 arn: "arn:aws:elasticache:us-east-1:123456789012:replicationgroup:my-group"
1319 .to_string(),
1320 created_at: "2024-01-01T00:00:00Z".to_string(),
1321 container_id: "abc123".to_string(),
1322 host_port: 12345,
1323 member_clusters: vec!["my-group-001".to_string()],
1324 snapshot_retention_limit: 0,
1325 snapshot_window: "05:00-09:00".to_string(),
1326 transit_encryption_enabled: false,
1327 at_rest_encryption_enabled: false,
1328 cluster_enabled: false,
1329 kms_key_id: None,
1330 auth_token_enabled: false,
1331 user_group_ids: Vec::new(),
1332 multi_az_enabled: false,
1333 log_delivery_configurations: Vec::new(),
1334 data_tiering: None,
1335 ip_discovery: None,
1336 network_type: None,
1337 transit_encryption_mode: None,
1338 num_node_groups: 1,
1339 configuration_endpoint_address: None,
1340 configuration_endpoint_port: None,
1341 replicas_per_node_group: None,
1342 auth_token: None,
1343 port: 6379,
1344 notification_topic_arn: None,
1345 cluster_mode: None,
1346 data_tiering_enabled: None,
1347 notification_topic_status: None,
1348 cache_parameter_group_name: None,
1349 cache_subnet_group_name: None,
1350 security_group_ids: Vec::new(),
1351 preferred_maintenance_window: None,
1352 snapshot_name: None,
1353 snapshot_arns: Vec::new(),
1354 auto_minor_version_upgrade: true,
1355 },
1356 );
1357 assert_eq!(state.replication_groups.len(), 1);
1358 state.reset();
1359 assert!(state.replication_groups.is_empty());
1360 }
1361
1362 #[test]
1363 fn reset_clears_global_replication_groups() {
1364 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1365 state.global_replication_groups.insert(
1366 "global-rg".to_string(),
1367 GlobalReplicationGroup {
1368 global_replication_group_id: "global-rg".to_string(),
1369 global_replication_group_description: "test".to_string(),
1370 status: "available".to_string(),
1371 cache_node_type: "cache.t3.micro".to_string(),
1372 engine: "redis".to_string(),
1373 engine_version: "7.1".to_string(),
1374 members: vec![GlobalReplicationGroupMember {
1375 replication_group_id: "rg-1".to_string(),
1376 replication_group_region: "us-east-1".to_string(),
1377 role: "primary".to_string(),
1378 automatic_failover: false,
1379 status: "associated".to_string(),
1380 }],
1381 cluster_enabled: false,
1382 arn: "arn:aws:elasticache:us-east-1:123456789012:globalreplicationgroup:global-rg"
1383 .to_string(),
1384 num_node_groups: 1,
1385 },
1386 );
1387 assert_eq!(state.global_replication_groups.len(), 1);
1388 state.reset();
1389 assert!(state.global_replication_groups.is_empty());
1390 }
1391
1392 #[test]
1393 fn reset_clears_cache_clusters() {
1394 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1395 state.cache_clusters.insert(
1396 "classic-cluster".to_string(),
1397 CacheCluster {
1398 cache_cluster_id: "classic-cluster".to_string(),
1399 cache_node_type: "cache.t3.micro".to_string(),
1400 engine: "redis".to_string(),
1401 engine_version: "7.1".to_string(),
1402 cache_cluster_status: "available".to_string(),
1403 num_cache_nodes: 1,
1404 preferred_availability_zone: "us-east-1a".to_string(),
1405 cache_subnet_group_name: Some("default".to_string()),
1406 auto_minor_version_upgrade: true,
1407 arn: "arn:aws:elasticache:us-east-1:123456789012:cluster:classic-cluster"
1408 .to_string(),
1409 created_at: "2024-01-01T00:00:00Z".to_string(),
1410 endpoint_address: "127.0.0.1".to_string(),
1411 endpoint_port: 6379,
1412 container_id: "abc123".to_string(),
1413 host_port: 12345,
1414 replication_group_id: None,
1415 cache_parameter_group_name: None,
1416 security_group_ids: Vec::new(),
1417 log_delivery_configurations: Vec::new(),
1418 transit_encryption_enabled: false,
1419 at_rest_encryption_enabled: false,
1420 auth_token_enabled: false,
1421 port: 6379,
1422 preferred_maintenance_window: None,
1423 preferred_availability_zones: Vec::new(),
1424 notification_topic_arn: None,
1425 cache_security_group_names: Vec::new(),
1426 snapshot_arns: Vec::new(),
1427 snapshot_name: None,
1428 snapshot_retention_limit: 0,
1429 snapshot_window: None,
1430 outpost_mode: None,
1431 preferred_outpost_arn: None,
1432 network_type: None,
1433 ip_discovery: None,
1434 az_mode: None,
1435 auth_token: None,
1436 kms_key_id: None,
1437 transit_encryption_mode: None,
1438 data_tiering_enabled: None,
1439 cluster_mode: None,
1440 preferred_outpost_arns: Vec::new(),
1441 },
1442 );
1443 assert_eq!(state.cache_clusters.len(), 1);
1444 state.reset();
1445 assert!(state.cache_clusters.is_empty());
1446 }
1447
1448 #[test]
1449 fn reset_restores_reserved_cache_node_metadata() {
1450 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1451 state.reserved_cache_nodes.insert(
1452 "rcn-a".to_string(),
1453 ReservedCacheNode {
1454 reserved_cache_node_id: "rcn-a".to_string(),
1455 reserved_cache_nodes_offering_id: "offering-a".to_string(),
1456 cache_node_type: "cache.t3.micro".to_string(),
1457 start_time: "2024-01-01T00:00:00Z".to_string(),
1458 duration: 31_536_000,
1459 fixed_price: 0.0,
1460 usage_price: 0.011,
1461 cache_node_count: 1,
1462 product_description: "redis".to_string(),
1463 offering_type: "No Upfront".to_string(),
1464 state: "payment-pending".to_string(),
1465 recurring_charges: Vec::new(),
1466 reservation_arn:
1467 "arn:aws:elasticache:us-east-1:123456789012:reserved-instance:test".to_string(),
1468 },
1469 );
1470 state.reserved_cache_nodes_offerings.clear();
1471
1472 state.reset();
1473
1474 assert!(state.reserved_cache_nodes.is_empty());
1475 assert!(!state.reserved_cache_nodes_offerings.is_empty());
1476 }
1477
1478 #[test]
1479 fn reset_clears_serverless_cache_state() {
1480 let mut state = ElastiCacheState::new("123456789012", "us-east-1");
1481 state.serverless_caches.insert(
1482 "serverless".to_string(),
1483 ServerlessCache {
1484 serverless_cache_name: "serverless".to_string(),
1485 description: "test".to_string(),
1486 engine: "redis".to_string(),
1487 major_engine_version: "7.1".to_string(),
1488 full_engine_version: "7.1".to_string(),
1489 status: "available".to_string(),
1490 endpoint: ServerlessCacheEndpoint {
1491 address: "127.0.0.1".to_string(),
1492 port: 6379,
1493 },
1494 reader_endpoint: ServerlessCacheEndpoint {
1495 address: "127.0.0.1".to_string(),
1496 port: 6379,
1497 },
1498 arn: "arn:aws:elasticache:us-east-1:123456789012:serverlesscache:serverless"
1499 .to_string(),
1500 created_at: "2024-01-01T00:00:00Z".to_string(),
1501 cache_usage_limits: None,
1502 security_group_ids: Vec::new(),
1503 subnet_ids: Vec::new(),
1504 kms_key_id: None,
1505 user_group_id: None,
1506 snapshot_retention_limit: None,
1507 daily_snapshot_time: None,
1508 container_id: "cid".to_string(),
1509 host_port: 6379,
1510 },
1511 );
1512 state.serverless_cache_snapshots.insert(
1513 "snap-1".to_string(),
1514 ServerlessCacheSnapshot {
1515 serverless_cache_snapshot_name: "snap-1".to_string(),
1516 arn: "arn:aws:elasticache:us-east-1:123456789012:serverlesssnapshot:snap-1"
1517 .to_string(),
1518 kms_key_id: None,
1519 snapshot_type: "manual".to_string(),
1520 status: "available".to_string(),
1521 create_time: "2024-01-01T00:00:00Z".to_string(),
1522 expiry_time: None,
1523 bytes_used_for_cache: None,
1524 serverless_cache_name: "serverless".to_string(),
1525 engine: "redis".to_string(),
1526 major_engine_version: "7.1".to_string(),
1527 },
1528 );
1529
1530 state.reset();
1531
1532 assert!(state.serverless_caches.is_empty());
1533 assert!(state.serverless_cache_snapshots.is_empty());
1534 }
1535}