From 3cb7c3e5688c7bfcc3f5d1c61292da02523cb21c Mon Sep 17 00:00:00 2001 From: Wayne Metcalfe Date: Thu, 26 Sep 2019 16:20:28 +0100 Subject: [PATCH 1/3] feat: implement logs list_tags_log_group --- IMPLEMENTATION_COVERAGE.md | 2 +- moto/logs/models.py | 9 +++++++++ moto/logs/responses.py | 8 ++++++++ tests/test_logs/test_logs.py | 18 ++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 1ad96aeb4..aac1d3858 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -4080,7 +4080,7 @@ - [ ] get_log_group_fields - [ ] get_log_record - [ ] get_query_results -- [ ] list_tags_log_group +- [X] list_tags_log_group - [ ] put_destination - [ ] put_destination_policy - [X] put_log_events diff --git a/moto/logs/models.py b/moto/logs/models.py index 3c5360371..de23e7dfb 100644 --- a/moto/logs/models.py +++ b/moto/logs/models.py @@ -231,6 +231,9 @@ class LogGroup: def set_retention_policy(self, retention_in_days): self.retentionInDays = retention_in_days + def list_tags(self): + return self.tags if self.tags else {} + class LogsBackend(BaseBackend): def __init__(self, region_name): @@ -322,5 +325,11 @@ class LogsBackend(BaseBackend): log_group = self.groups[log_group_name] return log_group.set_retention_policy(None) + def list_tags_log_group(self, log_group_name): + if log_group_name not in self.groups: + raise ResourceNotFoundException() + log_group = self.groups[log_group_name] + return log_group.list_tags() + logs_backends = {region.name: LogsBackend(region.name) for region in boto.logs.regions()} diff --git a/moto/logs/responses.py b/moto/logs/responses.py index 39f24a260..fd6084f7c 100644 --- a/moto/logs/responses.py +++ b/moto/logs/responses.py @@ -134,3 +134,11 @@ class LogsResponse(BaseResponse): log_group_name = self._get_param('logGroupName') self.logs_backend.delete_retention_policy(log_group_name) return '' + + def list_tags_log_group(self): + log_group_name = self._get_param('logGroupName') + tags = self.logs_backend.list_tags_log_group(log_group_name) + return json.dumps({ + 'tags': tags + }) + diff --git a/tests/test_logs/test_logs.py b/tests/test_logs/test_logs.py index 0a63308c2..61dd0ad3a 100644 --- a/tests/test_logs/test_logs.py +++ b/tests/test_logs/test_logs.py @@ -225,3 +225,21 @@ def test_get_log_events(): for i in range(10): resp['events'][i]['timestamp'].should.equal(i) resp['events'][i]['message'].should.equal(str(i)) + + +@mock_logs +def test_list_tags_log_group(): + conn = boto3.client('logs', 'us-west-2') + 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) + 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) + response = conn.list_tags_log_group(logGroupName=log_group_name) + assert response['tags'] == tags + + response = conn.delete_log_group(logGroupName=log_group_name) From 0561a0050fb7d68c1cfac5877690e786d3d2c083 Mon Sep 17 00:00:00 2001 From: Wayne Metcalfe Date: Thu, 26 Sep 2019 17:09:10 +0100 Subject: [PATCH 2/3] feat: implement logs tag_log_group --- IMPLEMENTATION_COVERAGE.md | 2 +- moto/logs/models.py | 12 ++++++++++++ moto/logs/responses.py | 5 +++++ tests/test_logs/test_logs.py | 24 ++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index aac1d3858..1377c67be 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -4090,7 +4090,7 @@ - [ ] put_subscription_filter - [ ] start_query - [ ] stop_query -- [ ] tag_log_group +- [X] tag_log_group - [ ] test_metric_filter - [ ] untag_log_group diff --git a/moto/logs/models.py b/moto/logs/models.py index de23e7dfb..01738fa39 100644 --- a/moto/logs/models.py +++ b/moto/logs/models.py @@ -234,6 +234,12 @@ class LogGroup: def list_tags(self): return self.tags if self.tags else {} + def tag(self, tags): + if self.tags: + self.tags.update(tags) + else: + self.tags = tags + class LogsBackend(BaseBackend): def __init__(self, region_name): @@ -331,5 +337,11 @@ class LogsBackend(BaseBackend): log_group = self.groups[log_group_name] return log_group.list_tags() + def tag_log_group(self, log_group_name, tags): + if log_group_name not in self.groups: + raise ResourceNotFoundException() + log_group = self.groups[log_group_name] + log_group.tag(tags) + logs_backends = {region.name: LogsBackend(region.name) for region in boto.logs.regions()} diff --git a/moto/logs/responses.py b/moto/logs/responses.py index fd6084f7c..0c8750ce2 100644 --- a/moto/logs/responses.py +++ b/moto/logs/responses.py @@ -142,3 +142,8 @@ class LogsResponse(BaseResponse): 'tags': tags }) + def tag_log_group(self): + log_group_name = self._get_param('logGroupName') + tags = self._get_param('tags') + self.logs_backend.tag_log_group(log_group_name, tags) + return '' diff --git a/tests/test_logs/test_logs.py b/tests/test_logs/test_logs.py index 61dd0ad3a..fced03363 100644 --- a/tests/test_logs/test_logs.py +++ b/tests/test_logs/test_logs.py @@ -243,3 +243,27 @@ def test_list_tags_log_group(): assert response['tags'] == tags response = conn.delete_log_group(logGroupName=log_group_name) + + +@mock_logs +def test_tag_log_group(): + conn = boto3.client('logs', 'us-west-2') + log_group_name = 'dummy' + tags = {'tag_key_1': 'tag_value_1'} + response = conn.create_log_group(logGroupName=log_group_name) + + response = 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'}) + 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'}) + 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) From 9c66839f853251f25c2a57b5a573f9205d877983 Mon Sep 17 00:00:00 2001 From: Wayne Metcalfe Date: Thu, 26 Sep 2019 20:20:53 +0100 Subject: [PATCH 3/3] feat: implement logs untag_log_group --- IMPLEMENTATION_COVERAGE.md | 2 +- moto/logs/models.py | 10 ++++++++++ moto/logs/responses.py | 6 ++++++ tests/test_logs/test_logs.py | 20 ++++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 1377c67be..4c7757316 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -4092,7 +4092,7 @@ - [ ] stop_query - [X] tag_log_group - [ ] test_metric_filter -- [ ] untag_log_group +- [X] untag_log_group ## machinelearning 0% implemented diff --git a/moto/logs/models.py b/moto/logs/models.py index 01738fa39..cc0d21d47 100644 --- a/moto/logs/models.py +++ b/moto/logs/models.py @@ -240,6 +240,10 @@ class LogGroup: else: self.tags = tags + def untag(self, tags_to_remove): + if self.tags: + self.tags = {k: v for (k, v) in self.tags.items() if k not in tags_to_remove} + class LogsBackend(BaseBackend): def __init__(self, region_name): @@ -343,5 +347,11 @@ class LogsBackend(BaseBackend): log_group = self.groups[log_group_name] log_group.tag(tags) + def untag_log_group(self, log_group_name, tags): + if log_group_name not in self.groups: + raise ResourceNotFoundException() + log_group = self.groups[log_group_name] + log_group.untag(tags) + logs_backends = {region.name: LogsBackend(region.name) for region in boto.logs.regions()} diff --git a/moto/logs/responses.py b/moto/logs/responses.py index 0c8750ce2..b91662cf8 100644 --- a/moto/logs/responses.py +++ b/moto/logs/responses.py @@ -147,3 +147,9 @@ class LogsResponse(BaseResponse): tags = self._get_param('tags') self.logs_backend.tag_log_group(log_group_name, tags) return '' + + def untag_log_group(self): + log_group_name = self._get_param('logGroupName') + tags = self._get_param('tags') + self.logs_backend.untag_log_group(log_group_name, tags) + return '' diff --git a/tests/test_logs/test_logs.py b/tests/test_logs/test_logs.py index fced03363..22e7edeec 100644 --- a/tests/test_logs/test_logs.py +++ b/tests/test_logs/test_logs.py @@ -267,3 +267,23 @@ def test_tag_log_group(): assert response['tags'] == tags_with_updated_value response = conn.delete_log_group(logGroupName=log_group_name) + + +@mock_logs +def test_untag_log_group(): + conn = boto3.client('logs', 'us-west-2') + log_group_name = 'dummy' + response = 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) + 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) + response = conn.list_tags_log_group(logGroupName=log_group_name) + assert response['tags'] == remaining_tags + + response = conn.delete_log_group(logGroupName=log_group_name)