diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py index 93ed5592f2..8c8df6d7e6 100644 --- a/samtranslator/model/eventsources/push.py +++ b/samtranslator/model/eventsources/push.py @@ -96,6 +96,7 @@ class Schedule(PushEventSource): "Schedule": PropertyType(True, is_str()), "Input": PropertyType(False, is_str()), "Enabled": PropertyType(False, is_type(bool)), + "State": PropertyType(False, is_str()), "Name": PropertyType(False, is_str()), "Description": PropertyType(False, is_str()), "DeadLetterConfig": PropertyType(False, is_type(dict)), @@ -122,8 +123,16 @@ def to_cloudformation(self, **kwargs): resources.append(events_rule) events_rule.ScheduleExpression = self.Schedule + + if self.State and self.Enabled is not None: + raise InvalidEventException(self.relative_id, "State and Enabled Properties cannot both be specified.") + + if self.State: + events_rule.State = self.State + if self.Enabled is not None: events_rule.State = "ENABLED" if self.Enabled else "DISABLED" + events_rule.Name = self.Name events_rule.Description = self.Description diff --git a/tests/model/eventsources/test_schedule_event_source.py b/tests/model/eventsources/test_schedule_event_source.py index 2dff522ca8..78a9e49e6c 100644 --- a/tests/model/eventsources/test_schedule_event_source.py +++ b/tests/model/eventsources/test_schedule_event_source.py @@ -1,9 +1,9 @@ -from unittest.mock import Mock, patch from unittest import TestCase from samtranslator.model.eventsources.push import Schedule from samtranslator.model.lambda_ import LambdaFunction from samtranslator.model.exceptions import InvalidEventException +from parameterized import parameterized class ScheduleEventSource(TestCase): @@ -13,6 +13,27 @@ def setUp(self): self.schedule_event_source.Schedule = "rate(1 minute)" self.func = LambdaFunction("func") + def test_to_cloudformation_returns_permission_and_schedule_resources(self): + resources = self.schedule_event_source.to_cloudformation(function=self.func) + self.assertEqual(len(resources), 2) + self.assertEqual(resources[0].resource_type, "AWS::Events::Rule") + self.assertEqual(resources[1].resource_type, "AWS::Lambda::Permission") + + schedule = resources[0] + self.assertEqual(schedule.ScheduleExpression, "rate(1 minute)") + self.assertIsNone(schedule.State) + + def test_to_cloudformation_transforms_enabled_boolean_to_state(self): + self.schedule_event_source.Enabled = True + resources = self.schedule_event_source.to_cloudformation(function=self.func) + schedule = resources[0] + self.assertEqual(schedule.State, "ENABLED") + + self.schedule_event_source.Enabled = False + resources = self.schedule_event_source.to_cloudformation(function=self.func) + schedule = resources[0] + self.assertEqual(schedule.State, "DISABLED") + def test_to_cloudformation_with_retry_policy(self): retry_policy = {"MaximumRetryAttempts": "10", "MaximumEventAgeInSeconds": "300"} self.schedule_event_source.RetryPolicy = retry_policy @@ -70,3 +91,19 @@ def test_to_cloudformation_with_dlq_generated_with_intrinsic_function_custom_log self.schedule_event_source.DeadLetterConfig = dead_letter_config with self.assertRaises(InvalidEventException): self.schedule_event_source.to_cloudformation(function=self.func) + + @parameterized.expand( + [ + (True, "Enabled"), + (True, "Disabled"), + (True, {"FN:FakeIntrinsic": "something"}), + (False, "Enabled"), + (False, "Disabled"), + (False, {"FN:FakeIntrinsic": "something"}), + ] + ) + def test_to_cloudformation_invalid_defined_both_enabled_and_state_provided(self, enabled_value, state_value): + self.schedule_event_source.Enabled = enabled_value + self.schedule_event_source.State = state_value + with self.assertRaises(InvalidEventException): + self.schedule_event_source.to_cloudformation(function=self.func) diff --git a/tests/translator/input/error_function_with_invalid_schedule_event.yaml b/tests/translator/input/error_function_with_invalid_schedule_event.yaml new file mode 100644 index 0000000000..eb6bbffeae --- /dev/null +++ b/tests/translator/input/error_function_with_invalid_schedule_event.yaml @@ -0,0 +1,18 @@ +Transform: "AWS::Serverless-2016-10-31" + +Resources: + ScheduledFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip?versionId=3Tcgv52_0GaDvhDva4YciYeqRyPnpIcO + Handler: hello.handler + Runtime: python3.10 + Events: + Schedule1: + Type: Schedule + Properties: + Schedule: 'rate(1 minute)' + Name: test-schedule + Description: Test Schedule + State: "Enabled" + Enabled: True \ No newline at end of file diff --git a/tests/translator/input/function_with_event_schedule_state.yaml b/tests/translator/input/function_with_event_schedule_state.yaml new file mode 100644 index 0000000000..17a13f6549 --- /dev/null +++ b/tests/translator/input/function_with_event_schedule_state.yaml @@ -0,0 +1,35 @@ +Transform: "AWS::Serverless-2016-10-31" +Parameters: + ScheduleState: + Type: String + Default: Disabled + +Resources: + ScheduledFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/hello.zip?versionId=3Tcgv52_0GaDvhDva4YciYeqRyPnpIcO + Handler: hello.handler + Runtime: python3.10 + Events: + Schedule1: + Type: Schedule + Properties: + Schedule: 'rate(1 minute)' + Name: test-schedule + Description: Test Schedule + State: "Enabled" + Schedule2: + Type: Schedule + Properties: + Schedule: 'rate(1 minute)' + Name: test-schedule + Description: Test Schedule + State: !Sub "Enabled" + Schedule3: + Type: Schedule + Properties: + Schedule: 'rate(1 minute)' + Name: test-schedule + Description: Test Schedule + State: !Ref ScheduleState \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_event_schedule_state.json b/tests/translator/output/aws-cn/function_with_event_schedule_state.json new file mode 100644 index 0000000000..7f816444cd --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_event_schedule_state.json @@ -0,0 +1,176 @@ +{ + "Parameters": { + "ScheduleState": { + "Type": "String", + "Default": "Disabled" + } + }, + "Resources": { + "ScheduledFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "S3ObjectVersion": "3Tcgv52_0GaDvhDva4YciYeqRyPnpIcO" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "ScheduledFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.10", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ScheduledFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ScheduledFunctionSchedule1": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": "Enabled", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule1LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule1Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule1", + "Arn" + ] + } + } + }, + "ScheduledFunctionSchedule2": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": { + "Fn::Sub": "Enabled" + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule2LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule2Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule2", + "Arn" + ] + } + } + }, + "ScheduledFunctionSchedule3": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": { + "Ref": "ScheduleState" + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule3LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule3Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule3", + "Arn" + ] + } + } + } + } + } \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_event_schedule_state.json b/tests/translator/output/aws-us-gov/function_with_event_schedule_state.json new file mode 100644 index 0000000000..a49503a4e0 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_event_schedule_state.json @@ -0,0 +1,176 @@ +{ + "Parameters": { + "ScheduleState": { + "Type": "String", + "Default": "Disabled" + } + }, + "Resources": { + "ScheduledFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "S3ObjectVersion": "3Tcgv52_0GaDvhDva4YciYeqRyPnpIcO" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "ScheduledFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.10", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ScheduledFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ScheduledFunctionSchedule1": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": "Enabled", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule1LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule1Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule1", + "Arn" + ] + } + } + }, + "ScheduledFunctionSchedule2": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": { + "Fn::Sub": "Enabled" + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule2LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule2Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule2", + "Arn" + ] + } + } + }, + "ScheduledFunctionSchedule3": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": { + "Ref": "ScheduleState" + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule3LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule3Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule3", + "Arn" + ] + } + } + } + } + } \ No newline at end of file diff --git a/tests/translator/output/error_function_with_invalid_schedule_event.json b/tests/translator/output/error_function_with_invalid_schedule_event.json new file mode 100644 index 0000000000..8311582e43 --- /dev/null +++ b/tests/translator/output/error_function_with_invalid_schedule_event.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [ScheduledFunction] is invalid. Event with id [Schedule1] is invalid. State and Enabled Properties cannot both be specified." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [ScheduledFunction] is invalid. Event with id [Schedule1] is invalid. State and Enabled Properties cannot both be specified." + } \ No newline at end of file diff --git a/tests/translator/output/function_with_event_schedule_state.json b/tests/translator/output/function_with_event_schedule_state.json new file mode 100644 index 0000000000..04f1131b6e --- /dev/null +++ b/tests/translator/output/function_with_event_schedule_state.json @@ -0,0 +1,176 @@ +{ + "Parameters": { + "ScheduleState": { + "Type": "String", + "Default": "Disabled" + } + }, + "Resources": { + "ScheduledFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "hello.zip", + "S3ObjectVersion": "3Tcgv52_0GaDvhDva4YciYeqRyPnpIcO" + }, + "Handler": "hello.handler", + "Role": { + "Fn::GetAtt": [ + "ScheduledFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.10", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ScheduledFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "ScheduledFunctionSchedule1": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": "Enabled", + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule1LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule1Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule1", + "Arn" + ] + } + } + }, + "ScheduledFunctionSchedule2": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": { + "Fn::Sub": "Enabled" + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule2LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule2Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule2", + "Arn" + ] + } + } + }, + "ScheduledFunctionSchedule3": { + "Type": "AWS::Events::Rule", + "Properties": { + "Description": "Test Schedule", + "Name": "test-schedule", + "ScheduleExpression": "rate(1 minute)", + "State": { + "Ref": "ScheduleState" + }, + "Targets": [ + { + "Arn": { + "Fn::GetAtt": [ + "ScheduledFunction", + "Arn" + ] + }, + "Id": "ScheduledFunctionSchedule3LambdaTarget" + } + ] + } + }, + "ScheduledFunctionSchedule3Permission": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Ref": "ScheduledFunction" + }, + "Principal": "events.amazonaws.com", + "SourceArn": { + "Fn::GetAtt": [ + "ScheduledFunctionSchedule3", + "Arn" + ] + } + } + } + } + } \ No newline at end of file