From c33f019c2ef5600c857858c99de76a79158b9ec1 Mon Sep 17 00:00:00 2001 From: gomi_ningen Date: Thu, 31 Oct 2019 17:56:57 +0900 Subject: [PATCH 1/7] refinement: SNS.to_cloudformation to minimize the diff, refine SNS.to_cloudformation in advance --- samtranslator/model/eventsources/push.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 6473c945a..f4d53a4b8 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -395,13 +395,15 @@ def to_cloudformation(self, **kwargs): # SNS -> SQS -> Lambda resources = [] queue = self._inject_sqs_queue() - queue_policy = self._inject_sqs_queue_policy(self.Topic, queue) + queue_arn = queue.get_runtime_attr('arn') + queue_url = queue.get_runtime_attr('queue_url') + queue_policy = self._inject_sqs_queue_policy(self.Topic, queue_arn, queue_url) subscription = self._inject_subscription( - 'sqs', queue.get_runtime_attr('arn'), + 'sqs', queue_arn, self.Topic, self.Region, self.FilterPolicy, function.resource_attributes ) - resources = resources + self._inject_sqs_event_source_mapping(function, role, queue.get_runtime_attr('arn')) + resources = resources + self._inject_sqs_event_source_mapping(function, role, queue_arn) resources.append(queue) resources.append(queue_policy) resources.append(subscription) @@ -432,12 +434,12 @@ def _inject_sqs_event_source_mapping(self, function, role, queue_arn): event_source.Enabled = True return event_source.to_cloudformation(function=function, role=role) - def _inject_sqs_queue_policy(self, topic_arn, queue): + def _inject_sqs_queue_policy(self, topic_arn, queue_arn, queue_url): policy = SQSQueuePolicy(self.logical_id + 'QueuePolicy') policy.PolicyDocument = SQSQueuePolicies.sns_topic_send_message_role_policy( - topic_arn, queue.get_runtime_attr('arn') + topic_arn, queue_arn ) - policy.Queues = [queue.get_runtime_attr('queue_url')] + policy.Queues = [queue_url] return policy From 6da31a7d806e10e1c8e510f36b9d9bed14ff240b Mon Sep 17 00:00:00 2001 From: gomi_ningen Date: Thu, 31 Oct 2019 18:56:18 +0900 Subject: [PATCH 2/7] refactor: make some parameters injectable to SQSEventSourceMapping and QueuePolicy --- samtranslator/model/eventsources/push.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index f4d53a4b8..08c86a924 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -403,7 +403,8 @@ def to_cloudformation(self, **kwargs): self.Topic, self.Region, self.FilterPolicy, function.resource_attributes ) - resources = resources + self._inject_sqs_event_source_mapping(function, role, queue_arn) + event_source = self._inject_sqs_event_source_mapping(function, role, queue_arn) + resources = resources + event_source resources.append(queue) resources.append(queue_policy) resources.append(subscription) @@ -427,15 +428,15 @@ def _inject_subscription(self, protocol, endpoint, topic, region, filterPolicy, def _inject_sqs_queue(self): return SQSQueue(self.logical_id + 'Queue') - def _inject_sqs_event_source_mapping(self, function, role, queue_arn): + def _inject_sqs_event_source_mapping(self, function, role, queue_arn, batch_size=None, enabled=None): event_source = SQS(self.logical_id + 'EventSourceMapping') event_source.Queue = queue_arn - event_source.BatchSize = 10 - event_source.Enabled = True + event_source.BatchSize = batch_size or 10 + event_source.Enabled = enabled or True return event_source.to_cloudformation(function=function, role=role) - def _inject_sqs_queue_policy(self, topic_arn, queue_arn, queue_url): - policy = SQSQueuePolicy(self.logical_id + 'QueuePolicy') + def _inject_sqs_queue_policy(self, topic_arn, queue_arn, queue_url, logical_id=None): + policy = SQSQueuePolicy(logical_id or self.logical_id + 'QueuePolicy') policy.PolicyDocument = SQSQueuePolicies.sns_topic_send_message_role_policy( topic_arn, queue_arn ) From f0f2be5ae9efacd764c05650aec5b9e99bf6ac6e Mon Sep 17 00:00:00 2001 From: gomi_ningen Date: Fri, 1 Nov 2019 04:28:42 +0900 Subject: [PATCH 3/7] feat: Add an existing SQS queue option to SNS event --- samtranslator/model/eventsources/push.py | 38 +++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 08c86a924..4e1498403 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -368,7 +368,7 @@ class SNS(PushEventSource): 'Topic': PropertyType(True, is_str()), 'Region': PropertyType(False, is_str()), 'FilterPolicy': PropertyType(False, dict_of(is_str(), list_of(one_of(is_str(), is_type(dict))))), - 'SqsSubscription': PropertyType(False, is_type(bool)) + 'SqsSubscription': PropertyType(False, one_of(is_type(bool), is_type(dict))) } def to_cloudformation(self, **kwargs): @@ -392,20 +392,42 @@ def to_cloudformation(self, **kwargs): ) return [self._construct_permission(function, source_arn=self.Topic), subscription] - # SNS -> SQS -> Lambda + # SNS -> SQS(Create New) -> Lambda + if isinstance(self.SqsSubscription, bool): + resources = [] + queue = self._inject_sqs_queue() + queue_arn = queue.get_runtime_attr('arn') + queue_url = queue.get_runtime_attr('queue_url') + + queue_policy = self._inject_sqs_queue_policy(self.Topic, queue_arn, queue_url) + subscription = self._inject_subscription( + 'sqs', queue_arn, + self.Topic, self.Region, self.FilterPolicy, function.resource_attributes + ) + event_source = self._inject_sqs_event_source_mapping(function, role, queue_arn) + + resources = resources + event_source + resources.append(queue) + resources.append(queue_policy) + resources.append(subscription) + return resources + + # SNS -> SQS(Existing) -> Lambda resources = [] - queue = self._inject_sqs_queue() - queue_arn = queue.get_runtime_attr('arn') - queue_url = queue.get_runtime_attr('queue_url') - queue_policy = self._inject_sqs_queue_policy(self.Topic, queue_arn, queue_url) + queue_arn = self.SqsSubscription.get('QueueArn', None) + queue_url = self.SqsSubscription.get('QueueUrl', None) + queue_policy_logical_id = self.SqsSubscription.get('QueuePolicyLogicalId', None) + batch_size = self.SqsSubscription.get('BatchSize', None) + enabled = self.SqsSubscription.get('Enabled', None) + + queue_policy = self._inject_sqs_queue_policy(self.Topic, queue_arn, queue_url, queue_policy_logical_id) subscription = self._inject_subscription( 'sqs', queue_arn, self.Topic, self.Region, self.FilterPolicy, function.resource_attributes ) + event_source = self._inject_sqs_event_source_mapping(function, role, queue_arn, batch_size, enabled) - event_source = self._inject_sqs_event_source_mapping(function, role, queue_arn) resources = resources + event_source - resources.append(queue) resources.append(queue_policy) resources.append(subscription) return resources From 90192b646c795253f8f4570c477dac11e5c2c205 Mon Sep 17 00:00:00 2001 From: gomi_ningen Date: Fri, 8 Nov 2019 03:06:36 +0900 Subject: [PATCH 4/7] Add tests --- tests/translator/input/sns_existing_sqs.yaml | 31 ++++ tests/translator/input/sns_outside_sqs.yaml | 28 ++++ .../output/aws-cn/sns_existing_sqs.json | 141 ++++++++++++++++++ .../output/aws-cn/sns_outside_sqs.json | 121 +++++++++++++++ .../output/aws-us-gov/sns_existing_sqs.json | 141 ++++++++++++++++++ .../output/aws-us-gov/sns_outside_sqs.json | 121 +++++++++++++++ tests/translator/output/sns_existing_sqs.json | 141 ++++++++++++++++++ tests/translator/output/sns_outside_sqs.json | 121 +++++++++++++++ tests/translator/test_translator.py | 2 + 9 files changed, 847 insertions(+) create mode 100644 tests/translator/input/sns_existing_sqs.yaml create mode 100644 tests/translator/input/sns_outside_sqs.yaml create mode 100644 tests/translator/output/aws-cn/sns_existing_sqs.json create mode 100644 tests/translator/output/aws-cn/sns_outside_sqs.json create mode 100644 tests/translator/output/aws-us-gov/sns_existing_sqs.json create mode 100644 tests/translator/output/aws-us-gov/sns_outside_sqs.json create mode 100644 tests/translator/output/sns_existing_sqs.json create mode 100644 tests/translator/output/sns_outside_sqs.json diff --git a/tests/translator/input/sns_existing_sqs.yaml b/tests/translator/input/sns_existing_sqs.yaml new file mode 100644 index 000000000..b6da05861 --- /dev/null +++ b/tests/translator/input/sns_existing_sqs.yaml @@ -0,0 +1,31 @@ +Resources: + SaveNotificationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/notifications.zip + Handler: index.save_notification + Runtime: nodejs8.10 + Events: + NotificationTopic: + Type: SNS + Properties: + Topic: !Ref Notifications + SqsSubscription: + QueueUrl: !Ref Queue + QueueArn: !GetAtt Queue.Arn + QueuePolicyLogicalId: NotificationA + BatchSize: 8 + Enabled: true + FilterPolicy: + store: + - example_corp + price_usd: + - numeric: + - ">=" + - 100 + + Notifications: + Type: AWS::SNS::Topic + + Queue: + Type: AWS::SQS::Queue \ No newline at end of file diff --git a/tests/translator/input/sns_outside_sqs.yaml b/tests/translator/input/sns_outside_sqs.yaml new file mode 100644 index 000000000..4116b28d0 --- /dev/null +++ b/tests/translator/input/sns_outside_sqs.yaml @@ -0,0 +1,28 @@ +Resources: + SaveNotificationFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/notifications.zip + Handler: index.save_notification + Runtime: nodejs8.10 + Events: + NotificationTopic: + Type: SNS + Properties: + Topic: !Ref Notifications + SqsSubscription: + QueueUrl: https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue + QueueArn: arn:aws:sqs:us-east-1:123456789012:MyQueue + QueuePolicyLogicalId: NotificationB + BatchSize: 8 + Enabled: true + FilterPolicy: + store: + - example_corp + price_usd: + - numeric: + - ">=" + - 100 + + Notifications: + Type: AWS::SNS::Topic diff --git a/tests/translator/output/aws-cn/sns_existing_sqs.json b/tests/translator/output/aws-cn/sns_existing_sqs.json new file mode 100644 index 000000000..3cc5426f5 --- /dev/null +++ b/tests/translator/output/aws-cn/sns_existing_sqs.json @@ -0,0 +1,141 @@ +{ + "Resources": { + "Queue": { + "Type": "AWS::SQS::Queue" + }, + "Notifications": { + "Type": "AWS::SNS::Topic" + }, + "NotificationA": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "Queues": [ + { + "Ref": "Queue" + } + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sqs:SendMessage", + "Resource": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + }, + "Effect": "Allow", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "Notifications" + } + } + }, + "Principal": "*" + } + ] + } + } + }, + "SaveNotificationFunctionNotificationTopic": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "FilterPolicy": { + "price_usd": [ + { + "numeric": [ + ">=", + 100 + ] + } + ], + "store": [ + "example_corp" + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + }, + "Protocol": "sqs", + "TopicArn": { + "Ref": "Notifications" + } + } + }, + "SaveNotificationFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "SaveNotificationFunctionNotificationTopicEventSourceMapping": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 8, + "Enabled": true, + "FunctionName": { + "Ref": "SaveNotificationFunction" + }, + "EventSourceArn": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + } + } + }, + "SaveNotificationFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.save_notification", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "notifications.zip" + }, + "Role": { + "Fn::GetAtt": [ + "SaveNotificationFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/sns_outside_sqs.json b/tests/translator/output/aws-cn/sns_outside_sqs.json new file mode 100644 index 000000000..7122128f3 --- /dev/null +++ b/tests/translator/output/aws-cn/sns_outside_sqs.json @@ -0,0 +1,121 @@ +{ + "Resources": { + "NotificationB": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "Queues": [ + "https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue" + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sqs:SendMessage", + "Resource": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "Effect": "Allow", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "Notifications" + } + } + }, + "Principal": "*" + } + ] + } + } + }, + "Notifications": { + "Type": "AWS::SNS::Topic" + }, + "SaveNotificationFunctionNotificationTopic": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "FilterPolicy": { + "price_usd": [ + { + "numeric": [ + ">=", + 100 + ] + } + ], + "store": [ + "example_corp" + ] + }, + "Endpoint": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "Protocol": "sqs", + "TopicArn": { + "Ref": "Notifications" + } + } + }, + "SaveNotificationFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "SaveNotificationFunctionNotificationTopicEventSourceMapping": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 8, + "Enabled": true, + "FunctionName": { + "Ref": "SaveNotificationFunction" + }, + "EventSourceArn": "arn:aws:sqs:us-east-1:123456789012:MyQueue" + } + }, + "SaveNotificationFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.save_notification", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "notifications.zip" + }, + "Role": { + "Fn::GetAtt": [ + "SaveNotificationFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/sns_existing_sqs.json b/tests/translator/output/aws-us-gov/sns_existing_sqs.json new file mode 100644 index 000000000..cdf5e0556 --- /dev/null +++ b/tests/translator/output/aws-us-gov/sns_existing_sqs.json @@ -0,0 +1,141 @@ +{ + "Resources": { + "Queue": { + "Type": "AWS::SQS::Queue" + }, + "Notifications": { + "Type": "AWS::SNS::Topic" + }, + "NotificationA": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "Queues": [ + { + "Ref": "Queue" + } + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sqs:SendMessage", + "Resource": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + }, + "Effect": "Allow", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "Notifications" + } + } + }, + "Principal": "*" + } + ] + } + } + }, + "SaveNotificationFunctionNotificationTopic": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "FilterPolicy": { + "price_usd": [ + { + "numeric": [ + ">=", + 100 + ] + } + ], + "store": [ + "example_corp" + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + }, + "Protocol": "sqs", + "TopicArn": { + "Ref": "Notifications" + } + } + }, + "SaveNotificationFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "SaveNotificationFunctionNotificationTopicEventSourceMapping": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 8, + "Enabled": true, + "FunctionName": { + "Ref": "SaveNotificationFunction" + }, + "EventSourceArn": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + } + } + }, + "SaveNotificationFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.save_notification", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "notifications.zip" + }, + "Role": { + "Fn::GetAtt": [ + "SaveNotificationFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/sns_outside_sqs.json b/tests/translator/output/aws-us-gov/sns_outside_sqs.json new file mode 100644 index 000000000..81af210ac --- /dev/null +++ b/tests/translator/output/aws-us-gov/sns_outside_sqs.json @@ -0,0 +1,121 @@ +{ + "Resources": { + "NotificationB": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "Queues": [ + "https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue" + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sqs:SendMessage", + "Resource": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "Effect": "Allow", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "Notifications" + } + } + }, + "Principal": "*" + } + ] + } + } + }, + "Notifications": { + "Type": "AWS::SNS::Topic" + }, + "SaveNotificationFunctionNotificationTopic": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "FilterPolicy": { + "price_usd": [ + { + "numeric": [ + ">=", + 100 + ] + } + ], + "store": [ + "example_corp" + ] + }, + "Endpoint": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "Protocol": "sqs", + "TopicArn": { + "Ref": "Notifications" + } + } + }, + "SaveNotificationFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "SaveNotificationFunctionNotificationTopicEventSourceMapping": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 8, + "Enabled": true, + "FunctionName": { + "Ref": "SaveNotificationFunction" + }, + "EventSourceArn": "arn:aws:sqs:us-east-1:123456789012:MyQueue" + } + }, + "SaveNotificationFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.save_notification", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "notifications.zip" + }, + "Role": { + "Fn::GetAtt": [ + "SaveNotificationFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/sns_existing_sqs.json b/tests/translator/output/sns_existing_sqs.json new file mode 100644 index 000000000..5b8936ce5 --- /dev/null +++ b/tests/translator/output/sns_existing_sqs.json @@ -0,0 +1,141 @@ +{ + "Resources": { + "Queue": { + "Type": "AWS::SQS::Queue" + }, + "Notifications": { + "Type": "AWS::SNS::Topic" + }, + "NotificationA": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "Queues": [ + { + "Ref": "Queue" + } + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sqs:SendMessage", + "Resource": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + }, + "Effect": "Allow", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "Notifications" + } + } + }, + "Principal": "*" + } + ] + } + } + }, + "SaveNotificationFunctionNotificationTopic": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "FilterPolicy": { + "price_usd": [ + { + "numeric": [ + ">=", + 100 + ] + } + ], + "store": [ + "example_corp" + ] + }, + "Endpoint": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + }, + "Protocol": "sqs", + "TopicArn": { + "Ref": "Notifications" + } + } + }, + "SaveNotificationFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "SaveNotificationFunctionNotificationTopicEventSourceMapping": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 8, + "Enabled": true, + "FunctionName": { + "Ref": "SaveNotificationFunction" + }, + "EventSourceArn": { + "Fn::GetAtt": [ + "Queue", + "Arn" + ] + } + } + }, + "SaveNotificationFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.save_notification", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "notifications.zip" + }, + "Role": { + "Fn::GetAtt": [ + "SaveNotificationFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/sns_outside_sqs.json b/tests/translator/output/sns_outside_sqs.json new file mode 100644 index 000000000..ea040d72f --- /dev/null +++ b/tests/translator/output/sns_outside_sqs.json @@ -0,0 +1,121 @@ +{ + "Resources": { + "NotificationB": { + "Type": "AWS::SQS::QueuePolicy", + "Properties": { + "Queues": [ + "https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue" + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "sqs:SendMessage", + "Resource": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "Effect": "Allow", + "Condition": { + "ArnEquals": { + "aws:SourceArn": { + "Ref": "Notifications" + } + } + }, + "Principal": "*" + } + ] + } + } + }, + "Notifications": { + "Type": "AWS::SNS::Topic" + }, + "SaveNotificationFunctionNotificationTopic": { + "Type": "AWS::SNS::Subscription", + "Properties": { + "FilterPolicy": { + "price_usd": [ + { + "numeric": [ + ">=", + 100 + ] + } + ], + "store": [ + "example_corp" + ] + }, + "Endpoint": "arn:aws:sqs:us-east-1:123456789012:MyQueue", + "Protocol": "sqs", + "TopicArn": { + "Ref": "Notifications" + } + } + }, + "SaveNotificationFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + }, + "SaveNotificationFunctionNotificationTopicEventSourceMapping": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 8, + "Enabled": true, + "FunctionName": { + "Ref": "SaveNotificationFunction" + }, + "EventSourceArn": "arn:aws:sqs:us-east-1:123456789012:MyQueue" + } + }, + "SaveNotificationFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Handler": "index.save_notification", + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "notifications.zip" + }, + "Role": { + "Fn::GetAtt": [ + "SaveNotificationFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs8.10", + "Tags": [ + { + "Value": "SAM", + "Key": "lambda:createdBy" + } + ] + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 486e38faa..d40ff8fd7 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -199,6 +199,8 @@ class TestTranslatorEndToEnd(TestCase): 's3_with_dependsOn', 'sns', 'sns_sqs', + 'sns_existing_sqs', + 'sns_outside_sqs', 'sns_existing_other_subscription', 'sns_topic_outside_template', 'alexa_skill', From fd99293a40ba3ad8918914a7e1f6339b028c1ed2 Mon Sep 17 00:00:00 2001 From: gomi_ningen Date: Fri, 8 Nov 2019 23:13:05 +0900 Subject: [PATCH 5/7] update a document --- docs/internals/generated_resources.rst | 29 +++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/docs/internals/generated_resources.rst b/docs/internals/generated_resources.rst index 8f38f6641..9404bc1fa 100644 --- a/docs/internals/generated_resources.rst +++ b/docs/internals/generated_resources.rst @@ -224,7 +224,12 @@ Example: Type: SNS Properties: Topic: arn:aws:sns:us-east-1:123456789012:my_topic - SqsSubscription: true + SqsSubscription: + QueuePolicyLogicalId: CustomQueuePolicyLogicalId + QueueArn: !GetAtt MyCustomQueue.Arn + QueueUrl: !Ref MyCustomQueue + BatchSize: 5 + Enabled: true ... Additional generated resources: @@ -241,6 +246,24 @@ AWS::SQS::QueuePolicy MyFunction\ **MyTrigger**\ QueuePolicy NOTE: ``AWS::Lambda::Permission`` resources are only generated if SqsSubscription is ``false``. ``AWS::Lambda::EventSourceMapping``, ``AWS::SQS::Queue``, ``AWS::SQS::QueuePolicy`` resources are only generated if SqsSubscription is ``true``. + ``AWS::SQS::Queue`` resources are only generated if SqsSubscription is ``true``. + + Example: + + .. code:: yaml + + MyFunction: + Type: AWS::Serverless::Function + Properties: + ... + Events: + MyTrigger: + Type: SNS + Properties: + Topic: arn:aws:sns:us-east-1:123456789012:my_topic + SqsSubscription: true + ... + Kinesis ^^^^^^^ @@ -410,9 +433,9 @@ AWS::ApiGateway::Stage MyApi\ **dev**\ Stage AWS::ApiGateway::Deployment MyApi\ Deployment\ *SHA* (10 Digits of SHA256 of DefinitionUri or DefinitionBody value) ================================== ================================ - NOTE: By just specifying AWS::Serverless::Api resource, SAM will *not* add permission for API Gateway to invoke the + NOTE: By just specifying AWS::Serverless::Api resource, SAM will *not* add permission for API Gateway to invoke the the Lambda Function backing the APIs. You should explicitly re-define all APIs under ``Events`` section of the - AWS::Serverless::Function resource but include a `RestApiId` property that references the AWS::Serverless::Api + AWS::Serverless::Function resource but include a `RestApiId` property that references the AWS::Serverless::Api resource. SAM will add permission for these APIs to invoke the function. Example: From 40cba76e9788728ace35fbe55886f93f9cc4991d Mon Sep 17 00:00:00 2001 From: gomi_ningen Date: Fri, 6 Dec 2019 01:45:29 +0900 Subject: [PATCH 6/7] throws exception when a QueueARN or QueueURL not given --- samtranslator/model/eventsources/push.py | 4 ++++ tests/model/eventsources/test_sns_event_source.py | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 4e1498403..397edddd0 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -416,6 +416,10 @@ def to_cloudformation(self, **kwargs): resources = [] queue_arn = self.SqsSubscription.get('QueueArn', None) queue_url = self.SqsSubscription.get('QueueUrl', None) + if not queue_arn or not queue_url: + raise InvalidEventException( + self.relative_id, "No QueueARN or QueueURL provided.") + queue_policy_logical_id = self.SqsSubscription.get('QueuePolicyLogicalId', None) batch_size = self.SqsSubscription.get('BatchSize', None) enabled = self.SqsSubscription.get('Enabled', None) diff --git a/tests/model/eventsources/test_sns_event_source.py b/tests/model/eventsources/test_sns_event_source.py index 434aca948..62acdd74d 100644 --- a/tests/model/eventsources/test_sns_event_source.py +++ b/tests/model/eventsources/test_sns_event_source.py @@ -64,3 +64,10 @@ def test_to_cloudformation_passes_the_filter_policy(self): def test_to_cloudformation_throws_when_no_function(self): self.assertRaises(TypeError, self.sns_event_source.to_cloudformation) + + def test_to_cloudformation_throws_when_queue_url_or_queue_arn_not_given(self): + sqsSubscription = { + 'BatchSize': 5 + } + self.sns_event_source.SqsSubscription = sqsSubscription + self.assertRaises(TypeError, self.sns_event_source.to_cloudformation) From 9189a2e6db9100d7eca5e137094ae391e73eea1e Mon Sep 17 00:00:00 2001 From: gomi_ningen Date: Fri, 6 Dec 2019 01:47:53 +0900 Subject: [PATCH 7/7] add a unit test for SqsSubscription: false --- .../eventsources/test_sns_event_source.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/model/eventsources/test_sns_event_source.py b/tests/model/eventsources/test_sns_event_source.py index 62acdd74d..f877fe9bd 100644 --- a/tests/model/eventsources/test_sns_event_source.py +++ b/tests/model/eventsources/test_sns_event_source.py @@ -71,3 +71,22 @@ def test_to_cloudformation_throws_when_queue_url_or_queue_arn_not_given(self): } self.sns_event_source.SqsSubscription = sqsSubscription self.assertRaises(TypeError, self.sns_event_source.to_cloudformation) + + def test_to_cloudformation_when_sqs_subscription_disable(self): + sqsSubscription = False + self.sns_event_source.SqsSubscription = sqsSubscription + + resources = self.sns_event_source.to_cloudformation( + function=self.function) + self.assertEqual(len(resources), 2) + self.assertEqual(resources[0].resource_type, + 'AWS::Lambda::Permission') + self.assertEqual(resources[1].resource_type, + 'AWS::SNS::Subscription') + + subscription = resources[1] + self.assertEqual(subscription.TopicArn, 'arn:aws:sns:MyTopic') + self.assertEqual(subscription.Protocol, 'lambda') + self.assertEqual(subscription.Endpoint, 'arn:aws:lambda:mock') + self.assertIsNone(subscription.Region) + self.assertIsNone(subscription.FilterPolicy)