Skip to content

Commit 690bfff

Browse files
authored
Merge pull request #3637 from aws/release-v1.90.0
Release 1.90.0 (to main)
2 parents 44562dc + b86f1c5 commit 690bfff

File tree

53 files changed

+3686
-1063
lines changed

Some content is hidden

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

53 files changed

+3686
-1063
lines changed

.cfnlintrc.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ ignore_templates:
8585
- tests/translator/output/**/function_with_event_dest_conditional.json
8686
- tests/translator/output/**/function_with_event_schedule_state.json
8787
- tests/translator/output/**/function_with_file_system_config.json
88+
- tests/translator/output/**/function_with_event_filtering.json # TODO: remove once Event's KmsKeyArn is available
8889
- tests/translator/output/**/function_with_function_url_config_conditions.json
8990
- tests/translator/output/**/function_with_globals_role_path.json
9091
- tests/translator/output/**/function_with_intrinsic_architecture.json

Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ black-check:
5050
make format-check
5151

5252
lint:
53-
ruff samtranslator bin schema_source integration tests
53+
ruff check samtranslator bin schema_source integration tests
5454
# mypy performs type check
5555
mypy --strict samtranslator bin schema_source
5656
# cfn-lint to make sure generated CloudFormation makes sense
5757
bin/run_cfn_lint.sh
5858

5959
lint-fix:
60-
ruff --fix samtranslator bin schema_source integration tests
60+
ruff check --fix samtranslator bin schema_source integration tests
6161

6262
prepare-companion-stack:
6363
pytest -v --no-cov integration/setup -m setup

integration/conftest.py

-33
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import logging
2-
import time
32
from pathlib import Path
43

54
import boto3
@@ -61,29 +60,6 @@ def clean_all_integ_buckets():
6160
clean_bucket(bucket.name, s3_client)
6261

6362

64-
def _delete_unused_network_interface_by_subnet(ec2_client, subnet_id):
65-
"""Deletes unused network interface under the provided subnet"""
66-
paginator = ec2_client.get_paginator("describe_network_interfaces")
67-
response_iterator = paginator.paginate(
68-
Filters=[
69-
{"Name": "subnet-id", "Values": [subnet_id]},
70-
{"Name": "status", "Values": ["available"]},
71-
]
72-
)
73-
network_interface_ids = []
74-
for page in response_iterator:
75-
network_interface_ids += [ni["NetworkInterfaceId"] for ni in page["NetworkInterfaces"]]
76-
77-
for ni_id in network_interface_ids:
78-
try:
79-
ec2_client.delete_network_interface(NetworkInterfaceId=ni_id)
80-
except ClientError as e:
81-
LOG.error("Unable to delete network interface %s", ni_id, exc_info=e)
82-
time.sleep(0.5)
83-
84-
LOG.info("Deleted %s unused network interfaces under subnet %s", len(network_interface_ids), subnet_id)
85-
86-
8763
@pytest.fixture()
8864
def setup_companion_stack_once(tmpdir_factory, get_prefix):
8965
tests_integ_dir = Path(__file__).resolve().parents[1]
@@ -95,15 +71,6 @@ def setup_companion_stack_once(tmpdir_factory, get_prefix):
9571
companion_stack = Stack(stack_name, companion_stack_tempalte_path, cfn_client, output_dir)
9672
companion_stack.create_or_update(_stack_exists(stack_name))
9773

98-
ec2_client = ClientProvider().ec2_client
99-
precreated_subnet_ids = [
100-
resource["PhysicalResourceId"]
101-
for resource in companion_stack.stack_resources["StackResourceSummaries"]
102-
if resource["LogicalResourceId"].startswith("PreCreatedSubnet")
103-
]
104-
for subnet_id in precreated_subnet_ids:
105-
_delete_unused_network_interface_by_subnet(ec2_client, subnet_id)
106-
10774

