diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index 3c8ab501a..e4117e839 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -9,6 +9,7 @@ from moto.elb import models as elb_models from moto.iam import models as iam_models from moto.rds import models as rds_models from moto.route53 import models as route53_models +from moto.sns import models as sns_models from moto.sqs import models as sqs_models from .utils import random_suffix from .exceptions import MissingParameterError, UnformattedGetAttTemplateException @@ -41,6 +42,7 @@ MODEL_MAP = { "AWS::Route53::HostedZone": route53_models.FakeZone, "AWS::Route53::RecordSet": route53_models.RecordSet, "AWS::Route53::RecordSetGroup": route53_models.RecordSetGroup, + "AWS::SNS::Topic": sns_models.Topic, "AWS::SQS::Queue": sqs_models.Queue, } diff --git a/moto/sns/models.py b/moto/sns/models.py index b51d197c3..769b755aa 100644 --- a/moto/sns/models.py +++ b/moto/sns/models.py @@ -45,6 +45,22 @@ class Topic(object): return self.name raise UnformattedGetAttTemplateException() + @property + def physical_resource_id(self): + return self.arn + + @classmethod + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): + sns_backend = sns_backends[region_name] + properties = cloudformation_json['Properties'] + + topic = sns_backend.create_topic( + properties.get("TopicName") + ) + for subscription in properties.get("Subscription", []): + sns_backend.subscribe(topic.arn, subscription['Endpoint'], subscription['Protocol']) + return topic + class Subscription(object): def __init__(self, topic, endpoint, protocol): diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index 944457ac4..c1f356bca 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -8,6 +8,7 @@ import boto.ec2.autoscale import boto.ec2.elb from boto.exception import BotoServerError import boto.iam +import boto.sns import boto.sqs import boto.vpc import sure # noqa @@ -20,6 +21,7 @@ from moto import ( mock_iam, mock_rds, mock_route53, + mock_sns, mock_sqs, ) @@ -825,7 +827,7 @@ def test_route53_ec2_instance_with_public_ip(): template_json = json.dumps(route53_ec2_instance_with_public_ip.template) conn = boto.cloudformation.connect_to_region("us-west-1") - stack = conn.create_stack( + conn.create_stack( "test_stack", template_body=template_json, ) @@ -855,7 +857,7 @@ def test_route53_associate_health_check(): template_json = json.dumps(route53_health_check.template) conn = boto.cloudformation.connect_to_region("us-west-1") - stack = conn.create_stack( + conn.create_stack( "test_stack", template_body=template_json, ) @@ -881,3 +883,56 @@ def test_route53_associate_health_check(): record_set = rrsets[0] record_set.health_check.should.equal(health_check_id) + + +@mock_cloudformation() +@mock_sns() +def test_sns_topic(): + dummy_template = { + "AWSTemplateFormatVersion": "2010-09-09", + "Resources": { + "MySNSTopic": { + "Type": "AWS::SNS::Topic", + "Properties": { + "Subscription": [ + {"Endpoint": "https://example.com", "Protocol": "https"}, + ], + "TopicName": "my_topics", + } + } + }, + "Outputs": { + "topic_name": { + "Value": {"Fn::GetAtt": ["MySNSTopic", "TopicName"]} + }, + "topic_arn": { + "Value": {"Ref": "MySNSTopic"} + }, + } + } + template_json = json.dumps(dummy_template) + conn = boto.cloudformation.connect_to_region("us-west-1") + stack = conn.create_stack( + "test_stack", + template_body=template_json, + ) + + sns_conn = boto.sns.connect_to_region("us-west-1") + topics = sns_conn.get_all_topics()["ListTopicsResponse"]["ListTopicsResult"]["Topics"] + topics.should.have.length_of(1) + topic_arn = topics[0]['TopicArn'] + topic_arn.should.contain("my_topics") + + subscriptions = sns_conn.get_all_subscriptions()["ListSubscriptionsResponse"]["ListSubscriptionsResult"]["Subscriptions"] + subscriptions.should.have.length_of(1) + subscription = subscriptions[0] + subscription["TopicArn"].should.equal(topic_arn) + subscription["Protocol"].should.equal("https") + subscription["SubscriptionArn"].should.contain(topic_arn) + subscription["Endpoint"].should.equal("https://example.com") + + stack = conn.describe_stacks()[0] + topic_name_output = [x for x in stack.outputs if x.key == 'topic_name'][0] + topic_name_output.value.should.equal("my_topics") + topic_arn_output = [x for x in stack.outputs if x.key == 'topic_arn'][0] + topic_arn_output.value.should.equal(topic_arn)