diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 15d2b7f49..a6a66e7ce 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -4368,7 +4368,7 @@
## logs
-60% implemented
+66% implemented
- [ ] associate_kms_key
- [ ] cancel_export_task
@@ -4402,7 +4402,7 @@
- [ ] get_log_group_fields
- [ ] get_log_record
- [X] get_query_results
-- [ ] list_tags_for_resource
+- [X] list_tags_for_resource
- [X] list_tags_log_group
- [ ] put_account_policy
- [ ] put_data_protection_policy
@@ -4417,10 +4417,10 @@
- [X] start_query
- [ ] stop_query
- [X] tag_log_group
-- [ ] tag_resource
+- [X] tag_resource
- [ ] test_metric_filter
- [X] untag_log_group
-- [ ] untag_resource
+- [X] untag_resource
## managedblockchain
@@ -5792,7 +5792,7 @@
## resourcegroupstaggingapi
-37% implemented
+50% implemented
- [ ] describe_report_creation
- [ ] get_compliance_summary
@@ -5800,7 +5800,7 @@
- [X] get_tag_keys
- [X] get_tag_values
- [ ] start_report_creation
-- [ ] tag_resources
+- [X] tag_resources
- [ ] untag_resources
diff --git a/docs/docs/services/logs.rst b/docs/docs/services/logs.rst
index af2c105ec..b9862a937 100644
--- a/docs/docs/services/logs.rst
+++ b/docs/docs/services/logs.rst
@@ -38,7 +38,9 @@ logs
- [X] delete_metric_filter
- [ ] delete_query_definition
- [X] delete_resource_policy
- Remove resource policy with a policy name matching given name.
+
+ Remove resource policy with a policy name matching given name.
+
- [X] delete_retention_policy
- [X] delete_subscription_filter
@@ -55,7 +57,8 @@ logs
- [ ] describe_query_definitions
- [X] describe_resource_policies
- Return list of resource policies.
+
+ Return list of resource policies.
The next_token and limit arguments are ignored. The maximum
number of resource policies per region is a small number (less
@@ -79,7 +82,7 @@ logs
Not all query commands are implemented yet. Please raise an issue if you encounter unexpected results.
-- [ ] list_tags_for_resource
+- [X] list_tags_for_resource
- [X] list_tags_log_group
- [ ] put_account_policy
- [ ] put_data_protection_policy
@@ -93,15 +96,17 @@ logs
- [X] put_metric_filter
- [ ] put_query_definition
- [X] put_resource_policy
- Creates/updates resource policy and return policy object
+
+ Creates/updates resource policy and return policy object
+
- [X] put_retention_policy
- [X] put_subscription_filter
- [X] start_query
- [ ] stop_query
- [X] tag_log_group
-- [ ] tag_resource
+- [X] tag_resource
- [ ] test_metric_filter
- [X] untag_log_group
-- [ ] untag_resource
+- [X] untag_resource
diff --git a/docs/docs/services/resourcegroupstaggingapi.rst b/docs/docs/services/resourcegroupstaggingapi.rst
index af022c2d2..98c033535 100644
--- a/docs/docs/services/resourcegroupstaggingapi.rst
+++ b/docs/docs/services/resourcegroupstaggingapi.rst
@@ -31,6 +31,10 @@ resourcegroupstaggingapi
- [X] get_tag_keys
- [X] get_tag_values
- [ ] start_report_creation
-- [ ] tag_resources
+- [X] tag_resources
+
+ Only Logs and RDS resources are currently supported
+
+
- [ ] untag_resources
diff --git a/moto/awslambda/models.py b/moto/awslambda/models.py
index 5987a9a9a..6f96e7321 100644
--- a/moto/awslambda/models.py
+++ b/moto/awslambda/models.py
@@ -761,7 +761,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
def _invoke_lambda(self, event: Optional[str] = None) -> Tuple[str, bool, str]:
# Create the LogGroup if necessary, to write the result to
- self.logs_backend.ensure_log_group(self.logs_group_name, [])
+ self.logs_backend.ensure_log_group(self.logs_group_name)
# TODO: context not yet implemented
if event is None:
event = dict() # type: ignore[assignment]
diff --git a/moto/batch/models.py b/moto/batch/models.py
index 83abdd52c..a0ef1c80f 100644
--- a/moto/batch/models.py
+++ b/moto/batch/models.py
@@ -858,7 +858,7 @@ class Job(threading.Thread, BaseModel, DockerModel, ManagedState):
# Send to cloudwatch
self.log_stream_name = self._stream_name
- self._log_backend.ensure_log_group(self._log_group, None)
+ self._log_backend.ensure_log_group(self._log_group)
self._log_backend.ensure_log_stream(
self._log_group, self.log_stream_name
)
diff --git a/moto/logs/models.py b/moto/logs/models.py
index 6cd2700d5..4fb14dddf 100644
--- a/moto/logs/models.py
+++ b/moto/logs/models.py
@@ -14,6 +14,7 @@ from moto.logs.logs_query import execute_query
from moto.moto_api._internal import mock_random
from moto.s3.models import s3_backends
from moto.utilities.paginator import paginate
+from moto.utilities.tagging_service import TaggingService
from .utils import PAGINATION_MODEL, EventMessageFilter
MAX_RESOURCE_POLICIES_PER_REGION = 10
@@ -373,7 +374,6 @@ class LogGroup(CloudFormationModel):
account_id: str,
region: str,
name: str,
- tags: Optional[Dict[str, str]],
**kwargs: Any,
):
self.name = name
@@ -381,7 +381,6 @@ class LogGroup(CloudFormationModel):
self.region = region
self.arn = f"arn:aws:logs:{region}:{account_id}:log-group:{name}"
self.creation_time = int(unix_time_millis())
- self.tags = tags
self.streams: Dict[str, LogStream] = dict() # {name: LogStream}
# AWS defaults to Never Expire for log group retention
self.retention_in_days = kwargs.get("RetentionInDays")
@@ -607,21 +606,6 @@ class LogGroup(CloudFormationModel):
def set_retention_policy(self, retention_in_days: Optional[str]) -> None:
self.retention_in_days = retention_in_days
- def list_tags(self) -> Dict[str, str]:
- return self.tags if self.tags else {}
-
- def tag(self, tags: Dict[str, str]) -> None:
- if self.tags:
- self.tags.update(tags)
- else:
- self.tags = tags
-
- def untag(self, tags_to_remove: List[str]) -> None:
- if self.tags:
- self.tags = {
- k: v for (k, v) in self.tags.items() if k not in tags_to_remove
- }
-
def describe_subscription_filters(self) -> Iterable[SubscriptionFilter]:
return self.subscription_filters.values()
@@ -741,6 +725,7 @@ class LogsBackend(BaseBackend):
self.queries: Dict[str, LogQuery] = dict()
self.resource_policies: Dict[str, LogResourcePolicy] = dict()
self.destinations: Dict[str, Destination] = dict()
+ self.tagger = TaggingService()
@staticmethod
def default_vpc_endpoint_service(
@@ -763,17 +748,18 @@ class LogsBackend(BaseBackend):
value=log_group_name,
)
self.groups[log_group_name] = LogGroup(
- self.account_id, self.region_name, log_group_name, tags, **kwargs
+ self.account_id, self.region_name, log_group_name, **kwargs
)
+ self.tag_resource(self.groups[log_group_name].arn, tags)
return self.groups[log_group_name]
- def ensure_log_group(
- self, log_group_name: str, tags: Optional[Dict[str, str]]
- ) -> None:
+ def ensure_log_group(self, log_group_name: str) -> None:
if log_group_name in self.groups:
return
self.groups[log_group_name] = LogGroup(
- self.account_id, self.region_name, log_group_name, tags
+ self.account_id,
+ self.region_name,
+ log_group_name,
)
def delete_log_group(self, log_group_name: str) -> None:
@@ -801,7 +787,11 @@ class LogsBackend(BaseBackend):
raise ResourceNotFoundException()
def put_destination(
- self, destination_name: str, role_arn: str, target_arn: str
+ self,
+ destination_name: str,
+ role_arn: str,
+ target_arn: str,
+ tags: Dict[str, str],
) -> Destination:
for _, destination in self.destinations.items():
if destination.destination_name == destination_name:
@@ -814,6 +804,7 @@ class LogsBackend(BaseBackend):
self.account_id, self.region_name, destination_name, role_arn, target_arn
)
self.destinations[destination.arn] = destination
+ self.tag_resource(destination.arn, tags)
return destination
def delete_destination(self, destination_name: str) -> None:
@@ -1010,7 +1001,8 @@ class LogsBackend(BaseBackend):
self.groups[log_group_name].set_retention_policy(None)
def describe_resource_policies(self) -> List[LogResourcePolicy]:
- """Return list of resource policies.
+ """
+ Return list of resource policies.
The next_token and limit arguments are ignored. The maximum
number of resource policies per region is a small number (less
@@ -1022,7 +1014,9 @@ class LogsBackend(BaseBackend):
def put_resource_policy(
self, policy_name: str, policy_doc: str
) -> LogResourcePolicy:
- """Creates/updates resource policy and return policy object"""
+ """
+ Creates/updates resource policy and return policy object
+ """
if policy_name in self.resource_policies:
policy = self.resource_policies[policy_name]
policy.update(policy_doc)
@@ -1034,7 +1028,9 @@ class LogsBackend(BaseBackend):
return policy
def delete_resource_policy(self, policy_name: str) -> None:
- """Remove resource policy with a policy name matching given name."""
+ """
+ Remove resource policy with a policy name matching given name.
+ """
if policy_name not in self.resource_policies:
raise ResourceNotFoundException(
msg=f"Policy with name [{policy_name}] does not exist"
@@ -1045,19 +1041,19 @@ class LogsBackend(BaseBackend):
if log_group_name not in self.groups:
raise ResourceNotFoundException()
log_group = self.groups[log_group_name]
- return log_group.list_tags()
+ return self.list_tags_for_resource(log_group.arn)
def tag_log_group(self, log_group_name: str, tags: Dict[str, str]) -> None:
if log_group_name not in self.groups:
raise ResourceNotFoundException()
log_group = self.groups[log_group_name]
- log_group.tag(tags)
+ self.tag_resource(log_group.arn, tags)
def untag_log_group(self, log_group_name: str, tags: List[str]) -> None:
if log_group_name not in self.groups:
raise ResourceNotFoundException()
log_group = self.groups[log_group_name]
- log_group.untag(tags)
+ self.untag_resource(log_group.arn, tags)
def put_metric_filter(
self,
@@ -1213,5 +1209,14 @@ class LogsBackend(BaseBackend):
raise ResourceNotFoundException()
return str(mock_random.uuid4())
+ def list_tags_for_resource(self, resource_arn: str) -> Dict[str, str]:
+ return self.tagger.get_tag_dict_for_resource(resource_arn)
+
+ def tag_resource(self, arn: str, tags: Dict[str, str]) -> None:
+ self.tagger.tag_resource(arn, TaggingService.convert_dict_to_tags_input(tags))
+
+ def untag_resource(self, arn: str, tag_keys: List[str]) -> None:
+ self.tagger.untag_resource_using_names(arn, tag_keys)
+
logs_backends = BackendDict(LogsBackend, "logs")
diff --git a/moto/logs/responses.py b/moto/logs/responses.py
index a798b4039..ae7d6d434 100644
--- a/moto/logs/responses.py
+++ b/moto/logs/responses.py
@@ -187,9 +187,13 @@ class LogsResponse(BaseResponse):
destination_name = self._get_param("destinationName")
role_arn = self._get_param("roleArn")
target_arn = self._get_param("targetArn")
+ tags = self._get_param("tags")
destination = self.logs_backend.put_destination(
- destination_name, role_arn, target_arn
+ destination_name,
+ role_arn,
+ target_arn,
+ tags,
)
result = {"destination": destination.to_dict()}
return json.dumps(result)
@@ -435,3 +439,20 @@ class LogsResponse(BaseResponse):
log_group_name=log_group_name, destination=destination
)
return json.dumps(dict(taskId=str(task_id)))
+
+ def list_tags_for_resource(self) -> str:
+ resource_arn = self._get_param("resourceArn")
+ tags = self.logs_backend.list_tags_for_resource(resource_arn)
+ return json.dumps({"tags": tags})
+
+ def tag_resource(self) -> str:
+ resource_arn = self._get_param("resourceArn")
+ tags = self._get_param("tags")
+ self.logs_backend.tag_resource(resource_arn, tags)
+ return "{}"
+
+ def untag_resource(self) -> str:
+ resource_arn = self._get_param("resourceArn")
+ tag_keys = self._get_param("tagKeys")
+ self.logs_backend.untag_resource(resource_arn, tag_keys)
+ return "{}"
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index 67a03e2db..898921b2d 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -10,6 +10,7 @@ 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.logs.models import logs_backends, LogsBackend
from moto.kms.models import kms_backends, KmsBackend
from moto.rds.models import rds_backends, RDSBackend
from moto.glacier.models import glacier_backends, GlacierBackend
@@ -31,7 +32,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
# Like 'someuuid': {'gen': , 'misc': None}
# Misc is there for peeking from a generator and it cant
# fit in the current request. As we only store generators
- # theres not really any point to clean up
+ # there is really no point cleaning up
@property
def s3_backend(self) -> S3Backend:
@@ -61,6 +62,10 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
def kms_backend(self) -> KmsBackend:
return kms_backends[self.account_id][self.region_name]
+ @property
+ def logs_backend(self) -> LogsBackend:
+ return logs_backends[self.account_id][self.region_name]
+
@property
def rds_backend(self) -> RDSBackend:
return rds_backends[self.account_id][self.region_name]
@@ -101,7 +106,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
# Check key matches
filters.append(lambda t, v, key=tag_filter_dict["Key"]: t == key)
elif len(values) == 1:
- # Check its exactly the same as key, value
+ # Check it's exactly the same as key, value
filters.append(
lambda t, v, key=tag_filter_dict["Key"], value=values[0]: t == key # type: ignore
and v == value
@@ -371,6 +376,21 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
yield {"ResourceARN": f"{kms_key.arn}", "Tags": tags}
+ # LOGS
+ if (
+ not resource_type_filters
+ or "logs" in resource_type_filters
+ or "logs:loggroup" in resource_type_filters
+ ):
+ for group in self.logs_backend.groups.values():
+ log_tags = self.logs_backend.list_tags_for_resource(group.arn)
+ tags = format_tags(log_tags)
+
+ if not log_tags or not tag_filter(tags):
+ # Skip if no tags, or invalid filter
+ continue
+ yield {"ResourceARN": group.arn, "Tags": tags}
+
# RDS resources
resource_map: Dict[str, Dict[str, Any]] = {
"rds:cluster": self.rds_backend.clusters,
@@ -733,7 +753,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
self, resource_arns: List[str], tags: Dict[str, str]
) -> Dict[str, Dict[str, Any]]:
"""
- Only RDS resources are currently supported
+ Only Logs and RDS resources are currently supported
"""
missing_resources = []
missing_error: Dict[str, Any] = {
@@ -746,6 +766,8 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
self.rds_backend.add_tags_to_resource(
arn, TaggingService.convert_dict_to_tags_input(tags)
)
+ if arn.startswith("arn:aws:logs:"):
+ self.logs_backend.tag_resource(arn, tags)
else:
missing_resources.append(arn)
return {arn: missing_error for arn in missing_resources}
diff --git a/tests/terraformtests/terraform-tests.success.txt b/tests/terraformtests/terraform-tests.success.txt
index cca9beeb6..b3c0f2f87 100644
--- a/tests/terraformtests/terraform-tests.success.txt
+++ b/tests/terraformtests/terraform-tests.success.txt
@@ -367,6 +367,13 @@ lambda:
- TestAccLambdaFunctionURL_Alias
- TestAccLambdaFunctionURL_basic
- TestAccLambdaFunctionURL_TwoURLs
+logs:
+ - TestAccLogsDestination_
+ - TestAccLogsGroupDataSource_basic
+ - TestAccLogsGroupsDataSource_basic
+ - TestAccLogsGroup_basic
+ - TestAccLogsGroup_tags
+ - TestAccLogsStream
meta:
- TestAccMetaBillingServiceAccountDataSource
mq:
diff --git a/tests/test_logs/test_logs.py b/tests/test_logs/test_logs.py
index 2dfe9b3b6..350754ccb 100644
--- a/tests/test_logs/test_logs.py
+++ b/tests/test_logs/test_logs.py
@@ -440,7 +440,7 @@ def test_create_log_group(kms_key_id):
create_logs_params["kmsKeyId"] = kms_key_id
# When
- response = conn.create_log_group(**create_logs_params)
+ conn.create_log_group(**create_logs_params)
response = conn.describe_log_groups()
# Then
@@ -992,16 +992,16 @@ def test_list_tags_log_group():
log_group_name = "dummy"
tags = {"tag_key_1": "tag_value_1", "tag_key_2": "tag_value_2"}
- response = conn.create_log_group(logGroupName=log_group_name)
+ conn.create_log_group(logGroupName=log_group_name)
response = conn.list_tags_log_group(logGroupName=log_group_name)
assert response["tags"] == {}
- response = conn.delete_log_group(logGroupName=log_group_name)
- response = conn.create_log_group(logGroupName=log_group_name, tags=tags)
+ conn.delete_log_group(logGroupName=log_group_name)
+ conn.create_log_group(logGroupName=log_group_name, tags=tags)
response = conn.list_tags_log_group(logGroupName=log_group_name)
assert response["tags"] == tags
- response = conn.delete_log_group(logGroupName=log_group_name)
+ conn.delete_log_group(logGroupName=log_group_name)
@mock_logs
@@ -1009,47 +1009,43 @@ def test_tag_log_group():
conn = boto3.client("logs", TEST_REGION)
log_group_name = "dummy"
tags = {"tag_key_1": "tag_value_1"}
- response = conn.create_log_group(logGroupName=log_group_name)
+ conn.create_log_group(logGroupName=log_group_name)
- response = conn.tag_log_group(logGroupName=log_group_name, tags=tags)
+ conn.tag_log_group(logGroupName=log_group_name, tags=tags)
response = conn.list_tags_log_group(logGroupName=log_group_name)
assert response["tags"] == tags
tags_with_added_value = {"tag_key_1": "tag_value_1", "tag_key_2": "tag_value_2"}
- response = conn.tag_log_group(
- logGroupName=log_group_name, tags={"tag_key_2": "tag_value_2"}
- )
+ conn.tag_log_group(logGroupName=log_group_name, tags={"tag_key_2": "tag_value_2"})
response = conn.list_tags_log_group(logGroupName=log_group_name)
assert response["tags"] == tags_with_added_value
tags_with_updated_value = {"tag_key_1": "tag_value_XX", "tag_key_2": "tag_value_2"}
- response = conn.tag_log_group(
- logGroupName=log_group_name, tags={"tag_key_1": "tag_value_XX"}
- )
+ conn.tag_log_group(logGroupName=log_group_name, tags={"tag_key_1": "tag_value_XX"})
response = conn.list_tags_log_group(logGroupName=log_group_name)
assert response["tags"] == tags_with_updated_value
- response = conn.delete_log_group(logGroupName=log_group_name)
+ conn.delete_log_group(logGroupName=log_group_name)
@mock_logs
def test_untag_log_group():
conn = boto3.client("logs", TEST_REGION)
log_group_name = "dummy"
- response = conn.create_log_group(logGroupName=log_group_name)
+ conn.create_log_group(logGroupName=log_group_name)
tags = {"tag_key_1": "tag_value_1", "tag_key_2": "tag_value_2"}
- response = conn.tag_log_group(logGroupName=log_group_name, tags=tags)
+ conn.tag_log_group(logGroupName=log_group_name, tags=tags)
response = conn.list_tags_log_group(logGroupName=log_group_name)
assert response["tags"] == tags
tags_to_remove = ["tag_key_1"]
remaining_tags = {"tag_key_2": "tag_value_2"}
- response = conn.untag_log_group(logGroupName=log_group_name, tags=tags_to_remove)
+ conn.untag_log_group(logGroupName=log_group_name, tags=tags_to_remove)
response = conn.list_tags_log_group(logGroupName=log_group_name)
assert response["tags"] == remaining_tags
- response = conn.delete_log_group(logGroupName=log_group_name)
+ conn.delete_log_group(logGroupName=log_group_name)
@mock_logs
diff --git a/tests/test_logs/test_logs_tags.py b/tests/test_logs/test_logs_tags.py
new file mode 100644
index 000000000..acef5da1f
--- /dev/null
+++ b/tests/test_logs/test_logs_tags.py
@@ -0,0 +1,40 @@
+import boto3
+
+from moto import mock_logs
+
+
+@mock_logs
+def test_destination_tags():
+ logs = boto3.client("logs", "us-west-2")
+ destination_name = "test-destination"
+ role_arn = "arn:aws:iam::123456789012:role/my-subscription-role"
+ target_arn = "arn:aws:kinesis:us-east-1:123456789012:stream/my-kinesis-stream"
+
+ destination_arn = logs.put_destination(
+ destinationName=destination_name,
+ targetArn=target_arn,
+ roleArn=role_arn,
+ tags={"key1": "val1"},
+ )["destination"]["arn"]
+
+ _verify_tag_operations(destination_arn, logs)
+
+
+@mock_logs
+def test_log_groups_tags():
+ logs = boto3.client("logs", "us-west-2")
+ log_group_name = "test"
+
+ logs.create_log_group(logGroupName=log_group_name, tags={"key1": "val1"})
+ arn = logs.describe_log_groups()["logGroups"][0]["arn"]
+
+ _verify_tag_operations(arn, logs)
+
+
+def _verify_tag_operations(arn, logs):
+ logs.tag_resource(resourceArn=arn, tags={"key2": "val2"})
+ tags = logs.list_tags_for_resource(resourceArn=arn)["tags"]
+ assert tags == {"key1": "val1", "key2": "val2"}
+ logs.untag_resource(resourceArn=arn, tagKeys=["key2"])
+ tags = logs.list_tags_for_resource(resourceArn=arn)["tags"]
+ assert tags == {"key1": "val1"}
diff --git a/tests/test_logs/test_models.py b/tests/test_logs/test_models.py
index d010d4874..18c25eb8f 100644
--- a/tests/test_logs/test_models.py
+++ b/tests/test_logs/test_models.py
@@ -6,14 +6,13 @@ def test_log_group_to_describe_dict():
# Given
region = "us-east-1"
name = "test-log-group"
- tags = {"TestTag": "TestValue"}
kms_key_id = (
"arn:aws:kms:us-east-1:000000000000:key/51d81fab-b138-4bd2-8a09-07fd6d37224d"
)
kwargs = dict(kmsKeyId=kms_key_id)
# When
- log_group = LogGroup(DEFAULT_ACCOUNT_ID, region, name, tags, **kwargs)
+ log_group = LogGroup(DEFAULT_ACCOUNT_ID, region, name, **kwargs)
describe_dict = log_group.to_describe_dict()
# Then
diff --git a/tests/test_resourcegroupstaggingapi/test_resourcegroupstagging_logs.py b/tests/test_resourcegroupstaggingapi/test_resourcegroupstagging_logs.py
new file mode 100644
index 000000000..9bae4d153
--- /dev/null
+++ b/tests/test_resourcegroupstaggingapi/test_resourcegroupstagging_logs.py
@@ -0,0 +1,41 @@
+import boto3
+import unittest
+
+from moto import mock_logs
+from moto import mock_resourcegroupstaggingapi
+
+
+@mock_logs
+@mock_resourcegroupstaggingapi
+class TestLogsTagging(unittest.TestCase):
+ def setUp(self) -> None:
+ self.logs = boto3.client("logs", region_name="us-east-2")
+ self.rtapi = boto3.client("resourcegroupstaggingapi", region_name="us-east-2")
+ self.resources_tagged = []
+ self.resources_untagged = []
+ for i in range(3):
+ self.logs.create_log_group(logGroupName=f"test{i}", tags={"key1": "val1"})
+ self.arns = [lg["arn"] for lg in self.logs.describe_log_groups()["logGroups"]]
+
+ def test_get_resources_logs(self):
+ resp = self.rtapi.get_resources(ResourceTypeFilters=["logs"])
+ assert len(resp["ResourceTagMappingList"]) == 3
+
+ resp = self.rtapi.get_resources(ResourceTypeFilters=["logs:loggroup"])
+ assert len(resp["ResourceTagMappingList"]) == 3
+
+ def test_tag_resources_logs(self):
+ # WHEN
+ # we tag resources
+ self.rtapi.tag_resources(
+ ResourceARNList=self.arns,
+ Tags={"key2": "val2"},
+ )
+
+ # THEN
+ # we can retrieve the tags using the RDS API
+ def get_tags(arn):
+ return self.logs.list_tags_for_resource(resourceArn=arn)["tags"]
+
+ for arn in self.arns:
+ assert get_tags(arn) == {"key1": "val1", "key2": "val2"}