Add list_tags_for_resource for Elasticache (#7463)
This commit is contained in:
parent
5a0bc0d6cc
commit
0f6f689353
@ -82,3 +82,14 @@ class CacheClusterNotFound(ElastiCacheException):
|
|||||||
"CacheClusterNotFound",
|
"CacheClusterNotFound",
|
||||||
message=f"Cache cluster {cache_cluster_id} not found.",
|
message=f"Cache cluster {cache_cluster_id} not found.",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidARNFault(ElastiCacheException):
|
||||||
|
|
||||||
|
code = 400
|
||||||
|
|
||||||
|
def __init__(self, arn: str):
|
||||||
|
super().__init__(
|
||||||
|
"InvalidARNFault",
|
||||||
|
message=f"ARN {arn} is invalid.",
|
||||||
|
)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from re import compile as re_compile
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from moto.core.base_backend import BackendDict, BaseBackend
|
from moto.core.base_backend import BackendDict, BaseBackend
|
||||||
@ -8,6 +9,7 @@ from ..moto_api._internal import mock_random
|
|||||||
from .exceptions import (
|
from .exceptions import (
|
||||||
CacheClusterAlreadyExists,
|
CacheClusterAlreadyExists,
|
||||||
CacheClusterNotFound,
|
CacheClusterNotFound,
|
||||||
|
InvalidARNFault,
|
||||||
UserAlreadyExists,
|
UserAlreadyExists,
|
||||||
UserNotFound,
|
UserNotFound,
|
||||||
)
|
)
|
||||||
@ -78,7 +80,6 @@ class CacheCluster(BaseModel):
|
|||||||
):
|
):
|
||||||
if tags is None:
|
if tags is None:
|
||||||
tags = []
|
tags = []
|
||||||
|
|
||||||
self.cache_cluster_id = cache_cluster_id
|
self.cache_cluster_id = cache_cluster_id
|
||||||
self.az_mode = az_mode
|
self.az_mode = az_mode
|
||||||
self.preferred_availability_zone = preferred_availability_zone
|
self.preferred_availability_zone = preferred_availability_zone
|
||||||
@ -121,15 +122,23 @@ class CacheCluster(BaseModel):
|
|||||||
self.cache_cluster_create_time = utcnow()
|
self.cache_cluster_create_time = utcnow()
|
||||||
self.auth_token_last_modified_date = utcnow()
|
self.auth_token_last_modified_date = utcnow()
|
||||||
self.cache_cluster_status = "available"
|
self.cache_cluster_status = "available"
|
||||||
self.arn = f"arn:aws:elasticache:{region_name}:{account_id}:{cache_cluster_id}"
|
self.arn = (
|
||||||
|
f"arn:aws:elasticache:{region_name}:{account_id}:cluster:{cache_cluster_id}"
|
||||||
|
)
|
||||||
self.cache_node_id = str(mock_random.uuid4())
|
self.cache_node_id = str(mock_random.uuid4())
|
||||||
|
|
||||||
|
def get_tags(self) -> List[Dict[str, str]]:
|
||||||
|
return self.tags
|
||||||
|
|
||||||
|
|
||||||
class ElastiCacheBackend(BaseBackend):
|
class ElastiCacheBackend(BaseBackend):
|
||||||
"""Implementation of ElastiCache APIs."""
|
"""Implementation of ElastiCache APIs."""
|
||||||
|
|
||||||
def __init__(self, region_name: str, account_id: str):
|
def __init__(self, region_name: str, account_id: str):
|
||||||
super().__init__(region_name, account_id)
|
super().__init__(region_name, account_id)
|
||||||
|
self.arn_regex = re_compile(
|
||||||
|
r"^arn:aws:elasticache:.*:[0-9]*:(cluster|snapshot):.*$"
|
||||||
|
)
|
||||||
self.users = dict()
|
self.users = dict()
|
||||||
self.users["default"] = User(
|
self.users["default"] = User(
|
||||||
account_id=self.account_id,
|
account_id=self.account_id,
|
||||||
@ -331,5 +340,19 @@ class ElastiCacheBackend(BaseBackend):
|
|||||||
return cache_cluster
|
return cache_cluster
|
||||||
raise CacheClusterNotFound(cache_cluster_id)
|
raise CacheClusterNotFound(cache_cluster_id)
|
||||||
|
|
||||||
|
def list_tags_for_resource(self, arn: str) -> List[Dict[str, str]]:
|
||||||
|
if self.arn_regex.match(arn):
|
||||||
|
arn_breakdown = arn.split(":")
|
||||||
|
resource_type = arn_breakdown[len(arn_breakdown) - 2]
|
||||||
|
resource_name = arn_breakdown[len(arn_breakdown) - 1]
|
||||||
|
if resource_type == "cluster":
|
||||||
|
if resource_name in self.cache_clusters:
|
||||||
|
return self.cache_clusters[resource_name].get_tags()
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
raise InvalidARNFault(arn)
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
elasticache_backends = BackendDict(ElastiCacheBackend, "elasticache")
|
elasticache_backends = BackendDict(ElastiCacheBackend, "elasticache")
|
||||||
|
@ -67,7 +67,7 @@ class ElastiCacheResponse(BaseResponse):
|
|||||||
cache_subnet_group_name = self._get_param("CacheSubnetGroupName")
|
cache_subnet_group_name = self._get_param("CacheSubnetGroupName")
|
||||||
cache_security_group_names = self._get_param("CacheSecurityGroupNames")
|
cache_security_group_names = self._get_param("CacheSecurityGroupNames")
|
||||||
security_group_ids = self._get_param("SecurityGroupIds")
|
security_group_ids = self._get_param("SecurityGroupIds")
|
||||||
tags = self._get_param("Tags")
|
tags = (self._get_multi_param_dict("Tags") or {}).get("Tag", [])
|
||||||
snapshot_arns = self._get_param("SnapshotArns")
|
snapshot_arns = self._get_param("SnapshotArns")
|
||||||
snapshot_name = self._get_param("SnapshotName")
|
snapshot_name = self._get_param("SnapshotName")
|
||||||
preferred_maintenance_window = self._get_param("PreferredMaintenanceWindow")
|
preferred_maintenance_window = self._get_param("PreferredMaintenanceWindow")
|
||||||
@ -144,6 +144,12 @@ class ElastiCacheResponse(BaseResponse):
|
|||||||
template = self.response_template(DELETE_CACHE_CLUSTER_TEMPLATE)
|
template = self.response_template(DELETE_CACHE_CLUSTER_TEMPLATE)
|
||||||
return template.render(cache_cluster=cache_cluster)
|
return template.render(cache_cluster=cache_cluster)
|
||||||
|
|
||||||
|
def list_tags_for_resource(self) -> str:
|
||||||
|
arn = self._get_param("ResourceName")
|
||||||
|
template = self.response_template(LIST_TAGS_FOR_RESOURCE_TEMPLATE)
|
||||||
|
tags = self.elasticache_backend.list_tags_for_resource(arn)
|
||||||
|
return template.render(tags=tags)
|
||||||
|
|
||||||
|
|
||||||
USER_TEMPLATE = """<UserId>{{ user.id }}</UserId>
|
USER_TEMPLATE = """<UserId>{{ user.id }}</UserId>
|
||||||
<UserName>{{ user.name }}</UserName>
|
<UserName>{{ user.name }}</UserName>
|
||||||
@ -545,3 +551,19 @@ DELETE_CACHE_CLUSTER_TEMPLATE = """<DeleteCacheClusterResponse xmlns="http://ela
|
|||||||
</CacheCluster>
|
</CacheCluster>
|
||||||
</DeleteCacheClusterResult>
|
</DeleteCacheClusterResult>
|
||||||
</DeleteCacheClusterResponse>"""
|
</DeleteCacheClusterResponse>"""
|
||||||
|
|
||||||
|
LIST_TAGS_FOR_RESOURCE_TEMPLATE = """<ListTagsForResourceResponse xmlns="http://elasticache.amazonaws.com/doc/2015-02-02/">
|
||||||
|
<ListTagsForResourceResult>
|
||||||
|
<TagList>
|
||||||
|
{%- for tag in tags -%}
|
||||||
|
<Tag>
|
||||||
|
<Key>{{ tag['Key'] }}</Key>
|
||||||
|
<Value>{{ tag['Value'] }}</Value>
|
||||||
|
</Tag>
|
||||||
|
{%- endfor -%}
|
||||||
|
</TagList>
|
||||||
|
</ListTagsForResourceResult>
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>8c21ba39-a598-11e4-b688-194eaf8658fa</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</ListTagsForResourceResponse>"""
|
||||||
|
@ -417,3 +417,26 @@ def test_delete_unknown_cache_cluster():
|
|||||||
err = exc.value.response["Error"]
|
err = exc.value.response["Error"]
|
||||||
assert err["Code"] == "CacheClusterNotFound"
|
assert err["Code"] == "CacheClusterNotFound"
|
||||||
assert err["Message"] == f"Cache cluster {cache_cluster_id_unknown} not found."
|
assert err["Message"] == f"Cache cluster {cache_cluster_id_unknown} not found."
|
||||||
|
|
||||||
|
|
||||||
|
@mock_aws
|
||||||
|
def test_list_tags_cache_cluster():
|
||||||
|
conn = boto3.client("elasticache", region_name="ap-southeast-1")
|
||||||
|
result = conn.list_tags_for_resource(
|
||||||
|
ResourceName="arn:aws:elasticache:us-west-2:1234567890:cluster:foo"
|
||||||
|
)
|
||||||
|
assert result["TagList"] == []
|
||||||
|
test_instance = conn.create_cache_cluster(
|
||||||
|
CacheClusterId="test-cache-cluster",
|
||||||
|
Engine="memcached",
|
||||||
|
NumCacheNodes=2,
|
||||||
|
Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
|
||||||
|
SecurityGroupIds=["sg-1234"],
|
||||||
|
)
|
||||||
|
post_create_result = conn.list_tags_for_resource(
|
||||||
|
ResourceName=test_instance["CacheCluster"]["ARN"],
|
||||||
|
)
|
||||||
|
assert post_create_result["TagList"] == [
|
||||||
|
{"Value": "bar", "Key": "foo"},
|
||||||
|
{"Value": "bar1", "Key": "foo1"},
|
||||||
|
]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user