2023-09-02 09:00:25 -04:00
|
|
|
from typing import List, Optional, Dict, Any, Tuple
|
|
|
|
|
2022-11-10 08:43:20 -01:00
|
|
|
from moto.core import BaseBackend, BackendDict, BaseModel
|
2023-09-11 22:23:44 +00:00
|
|
|
from moto.core.utils import utcnow
|
2021-12-07 11:09:13 -01:00
|
|
|
|
2023-09-02 09:00:25 -04:00
|
|
|
from .exceptions import (
|
|
|
|
UserAlreadyExists,
|
|
|
|
UserNotFound,
|
|
|
|
CacheClusterAlreadyExists,
|
|
|
|
CacheClusterNotFound,
|
|
|
|
)
|
|
|
|
from ..moto_api._internal import mock_random
|
2021-12-07 11:09:13 -01:00
|
|
|
|
|
|
|
|
|
|
|
class User(BaseModel):
|
|
|
|
def __init__(
|
|
|
|
self,
|
2023-02-24 09:29:21 -01:00
|
|
|
account_id: str,
|
|
|
|
region: str,
|
|
|
|
user_id: str,
|
|
|
|
user_name: str,
|
|
|
|
access_string: str,
|
|
|
|
engine: str,
|
|
|
|
no_password_required: bool,
|
|
|
|
passwords: Optional[List[str]] = None,
|
2021-12-07 11:09:13 -01:00
|
|
|
):
|
|
|
|
self.id = user_id
|
|
|
|
self.name = user_name
|
|
|
|
self.engine = engine
|
|
|
|
self.passwords = passwords or []
|
|
|
|
self.access_string = access_string
|
|
|
|
self.no_password_required = no_password_required
|
|
|
|
self.status = "active"
|
|
|
|
self.minimum_engine_version = "6.0"
|
2023-02-24 09:29:21 -01:00
|
|
|
self.usergroupids: List[str] = []
|
2021-12-07 11:09:13 -01:00
|
|
|
self.region = region
|
2022-08-13 09:49:43 +00:00
|
|
|
self.arn = f"arn:aws:elasticache:{self.region}:{account_id}:user:{self.id}"
|
2021-12-07 11:09:13 -01:00
|
|
|
|
|
|
|
|
2023-09-02 09:00:25 -04:00
|
|
|
class CacheCluster(BaseModel):
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
account_id: str,
|
|
|
|
region_name: str,
|
|
|
|
cache_cluster_id: str,
|
|
|
|
replication_group_id: Optional[str],
|
|
|
|
az_mode: Optional[str],
|
|
|
|
preferred_availability_zone: Optional[str],
|
|
|
|
num_cache_nodes: Optional[int],
|
|
|
|
cache_node_type: Optional[str],
|
|
|
|
engine: Optional[str],
|
|
|
|
engine_version: Optional[str],
|
|
|
|
cache_parameter_group_name: Optional[str],
|
|
|
|
cache_subnet_group_name: Optional[str],
|
|
|
|
transit_encryption_enabled: Optional[bool],
|
|
|
|
network_type: Optional[str],
|
|
|
|
ip_discovery: Optional[str],
|
|
|
|
snapshot_name: Optional[str],
|
|
|
|
preferred_maintenance_window: Optional[str],
|
|
|
|
port: Optional[int],
|
|
|
|
notification_topic_arn: Optional[str],
|
|
|
|
auto_minor_version_upgrade: Optional[bool],
|
|
|
|
snapshot_retention_limit: Optional[int],
|
|
|
|
snapshot_window: Optional[str],
|
|
|
|
auth_token: Optional[str],
|
|
|
|
outpost_mode: Optional[str],
|
|
|
|
preferred_outpost_arn: Optional[str],
|
|
|
|
preferred_availability_zones: Optional[List[str]],
|
|
|
|
cache_security_group_names: Optional[List[str]],
|
|
|
|
security_group_ids: Optional[List[str]],
|
|
|
|
tags: Optional[List[Dict[str, str]]],
|
|
|
|
snapshot_arns: Optional[List[str]],
|
|
|
|
preferred_outpost_arns: Optional[List[str]],
|
|
|
|
log_delivery_configurations: List[Dict[str, Any]],
|
|
|
|
cache_node_ids_to_remove: Optional[List[str]],
|
|
|
|
cache_node_ids_to_reboot: Optional[List[str]],
|
|
|
|
):
|
|
|
|
if tags is None:
|
|
|
|
tags = []
|
|
|
|
|
|
|
|
self.cache_cluster_id = cache_cluster_id
|
|
|
|
self.az_mode = az_mode
|
|
|
|
self.preferred_availability_zone = preferred_availability_zone
|
|
|
|
self.preferred_availability_zones = preferred_availability_zones or []
|
|
|
|
self.engine = engine or "redis"
|
|
|
|
self.engine_version = engine_version
|
|
|
|
if engine == "redis":
|
|
|
|
self.num_cache_nodes = 1
|
|
|
|
self.replication_group_id = replication_group_id
|
|
|
|
self.snapshot_arns = snapshot_arns or []
|
|
|
|
self.snapshot_name = snapshot_name
|
|
|
|
self.snapshot_window = snapshot_window
|
|
|
|
if engine == "memcached":
|
|
|
|
if num_cache_nodes is None:
|
|
|
|
self.num_cache_nodes = 1
|
|
|
|
elif 1 <= num_cache_nodes <= 40:
|
|
|
|
self.num_cache_nodes = num_cache_nodes
|
|
|
|
self.cache_node_type = cache_node_type
|
|
|
|
self.cache_parameter_group_name = cache_parameter_group_name
|
|
|
|
self.cache_subnet_group_name = cache_subnet_group_name
|
|
|
|
self.cache_security_group_names = cache_security_group_names or []
|
|
|
|
self.security_group_ids = security_group_ids or []
|
|
|
|
self.tags = tags
|
|
|
|
self.preferred_maintenance_window = preferred_maintenance_window
|
|
|
|
self.port = port or 6379
|
|
|
|
self.notification_topic_arn = notification_topic_arn
|
|
|
|
self.auto_minor_version_upgrade = auto_minor_version_upgrade
|
|
|
|
self.snapshot_retention_limit = snapshot_retention_limit or 0
|
|
|
|
self.auth_token = auth_token
|
|
|
|
self.outpost_mode = outpost_mode
|
|
|
|
self.preferred_outpost_arn = preferred_outpost_arn
|
|
|
|
self.preferred_outpost_arns = preferred_outpost_arns or []
|
|
|
|
self.log_delivery_configurations = log_delivery_configurations or []
|
|
|
|
self.transit_encryption_enabled = transit_encryption_enabled
|
|
|
|
self.network_type = network_type
|
|
|
|
self.ip_discovery = ip_discovery
|
|
|
|
self.cache_node_ids_to_remove = cache_node_ids_to_remove
|
|
|
|
self.cache_node_ids_to_reboot = cache_node_ids_to_reboot
|
|
|
|
|
2023-09-11 22:23:44 +00:00
|
|
|
self.cache_cluster_create_time = utcnow()
|
|
|
|
self.auth_token_last_modified_date = utcnow()
|
2023-09-02 09:00:25 -04:00
|
|
|
self.cache_cluster_status = "available"
|
|
|
|
self.arn = f"arn:aws:elasticache:{region_name}:{account_id}:{cache_cluster_id}"
|
|
|
|
self.cache_node_id = str(mock_random.uuid4())
|
|
|
|
|
|
|
|
|
2021-12-07 11:09:13 -01:00
|
|
|
class ElastiCacheBackend(BaseBackend):
|
|
|
|
"""Implementation of ElastiCache APIs."""
|
|
|
|
|
2023-02-24 09:29:21 -01:00
|
|
|
def __init__(self, region_name: str, account_id: str):
|
2022-06-04 11:30:16 +00:00
|
|
|
super().__init__(region_name, account_id)
|
2021-12-07 11:09:13 -01:00
|
|
|
self.users = dict()
|
|
|
|
self.users["default"] = User(
|
2022-08-13 09:49:43 +00:00
|
|
|
account_id=self.account_id,
|
2021-12-07 11:09:13 -01:00
|
|
|
region=self.region_name,
|
|
|
|
user_id="default",
|
|
|
|
user_name="default",
|
|
|
|
engine="redis",
|
|
|
|
access_string="on ~* +@all",
|
|
|
|
no_password_required=True,
|
|
|
|
)
|
|
|
|
|
2023-09-02 09:00:25 -04:00
|
|
|
# Define the cache_clusters dictionary to detect duplicates
|
|
|
|
self.cache_clusters = dict()
|
|
|
|
self.cache_clusters["default"] = CacheCluster(
|
|
|
|
account_id=self.account_id,
|
|
|
|
region_name=self.region_name,
|
|
|
|
cache_cluster_id="default",
|
|
|
|
replication_group_id=None,
|
|
|
|
az_mode=None,
|
|
|
|
preferred_availability_zone=None,
|
|
|
|
num_cache_nodes=1,
|
|
|
|
cache_node_type=None,
|
|
|
|
engine="redis",
|
|
|
|
engine_version=None,
|
|
|
|
cache_parameter_group_name=None,
|
|
|
|
cache_subnet_group_name=None,
|
|
|
|
transit_encryption_enabled=True,
|
|
|
|
network_type=None,
|
|
|
|
ip_discovery=None,
|
|
|
|
snapshot_name=None,
|
|
|
|
preferred_maintenance_window=None,
|
|
|
|
port=6379,
|
|
|
|
notification_topic_arn=None,
|
|
|
|
auto_minor_version_upgrade=True,
|
|
|
|
snapshot_retention_limit=0,
|
|
|
|
snapshot_window=None,
|
|
|
|
auth_token=None,
|
|
|
|
outpost_mode=None,
|
|
|
|
preferred_outpost_arn=None,
|
|
|
|
preferred_availability_zones=[],
|
|
|
|
cache_security_group_names=[],
|
|
|
|
security_group_ids=[],
|
|
|
|
tags=[],
|
|
|
|
snapshot_arns=[],
|
|
|
|
preferred_outpost_arns=[],
|
|
|
|
log_delivery_configurations=[],
|
|
|
|
cache_node_ids_to_remove=[],
|
|
|
|
cache_node_ids_to_reboot=[],
|
|
|
|
)
|
|
|
|
|
2021-12-07 11:09:13 -01:00
|
|
|
def create_user(
|
2023-02-24 09:29:21 -01:00
|
|
|
self,
|
|
|
|
user_id: str,
|
|
|
|
user_name: str,
|
|
|
|
engine: str,
|
|
|
|
passwords: List[str],
|
|
|
|
access_string: str,
|
|
|
|
no_password_required: bool,
|
|
|
|
) -> User:
|
2021-12-07 11:09:13 -01:00
|
|
|
if user_id in self.users:
|
|
|
|
raise UserAlreadyExists
|
|
|
|
user = User(
|
2022-08-13 09:49:43 +00:00
|
|
|
account_id=self.account_id,
|
2021-12-07 11:09:13 -01:00
|
|
|
region=self.region_name,
|
|
|
|
user_id=user_id,
|
|
|
|
user_name=user_name,
|
|
|
|
engine=engine,
|
|
|
|
passwords=passwords,
|
|
|
|
access_string=access_string,
|
|
|
|
no_password_required=no_password_required,
|
|
|
|
)
|
|
|
|
self.users[user_id] = user
|
|
|
|
return user
|
|
|
|
|
2023-02-24 09:29:21 -01:00
|
|
|
def delete_user(self, user_id: str) -> User:
|
2021-12-07 11:09:13 -01:00
|
|
|
if user_id in self.users:
|
|
|
|
user = self.users[user_id]
|
|
|
|
if user.status == "active":
|
|
|
|
user.status = "deleting"
|
|
|
|
return user
|
|
|
|
raise UserNotFound(user_id)
|
|
|
|
|
2023-02-24 09:29:21 -01:00
|
|
|
def describe_users(self, user_id: Optional[str]) -> List[User]:
|
2021-12-07 11:09:13 -01:00
|
|
|
"""
|
|
|
|
Only the `user_id` parameter is currently supported.
|
|
|
|
Pagination is not yet implemented.
|
|
|
|
"""
|
|
|
|
if user_id:
|
|
|
|
if user_id in self.users:
|
|
|
|
user = self.users[user_id]
|
|
|
|
if user.status == "deleting":
|
|
|
|
self.users.pop(user_id)
|
|
|
|
return [user]
|
|
|
|
else:
|
|
|
|
raise UserNotFound(user_id)
|
2023-02-24 09:29:21 -01:00
|
|
|
return list(self.users.values())
|
2021-12-07 11:09:13 -01:00
|
|
|
|
2023-09-02 09:00:25 -04:00
|
|
|
def create_cache_cluster(
|
|
|
|
self,
|
|
|
|
cache_cluster_id: str,
|
|
|
|
replication_group_id: str,
|
|
|
|
az_mode: str,
|
|
|
|
preferred_availability_zone: str,
|
|
|
|
num_cache_nodes: int,
|
|
|
|
cache_node_type: str,
|
|
|
|
engine: str,
|
|
|
|
engine_version: str,
|
|
|
|
cache_parameter_group_name: str,
|
|
|
|
cache_subnet_group_name: str,
|
|
|
|
transit_encryption_enabled: bool,
|
|
|
|
network_type: str,
|
|
|
|
ip_discovery: str,
|
|
|
|
snapshot_name: str,
|
|
|
|
preferred_maintenance_window: str,
|
|
|
|
port: int,
|
|
|
|
notification_topic_arn: str,
|
|
|
|
auto_minor_version_upgrade: bool,
|
|
|
|
snapshot_retention_limit: int,
|
|
|
|
snapshot_window: str,
|
|
|
|
auth_token: str,
|
|
|
|
outpost_mode: str,
|
|
|
|
preferred_outpost_arn: str,
|
|
|
|
preferred_availability_zones: List[str],
|
|
|
|
cache_security_group_names: List[str],
|
|
|
|
security_group_ids: List[str],
|
|
|
|
tags: List[Dict[str, str]],
|
|
|
|
snapshot_arns: List[str],
|
|
|
|
preferred_outpost_arns: List[str],
|
|
|
|
log_delivery_configurations: List[Dict[str, Any]],
|
|
|
|
cache_node_ids_to_remove: List[str],
|
|
|
|
cache_node_ids_to_reboot: List[str],
|
|
|
|
) -> CacheCluster:
|
|
|
|
if cache_cluster_id in self.cache_clusters:
|
|
|
|
raise CacheClusterAlreadyExists(cache_cluster_id)
|
|
|
|
cache_cluster = CacheCluster(
|
|
|
|
account_id=self.account_id,
|
|
|
|
region_name=self.region_name,
|
|
|
|
cache_cluster_id=cache_cluster_id,
|
|
|
|
replication_group_id=replication_group_id,
|
|
|
|
az_mode=az_mode,
|
|
|
|
preferred_availability_zone=preferred_availability_zone,
|
|
|
|
preferred_availability_zones=preferred_availability_zones,
|
|
|
|
num_cache_nodes=num_cache_nodes,
|
|
|
|
cache_node_type=cache_node_type,
|
|
|
|
engine=engine,
|
|
|
|
engine_version=engine_version,
|
|
|
|
cache_parameter_group_name=cache_parameter_group_name,
|
|
|
|
cache_subnet_group_name=cache_subnet_group_name,
|
|
|
|
cache_security_group_names=cache_security_group_names,
|
|
|
|
security_group_ids=security_group_ids,
|
|
|
|
tags=tags,
|
|
|
|
snapshot_arns=snapshot_arns,
|
|
|
|
snapshot_name=snapshot_name,
|
|
|
|
preferred_maintenance_window=preferred_maintenance_window,
|
|
|
|
port=port,
|
|
|
|
notification_topic_arn=notification_topic_arn,
|
|
|
|
auto_minor_version_upgrade=auto_minor_version_upgrade,
|
|
|
|
snapshot_retention_limit=snapshot_retention_limit,
|
|
|
|
snapshot_window=snapshot_window,
|
|
|
|
auth_token=auth_token,
|
|
|
|
outpost_mode=outpost_mode,
|
|
|
|
preferred_outpost_arn=preferred_outpost_arn,
|
|
|
|
preferred_outpost_arns=preferred_outpost_arns,
|
|
|
|
log_delivery_configurations=log_delivery_configurations,
|
|
|
|
transit_encryption_enabled=transit_encryption_enabled,
|
|
|
|
network_type=network_type,
|
|
|
|
ip_discovery=ip_discovery,
|
|
|
|
cache_node_ids_to_remove=cache_node_ids_to_remove,
|
|
|
|
cache_node_ids_to_reboot=cache_node_ids_to_reboot,
|
|
|
|
)
|
|
|
|
self.cache_clusters[cache_cluster_id] = cache_cluster
|
|
|
|
return cache_cluster
|
|
|
|
|
|
|
|
def describe_cache_clusters(
|
|
|
|
self,
|
|
|
|
cache_cluster_id: str,
|
|
|
|
max_records: int,
|
|
|
|
marker: str,
|
|
|
|
) -> Tuple[str, List[CacheCluster]]:
|
|
|
|
if marker is None:
|
|
|
|
marker = str(mock_random.uuid4())
|
|
|
|
if max_records is None:
|
|
|
|
max_records = 100
|
|
|
|
if cache_cluster_id:
|
|
|
|
if cache_cluster_id in self.cache_clusters:
|
|
|
|
cache_cluster = self.cache_clusters[cache_cluster_id]
|
|
|
|
return marker, [cache_cluster]
|
|
|
|
else:
|
|
|
|
raise CacheClusterNotFound(cache_cluster_id)
|
|
|
|
cache_clusters = list(self.cache_clusters.values())[:max_records]
|
|
|
|
|
|
|
|
return marker, cache_clusters
|
|
|
|
|
|
|
|
def delete_cache_cluster(self, cache_cluster_id: str) -> CacheCluster:
|
|
|
|
if cache_cluster_id:
|
|
|
|
if cache_cluster_id in self.cache_clusters:
|
|
|
|
cache_cluster = self.cache_clusters[cache_cluster_id]
|
|
|
|
cache_cluster.cache_cluster_status = "deleting"
|
|
|
|
return cache_cluster
|
|
|
|
raise CacheClusterNotFound(cache_cluster_id)
|
|
|
|
|
2021-12-07 11:09:13 -01:00
|
|
|
|
2021-12-24 20:02:45 -01:00
|
|
|
elasticache_backends = BackendDict(ElastiCacheBackend, "elasticache")
|