From 8878af711d5c35e5c50f57998075f89e8871011c Mon Sep 17 00:00:00 2001 From: Aayush Thapa Date: Thu, 21 Nov 2024 18:11:44 +0000 Subject: [PATCH 1/4] feat: support observability for ESM (DDB and Kinesis and SQS) --- .cfnlintrc.yaml | 1 + .../schema_source/aws_serverless_function.py | 3 + samtranslator/model/eventsources/pull.py | 3 + samtranslator/model/lambda_.py | 1 + samtranslator/schema/schema.json | 9 + schema_source/sam.schema.json | 9 + .../input/function_with_metrics_config.yaml | 58 +++++++ .../aws-cn/function_with_metrics_config.json | 164 ++++++++++++++++++ .../function_with_metrics_config.json | 164 ++++++++++++++++++ .../output/function_with_metrics_config.json | 164 ++++++++++++++++++ 10 files changed, 576 insertions(+) create mode 100644 tests/translator/input/function_with_metrics_config.yaml create mode 100644 tests/translator/output/aws-cn/function_with_metrics_config.json create mode 100644 tests/translator/output/aws-us-gov/function_with_metrics_config.json create mode 100644 tests/translator/output/function_with_metrics_config.json diff --git a/.cfnlintrc.yaml b/.cfnlintrc.yaml index cda80d6fc..eb3641cec 100644 --- a/.cfnlintrc.yaml +++ b/.cfnlintrc.yaml @@ -136,6 +136,7 @@ ignore_templates: - tests/translator/output/**/function_with_intrinsics_resource_attribute.json # CFN now supports intrinsics in DeletionPolicy - tests/translator/output/**/function_with_snapstart.json # Snapstart intentionally not attached to a lambda version which causes lint issues - tests/translator/output/**/managed_policies_everything.json # intentionally contains wrong arns + - tests/translator/output/**/function_with_metrics_config.json ignore_checks: - E2531 # Deprecated runtime; not relevant for transform tests - E2533 # Another deprecated runtime; not relevant for transform tests diff --git a/samtranslator/internal/schema_source/aws_serverless_function.py b/samtranslator/internal/schema_source/aws_serverless_function.py index 74c252dce..349ab73bc 100644 --- a/samtranslator/internal/schema_source/aws_serverless_function.py +++ b/samtranslator/internal/schema_source/aws_serverless_function.py @@ -178,6 +178,7 @@ class KinesisEventProperties(BaseModel): StartingPositionTimestamp: Optional[PassThroughProp] = kinesiseventproperties("StartingPositionTimestamp") Stream: PassThroughProp = kinesiseventproperties("Stream") TumblingWindowInSeconds: Optional[PassThroughProp] = kinesiseventproperties("TumblingWindowInSeconds") + MetricsConfig: Optional[PassThroughProp] class KinesisEvent(BaseModel): @@ -203,6 +204,7 @@ class DynamoDBEventProperties(BaseModel): StartingPositionTimestamp: Optional[PassThroughProp] = dynamodbeventproperties("StartingPositionTimestamp") Stream: PassThroughProp = dynamodbeventproperties("Stream") TumblingWindowInSeconds: Optional[PassThroughProp] = dynamodbeventproperties("TumblingWindowInSeconds") + MetricsConfig: Optional[PassThroughProp] class DynamoDBEvent(BaseModel): @@ -241,6 +243,7 @@ class SQSEventProperties(BaseModel): MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = sqseventproperties("MaximumBatchingWindowInSeconds") Queue: PassThroughProp = sqseventproperties("Queue") ScalingConfig: Optional[PassThroughProp] # Update docs when live + MetricsConfig: Optional[PassThroughProp] class SQSEvent(BaseModel): diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index b5298771c..4b1bfe828 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -55,6 +55,7 @@ class PullEventSource(ResourceMacro, metaclass=ABCMeta): "KmsKeyArn": PassThroughProperty(False), "ConsumerGroupId": PropertyType(False, IS_STR), "ScalingConfig": PropertyType(False, IS_DICT), + "MetricsConfig": PropertyType(False, IS_DICT), } BatchSize: Optional[Intrinsicable[int]] @@ -78,6 +79,7 @@ class PullEventSource(ResourceMacro, metaclass=ABCMeta): KmsKeyArn: Optional[Intrinsicable[str]] ConsumerGroupId: Optional[Intrinsicable[str]] ScalingConfig: Optional[Dict[str, Any]] + MetricsConfig: Optional[Dict[str, Any]] @abstractmethod def get_policy_arn(self) -> Optional[str]: @@ -145,6 +147,7 @@ def to_cloudformation(self, **kwargs): # type: ignore[no-untyped-def] # noqa: P lambda_eventsourcemapping.FilterCriteria = self.FilterCriteria lambda_eventsourcemapping.KmsKeyArn = self.KmsKeyArn lambda_eventsourcemapping.ScalingConfig = self.ScalingConfig + lambda_eventsourcemapping.MetricsConfig = self.MetricsConfig self._validate_filter_criteria() if self.KafkaBootstrapServers: diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index e76809735..fe98dd3d4 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -122,6 +122,7 @@ class LambdaEventSourceMapping(Resource): "AmazonManagedKafkaEventSourceConfig": GeneratedProperty(), "SelfManagedKafkaEventSourceConfig": GeneratedProperty(), "ScalingConfig": GeneratedProperty(), + "MetricsConfig": GeneratedProperty(), } runtime_attrs = {"name": lambda self: ref(self.logical_id)} diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index 4cb9191f0..707a03a16 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -274755,6 +274755,9 @@ "markdownDescription": "The maximum number of times to retry when the function returns an error\\. \n*Type*: Integer \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MaximumRetryAttempts`](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-maximumretryattempts) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "MaximumRetryAttempts" }, + "MetricsConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "ParallelizationFactor": { "allOf": [ { @@ -275502,6 +275505,9 @@ "markdownDescription": "The maximum number of times to retry when the function returns an error\\. \n*Type*: Integer \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MaximumRetryAttempts`](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-maximumretryattempts) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "MaximumRetryAttempts" }, + "MetricsConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "ParallelizationFactor": { "allOf": [ { @@ -276775,6 +276781,9 @@ "markdownDescription": "The maximum amount of time, in seconds, to gather records before invoking the function\\. \n*Type*: Integer \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MaximumBatchingWindowInSeconds`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-maximumbatchingwindowinseconds) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "MaximumBatchingWindowInSeconds" }, + "MetricsConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "Queue": { "allOf": [ { diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index e1b638631..226c672c0 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -1004,6 +1004,9 @@ "markdownDescription": "The maximum number of times to retry when the function returns an error\\. \n*Type*: Integer \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MaximumRetryAttempts`](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-maximumretryattempts) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "MaximumRetryAttempts" }, + "MetricsConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "ParallelizationFactor": { "allOf": [ { @@ -1769,6 +1772,9 @@ "markdownDescription": "The maximum number of times to retry when the function returns an error\\. \n*Type*: Integer \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MaximumRetryAttempts`](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-maximumretryattempts) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "MaximumRetryAttempts" }, + "MetricsConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "ParallelizationFactor": { "allOf": [ { @@ -2973,6 +2979,9 @@ "markdownDescription": "The maximum amount of time, in seconds, to gather records before invoking the function\\. \n*Type*: Integer \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`MaximumBatchingWindowInSeconds`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html#cfn-lambda-eventsourcemapping-maximumbatchingwindowinseconds) property of an `AWS::Lambda::EventSourceMapping` resource\\.", "title": "MaximumBatchingWindowInSeconds" }, + "MetricsConfig": { + "$ref": "#/definitions/PassThroughProp" + }, "Queue": { "allOf": [ { diff --git a/tests/translator/input/function_with_metrics_config.yaml b/tests/translator/input/function_with_metrics_config.yaml new file mode 100644 index 000000000..e1b581efb --- /dev/null +++ b/tests/translator/input/function_with_metrics_config.yaml @@ -0,0 +1,58 @@ +Resources: + FilteredEventsFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/metricsConfig.zip + Handler: index.handler + Runtime: nodejs16.x + Events: + KinesisStream: + Type: Kinesis + Properties: + Stream: !GetAtt KinesisStream.Arn + StartingPosition: LATEST + MetricsConfig: + Metrics: + - EventCount + FilterCriteria: + Filters: + - Pattern: '{"name": "value"}' + - Pattern: '{"name2": "value2"}' + DynamoDBStreamEvent: + Type: DynamoDB + Properties: + Stream: !GetAtt DynamoDBTable.StreamArn + StartingPosition: TRIM_HORIZON + MetricsConfig: + Metrics: [] + FilterCriteria: + Filters: [] + MySqsQueue: + Type: SQS + Properties: + Queue: !GetAtt MySqsQueue.Arn + MetricsConfig: + Metrics: + - EventCount + FilterCriteria: + Filters: + - Pattern: '{"name": "value"}' + + KinesisStream: + Type: AWS::Kinesis::Stream + Properties: + ShardCount: 1 + + DynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: id + AttributeType: S + BillingMode: PAY_PER_REQUEST + KeySchema: + - AttributeName: id + KeyType: HASH + + MySqsQueue: + Type: AWS::SQS::Queue diff --git a/tests/translator/output/aws-cn/function_with_metrics_config.json b/tests/translator/output/aws-cn/function_with_metrics_config.json new file mode 100644 index 000000000..eae07b130 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_metrics_config.json @@ -0,0 +1,164 @@ +{ + "Resources": { + "DynamoDBTable": { + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST", + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ] + }, + "Type": "AWS::DynamoDB::Table" + }, + "FilteredEventsFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "metricsConfig.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "FilteredEventsFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs16.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "FilteredEventsFunctionDynamoDBStreamEvent": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "DynamoDBTable", + "StreamArn" + ] + }, + "FilterCriteria": { + "Filters": [] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [] + }, + "StartingPosition": "TRIM_HORIZON" + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionKinesisStream": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "KinesisStream", + "Arn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"name\": \"value\"}" + }, + { + "Pattern": "{\"name2\": \"value2\"}" + } + ] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [ + "EventCount" + ] + }, + "StartingPosition": "LATEST" + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionMySqsQueue": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "MySqsQueue", + "Arn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"name\": \"value\"}" + } + ] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [ + "EventCount" + ] + } + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole", + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "KinesisStream": { + "Properties": { + "ShardCount": 1 + }, + "Type": "AWS::Kinesis::Stream" + }, + "MySqsQueue": { + "Type": "AWS::SQS::Queue" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_metrics_config.json b/tests/translator/output/aws-us-gov/function_with_metrics_config.json new file mode 100644 index 000000000..30b9b857e --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_metrics_config.json @@ -0,0 +1,164 @@ +{ + "Resources": { + "DynamoDBTable": { + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST", + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ] + }, + "Type": "AWS::DynamoDB::Table" + }, + "FilteredEventsFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "metricsConfig.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "FilteredEventsFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs16.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "FilteredEventsFunctionDynamoDBStreamEvent": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "DynamoDBTable", + "StreamArn" + ] + }, + "FilterCriteria": { + "Filters": [] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [] + }, + "StartingPosition": "TRIM_HORIZON" + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionKinesisStream": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "KinesisStream", + "Arn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"name\": \"value\"}" + }, + { + "Pattern": "{\"name2\": \"value2\"}" + } + ] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [ + "EventCount" + ] + }, + "StartingPosition": "LATEST" + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionMySqsQueue": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "MySqsQueue", + "Arn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"name\": \"value\"}" + } + ] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [ + "EventCount" + ] + } + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole", + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "KinesisStream": { + "Properties": { + "ShardCount": 1 + }, + "Type": "AWS::Kinesis::Stream" + }, + "MySqsQueue": { + "Type": "AWS::SQS::Queue" + } + } +} diff --git a/tests/translator/output/function_with_metrics_config.json b/tests/translator/output/function_with_metrics_config.json new file mode 100644 index 000000000..ed6521020 --- /dev/null +++ b/tests/translator/output/function_with_metrics_config.json @@ -0,0 +1,164 @@ +{ + "Resources": { + "DynamoDBTable": { + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "BillingMode": "PAY_PER_REQUEST", + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ] + }, + "Type": "AWS::DynamoDB::Table" + }, + "FilteredEventsFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "metricsConfig.zip" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "FilteredEventsFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs16.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "FilteredEventsFunctionDynamoDBStreamEvent": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "DynamoDBTable", + "StreamArn" + ] + }, + "FilterCriteria": { + "Filters": [] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [] + }, + "StartingPosition": "TRIM_HORIZON" + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionKinesisStream": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "KinesisStream", + "Arn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"name\": \"value\"}" + }, + { + "Pattern": "{\"name2\": \"value2\"}" + } + ] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [ + "EventCount" + ] + }, + "StartingPosition": "LATEST" + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionMySqsQueue": { + "Properties": { + "EventSourceArn": { + "Fn::GetAtt": [ + "MySqsQueue", + "Arn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"name\": \"value\"}" + } + ] + }, + "FunctionName": { + "Ref": "FilteredEventsFunction" + }, + "MetricsConfig": { + "Metrics": [ + "EventCount" + ] + } + }, + "Type": "AWS::Lambda::EventSourceMapping" + }, + "FilteredEventsFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaDynamoDBExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole", + "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "KinesisStream": { + "Properties": { + "ShardCount": 1 + }, + "Type": "AWS::Kinesis::Stream" + }, + "MySqsQueue": { + "Type": "AWS::SQS::Queue" + } + } +} From 556c3153e2d564bf0b2c24d9e0152e71d67e5afc Mon Sep 17 00:00:00 2001 From: Aayush Thapa Date: Thu, 21 Nov 2024 18:11:45 +0000 Subject: [PATCH 2/4] feat: Add Type S3Bucket to DestinationConfig --- .cfnlintrc.yaml | 1 + .../schema_source/aws_serverless_function.py | 7 +- samtranslator/model/sam_resources.py | 13 +- samtranslator/plugins/globals/globals.py | 3 +- samtranslator/schema/schema.json | 12 +- schema_source/sam.schema.json | 12 +- .../function_with_event_dest_s3_bucket.yaml | 77 +++++ .../input/function_with_sourcekmskeyarn.yaml | 21 ++ .../input/globals_for_function.yaml | 2 + .../function_with_event_dest_s3_bucket.json | 316 ++++++++++++++++++ .../aws-cn/function_with_sourcekmskeyarn.json | 120 +++++++ .../output/aws-cn/globals_for_function.json | 14 +- .../function_with_event_dest_s3_bucket.json | 316 ++++++++++++++++++ .../function_with_sourcekmskeyarn.json | 120 +++++++ .../aws-us-gov/globals_for_function.json | 14 +- .../error_function_with_event_dest_type.json | 4 +- .../function_with_event_dest_s3_bucket.json | 316 ++++++++++++++++++ .../output/function_with_sourcekmskeyarn.json | 120 +++++++ .../output/globals_for_function.json | 14 +- 19 files changed, 1472 insertions(+), 30 deletions(-) create mode 100644 tests/translator/input/function_with_event_dest_s3_bucket.yaml create mode 100644 tests/translator/input/function_with_sourcekmskeyarn.yaml create mode 100644 tests/translator/output/aws-cn/function_with_event_dest_s3_bucket.json create mode 100644 tests/translator/output/aws-cn/function_with_sourcekmskeyarn.json create mode 100644 tests/translator/output/aws-us-gov/function_with_event_dest_s3_bucket.json create mode 100644 tests/translator/output/aws-us-gov/function_with_sourcekmskeyarn.json create mode 100644 tests/translator/output/function_with_event_dest_s3_bucket.json create mode 100644 tests/translator/output/function_with_sourcekmskeyarn.json diff --git a/.cfnlintrc.yaml b/.cfnlintrc.yaml index eb3641cec..7d236593a 100644 --- a/.cfnlintrc.yaml +++ b/.cfnlintrc.yaml @@ -131,6 +131,7 @@ ignore_templates: - tests/translator/output/**/function_with_mq.json # Property "EventSourceArn" can Fn::GetAtt to a resource of types [AWS::DynamoDB::GlobalTable, AWS::DynamoDB::Table, AWS::Kinesis::Stream, AWS::Kinesis::StreamConsumer, AWS::SQS::Queue] - tests/translator/output/**/function_with_mq_using_autogen_role.json # Property "EventSourceArn" can Fn::GetAtt to a resource of types [AWS::DynamoDB::GlobalTable, AWS::DynamoDB::Table, AWS::Kinesis::Stream, AWS::Kinesis::StreamConsumer, AWS::SQS::Queue] - tests/translator/output/**/function_with_recursive_loop.json # Invalid Property Resources/RecursiveLoopParameterFunction/Properties/RecursiveLoop + - tests/translator/output/**/function_with_sourcekmskeyarn.json # Invalid Property Resources/SourceKMSKeyArnParameterFunction/Properties/SourceKMSKeyArn - tests/translator/output/**/function_with_tracing.json # Obsolete DependsOn on resource - tests/translator/output/**/api_with_propagate_tags.json # TODO: Intentional error transform tests. Will be updated. - tests/translator/output/**/function_with_intrinsics_resource_attribute.json # CFN now supports intrinsics in DeletionPolicy diff --git a/samtranslator/internal/schema_source/aws_serverless_function.py b/samtranslator/internal/schema_source/aws_serverless_function.py index 349ab73bc..aa607331c 100644 --- a/samtranslator/internal/schema_source/aws_serverless_function.py +++ b/samtranslator/internal/schema_source/aws_serverless_function.py @@ -105,12 +105,12 @@ class DeadLetterQueue(BaseModel): class EventInvokeOnFailure(BaseModel): Destination: Optional[SamIntrinsicable[str]] = eventinvokeonfailure("Destination") - Type: Optional[Literal["SQS", "SNS", "Lambda", "EventBridge"]] = eventinvokeonfailure("Type") + Type: Optional[Literal["SQS", "SNS", "Lambda", "EventBridge", "S3Bucket"]] = eventinvokeonfailure("Type") class EventInvokeOnSuccess(BaseModel): Destination: Optional[SamIntrinsicable[str]] = eventinvokeonsuccess("Destination") - Type: Optional[Literal["SQS", "SNS", "Lambda", "EventBridge"]] = eventinvokeonsuccess("Type") + Type: Optional[Literal["SQS", "SNS", "Lambda", "EventBridge", "S3Bucket"]] = eventinvokeonsuccess("Type") class EventInvokeDestinationConfig(BaseModel): @@ -516,6 +516,7 @@ class ScheduleV2Event(BaseModel): RuntimeManagementConfig = Optional[PassThroughProp] # TODO: check the type LoggingConfig = Optional[PassThroughProp] # TODO: add documentation RecursiveLoop = Optional[PassThroughProp] +SourceKMSKeyArn = Optional[PassThroughProp] class Properties(BaseModel): @@ -643,6 +644,7 @@ class Properties(BaseModel): VpcConfig: Optional[VpcConfig] = prop("VpcConfig") LoggingConfig: Optional[PassThroughProp] # TODO: add documentation RecursiveLoop: Optional[PassThroughProp] # TODO: add documentation + SourceKMSKeyArn: Optional[PassThroughProp] # TODO: add documentation class Globals(BaseModel): @@ -702,6 +704,7 @@ class Globals(BaseModel): RuntimeManagementConfig: Optional[RuntimeManagementConfig] = prop("RuntimeManagementConfig") LoggingConfig: Optional[PassThroughProp] # TODO: add documentation RecursiveLoop: Optional[PassThroughProp] # TODO: add documentation + SourceKMSKeyArn: Optional[PassThroughProp] # TODO: add documentation class Resource(ResourceAttributes): diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py index fffca961e..29d55f348 100644 --- a/samtranslator/model/sam_resources.py +++ b/samtranslator/model/sam_resources.py @@ -1,4 +1,4 @@ -""" SAM macro definitions """ +""" SAM macro definitions """ import copy from contextlib import suppress @@ -181,6 +181,7 @@ class SamFunction(SamResourceMacro): "RuntimeManagementConfig": PassThroughProperty(False), "LoggingConfig": PassThroughProperty(False), "RecursiveLoop": PassThroughProperty(False), + "SourceKMSKeyArn": PassThroughProperty(False), } FunctionName: Optional[Intrinsicable[str]] @@ -224,6 +225,7 @@ class SamFunction(SamResourceMacro): FunctionUrlConfig: Optional[Dict[str, Any]] LoggingConfig: Optional[Dict[str, Any]] RecursiveLoop: Optional[str] + SourceKMSKeyArn: Optional[str] event_resolver = ResourceTypeResolver( samtranslator.model.eventsources, @@ -439,7 +441,7 @@ def _validate_and_inject_resource( ARN property, so to handle conditional ifs we have to inject if conditions in the auto created SQS/SNS resources as well as in the policy documents. """ - accepted_types_list = ["SQS", "SNS", "EventBridge", "Lambda"] + accepted_types_list = ["SQS", "SNS", "EventBridge", "Lambda", "S3Bucket"] auto_inject_list = ["SQS", "SNS"] resource: Optional[Union[SNSTopic, SQSQueue]] = None policy = {} @@ -630,6 +632,8 @@ def _add_event_invoke_managed_policy( return IAMRolePolicies.event_bus_put_events_role_policy(dest_arn, logical_id) if _type == "Lambda": return IAMRolePolicies.lambda_invoke_function_role_policy(dest_arn, logical_id) + if _type == "S3Bucket": + return IAMRolePolicies.s3_send_event_payload_role_policy(dest_arn, logical_id) return {} def _construct_role( @@ -885,7 +889,10 @@ def _construct_inline_code(*args: Any, **kwargs: Dict[str, Any]) -> Dict[str, An else: raise InvalidResourceException(self.logical_id, "Either 'InlineCode' or 'CodeUri' must be set.") dispatch_function: Callable[..., Dict[str, Any]] = artifact_dispatch[filtered_key] - return dispatch_function(artifacts[filtered_key], self.logical_id, filtered_key) + code_dict = dispatch_function(artifacts[filtered_key], self.logical_id, filtered_key) + if self.SourceKMSKeyArn and packagetype == ZIP: + code_dict["SourceKMSKeyArn"] = self.SourceKMSKeyArn + return code_dict def _construct_version( # noqa: PLR0912 self, diff --git a/samtranslator/plugins/globals/globals.py b/samtranslator/plugins/globals/globals.py index fdc7b3835..edd3ebe1a 100644 --- a/samtranslator/plugins/globals/globals.py +++ b/samtranslator/plugins/globals/globals.py @@ -55,6 +55,7 @@ class Globals: "RuntimeManagementConfig", "LoggingConfig", "RecursiveLoop", + "SourceKMSKeyArn", ], # Everything except # DefinitionBody: because its hard to reason about merge of Swagger dictionaries @@ -100,7 +101,7 @@ class Globals: } # unreleased_properties *must be* part of supported_properties too unreleased_properties: Dict[str, List[str]] = { - SamResourceType.Function.value: ["RuntimeManagementConfig", "RecursiveLoop"], + SamResourceType.Function.value: ["RuntimeManagementConfig", "RecursiveLoop", "SourceKMSKeyArn"], } def __init__(self, template: Dict[str, Any]) -> None: diff --git a/samtranslator/schema/schema.json b/samtranslator/schema/schema.json index 707a03a16..7b3910543 100644 --- a/samtranslator/schema/schema.json +++ b/samtranslator/schema/schema.json @@ -274993,7 +274993,8 @@ "SQS", "SNS", "Lambda", - "EventBridge" + "EventBridge", + "S3Bucket" ], "markdownDescription": "Type of the resource referenced in the destination\\. Supported types are `SQS`, `SNS`, `Lambda`, and `EventBridge`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: If the type is SQS/SNS and the `Destination` property is left blank, then the SQS/SNS resource is auto generated by SAM\\. To reference the resource, use `.DestinationQueue` for SQS or `.DestinationTopic` for SNS\\. If the type is Lambda/EventBridge, `Destination` is required\\.", "title": "Type", @@ -275023,7 +275024,8 @@ "SQS", "SNS", "Lambda", - "EventBridge" + "EventBridge", + "S3Bucket" ], "markdownDescription": "Type of the resource referenced in the destination\\. Supported types are `SQS`, `SNS`, `Lambda`, and `EventBridge`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: If the type is SQS/SNS and the `Destination` property is left blank, then the SQS/SNS resource is auto generated by SAM\\. To reference the resource, use `.DestinationQueue` for SQS or `.DestinationTopic` for SNS\\. If the type is Lambda/EventBridge, `Destination` is required\\.", "title": "Type", @@ -278794,6 +278796,9 @@ "markdownDescription": "Create a snapshot of any new Lambda function version\\. A snapshot is a cached state of your initialized function, including all of its dependencies\\. The function is initialized just once and the cached state is reused for all future invocations, improving application performance by reducing the number of times your function must be initialized\\. To learn more, see [Improving startup performance with Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) in the *AWS Lambda Developer Guide*\\. \n*Type*: [SnapStart](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SnapStart`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) property of an `AWS::Lambda::Function` resource\\.", "title": "SnapStart" }, + "SourceKMSKeyArn": { + "$ref": "#/definitions/PassThroughProp" + }, "Tags": { "markdownDescription": "A map \\(string to string\\) that specifies the tags added to this function\\. For details about valid keys and values for tags, see [Tag Key and Value Requirements](https://docs.aws.amazon.com/lambda/latest/dg/configuration-tags.html#configuration-tags-restrictions) in the *AWS Lambda Developer Guide*\\. \nWhen the stack is created, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\. \n*Type*: Map \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Tags`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-tags) property of an `AWS::Lambda::Function` resource\\. The `Tags` property in AWS SAM consists of key\\-value pairs \\(whereas in AWS CloudFormation this property consists of a list of `Tag` objects\\)\\. Also, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\.", "title": "Tags", @@ -279188,6 +279193,9 @@ "markdownDescription": "Create a snapshot of any new Lambda function version\\. A snapshot is a cached state of your initialized function, including all of its dependencies\\. The function is initialized just once and the cached state is reused for all future invocations, improving application performance by reducing the number of times your function must be initialized\\. To learn more, see [Improving startup performance with Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) in the *AWS Lambda Developer Guide*\\. \n*Type*: [SnapStart](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SnapStart`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) property of an `AWS::Lambda::Function` resource\\.", "title": "SnapStart" }, + "SourceKMSKeyArn": { + "$ref": "#/definitions/PassThroughProp" + }, "Tags": { "markdownDescription": "A map \\(string to string\\) that specifies the tags added to this function\\. For details about valid keys and values for tags, see [Tag Key and Value Requirements](https://docs.aws.amazon.com/lambda/latest/dg/configuration-tags.html#configuration-tags-restrictions) in the *AWS Lambda Developer Guide*\\. \nWhen the stack is created, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\. \n*Type*: Map \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Tags`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-tags) property of an `AWS::Lambda::Function` resource\\. The `Tags` property in AWS SAM consists of key\\-value pairs \\(whereas in AWS CloudFormation this property consists of a list of `Tag` objects\\)\\. Also, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\.", "title": "Tags", diff --git a/schema_source/sam.schema.json b/schema_source/sam.schema.json index 226c672c0..e6485314a 100644 --- a/schema_source/sam.schema.json +++ b/schema_source/sam.schema.json @@ -1260,7 +1260,8 @@ "SQS", "SNS", "Lambda", - "EventBridge" + "EventBridge", + "S3Bucket" ], "markdownDescription": "Type of the resource referenced in the destination\\. Supported types are `SQS`, `SNS`, `Lambda`, and `EventBridge`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: If the type is SQS/SNS and the `Destination` property is left blank, then the SQS/SNS resource is auto generated by SAM\\. To reference the resource, use `.DestinationQueue` for SQS or `.DestinationTopic` for SNS\\. If the type is Lambda/EventBridge, `Destination` is required\\.", "title": "Type", @@ -1290,7 +1291,8 @@ "SQS", "SNS", "Lambda", - "EventBridge" + "EventBridge", + "S3Bucket" ], "markdownDescription": "Type of the resource referenced in the destination\\. Supported types are `SQS`, `SNS`, `Lambda`, and `EventBridge`\\. \n*Type*: String \n*Required*: No \n*AWS CloudFormation compatibility*: This property is unique to AWS SAM and doesn't have an AWS CloudFormation equivalent\\. \n*Additional notes*: If the type is SQS/SNS and the `Destination` property is left blank, then the SQS/SNS resource is auto generated by SAM\\. To reference the resource, use `.DestinationQueue` for SQS or `.DestinationTopic` for SNS\\. If the type is Lambda/EventBridge, `Destination` is required\\.", "title": "Type", @@ -5537,6 +5539,9 @@ "markdownDescription": "Create a snapshot of any new Lambda function version\\. A snapshot is a cached state of your initialized function, including all of its dependencies\\. The function is initialized just once and the cached state is reused for all future invocations, improving application performance by reducing the number of times your function must be initialized\\. To learn more, see [Improving startup performance with Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) in the *AWS Lambda Developer Guide*\\. \n*Type*: [SnapStart](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SnapStart`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) property of an `AWS::Lambda::Function` resource\\.", "title": "SnapStart" }, + "SourceKMSKeyArn": { + "$ref": "#/definitions/PassThroughProp" + }, "Tags": { "markdownDescription": "A map \\(string to string\\) that specifies the tags added to this function\\. For details about valid keys and values for tags, see [Tag Key and Value Requirements](https://docs.aws.amazon.com/lambda/latest/dg/configuration-tags.html#configuration-tags-restrictions) in the *AWS Lambda Developer Guide*\\. \nWhen the stack is created, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\. \n*Type*: Map \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Tags`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-tags) property of an `AWS::Lambda::Function` resource\\. The `Tags` property in AWS SAM consists of key\\-value pairs \\(whereas in AWS CloudFormation this property consists of a list of `Tag` objects\\)\\. Also, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\.", "title": "Tags", @@ -6122,6 +6127,9 @@ "markdownDescription": "Create a snapshot of any new Lambda function version\\. A snapshot is a cached state of your initialized function, including all of its dependencies\\. The function is initialized just once and the cached state is reused for all future invocations, improving application performance by reducing the number of times your function must be initialized\\. To learn more, see [Improving startup performance with Lambda SnapStart](https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html) in the *AWS Lambda Developer Guide*\\. \n*Type*: [SnapStart](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) \n*Required*: No \n*AWS CloudFormation compatibility*: This property is passed directly to the [`SnapStart`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-snapstart.html) property of an `AWS::Lambda::Function` resource\\.", "title": "SnapStart" }, + "SourceKMSKeyArn": { + "$ref": "#/definitions/PassThroughProp" + }, "Tags": { "markdownDescription": "A map \\(string to string\\) that specifies the tags added to this function\\. For details about valid keys and values for tags, see [Tag Key and Value Requirements](https://docs.aws.amazon.com/lambda/latest/dg/configuration-tags.html#configuration-tags-restrictions) in the *AWS Lambda Developer Guide*\\. \nWhen the stack is created, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\. \n*Type*: Map \n*Required*: No \n*AWS CloudFormation compatibility*: This property is similar to the [`Tags`](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-tags) property of an `AWS::Lambda::Function` resource\\. The `Tags` property in AWS SAM consists of key\\-value pairs \\(whereas in AWS CloudFormation this property consists of a list of `Tag` objects\\)\\. Also, AWS SAM automatically adds a `lambda:createdBy:SAM` tag to this Lambda function, and to the default roles that are generated for this function\\.", "title": "Tags", diff --git a/tests/translator/input/function_with_event_dest_s3_bucket.yaml b/tests/translator/input/function_with_event_dest_s3_bucket.yaml new file mode 100644 index 000000000..b7f6aa128 --- /dev/null +++ b/tests/translator/input/function_with_event_dest_s3_bucket.yaml @@ -0,0 +1,77 @@ +Parameters: + S3BucketArn1: + Type: String + Default: arn:aws:s3::123456789012:my_bucket-1 + S3BucketArn2: + Type: String + Default: arn:aws:s3::123456789012:my_bucket-2 +Globals: + Function: + AutoPublishAlias: live + EventInvokeConfig: + MaximumEventAgeInSeconds: 70 + MaximumRetryAttempts: 1 + DestinationConfig: + OnSuccess: + Type: S3Bucket + Destination: !Ref S3BucketArn1 + OnFailure: + Type: S3Bucket + Destination: !Ref S3BucketArn2 + +Resources: + MyTestFunction: + Type: AWS::Serverless::Function + Properties: + InlineCode: | + exports.handler = function(event, context, callback) { + var event_received_at = new Date().toISOString(); + console.log('Event received at: ' + event_received_at); + console.log('Received event:', JSON.stringify(event, null, 2)); + + if (event.Success) { + console.log("Success"); + context.callbackWaitsForEmptyEventLoop = false; + callback(null); + } else { + console.log("Failure"); + context.callbackWaitsForEmptyEventLoop = false; + callback(new Error("Failure from event, Success = false, I am failing!"), 'Destination Function Error Thrown'); + } + }; + Handler: index.handler + Runtime: nodejs12.x + MemorySize: 1024 + + MyTestFunction2: + Type: AWS::Serverless::Function + Properties: + EventInvokeConfig: + MaximumEventAgeInSeconds: 70 + MaximumRetryAttempts: 1 + DestinationConfig: + OnSuccess: + Type: S3Bucket + Destination: arn:aws:s3::123456789012:my_bucket-3 + OnFailure: + Type: S3Bucket + Destination: arn:aws:s3::123456789012:my_bucket-4 + InlineCode: | + exports.handler = function(event, context, callback) { + var event_received_at = new Date().toISOString(); + console.log('Event received at: ' + event_received_at); + console.log('Received event:', JSON.stringify(event, null, 2)); + + if (event.Success) { + console.log("Success"); + context.callbackWaitsForEmptyEventLoop = false; + callback(null); + } else { + console.log("Failure"); + context.callbackWaitsForEmptyEventLoop = false; + callback(new Error("Failure from event, Success = false, I am failing!"), 'Destination Function Error Thrown'); + } + }; + Handler: index.handler + Runtime: nodejs12.x + MemorySize: 1024 diff --git a/tests/translator/input/function_with_sourcekmskeyarn.yaml b/tests/translator/input/function_with_sourcekmskeyarn.yaml new file mode 100644 index 000000000..a666448f6 --- /dev/null +++ b/tests/translator/input/function_with_sourcekmskeyarn.yaml @@ -0,0 +1,21 @@ +Parameters: + SourceKMSKeyArnParam: + Type: String + Default: arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f + +Resources: + SourceKMSKeyArnFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.9 + SourceKMSKeyArn: arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f + + SourceKMSKeyArnParameterFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip + Handler: hello.handler + Runtime: python3.9 + SourceKMSKeyArn: !Ref SourceKMSKeyArnParam diff --git a/tests/translator/input/globals_for_function.yaml b/tests/translator/input/globals_for_function.yaml index 464a73204..a4cf09729 100644 --- a/tests/translator/input/globals_for_function.yaml +++ b/tests/translator/input/globals_for_function.yaml @@ -33,6 +33,7 @@ Globals: LoggingConfig: LogGroup: myJsonStructuredLogs RecursiveLoop: ALLOW + SourceKMSKeyArn: arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f @@ -67,3 +68,4 @@ Resources: RuntimeManagementConfig: UpdateRuntimeOn: FunctionChange RecursiveLoop: TERMINATE + SourceKMSKeyArn: arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f diff --git a/tests/translator/output/aws-cn/function_with_event_dest_s3_bucket.json b/tests/translator/output/aws-cn/function_with_event_dest_s3_bucket.json new file mode 100644 index 000000000..8a9c1a268 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_event_dest_s3_bucket.json @@ -0,0 +1,316 @@ +{ + "Parameters": { + "S3BucketArn1": { + "Default": "arn:aws:s3::123456789012:my_bucket-1", + "Type": "String" + }, + "S3BucketArn2": { + "Default": "arn:aws:s3::123456789012:my_bucket-2", + "Type": "String" + } + }, + "Resources": { + "MyTestFunction": { + "Properties": { + "Code": { + "ZipFile": "exports.handler = function(event, context, callback) {\n var event_received_at = new Date().toISOString();\n console.log('Event received at: ' + event_received_at);\n console.log('Received event:', JSON.stringify(event, null, 2));\n\n if (event.Success) {\n console.log(\"Success\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(null);\n } else {\n console.log(\"Failure\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(new Error(\"Failure from event, Success = false, I am failing!\"), 'Destination Function Error Thrown');\n }\n}; \n" + }, + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyTestFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyTestFunction2": { + "Properties": { + "Code": { + "ZipFile": "exports.handler = function(event, context, callback) {\n var event_received_at = new Date().toISOString();\n console.log('Event received at: ' + event_received_at);\n console.log('Received event:', JSON.stringify(event, null, 2));\n\n if (event.Success) {\n console.log(\"Success\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(null);\n } else {\n console.log(\"Failure\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(new Error(\"Failure from event, Success = false, I am failing!\"), 'Destination Function Error Thrown');\n }\n}; \n" + }, + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyTestFunction2Role", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyTestFunction2Aliaslive": { + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction2" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MyTestFunction2Versiondaf9da458d", + "Version" + ] + }, + "Name": "live" + }, + "Type": "AWS::Lambda::Alias" + }, + "MyTestFunction2EventInvokeConfig": { + "DependsOn": [ + "MyTestFunction2Aliaslive" + ], + "Properties": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:s3::123456789012:my_bucket-4" + }, + "OnSuccess": { + "Destination": "arn:aws:s3::123456789012:my_bucket-3" + } + }, + "FunctionName": { + "Ref": "MyTestFunction2" + }, + "MaximumEventAgeInSeconds": 70, + "MaximumRetryAttempts": 1, + "Qualifier": "live" + }, + "Type": "AWS::Lambda::EventInvokeConfig" + }, + "MyTestFunction2Role": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-3", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-3" + } + ] + }, + "PolicyName": "MyTestFunction2EventInvokeConfigOnSuccessS3Policy" + }, + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-4", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-4" + } + ] + }, + "PolicyName": "MyTestFunction2EventInvokeConfigOnFailureS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "MyTestFunction2Versiondaf9da458d": { + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction2" + } + }, + "Type": "AWS::Lambda::Version" + }, + "MyTestFunctionAliaslive": { + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MyTestFunctionVersiondaf9da458d", + "Version" + ] + }, + "Name": "live" + }, + "Type": "AWS::Lambda::Alias" + }, + "MyTestFunctionEventInvokeConfig": { + "DependsOn": [ + "MyTestFunctionAliaslive" + ], + "Properties": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:s3::123456789012:my_bucket-2" + }, + "OnSuccess": { + "Destination": "arn:aws:s3::123456789012:my_bucket-1" + } + }, + "FunctionName": { + "Ref": "MyTestFunction" + }, + "MaximumEventAgeInSeconds": 70, + "MaximumRetryAttempts": 1, + "Qualifier": "live" + }, + "Type": "AWS::Lambda::EventInvokeConfig" + }, + "MyTestFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-1", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-1" + } + ] + }, + "PolicyName": "MyTestFunctionEventInvokeConfigOnSuccessS3Policy" + }, + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-2", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-2" + } + ] + }, + "PolicyName": "MyTestFunctionEventInvokeConfigOnFailureS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "MyTestFunctionVersiondaf9da458d": { + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction" + } + }, + "Type": "AWS::Lambda::Version" + } + } +} diff --git a/tests/translator/output/aws-cn/function_with_sourcekmskeyarn.json b/tests/translator/output/aws-cn/function_with_sourcekmskeyarn.json new file mode 100644 index 000000000..2bb689221 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_sourcekmskeyarn.json @@ -0,0 +1,120 @@ +{ + "Parameters": { + "SourceKMSKeyArnParam": { + "Default": "arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f", + "Type": "String" + } + }, + "Resources": { + "SourceKMSKeyArnFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "SourceKMSKeyArnFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "SourceKMSKeyArnFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "SourceKMSKeyArnParameterFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "SourceKMSKeyArn": { + "Ref": "SourceKMSKeyArnParam" + } + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "SourceKMSKeyArnParameterFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "SourceKMSKeyArnParameterFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-cn/globals_for_function.json b/tests/translator/output/aws-cn/globals_for_function.json index 39b356f1b..293cf5109 100644 --- a/tests/translator/output/aws-cn/globals_for_function.json +++ b/tests/translator/output/aws-cn/globals_for_function.json @@ -7,7 +7,8 @@ ], "Code": { "S3Bucket": "sam-demo-bucket", - "S3Key": "hello.zip" + "S3Key": "hello.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f" }, "Environment": { "Variables": { @@ -84,7 +85,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "FunctionWithOverridesVersion096ed3b52b", + "FunctionWithOverridesVersionb52716e99f", "Version" ] }, @@ -133,7 +134,7 @@ }, "Type": "AWS::IAM::Role" }, - "FunctionWithOverridesVersion096ed3b52b": { + "FunctionWithOverridesVersionb52716e99f": { "DeletionPolicy": "Retain", "Properties": { "FunctionName": { @@ -149,7 +150,8 @@ ], "Code": { "S3Bucket": "global-bucket", - "S3Key": "global.zip" + "S3Key": "global.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f" }, "Environment": { "Variables": { @@ -217,7 +219,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "MinimalFunctionVersione7c6f56e4d", + "MinimalFunctionVersion5244f38b49", "Version" ] }, @@ -262,7 +264,7 @@ }, "Type": "AWS::IAM::Role" }, - "MinimalFunctionVersione7c6f56e4d": { + "MinimalFunctionVersion5244f38b49": { "DeletionPolicy": "Retain", "Properties": { "FunctionName": { diff --git a/tests/translator/output/aws-us-gov/function_with_event_dest_s3_bucket.json b/tests/translator/output/aws-us-gov/function_with_event_dest_s3_bucket.json new file mode 100644 index 000000000..e7350f2c6 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_event_dest_s3_bucket.json @@ -0,0 +1,316 @@ +{ + "Parameters": { + "S3BucketArn1": { + "Default": "arn:aws:s3::123456789012:my_bucket-1", + "Type": "String" + }, + "S3BucketArn2": { + "Default": "arn:aws:s3::123456789012:my_bucket-2", + "Type": "String" + } + }, + "Resources": { + "MyTestFunction": { + "Properties": { + "Code": { + "ZipFile": "exports.handler = function(event, context, callback) {\n var event_received_at = new Date().toISOString();\n console.log('Event received at: ' + event_received_at);\n console.log('Received event:', JSON.stringify(event, null, 2));\n\n if (event.Success) {\n console.log(\"Success\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(null);\n } else {\n console.log(\"Failure\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(new Error(\"Failure from event, Success = false, I am failing!\"), 'Destination Function Error Thrown');\n }\n}; \n" + }, + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyTestFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyTestFunction2": { + "Properties": { + "Code": { + "ZipFile": "exports.handler = function(event, context, callback) {\n var event_received_at = new Date().toISOString();\n console.log('Event received at: ' + event_received_at);\n console.log('Received event:', JSON.stringify(event, null, 2));\n\n if (event.Success) {\n console.log(\"Success\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(null);\n } else {\n console.log(\"Failure\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(new Error(\"Failure from event, Success = false, I am failing!\"), 'Destination Function Error Thrown');\n }\n}; \n" + }, + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyTestFunction2Role", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyTestFunction2Aliaslive": { + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction2" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MyTestFunction2Versiondaf9da458d", + "Version" + ] + }, + "Name": "live" + }, + "Type": "AWS::Lambda::Alias" + }, + "MyTestFunction2EventInvokeConfig": { + "DependsOn": [ + "MyTestFunction2Aliaslive" + ], + "Properties": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:s3::123456789012:my_bucket-4" + }, + "OnSuccess": { + "Destination": "arn:aws:s3::123456789012:my_bucket-3" + } + }, + "FunctionName": { + "Ref": "MyTestFunction2" + }, + "MaximumEventAgeInSeconds": 70, + "MaximumRetryAttempts": 1, + "Qualifier": "live" + }, + "Type": "AWS::Lambda::EventInvokeConfig" + }, + "MyTestFunction2Role": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-3", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-3" + } + ] + }, + "PolicyName": "MyTestFunction2EventInvokeConfigOnSuccessS3Policy" + }, + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-4", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-4" + } + ] + }, + "PolicyName": "MyTestFunction2EventInvokeConfigOnFailureS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "MyTestFunction2Versiondaf9da458d": { + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction2" + } + }, + "Type": "AWS::Lambda::Version" + }, + "MyTestFunctionAliaslive": { + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MyTestFunctionVersiondaf9da458d", + "Version" + ] + }, + "Name": "live" + }, + "Type": "AWS::Lambda::Alias" + }, + "MyTestFunctionEventInvokeConfig": { + "DependsOn": [ + "MyTestFunctionAliaslive" + ], + "Properties": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:s3::123456789012:my_bucket-2" + }, + "OnSuccess": { + "Destination": "arn:aws:s3::123456789012:my_bucket-1" + } + }, + "FunctionName": { + "Ref": "MyTestFunction" + }, + "MaximumEventAgeInSeconds": 70, + "MaximumRetryAttempts": 1, + "Qualifier": "live" + }, + "Type": "AWS::Lambda::EventInvokeConfig" + }, + "MyTestFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-1", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-1" + } + ] + }, + "PolicyName": "MyTestFunctionEventInvokeConfigOnSuccessS3Policy" + }, + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-2", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-2" + } + ] + }, + "PolicyName": "MyTestFunctionEventInvokeConfigOnFailureS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "MyTestFunctionVersiondaf9da458d": { + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction" + } + }, + "Type": "AWS::Lambda::Version" + } + } +} diff --git a/tests/translator/output/aws-us-gov/function_with_sourcekmskeyarn.json b/tests/translator/output/aws-us-gov/function_with_sourcekmskeyarn.json new file mode 100644 index 000000000..139dca0e6 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_sourcekmskeyarn.json @@ -0,0 +1,120 @@ +{ + "Parameters": { + "SourceKMSKeyArnParam": { + "Default": "arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f", + "Type": "String" + } + }, + "Resources": { + "SourceKMSKeyArnFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "SourceKMSKeyArnFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "SourceKMSKeyArnFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "SourceKMSKeyArnParameterFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "SourceKMSKeyArn": { + "Ref": "SourceKMSKeyArnParam" + } + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "SourceKMSKeyArnParameterFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "SourceKMSKeyArnParameterFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/aws-us-gov/globals_for_function.json b/tests/translator/output/aws-us-gov/globals_for_function.json index 7cb1d98a8..fceebdcdd 100644 --- a/tests/translator/output/aws-us-gov/globals_for_function.json +++ b/tests/translator/output/aws-us-gov/globals_for_function.json @@ -7,7 +7,8 @@ ], "Code": { "S3Bucket": "sam-demo-bucket", - "S3Key": "hello.zip" + "S3Key": "hello.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f" }, "Environment": { "Variables": { @@ -84,7 +85,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "FunctionWithOverridesVersion096ed3b52b", + "FunctionWithOverridesVersionb52716e99f", "Version" ] }, @@ -133,7 +134,7 @@ }, "Type": "AWS::IAM::Role" }, - "FunctionWithOverridesVersion096ed3b52b": { + "FunctionWithOverridesVersionb52716e99f": { "DeletionPolicy": "Retain", "Properties": { "FunctionName": { @@ -149,7 +150,8 @@ ], "Code": { "S3Bucket": "global-bucket", - "S3Key": "global.zip" + "S3Key": "global.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f" }, "Environment": { "Variables": { @@ -217,7 +219,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "MinimalFunctionVersione7c6f56e4d", + "MinimalFunctionVersion5244f38b49", "Version" ] }, @@ -262,7 +264,7 @@ }, "Type": "AWS::IAM::Role" }, - "MinimalFunctionVersione7c6f56e4d": { + "MinimalFunctionVersion5244f38b49": { "DeletionPolicy": "Retain", "Properties": { "FunctionName": { diff --git a/tests/translator/output/error_function_with_event_dest_type.json b/tests/translator/output/error_function_with_event_dest_type.json index 41e6a6901..6ffb2708d 100644 --- a/tests/translator/output/error_function_with_event_dest_type.json +++ b/tests/translator/output/error_function_with_event_dest_type.json @@ -3,10 +3,10 @@ "Invalid Serverless Application Specification document. ", "Number of errors found: 3. ", "Resource with id [MyTestFunction] is invalid. ", - "'Type: blah' must be one of ['SQS', 'SNS', 'EventBridge', 'Lambda'] Resource with id [MyTestFunctionInvalidDestinationConfigOnSuccessType] is invalid. ", + "'Type: blah' must be one of ['SQS', 'SNS', 'EventBridge', 'Lambda', 'S3Bucket'] Resource with id [MyTestFunctionInvalidDestinationConfigOnSuccessType] is invalid. ", "Property 'EventInvokeConfig.DestinationConfig.OnSuccess' should be a map. ", "Resource with id [MyTestFunctionInvalidDestinationConfigType] is invalid. ", "Property 'EventInvokeConfig.DestinationConfig' should be a map." ], - "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 3. Resource with id [MyTestFunction] is invalid. 'Type: blah' must be one of ['SQS', 'SNS', 'EventBridge', 'Lambda'] Resource with id [MyTestFunctionInvalidDestinationConfigOnSuccessType] is invalid. Property 'EventInvokeConfig.DestinationConfig.OnSuccess' should be a map. Resource with id [MyTestFunctionInvalidDestinationConfigType] is invalid. Property 'EventInvokeConfig.DestinationConfig' should be a map." + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 3. Resource with id [MyTestFunction] is invalid. 'Type: blah' must be one of ['SQS', 'SNS', 'EventBridge', 'Lambda', 'S3Bucket'] Resource with id [MyTestFunctionInvalidDestinationConfigOnSuccessType] is invalid. Property 'EventInvokeConfig.DestinationConfig.OnSuccess' should be a map. Resource with id [MyTestFunctionInvalidDestinationConfigType] is invalid. Property 'EventInvokeConfig.DestinationConfig' should be a map." } diff --git a/tests/translator/output/function_with_event_dest_s3_bucket.json b/tests/translator/output/function_with_event_dest_s3_bucket.json new file mode 100644 index 000000000..1b1dd0a48 --- /dev/null +++ b/tests/translator/output/function_with_event_dest_s3_bucket.json @@ -0,0 +1,316 @@ +{ + "Parameters": { + "S3BucketArn1": { + "Default": "arn:aws:s3::123456789012:my_bucket-1", + "Type": "String" + }, + "S3BucketArn2": { + "Default": "arn:aws:s3::123456789012:my_bucket-2", + "Type": "String" + } + }, + "Resources": { + "MyTestFunction": { + "Properties": { + "Code": { + "ZipFile": "exports.handler = function(event, context, callback) {\n var event_received_at = new Date().toISOString();\n console.log('Event received at: ' + event_received_at);\n console.log('Received event:', JSON.stringify(event, null, 2));\n\n if (event.Success) {\n console.log(\"Success\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(null);\n } else {\n console.log(\"Failure\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(new Error(\"Failure from event, Success = false, I am failing!\"), 'Destination Function Error Thrown');\n }\n}; \n" + }, + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyTestFunctionRole", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyTestFunction2": { + "Properties": { + "Code": { + "ZipFile": "exports.handler = function(event, context, callback) {\n var event_received_at = new Date().toISOString();\n console.log('Event received at: ' + event_received_at);\n console.log('Received event:', JSON.stringify(event, null, 2));\n\n if (event.Success) {\n console.log(\"Success\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(null);\n } else {\n console.log(\"Failure\");\n context.callbackWaitsForEmptyEventLoop = false;\n callback(new Error(\"Failure from event, Success = false, I am failing!\"), 'Destination Function Error Thrown');\n }\n}; \n" + }, + "Handler": "index.handler", + "MemorySize": 1024, + "Role": { + "Fn::GetAtt": [ + "MyTestFunction2Role", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "MyTestFunction2Aliaslive": { + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction2" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MyTestFunction2Versiondaf9da458d", + "Version" + ] + }, + "Name": "live" + }, + "Type": "AWS::Lambda::Alias" + }, + "MyTestFunction2EventInvokeConfig": { + "DependsOn": [ + "MyTestFunction2Aliaslive" + ], + "Properties": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:s3::123456789012:my_bucket-4" + }, + "OnSuccess": { + "Destination": "arn:aws:s3::123456789012:my_bucket-3" + } + }, + "FunctionName": { + "Ref": "MyTestFunction2" + }, + "MaximumEventAgeInSeconds": 70, + "MaximumRetryAttempts": 1, + "Qualifier": "live" + }, + "Type": "AWS::Lambda::EventInvokeConfig" + }, + "MyTestFunction2Role": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-3", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-3" + } + ] + }, + "PolicyName": "MyTestFunction2EventInvokeConfigOnSuccessS3Policy" + }, + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-4", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-4" + } + ] + }, + "PolicyName": "MyTestFunction2EventInvokeConfigOnFailureS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "MyTestFunction2Versiondaf9da458d": { + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction2" + } + }, + "Type": "AWS::Lambda::Version" + }, + "MyTestFunctionAliaslive": { + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction" + }, + "FunctionVersion": { + "Fn::GetAtt": [ + "MyTestFunctionVersiondaf9da458d", + "Version" + ] + }, + "Name": "live" + }, + "Type": "AWS::Lambda::Alias" + }, + "MyTestFunctionEventInvokeConfig": { + "DependsOn": [ + "MyTestFunctionAliaslive" + ], + "Properties": { + "DestinationConfig": { + "OnFailure": { + "Destination": "arn:aws:s3::123456789012:my_bucket-2" + }, + "OnSuccess": { + "Destination": "arn:aws:s3::123456789012:my_bucket-1" + } + }, + "FunctionName": { + "Ref": "MyTestFunction" + }, + "MaximumEventAgeInSeconds": 70, + "MaximumRetryAttempts": 1, + "Qualifier": "live" + }, + "Type": "AWS::Lambda::EventInvokeConfig" + }, + "MyTestFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-1", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-1" + } + ] + }, + "PolicyName": "MyTestFunctionEventInvokeConfigOnSuccessS3Policy" + }, + { + "PolicyDocument": { + "Statement": [ + { + "Action": "s3:PutObject", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "/", + [ + "arn:aws:s3::123456789012:my_bucket-2", + "*" + ] + ] + } + }, + { + "Action": "s3:ListBucket", + "Effect": "Allow", + "Resource": "arn:aws:s3::123456789012:my_bucket-2" + } + ] + }, + "PolicyName": "MyTestFunctionEventInvokeConfigOnFailureS3Policy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "MyTestFunctionVersiondaf9da458d": { + "DeletionPolicy": "Retain", + "Properties": { + "FunctionName": { + "Ref": "MyTestFunction" + } + }, + "Type": "AWS::Lambda::Version" + } + } +} diff --git a/tests/translator/output/function_with_sourcekmskeyarn.json b/tests/translator/output/function_with_sourcekmskeyarn.json new file mode 100644 index 000000000..cfe9d0f04 --- /dev/null +++ b/tests/translator/output/function_with_sourcekmskeyarn.json @@ -0,0 +1,120 @@ +{ + "Parameters": { + "SourceKMSKeyArnParam": { + "Default": "arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f", + "Type": "String" + } + }, + "Resources": { + "SourceKMSKeyArnFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "SourceKMSKeyArnFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "SourceKMSKeyArnFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + }, + "SourceKMSKeyArnParameterFunction": { + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "SourceKMSKeyArn": { + "Ref": "SourceKMSKeyArnParam" + } + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "SourceKMSKeyArnParameterFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::Lambda::Function" + }, + "SourceKMSKeyArnParameterFunctionRole": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + }, + "Type": "AWS::IAM::Role" + } + } +} diff --git a/tests/translator/output/globals_for_function.json b/tests/translator/output/globals_for_function.json index 375a948ca..d3749cd9a 100644 --- a/tests/translator/output/globals_for_function.json +++ b/tests/translator/output/globals_for_function.json @@ -7,7 +7,8 @@ ], "Code": { "S3Bucket": "sam-demo-bucket", - "S3Key": "hello.zip" + "S3Key": "hello.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:987654321098:key/dec86919-7219-4e8d-8871-7f1609df2c7f" }, "Environment": { "Variables": { @@ -84,7 +85,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "FunctionWithOverridesVersion096ed3b52b", + "FunctionWithOverridesVersionb52716e99f", "Version" ] }, @@ -133,7 +134,7 @@ }, "Type": "AWS::IAM::Role" }, - "FunctionWithOverridesVersion096ed3b52b": { + "FunctionWithOverridesVersionb52716e99f": { "DeletionPolicy": "Retain", "Properties": { "FunctionName": { @@ -149,7 +150,8 @@ ], "Code": { "S3Bucket": "global-bucket", - "S3Key": "global.zip" + "S3Key": "global.zip", + "SourceKMSKeyArn": "arn:aws:kms:us-west-2:123456789012:key/dec86919-7219-4e8d-8871-7f1609df2c7f" }, "Environment": { "Variables": { @@ -217,7 +219,7 @@ }, "FunctionVersion": { "Fn::GetAtt": [ - "MinimalFunctionVersione7c6f56e4d", + "MinimalFunctionVersion5244f38b49", "Version" ] }, @@ -262,7 +264,7 @@ }, "Type": "AWS::IAM::Role" }, - "MinimalFunctionVersione7c6f56e4d": { + "MinimalFunctionVersion5244f38b49": { "DeletionPolicy": "Retain", "Properties": { "FunctionName": { From e0746ed51f30575d6c27c618492fe390d253569d Mon Sep 17 00:00:00 2001 From: aws-sam-cli-bot <46753707+aws-sam-cli-bot@users.noreply.github.com> Date: Thu, 21 Nov 2024 18:11:45 +0000 Subject: [PATCH 3/4] chore: bump version to 1.93.0 --- samtranslator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index eb97acb8c..1dd4da685 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.91.0" +__version__ = "1.93.0" From b5df724436702d1fc5e0b431111ee941a7660321 Mon Sep 17 00:00:00 2001 From: Aayush Thapa Date: Thu, 21 Nov 2024 10:16:40 -0800 Subject: [PATCH 4/4] format fix --- samtranslator/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samtranslator/__init__.py b/samtranslator/__init__.py index afc42b815..1dd4da685 100644 --- a/samtranslator/__init__.py +++ b/samtranslator/__init__.py @@ -1 +1 @@ -__version__ = "1.93.0" \ No newline at end of file +__version__ = "1.93.0"