10875
@pytest.fixture()
10976
def get_serverless_application_repository_app():

integration/helpers/base_test.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,9 @@ def verify_options_request(self, url, expected_status_code, headers=None):
567567
)
568568
return response
569569

570-
def verify_post_request(self, url: str, body_obj, expected_status_code: int):
570+
def verify_post_request(self, url: str, body_obj, expected_status_code: int, headers=None):
571571
"""Return response to POST request and verify matches expected status code."""
572-
response = self.do_post_request(url, body_obj)
572+
response = self.do_post_request_with_logging(url, body_obj, headers)
573573
if response.status_code != expected_status_code:
574574
raise StatusCodeError(
575575
f"Request to {url} failed with status: {response.status_code}, expected status: {expected_status_code}"
@@ -650,12 +650,17 @@ def do_options_request_with_logging(self, url, headers=None):
650650
)
651651
return response
652652

653-
def do_post_request(self, url: str, body_obj):
653+
def do_post_request_with_logging(self, url: str, body_obj, requestHeaders=None):
654654
"""Perform a POST request with dict body body_obj."""
655-
response = requests.post(url, json=body_obj)
655+
response = (
656+
requests.post(url, json=body_obj, headers=requestHeaders)
657+
if requestHeaders
658+
else requests.post(url, json=body_obj)
659+
)
660+
amazon_headers = RequestUtils(response).get_amazon_headers()
656661
if self.internal:
657662
REQUEST_LOGGER.info(
658663
"POST request made to " + url,
659-
extra={"test": self.testcase, "status": response.status_code},
664+
extra={"test": self.testcase, "status": response.status_code, "headers": amazon_headers},
660665
)
661666
return response
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"LogicalResourceId": "BasicFunctionWithEventFilteringUsingKmsKeyArn",
4+
"ResourceType": "AWS::Lambda::Function"
5+
},
6+
{
7+
"LogicalResourceId": "BasicFunctionWithEventFilteringUsingKmsKeyArnRole",
8+
"ResourceType": "AWS::IAM::Role"
9+
},
10+
{
11+
"LogicalResourceId": "MyKey",
12+
"ResourceType": "AWS::KMS::Key"
13+
},
14+
{
15+
"LogicalResourceId": "MySqsQueue",
16+
"ResourceType": "AWS::SQS::Queue"
17+
},
18+
{
19+
"LogicalResourceId": "BasicFunctionWithEventFilteringUsingKmsKeyArnMySqsEvent",
20+
"ResourceType": "AWS::Lambda::EventSourceMapping"
21+
}
22+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
Resources:
2+
BasicFunctionWithEventFilteringUsingKmsKeyArn:
3+
Type: AWS::Serverless::Function
4+
Properties:
5+
Handler: index.handler
6+
Runtime: nodejs18.x
7+
CodeUri: ${codeuri}
8+
MemorySize: 128
9+
Events:
10+
MySqsEvent:
11+
Type: SQS
12+
Properties:
13+
Queue: !GetAtt MySqsQueue.Arn
14+
FilterCriteria:
15+
Filters:
16+
- Pattern: '{ "body" : { "RequestCode" : [ "BBBB" ] } }'
17+
KmsKeyArn: !GetAtt MyKey.Arn
18+
19+
MyKey:
20+
Type: AWS::KMS::Key
21+
Properties:
22+
Description: A sample key
23+
KeyPolicy:
24+
Version: '2012-10-17'
25+
Id: key-default-1
26+
Statement:
27+
- Sid: Allow administration of the key
28+
Effect: Allow
29+
Principal:
30+
AWS: !Sub arn:${AWS::Partition}:iam::${AWS::AccountId}:root
31+
Action:
32+
- kms:*
33+
Resource: '*'
34+
- Sid: Allow encryption/decryption access to Lambda Service Principal
35+
Effect: Allow
36+
Principal:
37+
Service: lambda.amazonaws.com
38+
Action: kms:Decrypt
39+
Resource: '*'
40+
41+
MySqsQueue:
42+
Type: AWS::SQS::Queue
43+
44+
Metadata:
45+
SamTransformTest: true

