Merge pull request #2694 from brady-gsa/events-tagging
adds tagging support for cloudwatch events service
This commit is contained in:
commit
4a89131ec4
@ -6,6 +6,7 @@ from boto3 import Session
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.sts.models import ACCOUNT_ID
|
||||
from moto.utilities.tagging_service import TaggingService
|
||||
|
||||
|
||||
class Rule(BaseModel):
|
||||
@ -104,6 +105,7 @@ class EventsBackend(BaseBackend):
|
||||
self.region_name = region_name
|
||||
self.event_buses = {}
|
||||
self.event_sources = {}
|
||||
self.tagger = TaggingService()
|
||||
|
||||
self._add_default_event_bus()
|
||||
|
||||
@ -141,6 +143,9 @@ class EventsBackend(BaseBackend):
|
||||
|
||||
def delete_rule(self, name):
|
||||
self.rules_order.pop(self.rules_order.index(name))
|
||||
arn = self.rules.get(name).arn
|
||||
if self.tagger.has_tags(arn):
|
||||
self.tagger.delete_all_tags_for_resource(arn)
|
||||
return self.rules.pop(name) is not None
|
||||
|
||||
def describe_rule(self, name):
|
||||
@ -361,6 +366,32 @@ class EventsBackend(BaseBackend):
|
||||
|
||||
self.event_buses.pop(name, None)
|
||||
|
||||
def list_tags_for_resource(self, arn):
|
||||
name = arn.split("/")[-1]
|
||||
if name in self.rules:
|
||||
return self.tagger.list_tags_for_resource(self.rules[name].arn)
|
||||
raise JsonRESTError(
|
||||
"ResourceNotFoundException", "An entity that you specified does not exist."
|
||||
)
|
||||
|
||||
def tag_resource(self, arn, tags):
|
||||
name = arn.split("/")[-1]
|
||||
if name in self.rules:
|
||||
self.tagger.tag_resource(self.rules[name].arn, tags)
|
||||
return {}
|
||||
raise JsonRESTError(
|
||||
"ResourceNotFoundException", "An entity that you specified does not exist."
|
||||
)
|
||||
|
||||
def untag_resource(self, arn, tag_names):
|
||||
name = arn.split("/")[-1]
|
||||
if name in self.rules:
|
||||
self.tagger.untag_resource_using_names(self.rules[name].arn, tag_names)
|
||||
return {}
|
||||
raise JsonRESTError(
|
||||
"ResourceNotFoundException", "An entity that you specified does not exist."
|
||||
)
|
||||
|
||||
|
||||
events_backends = {}
|
||||
for region in Session().get_available_regions("events"):
|
||||
|
@ -297,3 +297,26 @@ class EventsHandler(BaseResponse):
|
||||
self.events_backend.delete_event_bus(name)
|
||||
|
||||
return "", self.response_headers
|
||||
|
||||
def list_tags_for_resource(self):
|
||||
arn = self._get_param("ResourceARN")
|
||||
|
||||
result = self.events_backend.list_tags_for_resource(arn)
|
||||
|
||||
return json.dumps(result), self.response_headers
|
||||
|
||||
def tag_resource(self):
|
||||
arn = self._get_param("ResourceARN")
|
||||
tags = self._get_param("Tags")
|
||||
|
||||
result = self.events_backend.tag_resource(arn, tags)
|
||||
|
||||
return json.dumps(result), self.response_headers
|
||||
|
||||
def untag_resource(self):
|
||||
arn = self._get_param("ResourceARN")
|
||||
tags = self._get_param("TagKeys")
|
||||
|
||||
result = self.events_backend.untag_resource(arn, tags)
|
||||
|
||||
return json.dumps(result), self.response_headers
|
||||
|
0
moto/utilities/__init__.py
Normal file
0
moto/utilities/__init__.py
Normal file
62
moto/utilities/tagging_service.py
Normal file
62
moto/utilities/tagging_service.py
Normal file
@ -0,0 +1,62 @@
|
||||
class TaggingService:
|
||||
def __init__(self, tagName="Tags", keyName="Key", valueName="Value"):
|
||||
self.tagName = tagName
|
||||
self.keyName = keyName
|
||||
self.valueName = valueName
|
||||
self.tags = {}
|
||||
|
||||
def list_tags_for_resource(self, arn):
|
||||
result = []
|
||||
if arn in self.tags:
|
||||
for k, v in self.tags[arn].items():
|
||||
result.append({self.keyName: k, self.valueName: v})
|
||||
return {self.tagName: result}
|
||||
|
||||
def delete_all_tags_for_resource(self, arn):
|
||||
del self.tags[arn]
|
||||
|
||||
def has_tags(self, arn):
|
||||
return arn in self.tags
|
||||
|
||||
def tag_resource(self, arn, tags):
|
||||
if arn not in self.tags:
|
||||
self.tags[arn] = {}
|
||||
for t in tags:
|
||||
if self.valueName in t:
|
||||
self.tags[arn][t[self.keyName]] = t[self.valueName]
|
||||
else:
|
||||
self.tags[arn][t[self.keyName]] = None
|
||||
|
||||
def untag_resource_using_names(self, arn, tag_names):
|
||||
for name in tag_names:
|
||||
if name in self.tags.get(arn, {}):
|
||||
del self.tags[arn][name]
|
||||
|
||||
def untag_resource_using_tags(self, arn, tags):
|
||||
m = self.tags.get(arn, {})
|
||||
for t in tags:
|
||||
if self.keyName in t:
|
||||
if t[self.keyName] in m:
|
||||
if self.valueName in t:
|
||||
if m[t[self.keyName]] != t[self.valueName]:
|
||||
continue
|
||||
# If both key and value are provided, match both before deletion
|
||||
del m[t[self.keyName]]
|
||||
|
||||
def extract_tag_names(self, tags):
|
||||
results = []
|
||||
if len(tags) == 0:
|
||||
return results
|
||||
for tag in tags:
|
||||
if self.keyName in tag:
|
||||
results.append(tag[self.keyName])
|
||||
return results
|
||||
|
||||
def flatten_tag_list(self, tags):
|
||||
result = {}
|
||||
for t in tags:
|
||||
if self.valueName in t:
|
||||
result[t[self.keyName]] = t[self.valueName]
|
||||
else:
|
||||
result[t[self.keyName]] = None
|
||||
return result
|
@ -1,12 +1,15 @@
|
||||
import random
|
||||
import boto3
|
||||
import json
|
||||
import sure # noqa
|
||||
import random
|
||||
import unittest
|
||||
|
||||
from moto.events import mock_events
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
from nose.tools import assert_raises
|
||||
|
||||
from moto.core import ACCOUNT_ID
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
from moto.events import mock_events
|
||||
from moto.events.models import EventsBackend
|
||||
|
||||
RULES = [
|
||||
{"Name": "test1", "ScheduleExpression": "rate(5 minutes)"},
|
||||
@ -136,14 +139,6 @@ def test_list_rule_names_by_target():
|
||||
assert rule in test_2_target["Rules"]
|
||||
|
||||
|
||||
@mock_events
|
||||
def test_list_rules():
|
||||
client = generate_environment()
|
||||
|
||||
rules = client.list_rules()
|
||||
assert len(rules["Rules"]) == len(RULES)
|
||||
|
||||
|
||||
@mock_events
|
||||
def test_delete_rule():
|
||||
client = generate_environment()
|
||||
@ -461,3 +456,50 @@ def test_delete_event_bus_errors():
|
||||
client.delete_event_bus.when.called_with(Name="default").should.throw(
|
||||
ClientError, "Cannot delete event bus default."
|
||||
)
|
||||
|
||||
|
||||
@mock_events
|
||||
def test_rule_tagging_happy():
|
||||
client = generate_environment()
|
||||
rule_name = get_random_rule()["Name"]
|
||||
rule_arn = client.describe_rule(Name=rule_name).get("Arn")
|
||||
|
||||
tags = [{"Key": "key1", "Value": "value1"}, {"Key": "key2", "Value": "value2"}]
|
||||
client.tag_resource(ResourceARN=rule_arn, Tags=tags)
|
||||
|
||||
actual = client.list_tags_for_resource(ResourceARN=rule_arn).get("Tags")
|
||||
tc = unittest.TestCase("__init__")
|
||||
expected = [{"Value": "value1", "Key": "key1"}, {"Value": "value2", "Key": "key2"}]
|
||||
tc.assertTrue(
|
||||
(expected[0] == actual[0] and expected[1] == actual[1])
|
||||
or (expected[1] == actual[0] and expected[0] == actual[1])
|
||||
)
|
||||
|
||||
client.untag_resource(ResourceARN=rule_arn, TagKeys=["key1"])
|
||||
|
||||
actual = client.list_tags_for_resource(ResourceARN=rule_arn).get("Tags")
|
||||
expected = [{"Key": "key2", "Value": "value2"}]
|
||||
assert expected == actual
|
||||
|
||||
|
||||
@mock_events
|
||||
def test_rule_tagging_sad():
|
||||
back_end = EventsBackend("us-west-2")
|
||||
|
||||
try:
|
||||
back_end.tag_resource("unknown", [])
|
||||
raise "tag_resource should fail if ResourceARN is not known"
|
||||
except JsonRESTError:
|
||||
pass
|
||||
|
||||
try:
|
||||
back_end.untag_resource("unknown", [])
|
||||
raise "untag_resource should fail if ResourceARN is not known"
|
||||
except JsonRESTError:
|
||||
pass
|
||||
|
||||
try:
|
||||
back_end.list_tags_for_resource("unknown")
|
||||
raise "list_tags_for_resource should fail if ResourceARN is not known"
|
||||
except JsonRESTError:
|
||||
pass
|
||||
|
79
tests/test_utilities/test_tagging_service.py
Normal file
79
tests/test_utilities/test_tagging_service.py
Normal file
@ -0,0 +1,79 @@
|
||||
import sure
|
||||
|
||||
from moto.utilities.tagging_service import TaggingService
|
||||
|
||||
|
||||
def test_list_empty():
|
||||
svc = TaggingService()
|
||||
result = svc.list_tags_for_resource("test")
|
||||
|
||||
{"Tags": []}.should.be.equal(result)
|
||||
|
||||
|
||||
def test_create_tag():
|
||||
svc = TaggingService("TheTags", "TagKey", "TagValue")
|
||||
tags = [{"TagKey": "key_key", "TagValue": "value_value"}]
|
||||
svc.tag_resource("arn", tags)
|
||||
actual = svc.list_tags_for_resource("arn")
|
||||
expected = {"TheTags": [{"TagKey": "key_key", "TagValue": "value_value"}]}
|
||||
|
||||
expected.should.be.equal(actual)
|
||||
|
||||
|
||||
def test_create_tag_without_value():
|
||||
svc = TaggingService()
|
||||
tags = [{"Key": "key_key"}]
|
||||
svc.tag_resource("arn", tags)
|
||||
actual = svc.list_tags_for_resource("arn")
|
||||
expected = {"Tags": [{"Key": "key_key", "Value": None}]}
|
||||
|
||||
expected.should.be.equal(actual)
|
||||
|
||||
|
||||
def test_delete_tag_using_names():
|
||||
svc = TaggingService()
|
||||
tags = [{"Key": "key_key", "Value": "value_value"}]
|
||||
svc.tag_resource("arn", tags)
|
||||
svc.untag_resource_using_names("arn", ["key_key"])
|
||||
result = svc.list_tags_for_resource("arn")
|
||||
|
||||
{"Tags": []}.should.be.equal(result)
|
||||
|
||||
|
||||
def test_delete_all_tags_for_resource():
|
||||
svc = TaggingService()
|
||||
tags = [{"Key": "key_key", "Value": "value_value"}]
|
||||
tags2 = [{"Key": "key_key2", "Value": "value_value2"}]
|
||||
svc.tag_resource("arn", tags)
|
||||
svc.tag_resource("arn", tags2)
|
||||
svc.delete_all_tags_for_resource("arn")
|
||||
result = svc.list_tags_for_resource("arn")
|
||||
|
||||
{"Tags": []}.should.be.equal(result)
|
||||
|
||||
|
||||
def test_list_empty_delete():
|
||||
svc = TaggingService()
|
||||
svc.untag_resource_using_names("arn", ["key_key"])
|
||||
result = svc.list_tags_for_resource("arn")
|
||||
|
||||
{"Tags": []}.should.be.equal(result)
|
||||
|
||||
|
||||
def test_delete_tag_using_tags():
|
||||
svc = TaggingService()
|
||||
tags = [{"Key": "key_key", "Value": "value_value"}]
|
||||
svc.tag_resource("arn", tags)
|
||||
svc.untag_resource_using_tags("arn", tags)
|
||||
result = svc.list_tags_for_resource("arn")
|
||||
|
||||
{"Tags": []}.should.be.equal(result)
|
||||
|
||||
|
||||
def test_extract_tag_names():
|
||||
svc = TaggingService()
|
||||
tags = [{"Key": "key1", "Value": "value1"}, {"Key": "key2", "Value": "value2"}]
|
||||
actual = svc.extract_tag_names(tags)
|
||||
expected = ["key1", "key2"]
|
||||
|
||||
expected.should.be.equal(actual)
|
Loading…
Reference in New Issue
Block a user