diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index 81d4d1c7d..05ebdace8 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -96,6 +96,7 @@ MODEL_MAP = { "AWS::S3::Bucket": s3_models.FakeBucket, "AWS::SQS::Queue": sqs_models.Queue, "AWS::Events::Rule": events_models.Rule, + "AWS::Events::EventBus": events_models.EventBus, } UNDOCUMENTED_NAME_TYPE_MAP = { diff --git a/moto/events/models.py b/moto/events/models.py index 5397f28ca..360c8d631 100644 --- a/moto/events/models.py +++ b/moto/events/models.py @@ -134,6 +134,52 @@ class EventBus(BaseModel): return json.dumps(policy) + def delete(self, region_name): + event_backend = events_backends[region_name] + event_backend.delete_event_bus(name=self.name) + + def get_cfn_attribute(self, attribute_name): + from moto.cloudformation.exceptions import UnformattedGetAttTemplateException + + if attribute_name == "Arn": + return self.arn + elif attribute_name == "Name": + return self.name + elif attribute_name == "Policy": + return self.policy + + raise UnformattedGetAttTemplateException() + + @classmethod + def create_from_cloudformation_json( + cls, resource_name, cloudformation_json, region_name + ): + properties = cloudformation_json["Properties"] + event_backend = events_backends[region_name] + event_name = properties["Name"] + event_source_name = properties.get("EventSourceName") + return event_backend.create_event_bus( + name=event_name, event_source_name=event_source_name + ) + + @classmethod + def update_from_cloudformation_json( + cls, original_resource, new_resource_name, cloudformation_json, region_name + ): + original_resource.delete(region_name) + return cls.create_from_cloudformation_json( + new_resource_name, cloudformation_json, region_name + ) + + @classmethod + def delete_from_cloudformation_json( + cls, resource_name, cloudformation_json, region_name + ): + properties = cloudformation_json["Properties"] + event_backend = events_backends[region_name] + event_bus_name = properties["Name"] + event_backend.delete_event_bus(event_bus_name) + class EventsBackend(BaseBackend): ACCOUNT_ID = re.compile(r"^(\d{1,12}|\*)$") @@ -369,7 +415,7 @@ class EventsBackend(BaseBackend): return event_bus - def create_event_bus(self, name, event_source_name): + def create_event_bus(self, name, event_source_name=None): if name in self.event_buses: raise JsonRESTError( "ResourceAlreadyExistsException", @@ -406,7 +452,6 @@ class EventsBackend(BaseBackend): raise JsonRESTError( "ValidationException", "Cannot delete event bus default." ) - self.event_buses.pop(name, None) def list_tags_for_resource(self, arn): diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index 082a20e01..fec9891ad 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -2585,3 +2585,178 @@ def test_autoscaling_propagate_tags(): assert propagation_dict["test-key-propagate"] assert not propagation_dict["test-key-no-propagate"] + + +@mock_cloudformation +@mock_events +def test_stack_eventbus_create_from_cfn_integration(): + eventbus_template = """{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "EventBus": { + "Type": "AWS::Events::EventBus", + "Properties": { + "Name": "MyCustomEventBus" + }, + } + }, + }""" + + cf_conn = boto3.client("cloudformation", "us-west-2") + cf_conn.create_stack(StackName="test_stack", TemplateBody=eventbus_template) + + event_buses = boto3.client("events", "us-west-2").list_event_buses( + NamePrefix="MyCustom" + ) + + event_buses["EventBuses"].should.have.length_of(1) + event_buses["EventBuses"][0]["Name"].should.equal("MyCustomEventBus") + + +@mock_cloudformation +@mock_events +def test_stack_events_delete_eventbus_integration(): + eventbus_template = """{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "EventBus": { + "Type": "AWS::Events::EventBus", + "Properties": { + "Name": "MyCustomEventBus" + }, + } + }, + }""" + cf_conn = boto3.client("cloudformation", "us-west-2") + cf_conn.create_stack(StackName="test_stack", TemplateBody=eventbus_template) + + event_buses = boto3.client("events", "us-west-2").list_event_buses( + NamePrefix="MyCustom" + ) + event_buses["EventBuses"].should.have.length_of(1) + + cf_conn.delete_stack(StackName="test_stack") + + event_buses = boto3.client("events", "us-west-2").list_event_buses( + NamePrefix="MyCustom" + ) + event_buses["EventBuses"].should.have.length_of(0) + + +@mock_cloudformation +@mock_events +def test_stack_events_delete_from_cfn_integration(): + eventbus_template = Template( + """{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "$resource_name": { + "Type": "AWS::Events::EventBus", + "Properties": { + "Name": "$name" + }, + } + }, + }""" + ) + + cf_conn = boto3.client("cloudformation", "us-west-2") + + original_template = eventbus_template.substitute( + {"resource_name": "original", "name": "MyCustomEventBus"} + ) + cf_conn.create_stack(StackName="test_stack", TemplateBody=original_template) + + original_event_buses = boto3.client("events", "us-west-2").list_event_buses( + NamePrefix="MyCustom" + ) + original_event_buses["EventBuses"].should.have.length_of(1) + + original_eventbus = original_event_buses["EventBuses"][0] + + updated_template = eventbus_template.substitute( + {"resource_name": "updated", "name": "AnotherEventBus"} + ) + cf_conn.update_stack(StackName="test_stack", TemplateBody=updated_template) + + update_event_buses = boto3.client("events", "us-west-2").list_event_buses( + NamePrefix="AnotherEventBus" + ) + update_event_buses["EventBuses"].should.have.length_of(1) + update_event_buses["EventBuses"][0]["Arn"].shouldnt.equal(original_eventbus["Arn"]) + + +@mock_cloudformation +@mock_events +def test_stack_events_update_from_cfn_integration(): + eventbus_template = Template( + """{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "EventBus": { + "Type": "AWS::Events::EventBus", + "Properties": { + "Name": "$name" + }, + } + }, + }""" + ) + + cf_conn = boto3.client("cloudformation", "us-west-2") + + original_template = eventbus_template.substitute({"name": "MyCustomEventBus"}) + cf_conn.create_stack(StackName="test_stack", TemplateBody=original_template) + + original_event_buses = boto3.client("events", "us-west-2").list_event_buses( + NamePrefix="MyCustom" + ) + original_event_buses["EventBuses"].should.have.length_of(1) + + original_eventbus = original_event_buses["EventBuses"][0] + + updated_template = eventbus_template.substitute({"name": "NewEventBus"}) + cf_conn.update_stack(StackName="test_stack", TemplateBody=updated_template) + + update_event_buses = boto3.client("events", "us-west-2").list_event_buses( + NamePrefix="NewEventBus" + ) + update_event_buses["EventBuses"].should.have.length_of(1) + update_event_buses["EventBuses"][0]["Name"].should.equal("NewEventBus") + update_event_buses["EventBuses"][0]["Arn"].shouldnt.equal(original_eventbus["Arn"]) + + +@mock_cloudformation +@mock_events +def test_stack_events_get_attribute_integration(): + eventbus_template = """{ + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "EventBus": { + "Type": "AWS::Events::EventBus", + "Properties": { + "Name": "MyEventBus" + }, + } + }, + "Outputs": { + "bus_arn": {"Value": {"Fn::GetAtt": ["EventBus", "Arn"]}}, + "bus_name": {"Value": {"Fn::GetAtt": ["EventBus", "Name"]}}, + } + }""" + + cf = boto3.client("cloudformation", "us-west-2") + events = boto3.client("events", "us-west-2") + + cf.create_stack(StackName="test_stack", TemplateBody=eventbus_template) + + stack = cf.describe_stacks(StackName="test_stack")["Stacks"][0] + outputs = stack["Outputs"] + + output_arn = list(filter(lambda item: item["OutputKey"] == "bus_arn", outputs))[0] + output_name = list(filter(lambda item: item["OutputKey"] == "bus_name", outputs))[0] + + event_bus = events.list_event_buses(NamePrefix="MyEventBus")["EventBuses"][0] + + output_arn["OutputValue"].should.equal(event_bus["Arn"]) + output_name["OutputValue"].should.equal(event_bus["Name"])