integration/resources/templates/single/state_machine_with_api.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Resources:
44
Type: AWS::Serverless::Api
55
Properties:
66
StageName: Prod
7+
EndpointConfiguration:
8+
Type: REGIONAL
79
HelloWorldFunction:
810
Type: AWS::Serverless::Function
911
Properties:

integration/ruff.toml

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# black formatter takes care of the line length
22
line-length = 999
33

4+
# Mininal python version we support is 3.8
5+
target-version = "py38"
6+
47
# The code quality of tests can be a bit lower compared to samtranslator
5-
select = [
8+
lint.select = [
69
"E", # Pyflakes
710
"F", # Pyflakes
811
"PL", # pylint
@@ -15,10 +18,7 @@ select = [
1518
"UP", # pyupgrade
1619
]
1720

18-
# Mininal python version we support is 3.8
19-
target-version = "py38"
20-
21-
[per-file-ignores]
21+
[lint.per-file-ignores]
2222

2323
# The code quality of tests can be a bit lower:
2424
"**/*.py" = [

integration/single/test_basic_api.py

+4
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ def test_state_machine_with_api_single_quotes_input(self):
118118
"""
119119
Pass single quotes in input JSON to a StateMachine
120120
See https://github.com/aws/serverless-application-model/issues/1895
121+
122+
This test is known to sometimes be flaky, but we want to avoid marking it as non-blocking as this is a basic api test.
123+
Instead, we set the EndpointConfiguration to REGIONAL and added logging to the api request
124+
If this test continues to fail it should be marked as non-blocking
121125
"""
122126
self.create_and_verify_stack("single/state_machine_with_api")
123127

integration/single/test_basic_function.py

+22
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,28 @@ def test_basic_function_with_tracing(self):
274274
"Expecting tracing config mode to be set to PassThrough.",
275275
)
276276

277+
# TODO: add the integration test back after the feature launch on 06/05
278+
# @skipIf(current_region_does_not_support([KMS]), "KMS is not supported in this testing region")
279+
# def test_basic_function_with_event_filtering_using_kms(self):
280+
# """
281+
# Creates a basic lambda function with KMS key arn
282+
# """
283+
# self.create_and_verify_stack("single/basic_function_with_event_filtering_using_kms")
284+
285+
# lambda_function_name = self.get_physical_id_by_type("AWS::Lambda::Function")
286+
# event_source_mappings = self.client_provider.lambda_client.list_event_source_mappings(
287+
# FunctionName=lambda_function_name
288+
# )
289+
290+
# event_source_mapping = event_source_mappings["EventSourceMappings"][0]
291+
# function_uuid = event_source_mapping["UUID"]
292+
293+
# event_source_mapping_config = self.client_provider.lambda_client.get_event_source_mapping(UUID=function_uuid)
294+
295+
# kms_key_arn = event_source_mapping_config["KMSKeyArn"]
296+
297+
# self.assertIsNotNone(kms_key_arn, "Expecting KmsKeyArn to be set.")
298+
277299
def _assert_invoke(self, lambda_client, function_name, qualifier=None, expected_status_code=200):
278300
"""
279301
Assert if a Lambda invocation returns the expected status code

requirements/base.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ jsonschema<5,>=3.2 # TODO: evaluate risk of removing jsonschema 3.x support
33
typing_extensions>=4.4 # 3.8 doesn't have Required, TypeGuard and ParamSpec
44

55
# resource validation & schema generation
6-
pydantic>=1.8,<3
6+
# 1.10.15 and 1.10.17 included breaking change from pydantic, more info: https://github.com/aws/serverless-application-model/issues/3617
7+
pydantic>=1.8,<3,!=1.10.15,!=1.10.17

requirements/dev.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pytest-xdist>=2.5,<4
44
pytest-env>=0.6,<1
55
pytest-rerunfailures>=9.1,<12
66
pyyaml~=6.0
7-
ruff~=0.1.0
7+
ruff~=0.4.5
88

99
# Test requirements
1010
pytest>=6.2,<8

ruff.toml

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
# black formatter takes care of the line length
22
line-length = 999
33

4-
select = [
4+
# Mininal python version we support is 3.8
5+
target-version = "py38"
6+
7+
lint.select = [
58
"E", # pycodestyle
69
"W", # pycodestyle
710
"F", # Pyflakes
@@ -27,7 +30,7 @@ select = [
2730
"T20", # flake8-print
2831
]
2932

30-
ignore = [
33+
lint.ignore = [
3134
"UP006", # https://github.com/charliermarsh/ruff/pull/4427
3235
"UP007", # https://github.com/charliermarsh/ruff/pull/4427
3336
# Mutable class attributes should be annotated with `typing.ClassVar`
@@ -37,10 +40,7 @@ ignore = [
3740
"G004",
3841
]
3942

40-
# Mininal python version we support is 3.8
41-
target-version = "py38"
42-
43-
[per-file-ignores]
43+
[lint.per-file-ignores]
4444
# python scripts in bin/ needs some python path configurations before import
4545
"bin/*.py" = [
4646
# E402: module-import-not-at-top-of-file
@@ -53,5 +53,5 @@ target-version = "py38"
5353
"T201",
5454
]
5555

56-
[pylint]
56+
[lint.pylint]
5757
max-args = 6 # We have many functions reaching 6 args

samtranslator/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.89.0"
1+
__version__ = "1.90.0"

samtranslator/internal/schema_source/aws_serverless_function.py

+6
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class KinesisEventProperties(BaseModel):
169169
Enabled: Optional[PassThroughProp] = kinesiseventproperties("Enabled")
170170
FilterCriteria: Optional[PassThroughProp] = kinesiseventproperties("FilterCriteria")
171171
FunctionResponseTypes: Optional[PassThroughProp] = kinesiseventproperties("FunctionResponseTypes")
172+
KmsKeyArn: Optional[PassThroughProp] # TODO: add documentation
172173
MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = kinesiseventproperties("MaximumBatchingWindowInSeconds")
173174
MaximumRecordAgeInSeconds: Optional[PassThroughProp] = kinesiseventproperties("MaximumRecordAgeInSeconds")
174175
MaximumRetryAttempts: Optional[PassThroughProp] = kinesiseventproperties("MaximumRetryAttempts")
@@ -191,6 +192,7 @@ class DynamoDBEventProperties(BaseModel):
191192
Enabled: Optional[PassThroughProp] = dynamodbeventproperties("Enabled")
192193
FilterCriteria: Optional[PassThroughProp] = dynamodbeventproperties("FilterCriteria")
193194
FunctionResponseTypes: Optional[PassThroughProp] = dynamodbeventproperties("FunctionResponseTypes")
195+
KmsKeyArn: Optional[PassThroughProp] # TODO: add documentation
194196
MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = dynamodbeventproperties(
195197
"MaximumBatchingWindowInSeconds"
196198
)
@@ -235,6 +237,7 @@ class SQSEventProperties(BaseModel):
235237
Enabled: Optional[PassThroughProp] = sqseventproperties("Enabled")
236238
FilterCriteria: Optional[PassThroughProp] = sqseventproperties("FilterCriteria")
237239
FunctionResponseTypes: Optional[PassThroughProp] = sqseventproperties("FunctionResponseTypes")
240+
KmsKeyArn: Optional[PassThroughProp] # TODO: add documentation
238241
MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = sqseventproperties("MaximumBatchingWindowInSeconds")
239242
Queue: PassThroughProp = sqseventproperties("Queue")
240243
ScalingConfig: Optional[PassThroughProp] # Update docs when live
@@ -406,6 +409,7 @@ class HttpApiEvent(BaseModel):
406409
class MSKEventProperties(BaseModel):
407410
ConsumerGroupId: Optional[PassThroughProp] = mskeventproperties("ConsumerGroupId")
408411
FilterCriteria: Optional[PassThroughProp] = mskeventproperties("FilterCriteria")
412+
KmsKeyArn: Optional[PassThroughProp] # TODO: add documentation
409413
MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = mskeventproperties("MaximumBatchingWindowInSeconds")
410414
StartingPosition: Optional[PassThroughProp] = mskeventproperties("StartingPosition")
411415
StartingPositionTimestamp: Optional[PassThroughProp] = mskeventproperties("StartingPositionTimestamp")
@@ -426,6 +430,7 @@ class MQEventProperties(BaseModel):
426430
DynamicPolicyName: Optional[bool] = mqeventproperties("DynamicPolicyName")
427431
Enabled: Optional[PassThroughProp] = mqeventproperties("Enabled")
428432
FilterCriteria: Optional[PassThroughProp] = mqeventproperties("FilterCriteria")
433+
KmsKeyArn: Optional[PassThroughProp] # TODO: add documentation
429434
MaximumBatchingWindowInSeconds: Optional[PassThroughProp] = mqeventproperties("MaximumBatchingWindowInSeconds")
430435
Queues: PassThroughProp = mqeventproperties("Queues")
431436
SecretsManagerKmsKeyId: Optional[str] = mqeventproperties("SecretsManagerKmsKeyId")
@@ -445,6 +450,7 @@ class SelfManagedKafkaEventProperties(BaseModel):
445450
KafkaBootstrapServers: Optional[List[SamIntrinsicable[str]]] = selfmanagedkafkaeventproperties(
446451
"KafkaBootstrapServers"
447452
)
453+
KmsKeyArn: Optional[PassThroughProp] # TODO: add documentation
448454
SourceAccessConfigurations: PassThroughProp = selfmanagedkafkaeventproperties("SourceAccessConfigurations")
449455
StartingPosition: Optional[PassThroughProp] # TODO: add documentation
450456
StartingPositionTimestamp: Optional[PassThroughProp] # TODO: add documentation

samtranslator/model/api/http_api_generator.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,7 @@ def _get_authorizers(
633633
if "OpenIdConnectUrl" in authorizer:
634634
raise InvalidResourceException(
635635
self.logical_id,
636-
"'OpenIdConnectUrl' is no longer a supported property for authorizer '%s'. Please refer to the AWS SAM documentation."
637-
% (authorizer_name),
636+
f"'OpenIdConnectUrl' is no longer a supported property for authorizer '{authorizer_name}'. Please refer to the AWS SAM documentation.",
638637
)
639638
authorizers[authorizer_name] = ApiGatewayV2Authorizer( # type: ignore[no-untyped-call]
640639
api_logical_id=self.logical_id,

samtranslator/model/apigatewayv2.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class ApiGatewayV2HttpApi(Resource):
2525
}
2626

2727
runtime_attrs = {"http_api_id": lambda self: ref(self.logical_id)}
28-
Tags: Optional[PassThrough]
2928

3029
def assign_tags(self, tags: Dict[str, Any]) -> None:
3130
"""Overriding default 'assign_tags' function in Resource class
@@ -34,8 +33,8 @@ def assign_tags(self, tags: Dict[str, Any]) -> None:
3433
:param tags: Tags to be assigned to the resource
3534
3635
"""
37-
if tags is not None and "Tags" in self.property_types:
38-
self.Tags = tags
36+
# Tags are already defined in Body so they do not need to be assigned here
37+
return
3938

4039

4140
class ApiGatewayV2Stage(Resource):

0 commit comments

Comments
 (0)