Skip to content

Commit cf126c6

Browse files
hawflauejafarliaahungjfussvinayaksood
authored
Release v1.34.0 (#1893)
* Support DLQ, RetryPolicy properties for EventBridgeRule,Schedule event sources (#1842) * Add DeadLetterConfig,RetryPolicy properties for EventBridgeRule,Schedule event sources * Minor fix,rename function argument * Update test class name * Combine dlq extraction/generation into the utility class * Remove unused import * fix: propagate condition to sqs queue policy for sqssubscription (#1798) * fix: propagate condition to sqs queue policy for sqssubscription * Update unit test for function_event_conditions * Update black commands in Makefile to check only .py files * Update test with one more SNS event source with sqsSubscription set * Revert "Update black commands in Makefile to check only .py files" This reverts commit 115ff09. * chore: Remove biased language from pylintrc (#1847) * fix: Support new CodeDeploy ManagedPolicy (#1858) * fix: Support new CodeDeploy MangedPolicy in regions without AWSCodeDeployRoleForLambda CodeDeploy is migrating from AWSCodeDeployRoleForLambda to AWSCodeDeployRoleForLambdaLimited. Some partitions do not support AWSCodeDeployRoleForLambda and therefore we need to use the newer one in those partitions. We cannot widely update to AWSCodeDeployRoleForLambdaLimited since this can cause customer's stacks to fail unexpectedly. * Forgot to commit unit tests * Handle PR feedback Co-authored-by: Jacob Fuss <[email protected]> * fix: Update Slack invite link (#1877) Co-authored-by: Jacob Fuss <[email protected]> * feature: Support for custom checkpointing (#1883) Co-authored-by: Vinayak <[email protected]> * Fix: Description in AWS::Serverless::HttpApi (#1884) * Fix: Description in AWS::Serverless::HttpApi * Update _set to _add * Update AWS::S3::Bucket properties (#1885) * Update AWS::S3::Bucket properties * Fix type checking validators for AWS::S3::Bucket * Update to use any_type() in favor of supporing ref * Fix: Replaced invalid AMQ managed policy by providing policy statements (#1891) * Fix for invalid MQ event source managed policy * Fix for invalid managed policy for MQ, included support for new MQ event source property, updated test cases * Black reformatting * Test case changes * Changed policy name * Modified test cases with new policy name * chore: bump version 1.34.0 (#1892) * Fix: SAM crashes method_definition for path is invalid (#1802) * Fix: SAM crashes method_definition for path is invalid * Fix: SAM crashes whenmappings is null * Removed print statement in test_translator * Fix: Swagger security not a dict Co-authored-by: Mufaddal Makati <[email protected]> Co-authored-by: ejafarli <[email protected]> Co-authored-by: _sam <[email protected]> Co-authored-by: Jacob Fuss <[email protected]> Co-authored-by: Jacob Fuss <[email protected]> Co-authored-by: vinayaksood <[email protected]> Co-authored-by: Vinayak <[email protected]> Co-authored-by: Qingchuan Ma <[email protected]> Co-authored-by: Mufaddal Makati <[email protected]> Co-authored-by: Mufaddal Makati <[email protected]>
1 parent d2f18e7 commit cf126c6

File tree

109 files changed

+5107
-362
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+5107
-362
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# pygtk.require().
88
#init-hook=
99

10-
# Add files or directories to the blacklist. They should be base names, not
10+
# Add files or directories to the ignore list. They should be base names, not
1111
# paths.
1212
ignore=compat.py
1313

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ Read the [SAM Documentation Contribution Guide](https://github.com/awsdocs/aws-s
8787
started.
8888

8989
### Join the SAM Community on Slack
90-
[Join the SAM developers channel (#samdev)](https://join.slack.com/t/awsdevelopers/shared_invite/zt-h82odes6-qYN2Cxit7hBGIvC6oMjGpg) on Slack to collaborate with fellow community members and the AWS SAM team.
90+
[Join the SAM developers channel (#samdev)](https://join.slack.com/t/awsdevelopers/shared_invite/zt-idww18e8-Z1kXhI7GNuDewkweCF3YjA) on Slack to collaborate with fellow community members and the AWS SAM team.
9191

9292

9393

docs/cloudformation_compatibility.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ CloudWatchEvent (superseded by EventBridgeRule, see below)
169169
Pattern All
170170
Input All
171171
InputPath All
172+
DeadLetterConfig All
173+
RetryPolicy All
172174
======================== ================================== ========================
173175

174176
EventBridgeRule
@@ -179,6 +181,8 @@ EventBridgeRule
179181
Pattern All
180182
Input All
181183
InputPath All
184+
DeadLetterConfig All
185+
RetryPolicy All
182186
======================== ================================== ========================
183187

184188
IotRule

docs/internals/generated_resources.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,8 @@ Example:
455455
Type: Schedule
456456
Properties:
457457
Input: rate(5 minutes)
458+
DeadLetterConfig:
459+
Type: SQS
458460
...
459461
460462
Additional generated resources:
@@ -464,6 +466,8 @@ CloudFormation Resource Type Logical ID
464466
================================== ================================
465467
AWS::Lambda::Permission MyFunction\ **MyTimer**\ Permission
466468
AWS::Events::Rule MyFunction\ **MyTimer**
469+
AWS::SQS::Queue MyFunction\ **MyTimer**\ Queue
470+
AWS::SQS::QueuePolicy MyFunction\ **MyTimer**\ QueuePolicy
467471
================================== ================================
468472

469473
CloudWatchEvent (superseded by EventBridgeRule, see below)
@@ -523,6 +527,11 @@ Example:
523527
detail:
524528
state:
525529
- terminated
530+
DeadLetterConfig:
531+
Type: SQS
532+
RetryPolicy:
533+
MaximumEventAgeInSeconds: 600
534+
MaximumRetryAttempts:3
526535
...
527536
528537
Additional generated resources:
@@ -532,6 +541,8 @@ CloudFormation Resource Type Logical ID
532541
================================== ================================
533542
AWS::Lambda::Permission MyFunction\ **OnTerminate**\ Permission
534543
AWS::Events::Rule MyFunction\ **OnTerminate**
544+
AWS::SQS::Queue MyFunction\ **OnTerminate**\ Queue
545+
AWS::SQS::QueuePolicy MyFunction\ **OnTerminate**\ QueuePolicy
535546
================================== ================================
536547

537548
AWS::Serverless::Api

samtranslator/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.33.0"
1+
__version__ = "1.34.0"

samtranslator/intrinsics/resolver.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Help resolve intrinsic functions
22

33
from samtranslator.intrinsics.actions import Action, SubAction, RefAction, GetAttAction
4+
from samtranslator.model.exceptions import InvalidTemplateException, InvalidDocumentException
45

56
# All intrinsics are supported by default
67
DEFAULT_SUPPORTED_INTRINSICS = {action.intrinsic_name: action() for action in [RefAction, SubAction, GetAttAction]}
@@ -17,7 +18,9 @@ def __init__(self, parameters, supported_intrinsics=DEFAULT_SUPPORTED_INTRINSICS
1718
"""
1819

1920
if parameters is None or not isinstance(parameters, dict):
20-
raise TypeError("parameters must be a valid dictionary")
21+
raise InvalidDocumentException(
22+
[InvalidTemplateException("'Mappings' or 'Parameters' is either null or not a valid dictionary.")]
23+
)
2124

2225
if not isinstance(supported_intrinsics, dict) or not all(
2326
[isinstance(value, Action) for value in supported_intrinsics.values()]

samtranslator/model/api/http_api_generator.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ def _construct_http_api(self):
112112
if self.disable_execute_api_endpoint is not None:
113113
self._add_endpoint_configuration()
114114

115+
self._add_description()
116+
115117
if self.definition_uri:
116118
http_api.BodyS3Location = self._construct_body_s3_dict()
117119
elif self.definition_body:
@@ -124,9 +126,6 @@ def _construct_http_api(self):
124126
"add a 'HttpApi' event to an 'AWS::Serverless::Function'.",
125127
)
126128

127-
if self.description:
128-
http_api.Description = self.description
129-
130129
return http_api
131130

132131
def _add_endpoint_configuration(self):
@@ -586,6 +585,27 @@ def _construct_stage(self):
586585

587586
return stage
588587

588+
def _add_description(self):
589+
"""Add description to DefinitionBody if Description property is set in SAM"""
590+
if not self.description:
591+
return
592+
593+
if not self.definition_body:
594+
raise InvalidResourceException(
595+
self.logical_id,
596+
"Description works only with inline OpenApi specified in the 'DefinitionBody' property.",
597+
)
598+
if self.definition_body.get("info", {}).get("description"):
599+
raise InvalidResourceException(
600+
self.logical_id,
601+
"Unable to set Description because it is already defined within inline OpenAPI specified in the "
602+
"'DefinitionBody' property.",
603+
)
604+
605+
open_api_editor = OpenApiEditor(self.definition_body)
606+
open_api_editor.add_description(self.description)
607+
self.definition_body = open_api_editor.openapi
608+
589609
def to_cloudformation(self):
590610
"""Generates CloudFormation resources from a SAM HTTP API resource
591611
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from samtranslator.model.sqs import SQSQueue, SQSQueuePolicy, SQSQueuePolicies
2+
from samtranslator.model.exceptions import InvalidEventException
3+
4+
5+
class EventBridgeRuleUtils:
6+
@staticmethod
7+
def create_dead_letter_queue_with_policy(rule_logical_id, rule_arn, queue_logical_id=None):
8+
resources = []
9+
10+
queue = SQSQueue(queue_logical_id or rule_logical_id + "Queue")
11+
dlq_queue_arn = queue.get_runtime_attr("arn")
12+
dlq_queue_url = queue.get_runtime_attr("queue_url")
13+
14+
# grant necessary permission to Eventbridge Rule resource for sending messages to dead-letter queue
15+
policy = SQSQueuePolicy(rule_logical_id + "QueuePolicy")
16+
policy.PolicyDocument = SQSQueuePolicies.eventbridge_dlq_send_message_resource_based_policy(
17+
rule_arn, dlq_queue_arn
18+
)
19+
policy.Queues = [dlq_queue_url]
20+
21+
resources.append(queue)
22+
resources.append(policy)
23+
24+
return resources
25+
26+
@staticmethod
27+
def validate_dlq_config(source_logical_id, dead_letter_config):
28+
supported_types = ["SQS"]
29+
is_arn_defined = "Arn" in dead_letter_config
30+
is_type_defined = "Type" in dead_letter_config
31+
if is_arn_defined and is_type_defined:
32+
raise InvalidEventException(
33+
source_logical_id, "You can either define 'Arn' or 'Type' property of DeadLetterConfig"
34+
)
35+
if is_type_defined and dead_letter_config.get("Type") not in supported_types:
36+
raise InvalidEventException(
37+
source_logical_id,
38+
"The only valid value for 'Type' property of DeadLetterConfig is 'SQS'",
39+
)
40+
if not is_arn_defined and not is_type_defined:
41+
raise InvalidEventException(source_logical_id, "No 'Arn' or 'Type' property provided for DeadLetterConfig")
42+
43+
@staticmethod
44+
def get_dlq_queue_arn_and_resources(cw_event_source, source_arn):
45+
"""returns dlq queue arn and dlq_resources, assuming cw_event_source.DeadLetterConfig has been validated"""
46+
dlq_queue_arn = cw_event_source.DeadLetterConfig.get("Arn")
47+
if dlq_queue_arn is not None:
48+
return dlq_queue_arn, []
49+
queue_logical_id = cw_event_source.DeadLetterConfig.get("QueueLogicalId")
50+
dlq_resources = EventBridgeRuleUtils.create_dead_letter_queue_with_policy(
51+
cw_event_source.logical_id, source_arn, queue_logical_id
52+
)
53+
dlq_queue_arn = dlq_resources[0].get_runtime_attr("arn")
54+
return dlq_queue_arn, dlq_resources

samtranslator/model/eventsources/pull.py

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@ class PullEventSource(ResourceMacro):
3434
"Broker": PropertyType(False, is_str()),
3535
"Queues": PropertyType(False, is_type(list)),
3636
"SourceAccessConfigurations": PropertyType(False, is_type(list)),
37+
"SecretsManagerKmsKeyId": PropertyType(False, is_str()),
3738
"TumblingWindowInSeconds": PropertyType(False, is_type(int)),
39+
"FunctionResponseTypes": PropertyType(False, is_type(list)),
3840
}
3941

4042
def get_policy_arn(self):
4143
raise NotImplementedError("Subclass must implement this method")
4244

45+
def get_policy_statements(self):
46+
raise NotImplementedError("Subclass must implement this method")
47+
4348
def to_cloudformation(self, **kwargs):
4449
"""Returns the Lambda EventSourceMapping to which this pull event corresponds. Adds the appropriate managed
4550
policy to the function's execution role, if such a role is provided.
@@ -87,6 +92,7 @@ def to_cloudformation(self, **kwargs):
8792
lambda_eventsourcemapping.Queues = self.Queues
8893
lambda_eventsourcemapping.SourceAccessConfigurations = self.SourceAccessConfigurations
8994
lambda_eventsourcemapping.TumblingWindowInSeconds = self.TumblingWindowInSeconds
95+
lambda_eventsourcemapping.FunctionResponseTypes = self.FunctionResponseTypes
9096

9197
destination_config_policy = None
9298
if self.DestinationConfig:
@@ -131,8 +137,17 @@ def _link_policy(self, role, destination_config_policy=None):
131137
:param model.iam.IAMRole role: the execution role generated for the function
132138
"""
133139
policy_arn = self.get_policy_arn()
134-
if role is not None and policy_arn not in role.ManagedPolicyArns:
135-
role.ManagedPolicyArns.append(policy_arn)
140+
policy_statements = self.get_policy_statements()
141+
if role is not None:
142+
if policy_arn is not None and policy_arn not in role.ManagedPolicyArns:
143+
role.ManagedPolicyArns.append(policy_arn)
144+
if policy_statements is not None:
145+
if role.Policies is None:
146+
role.Policies = []
147+
for policy in policy_statements:
148+
if policy not in role.Policies:
149+
if not policy.get("PolicyDocument") in [d["PolicyDocument"] for d in role.Policies]:
150+
role.Policies.append(policy)
136151
# add SQS or SNS policy only if role is present in kwargs
137152
if role is not None and destination_config_policy is not None and destination_config_policy:
138153
if role.Policies is None:
@@ -152,6 +167,9 @@ class Kinesis(PullEventSource):
152167
def get_policy_arn(self):
153168
return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaKinesisExecutionRole")
154169

170+
def get_policy_statements(self):
171+
return None
172+
155173

156174
class DynamoDB(PullEventSource):
157175
"""DynamoDB Streams event source."""
@@ -161,6 +179,9 @@ class DynamoDB(PullEventSource):
161179
def get_policy_arn(self):
162180
return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaDynamoDBExecutionRole")
163181

182+
def get_policy_statements(self):
183+
return None
184+
164185

165186
class SQS(PullEventSource):
166187
"""SQS Queue event source."""
@@ -170,6 +191,9 @@ class SQS(PullEventSource):
170191
def get_policy_arn(self):
171192
return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaSQSQueueExecutionRole")
172193

194+
def get_policy_statements(self):
195+
return None
196+
173197

174198
class MSK(PullEventSource):
175199
"""MSK event source."""
@@ -179,11 +203,69 @@ class MSK(PullEventSource):
179203
def get_policy_arn(self):
180204
return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaMSKExecutionRole")
181205

206+
def get_policy_statements(self):
207+
return None
208+
182209

183210
class MQ(PullEventSource):
184211
"""MQ event source."""
185212

186213
resource_type = "MQ"
187214

188215
def get_policy_arn(self):
189-
return ArnGenerator.generate_aws_managed_policy_arn("service-role/AWSLambdaAMQExecutionRole")
216+
return None
217+
218+
def get_policy_statements(self):
219+
if not self.SourceAccessConfigurations:
220+
raise InvalidEventException(
221+
self.relative_id,
222+
"No SourceAccessConfigurations for ActiveMQ provided.",
223+
)
224+
if not type(self.SourceAccessConfigurations) is list:
225+
raise InvalidEventException(
226+
self.relative_id,
227+
"Provided SourceAccessConfigurations cannot be parsed into a list.",
228+
)
229+
# MQ only supports SourceAccessConfigurations with list size of 1
230+
if not (len(self.SourceAccessConfigurations) == 1):
231+
raise InvalidEventException(
232+
self.relative_id,
233+
"SourceAccessConfigurations for ActiveMQ only supports single configuration entry.",
234+
)
235+
if not self.SourceAccessConfigurations[0].get("URI"):
236+
raise InvalidEventException(
237+
self.relative_id,
238+
"No URI property specified in SourceAccessConfigurations for ActiveMQ.",
239+
)
240+
document = {
241+
"PolicyName": "SamAutoGeneratedAMQPolicy",
242+
"PolicyDocument": {
243+
"Statement": [
244+
{
245+
"Action": [
246+
"secretsmanager:GetSecretValue",
247+
],
248+
"Effect": "Allow",
249+
"Resource": self.SourceAccessConfigurations[0].get("URI"),
250+
},
251+
{
252+
"Action": [
253+
"mq:DescribeBroker",
254+
],
255+
"Effect": "Allow",
256+
"Resource": self.Broker,
257+
},
258+
]
259+
},
260+
}
261+
if self.SecretsManagerKmsKeyId:
262+
kms_policy = {
263+
"Action": "kms:Decrypt",
264+
"Effect": "Allow",
265+
"Resource": {
266+
"Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/"
267+
+ self.SecretsManagerKmsKeyId
268+
},
269+
}
270+
document["PolicyDocument"]["Statement"].append(kms_policy)
271+
return [document]

0 commit comments

Comments
 (0)