diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py index d7e77938e..48047dc50 100644 --- a/moto/resourcegroupstaggingapi/models.py +++ b/moto/resourcegroupstaggingapi/models.py @@ -7,6 +7,7 @@ from moto.s3.models import s3_backends, S3Backend from moto.ec2 import ec2_backends from moto.elb.models import elb_backends, ELBBackend from moto.elbv2.models import elbv2_backends, ELBv2Backend +from moto.glue.models import glue_backends, GlueBackend from moto.kinesis.models import kinesis_backends, KinesisBackend from moto.kms.models import kms_backends, KmsBackend from moto.rds.models import rds_backends, RDSBackend @@ -47,6 +48,10 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend): def elbv2_backend(self) -> ELBv2Backend: return elbv2_backends[self.account_id][self.region_name] + @property + def glue_backend(self) -> GlueBackend: + return glue_backends[self.account_id][self.region_name] + @property def kinesis_backend(self) -> KinesisBackend: return kinesis_backends[self.account_id][self.region_name] @@ -328,6 +333,29 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend): # Glacier Vault + # Glue + if not resource_type_filters or any( + ("glue" in _type) for _type in resource_type_filters + ): + if not resource_type_filters or "glue" in resource_type_filters: + arns_starting_with = [ + f"arn:aws:glue:{self.region_name}:{self.account_id}:" + ] + else: + arns_starting_with = [] + for resource_type in resource_type_filters: + if resource_type.startswith("glue:"): + glue_type = resource_type.split(":")[-1] + arns_starting_with.append( + f"arn:aws:glue:{self.region_name}:{self.account_id}:{glue_type}" + ) + for glue_arn in self.glue_backend.tagger.tags.keys(): + if any(glue_arn.startswith(arn) for arn in arns_starting_with): + tags = self.glue_backend.tagger.list_tags_for_resource(glue_arn)[ + "Tags" + ] + yield {"ResourceARN": glue_arn, "Tags": tags} + # Kinesis # KMS @@ -506,6 +534,11 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend): for key in get_ec2_keys(volume.id): # type: ignore[assignment] yield key + # Glue + for tag_dict in self.glue_backend.tagger.tags.values(): + for tag_key in tag_dict.keys(): + yield tag_key + def _get_tag_values_generator(self, tag_key: str) -> Iterator[str]: # Look at # https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html @@ -561,6 +594,12 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend): for value in get_ec2_values(volume.id): # type: ignore[assignment] yield value + # Glue + for tag_dict in self.glue_backend.tagger.tags.values(): + for key, tag_value in tag_dict.items(): + if key == tag_key and tag_value is not None: + yield tag_value + def get_resources( self, pagination_token: Optional[str] = None, diff --git a/scripts/dependency_test.sh b/scripts/dependency_test.sh index 8113dc781..1e1f94e96 100755 --- a/scripts/dependency_test.sh +++ b/scripts/dependency_test.sh @@ -33,7 +33,7 @@ valid_service() { # Verify whether this is a valid service # We'll ignore metadata folders, and folders that test generic Moto behaviour # We'll also ignore CloudFormation, as it will always depend on other services - local ignore_moto_folders="core instance_metadata __pycache__ templates cloudformation moto_api moto_server resourcegroupstaggingapi packages utilities s3bucket_path" + local ignore_moto_folders="core instance_metadata __pycache__ templates cloudformation moto_api moto_server packages utilities s3bucket_path" if echo $ignore_moto_folders | grep -q "$1"; then return 1 else diff --git a/setup.cfg b/setup.cfg index f833b2496..1c6491291 100644 --- a/setup.cfg +++ b/setup.cfg @@ -179,6 +179,17 @@ redshiftdata = rekognition = resourcegroups = resourcegroupstaggingapi = + python-jose[cryptography]>=3.1.0,<4.0.0 + ecdsa!=0.15 + docker>=3.0.0 + graphql-core + PyYAML>=5.1 + cfn-lint>=0.40.0 + sshpubkeys>=3.1.0 + openapi-spec-validator>=0.2.8 + pyparsing>=3.0.7 + jsondiff>=1.1.2 + py-partiql-parser==0.3.6 route53 = route53resolver = sshpubkeys>=3.1.0 s3 = diff --git a/tests/test_resourcegroupstaggingapi/test_resourcegroupstagging_glue.py b/tests/test_resourcegroupstaggingapi/test_resourcegroupstagging_glue.py new file mode 100644 index 000000000..3f1329b72 --- /dev/null +++ b/tests/test_resourcegroupstaggingapi/test_resourcegroupstagging_glue.py @@ -0,0 +1,55 @@ +import boto3 + +from moto import mock_glue, mock_resourcegroupstaggingapi +from moto.core import DEFAULT_ACCOUNT_ID +from uuid import uuid4 + + +@mock_glue +@mock_resourcegroupstaggingapi +def test_glue_jobs(): + glue = boto3.client("glue", region_name="us-west-1") + job_name = glue.create_job( + Name=str(uuid4()), + Role="test_role", + Command=dict(Name="test_command"), + Tags={"k1": "v1"}, + )["Name"] + job_arn = f"arn:aws:glue:us-west-1:{DEFAULT_ACCOUNT_ID}:job/{job_name}" + + rtapi = boto3.client("resourcegroupstaggingapi", region_name="us-west-1") + resources = rtapi.get_resources(ResourceTypeFilters=["glue"])[ + "ResourceTagMappingList" + ] + assert resources == [ + {"ResourceARN": job_arn, "Tags": [{"Key": "k1", "Value": "v1"}]} + ] + + resources = rtapi.get_resources(ResourceTypeFilters=["glue:job"])[ + "ResourceTagMappingList" + ] + assert resources == [ + {"ResourceARN": job_arn, "Tags": [{"Key": "k1", "Value": "v1"}]} + ] + + resources = rtapi.get_resources(TagFilters=[{"Key": "k1", "Values": ["v1"]}])[ + "ResourceTagMappingList" + ] + assert resources == [ + {"ResourceARN": job_arn, "Tags": [{"Key": "k1", "Value": "v1"}]} + ] + + resources = rtapi.get_resources(ResourceTypeFilters=["glue:table"])[ + "ResourceTagMappingList" + ] + assert resources == [] + + resources = rtapi.get_resources(ResourceTypeFilters=["ec2"])[ + "ResourceTagMappingList" + ] + assert resources == [] + + assert rtapi.get_tag_keys()["TagKeys"] == ["k1"] + + assert rtapi.get_tag_values(Key="k1")["TagValues"] == ["v1"] + assert rtapi.get_tag_values(Key="unknown")["TagValues"] == []