From ecf3de7851d30c37863f6f7a2f4b0fa41839c672 Mon Sep 17 00:00:00 2001 From: afuentes-19 <62731482+afuentes-19@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:29:57 -0500 Subject: [PATCH] Add ACM support for ResourceGroupsTaggingAPI (#7010) --- moto/backend_index.py | 2 +- moto/resourcegroupstaggingapi/models.py | 23 +++++++++-- .../test_resourcegroupstaggingapi.py | 41 +++++++++++++++++++ tests/test_sns/test_sns.py | 5 +++ 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 tests/test_sns/test_sns.py diff --git a/moto/backend_index.py b/moto/backend_index.py index 408f697ec..0fea6c0a4 100644 --- a/moto/backend_index.py +++ b/moto/backend_index.py @@ -1,4 +1,4 @@ -# autogenerated by /home/bblommers/Software/Code/bblommers/moto/scripts/update_backend_index.py +# autogenerated by moto/scripts/update_backend_index.py import re backend_url_patterns = [ diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py index 898921b2d..ed90dcaca 100644 --- a/moto/resourcegroupstaggingapi/models.py +++ b/moto/resourcegroupstaggingapi/models.py @@ -18,6 +18,7 @@ from moto.redshift.models import redshift_backends, RedshiftBackend from moto.emr.models import emr_backends, ElasticMapReduceBackend from moto.awslambda.models import lambda_backends, LambdaBackend from moto.ecs.models import ecs_backends, EC2ContainerServiceBackend +from moto.acm.models import acm_backends, AWSCertificateManagerBackend # Left: EC2 ElastiCache RDS ELB CloudFront WorkSpaces Lambda EMR Glacier Kinesis Redshift Route53 # StorageGateway DynamoDB MachineLearning ACM DirectConnect DirectoryService CloudHSM @@ -90,6 +91,10 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend): def ecs_backend(self) -> EC2ContainerServiceBackend: return ecs_backends[self.account_id][self.region_name] + @property + def acm_backend(self) -> AWSCertificateManagerBackend: + return acm_backends[self.account_id][self.region_name] + def _get_resources_generator( self, tag_filters: Optional[List[Dict[str, Any]]] = None, @@ -118,7 +123,9 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend): and v in vl ) - def tag_filter(tag_list: List[Dict[str, str]]) -> bool: + # Any in this case means: str | Optional[str] + # When we drop Python 3.7 we should look into replacing this with a TypedDict + def tag_filter(tag_list: List[Dict[str, Any]]) -> bool: result = [] if tag_filters: for f in filters: @@ -131,20 +138,28 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend): else: return True - def format_tags(tags: Dict[str, str]) -> List[Dict[str, str]]: + def format_tags(tags: Dict[str, Any]) -> List[Dict[str, Any]]: result = [] for key, value in tags.items(): result.append({"Key": key, "Value": value}) return result def format_tag_keys( - tags: List[Dict[str, str]], keys: List[str] - ) -> List[Dict[str, str]]: + tags: List[Dict[str, Any]], keys: List[str] + ) -> List[Dict[str, Any]]: result = [] for tag in tags: result.append({"Key": tag[keys[0]], "Value": tag[keys[1]]}) return result + # ACM + if not resource_type_filters or "acm" in resource_type_filters: + for certificate in self.acm_backend._certificates.values(): + tags = format_tags(certificate.tags) + if not tags or not tag_filter(tags): + continue + yield {"ResourceARN": f"{certificate.arn}", "Tags": tags} + # S3 if not resource_type_filters or "s3" in resource_type_filters: for bucket in self.s3_backend.buckets.values(): diff --git a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py index 2796a0480..5c921722e 100644 --- a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py +++ b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py @@ -3,6 +3,7 @@ import json import boto3 from botocore.client import ClientError +from moto import mock_acm from moto import mock_ec2 from moto import mock_elbv2 from moto import mock_kms @@ -13,6 +14,7 @@ from moto import mock_iam from moto import mock_cloudformation from moto import mock_ecs from tests import EXAMPLE_AMI_ID, EXAMPLE_AMI_ID2 +import logging @mock_kms @@ -75,6 +77,45 @@ def test_get_resources_cloudformation(): assert stack_two in resp["ResourceTagMappingList"][0]["ResourceARN"] +@mock_acm +@mock_resourcegroupstaggingapi +def test_get_resources_acm(): + client = boto3.client("acm", region_name="us-east-1") + cert_blue = client.request_certificate( + DomainName="helloworldone.com", + ValidationMethod="DNS", + Tags=[ + {"Key": "TagKey1", "Value": "TagValue1"}, + {"Key": "TagKey2", "Value": "TagValue2"}, + {"Key": "Color", "Value": "Blue"}, + ], + ) + client.request_certificate( + DomainName="helloworldtwo.com", + ValidationMethod="DNS", + Tags=[ + {"Key": "TagKey1", "Value": "TagValue1"}, + {"Key": "TagKey2", "Value": ""}, + {"Key": "Color", "Value": "Green"}, + ], + ) + rgta_client = boto3.client("resourcegroupstaggingapi", region_name="us-east-1") + resources_no_filter = rgta_client.get_resources( + ResourceTypeFilters=["acm"], + ) + assert len(resources_no_filter["ResourceTagMappingList"]) == 2 + logging.info(f"RESOURCES NO FILTER: {resources_no_filter}\n") + resources_blue_filter = rgta_client.get_resources( + TagFilters=[{"Key": "Color", "Values": ["Blue"]}] + ) + logging.info(f"RESOURCES BLUE FILTER: {resources_blue_filter}") + assert len(resources_blue_filter["ResourceTagMappingList"]) == 1 + assert ( + cert_blue["CertificateArn"] + == resources_blue_filter["ResourceTagMappingList"][0]["ResourceARN"] + ) + + @mock_ecs @mock_ec2 @mock_resourcegroupstaggingapi diff --git a/tests/test_sns/test_sns.py b/tests/test_sns/test_sns.py new file mode 100644 index 000000000..eca8fc81c --- /dev/null +++ b/tests/test_sns/test_sns.py @@ -0,0 +1,5 @@ +"""Unit tests for sns-supported APIs.""" + + +# See our Development Tips on writing tests for hints on how to write good tests: +# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html