diff --git a/docs/cloudformation_compatibility.rst b/docs/cloudformation_compatibility.rst index 187feb531..968819e1b 100644 --- a/docs/cloudformation_compatibility.rst +++ b/docs/cloudformation_compatibility.rst @@ -91,6 +91,7 @@ Kinesis Property Name Intrinsic(s) Supported Reasons ======================== ================================== ======================== Stream All +Queue All StartingPosition All BatchSize All ======================== ================================== ======================== diff --git a/docs/internals/generated_resources.rst b/docs/internals/generated_resources.rst index 7b3acd3bd..b8323871e 100644 --- a/docs/internals/generated_resources.rst +++ b/docs/internals/generated_resources.rst @@ -220,6 +220,33 @@ AWS::Lambda::Permissions MyFunction\ **MyTrigger**\ Permission AWS::Lambda::EventSourceMapping MyFunction\ **MyTrigger** ================================== ================================ +SQS +^^^^^^^ + +Example: + +.. code:: yaml + + MyFunction: + Type: AWS::Serverless::Function + Properties: + ... + Events: + MyTrigger: + Type: SQS + Properties: + Queue: arn:aws:sqs:us-east-1:123456789012:my-queue + ... + +Additional generated resources: + +================================== ================================ +CloudFormation Resource Type Logical ID +================================== ================================ +AWS::Lambda::Permissions MyFunction\ **MyTrigger**\ Permission +AWS::Lambda::EventSourceMapping MyFunction\ **MyTrigger** +================================== ================================ + DynamoDb ^^^^^^^^ diff --git a/examples/2016-10-31/sqs/.gitignore b/examples/2016-10-31/sqs/.gitignore new file mode 100644 index 000000000..97902fce3 --- /dev/null +++ b/examples/2016-10-31/sqs/.gitignore @@ -0,0 +1 @@ +transformed-cfn-template.yaml \ No newline at end of file diff --git a/examples/2016-10-31/sqs/README.md b/examples/2016-10-31/sqs/README.md new file mode 100644 index 000000000..ffe734d2f --- /dev/null +++ b/examples/2016-10-31/sqs/README.md @@ -0,0 +1,19 @@ +# SQS Event Source Example + +Example SAM template for processing messages on an SQS queue. + +## Running the example + +```bash +# Replace YOUR_S3_ARTIFACTS_BUCKET +YOUR_S3_ARTIFACTS_BUCKET='YOUR_S3_ARTIFACTS_BUCKET'; \ +aws cloudformation package --template-file template.yaml --output-template-file cfn-transformed-template.yaml --s3-bucket $YOUR_S3_ARTIFACTS_BUCKET +aws cloudformation deploy --template-file ./cfn-transformed-template.yaml --stack-name example-logs-processor --capabilities CAPABILITY_IAM +``` + +After your CloudFormation Stack has completed creation, push a message to the SQS queue. To see it in action, modify and run the command below: + +```bash +YOUR_SQS_QUEUE_URL=https://sqs.us-east-1.amazonaws.com/123456789012/my-queue; \ +aws sqs send-message --queue-url $YOUR_SQS_QUEUE_URL --message-body '{ "myMessage": "Hello SAM!" }' +``` \ No newline at end of file diff --git a/examples/2016-10-31/sqs/index.js b/examples/2016-10-31/sqs/index.js new file mode 100644 index 000000000..5ab77b9ed --- /dev/null +++ b/examples/2016-10-31/sqs/index.js @@ -0,0 +1,9 @@ +async function handler (event, context) { + // TODO: Handle message... + + console.log(event) + + return {} +} + +module.exports.handler = handler \ No newline at end of file diff --git a/examples/2016-10-31/sqs/template.yaml b/examples/2016-10-31/sqs/template.yaml new file mode 100644 index 000000000..f1c9f83a6 --- /dev/null +++ b/examples/2016-10-31/sqs/template.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Example of processing messages on an SQS queue with Lambda +Resources: + MySQSQueueFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: ./index.js + Handler: index.handler + Runtime: nodejs8.10 + Events: + MySQSEvent: + Type: SQS + Properties: + Queue: !Ref MyQueue + + MyQueue: + Type: AWS::SQS::Queue + Properties: \ No newline at end of file diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index 7a692d585..e2e309237 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -3,21 +3,23 @@ from samtranslator.model.lambda_ import LambdaEventSourceMapping from samtranslator.translator.arn_generator import ArnGenerator +from samtranslator.model.exceptions import InvalidEventException class PullEventSource(ResourceMacro): """Base class for pull event sources for SAM Functions. - The pull events are the streams--Kinesis and DynamoDB Streams. Both of these correspond to an EventSourceMapping in - Lambda, and require that the execution role be given to Kinesis or DynamoDB Streams, respectively. + The pull events are Kinesis Streams, DynamoDB Streams, and SQS Queues. All of these correspond to an EventSourceMapping in + Lambda, and require that the execution role be given to Kinesis Streams, DynamoDB Streams, or SQS Queues, respectively. :cvar str policy_arn: The ARN of the AWS managed role policy corresponding to this pull event source """ resource_type = None property_types = { - 'Stream': PropertyType(True, is_str()), - 'BatchSize': PropertyType(False, is_type(int)), - 'StartingPosition': PropertyType(True, is_str()) + 'Stream': PropertyType(False, is_str()), + 'Queue': PropertyType(False, is_str()), + 'BatchSize': PropertyType(False, is_type(int)), + 'StartingPosition': PropertyType(False, is_str()) } def get_policy_arn(self): @@ -32,7 +34,7 @@ def to_cloudformation(self, **kwargs): :rtype: list """ function = kwargs.get('function') - + if not function: raise TypeError("Missing required keyword argument: function") @@ -40,15 +42,23 @@ def to_cloudformation(self, **kwargs): lambda_eventsourcemapping = LambdaEventSourceMapping(self.logical_id) resources.append(lambda_eventsourcemapping) - + try: # Name will not be available for Alias resources function_name_or_arn = function.get_runtime_attr("name") except NotImplementedError: function_name_or_arn = function.get_runtime_attr("arn") + if not self.Stream and not self.Queue: + raise InvalidEventException( + self.relative_id, "No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided.") + + if self.Stream and not self.StartingPosition: + raise InvalidEventException( + self.relative_id, "StartingPosition is required for Kinesis and DynamoDB.") + lambda_eventsourcemapping.FunctionName = function_name_or_arn - lambda_eventsourcemapping.EventSourceArn = self.Stream + lambda_eventsourcemapping.EventSourceArn = self.Stream or self.Queue lambda_eventsourcemapping.StartingPosition = self.StartingPosition lambda_eventsourcemapping.BatchSize = self.BatchSize @@ -82,3 +92,11 @@ class DynamoDB(PullEventSource): def get_policy_arn(self): return ArnGenerator.generate_aws_managed_policy_arn('service-role/AWSLambdaDynamoDBExecutionRole') + + +class SQS(PullEventSource): + """SQS Queue event source.""" + resource_type = 'SQS' + + def get_policy_arn(self): + return ArnGenerator.generate_aws_managed_policy_arn('service-role/AWSLambdaSQSExecutionRole') diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index f1900a9d7..bf0fe6b12 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -61,7 +61,7 @@ class LambdaEventSourceMapping(Resource): 'Enabled': PropertyType(False, is_type(bool)), 'EventSourceArn': PropertyType(True, is_str()), 'FunctionName': PropertyType(True, is_str()), - 'StartingPosition': PropertyType(True, is_str()) + 'StartingPosition': PropertyType(False, is_str()) } runtime_attrs = { diff --git a/samtranslator/validator/sam_schema/schema.json b/samtranslator/validator/sam_schema/schema.json index cdcb08d4b..b0bd9db45 100644 --- a/samtranslator/validator/sam_schema/schema.json +++ b/samtranslator/validator/sam_schema/schema.json @@ -381,6 +381,9 @@ { "$ref": "#/definitions/AWS::Serverless::Function.KinesisEvent" }, + { + "$ref": "#/definitions/AWS::Serverless::Function.SQSEvent" + }, { "$ref": "#/definitions/AWS::Serverless::Function.DynamoDBEvent" }, @@ -481,6 +484,21 @@ ], "type": "object" }, + "AWS::Serverless::Function.SQSEvent": { + "additionalProperties": false, + "properties": { + "BatchSize": { + "type": "number" + }, + "Queue": { + "type": "string" + } + }, + "required": [ + "Queue" + ], + "type": "object" + }, "AWS::Serverless::Function.S3Event": { "additionalProperties": false, "properties": { diff --git a/tests/translator/input/error_missing_queue.yaml b/tests/translator/input/error_missing_queue.yaml new file mode 100644 index 000000000..499453c4a --- /dev/null +++ b/tests/translator/input/error_missing_queue.yaml @@ -0,0 +1,17 @@ +# File: sam.yml +# Version: 0.9 + +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + SQSFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/queues.zip + Handler: queue.sqs_handler + Runtime: python2.7 + Events: + MySqsQueue: + Type: SQS + Properties: + BatchSize: 10 diff --git a/tests/translator/input/error_missing_startingposition.yaml b/tests/translator/input/error_missing_startingposition.yaml new file mode 100644 index 000000000..fee16ded0 --- /dev/null +++ b/tests/translator/input/error_missing_startingposition.yaml @@ -0,0 +1,13 @@ +Resources: + KinesisFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/streams.zip + Handler: stream.kinesis_handler + Runtime: python2.7 + Events: + MyKinesisStream: + Type: Kinesis + Properties: + Stream: arn:aws:kinesis:us-west-2:012345678901:stream/my-stream + BatchSize: 100 \ No newline at end of file diff --git a/tests/translator/input/error_missing_stream.yaml b/tests/translator/input/error_missing_stream.yaml new file mode 100644 index 000000000..56a22aedb --- /dev/null +++ b/tests/translator/input/error_missing_stream.yaml @@ -0,0 +1,15 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + DynamoDBFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/streams.zip + Handler: stream.ddb_handler + Runtime: python2.7 + Events: + MyDDBStream: + Type: DynamoDB + Properties: + BatchSize: 200 + StartingPosition: LATEST \ No newline at end of file diff --git a/tests/translator/input/sqs.yaml b/tests/translator/input/sqs.yaml new file mode 100644 index 000000000..8ee02599a --- /dev/null +++ b/tests/translator/input/sqs.yaml @@ -0,0 +1,13 @@ +Resources: + SQSFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/queues.zip + Handler: queue.sqs_handler + Runtime: python2.7 + Events: + MySqsQueue: + Type: SQS + Properties: + Queue: arn:aws:sqs:us-west-2:012345678901:my-queue + BatchSize: 10 diff --git a/tests/translator/output/aws-cn/error_missing_queue.json b/tests/translator/output/aws-cn/error_missing_queue.json new file mode 100644 index 000000000..f92bbf80e --- /dev/null +++ b/tests/translator/output/aws-cn/error_missing_queue.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/error_missing_stream.json b/tests/translator/output/aws-cn/error_missing_stream.json new file mode 100644 index 000000000..d8925a773 --- /dev/null +++ b/tests/translator/output/aws-cn/error_missing_stream.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/sqs.json b/tests/translator/output/aws-cn/sqs.json new file mode 100644 index 000000000..29e529af5 --- /dev/null +++ b/tests/translator/output/aws-cn/sqs.json @@ -0,0 +1,58 @@ +{ + "Resources": { + "SQSFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaSQSExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "SQSFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "queues.zip" + }, + "Handler": "queue.sqs_handler", + "Role": { + "Fn::GetAtt": [ + "SQSFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "SQSFunctionMySqsQueue": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 10, + "EventSourceArn": "arn:aws:sqs:us-west-2:012345678901:my-queue", + "FunctionName": { + "Ref": "SQSFunction" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/streams.json b/tests/translator/output/aws-cn/streams.json index f760304aa..f4c82223c 100644 --- a/tests/translator/output/aws-cn/streams.json +++ b/tests/translator/output/aws-cn/streams.json @@ -1,120 +1,112 @@ { "Resources": { "DynamoDBFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "streams.zip" - }, - "Handler": "stream.ddb_handler", + }, + "Handler": "stream.ddb_handler", "Role": { "Fn::GetAtt": [ - "DynamoDBFunctionRole", + "DynamoDBFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] } - }, + }, "KinesisFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "streams.zip" - }, - "Handler": "stream.kinesis_handler", + }, + "Handler": "stream.kinesis_handler", "Role": { "Fn::GetAtt": [ - "KinesisFunctionRole", + "KinesisFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] } - }, + }, "DynamoDBFunctionMyDDBStream": { - "Type": "AWS::Lambda::EventSourceMapping", + "Type": "AWS::Lambda::EventSourceMapping", "Properties": { - "BatchSize": 200, + "BatchSize": 200, "EventSourceArn": "arn:aws:dynamodb:us-west-2:012345678901:table/TestTable/stream/2015-05-11T21:21:33.291", "FunctionName": { "Ref": "DynamoDBFunction" - }, + }, "StartingPosition": "LATEST" } - }, + }, "DynamoDBFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] } - ] + }] } } - }, + }, "KinesisFunctionMyKinesisStream": { - "Type": "AWS::Lambda::EventSourceMapping", + "Type": "AWS::Lambda::EventSourceMapping", "Properties": { - "BatchSize": 100, + "BatchSize": 100, "EventSourceArn": "arn:aws:kinesis:us-west-2:012345678901:stream/my-stream", "FunctionName": { "Ref": "KinesisFunction" - }, + }, "StartingPosition": "TRIM_HORIZON" } - }, + }, "KinesisFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] } - ] + }] } } } diff --git a/tests/translator/output/aws-us-gov/error_missing_queue.json b/tests/translator/output/aws-us-gov/error_missing_queue.json new file mode 100644 index 000000000..f92bbf80e --- /dev/null +++ b/tests/translator/output/aws-us-gov/error_missing_queue.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/error_missing_stream.json b/tests/translator/output/aws-us-gov/error_missing_stream.json new file mode 100644 index 000000000..d8925a773 --- /dev/null +++ b/tests/translator/output/aws-us-gov/error_missing_stream.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/sqs.json b/tests/translator/output/aws-us-gov/sqs.json new file mode 100644 index 000000000..f58f3df02 --- /dev/null +++ b/tests/translator/output/aws-us-gov/sqs.json @@ -0,0 +1,58 @@ +{ + "Resources": { + "SQSFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaSQSExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "SQSFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "queues.zip" + }, + "Handler": "queue.sqs_handler", + "Role": { + "Fn::GetAtt": [ + "SQSFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "SQSFunctionMySqsQueue": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 10, + "EventSourceArn": "arn:aws:sqs:us-west-2:012345678901:my-queue", + "FunctionName": { + "Ref": "SQSFunction" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/streams.json b/tests/translator/output/aws-us-gov/streams.json index 7d6098db6..d4f718822 100644 --- a/tests/translator/output/aws-us-gov/streams.json +++ b/tests/translator/output/aws-us-gov/streams.json @@ -1,120 +1,112 @@ { "Resources": { "DynamoDBFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "streams.zip" - }, - "Handler": "stream.ddb_handler", + }, + "Handler": "stream.ddb_handler", "Role": { "Fn::GetAtt": [ - "DynamoDBFunctionRole", + "DynamoDBFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] } - }, + }, "KinesisFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "streams.zip" - }, - "Handler": "stream.kinesis_handler", + }, + "Handler": "stream.kinesis_handler", "Role": { "Fn::GetAtt": [ - "KinesisFunctionRole", + "KinesisFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] } - }, + }, "DynamoDBFunctionMyDDBStream": { - "Type": "AWS::Lambda::EventSourceMapping", + "Type": "AWS::Lambda::EventSourceMapping", "Properties": { - "BatchSize": 200, + "BatchSize": 200, "EventSourceArn": "arn:aws:dynamodb:us-west-2:012345678901:table/TestTable/stream/2015-05-11T21:21:33.291", "FunctionName": { "Ref": "DynamoDBFunction" - }, + }, "StartingPosition": "LATEST" } - }, + }, "DynamoDBFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] } - ] + }] } } - }, + }, "KinesisFunctionMyKinesisStream": { - "Type": "AWS::Lambda::EventSourceMapping", + "Type": "AWS::Lambda::EventSourceMapping", "Properties": { - "BatchSize": 100, + "BatchSize": 100, "EventSourceArn": "arn:aws:kinesis:us-west-2:012345678901:stream/my-stream", "FunctionName": { "Ref": "KinesisFunction" - }, + }, "StartingPosition": "TRIM_HORIZON" } - }, + }, "KinesisFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] } - ] + }] } } } diff --git a/tests/translator/output/error_missing_queue.json b/tests/translator/output/error_missing_queue.json new file mode 100644 index 000000000..f92bbf80e --- /dev/null +++ b/tests/translator/output/error_missing_queue.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [SQSFunction] is invalid. Event with id [MySqsQueue] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." +} \ No newline at end of file diff --git a/tests/translator/output/error_missing_startingposition.json b/tests/translator/output/error_missing_startingposition.json new file mode 100644 index 000000000..4e3da968c --- /dev/null +++ b/tests/translator/output/error_missing_startingposition.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [KinesisFunction] is invalid. Event with id [MyKinesisStream] is invalid. StartingPosition is required for Kinesis and DynamoDB." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KinesisFunction] is invalid. Event with id [MyKinesisStream] is invalid. StartingPosition is required for Kinesis and DynamoDB." +} \ No newline at end of file diff --git a/tests/translator/output/error_missing_stream.json b/tests/translator/output/error_missing_stream.json new file mode 100644 index 000000000..d8925a773 --- /dev/null +++ b/tests/translator/output/error_missing_stream.json @@ -0,0 +1,6 @@ +{ + "errors": [{ + "errorMessage": "Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." + }], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [DynamoDBFunction] is invalid. Event with id [MyDDBStream] is invalid. No Queue (for SQS) or Stream (for Kinesis or DynamoDB) provided." +} \ No newline at end of file diff --git a/tests/translator/output/sqs.json b/tests/translator/output/sqs.json new file mode 100644 index 000000000..0ca3a1860 --- /dev/null +++ b/tests/translator/output/sqs.json @@ -0,0 +1,58 @@ +{ + "Resources": { + "SQSFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSExecutionRole" + ], + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + }] + } + } + }, + "SQSFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "queues.zip" + }, + "Handler": "queue.sqs_handler", + "Role": { + "Fn::GetAtt": [ + "SQSFunctionRole", + "Arn" + ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] + } + }, + "SQSFunctionMySqsQueue": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 10, + "EventSourceArn": "arn:aws:sqs:us-west-2:012345678901:my-queue", + "FunctionName": { + "Ref": "SQSFunction" + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/streams.json b/tests/translator/output/streams.json index b814232e7..b8847e458 100644 --- a/tests/translator/output/streams.json +++ b/tests/translator/output/streams.json @@ -1,120 +1,112 @@ { "Resources": { "DynamoDBFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "streams.zip" - }, - "Handler": "stream.ddb_handler", + }, + "Handler": "stream.ddb_handler", "Role": { "Fn::GetAtt": [ - "DynamoDBFunctionRole", + "DynamoDBFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] } - }, + }, "KinesisFunction": { - "Type": "AWS::Lambda::Function", + "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": "sam-demo-bucket", "S3Key": "streams.zip" - }, - "Handler": "stream.kinesis_handler", + }, + "Handler": "stream.kinesis_handler", "Role": { "Fn::GetAtt": [ - "KinesisFunctionRole", + "KinesisFunctionRole", "Arn" ] - }, - "Runtime": "python2.7", - "Tags": [ - { - "Value": "SAM", - "Key": "lambda:createdBy" - } - ] + }, + "Runtime": "python2.7", + "Tags": [{ + "Value": "SAM", + "Key": "lambda:createdBy" + }] } - }, + }, "DynamoDBFunctionMyDDBStream": { - "Type": "AWS::Lambda::EventSourceMapping", + "Type": "AWS::Lambda::EventSourceMapping", "Properties": { - "BatchSize": 200, - "EventSourceArn": "arn:aws:dynamodb:us-west-2:012345678901:table/TestTable/stream/2015-05-11T21:21:33.291", + "BatchSize": 200, + "EventSourceArn": "arn:aws:dynamodb:us-west-2:012345678901:table/TestTable/stream/2015-05-11T21:21:33.291", "FunctionName": { "Ref": "DynamoDBFunction" - }, + }, "StartingPosition": "LATEST" } - }, + }, "DynamoDBFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] } - ] + }] } } - }, + }, "KinesisFunctionMyKinesisStream": { - "Type": "AWS::Lambda::EventSourceMapping", + "Type": "AWS::Lambda::EventSourceMapping", "Properties": { - "BatchSize": 100, - "EventSourceArn": "arn:aws:kinesis:us-west-2:012345678901:stream/my-stream", + "BatchSize": 100, + "EventSourceArn": "arn:aws:kinesis:us-west-2:012345678901:stream/my-stream", "FunctionName": { "Ref": "KinesisFunction" - }, + }, "StartingPosition": "TRIM_HORIZON" } - }, + }, "KinesisFunctionRole": { - "Type": "AWS::IAM::Role", + "Type": "AWS::IAM::Role", "Properties": { "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole" - ], + ], "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Action": [ - "sts:AssumeRole" - ], - "Effect": "Allow", - "Principal": { - "Service": [ - "lambda.amazonaws.com" - ] - } + "Version": "2012-10-17", + "Statement": [{ + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] } - ] + }] } } } diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 664dba5be..f7e5c42e9 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -46,6 +46,7 @@ class TestTranslatorEndToEnd(TestCase): 'cloudwatch_logs_with_ref', 'cloudwatchlog', 'streams', + 'sqs', 'simpletable', 'simpletable_with_sse', 'implicit_api', @@ -156,6 +157,9 @@ def test_transform_success(self, testcase, partition_with_region): 'error_function_with_deployment_preference_missing_alias', 'error_function_with_invalid_deployment_preference_hook_property', 'error_invalid_logical_id', + 'error_missing_queue', + 'error_missing_startingposition', + 'error_missing_stream', 'error_multiple_resource_errors', 'error_s3_not_in_template', 'error_table_invalid_attributetype', diff --git a/tests/translator/validator/test_validator.py b/tests/translator/validator/test_validator.py index a7dd8a0c0..2f24a6695 100644 --- a/tests/translator/validator/test_validator.py +++ b/tests/translator/validator/test_validator.py @@ -12,6 +12,7 @@ 'cloudwatch_logs_with_ref', 'cloudwatchlog', 'streams', + 'sqs', 'simpletable', 'simpletable_with_sse', 'implicit_api', diff --git a/versions/2016-10-31.md b/versions/2016-10-31.md index 4c742eae6..f29251d4a 100644 --- a/versions/2016-10-31.md +++ b/versions/2016-10-31.md @@ -279,6 +279,7 @@ Properties: - [SNS](#sns) - [Kinesis](#kinesis) - [DynamoDB](#dynamodb) + - [SQS](#sqs) - [Api](#api) - [Schedule](#schedule) - [CloudWatchEvent](#cloudwatchevent) @@ -371,6 +372,26 @@ Properties: BatchSize: 10 ``` +#### SQS + +The object describing an event source with type `SQS`. + +##### Properties + +Property Name | Type | Description +---|:---:|--- +Queue | `string` | **Required.** ARN of the SQS queue. +BatchSize | `integer` | Maximum number of messages to process per function invocation. + +##### Example: SQS event source object + +```yaml +Type: SQS +Properties: + Queue: arn:aws:sqs:us-west-2:012345678901:my-queue # NOTE: FIFO SQS Queues are not yet supported + BatchSize: 10 +``` + #### Api The object describing an event source with type `Api`.