8000 ESM v2: Add EventSourceMappingArn field (#11675) · localstack/localstack@77d34cd · GitHub
[go: up one dir, main page]

Skip to content

Commit 77d34cd

Browse files
gregfurmanmacnev2013
authored andcommitted
ESM v2: Add EventSourceMappingArn field (#11675)
1 parent df7ab29 commit 77d34cd

18 files changed

+677
-548
lines changed

localstack-core/localstack/services/lambda_/event_source_mapping/esm_config_factory.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,50 @@
55
DestinationConfig,
66
EventSourceMappingConfiguration,
77
EventSourcePosition,
8+
RequestContext,
89
)
910
from localstack.services.lambda_ import hooks as lambda_hooks
1011
from localstack.services.lambda_.event_source_mapping.esm_worker import EsmState, EsmStateReason
1112
from localstack.services.lambda_.event_source_mapping.pipe_utils import (
1213
get_standardized_service_name,
1314
)
14-
from localstack.utils.aws.arns import parse_arn
15+
from localstack.utils.aws.arns import lambda_event_source_mapping_arn, parse_arn
1516
from localstack.utils.collections import merge_recursive
1617
from localstack.utils.strings import long_uid
1718

1819

1920
class EsmConfigFactory:
2021
request: CreateEventSourceMappingRequest
22+
context: RequestContext
2123
function_arn: str
2224

23-
def __init__(self, request: CreateEventSourceMappingRequest, function_arn: str):
25+
def __init__(
26+
self, request: CreateEventSourceMappingRequest, context: RequestContext, function_arn: str
27+
):
2428
self.request = request
2529
self.function_arn = function_arn
30+
self.context = context
2631

2732
def get_esm_config(self) -> EventSourceMappingConfiguration:
2833
"""Creates an Event Source Mapping (ESM) configuration based on a create ESM request.
2934
* CreateEventSourceMapping API: https://docs.aws.amazon.com/lambda/latest/api/API_CreateEventSourceMapping.html
3035
* CreatePipe API: https://docs.aws.amazon.com/eventbridge/latest/pipes-reference/API_CreatePipe.html
3136
The CreatePipe API covers largely the same parameters, but is better structured using hierarchical parameters.
3237
"""
33-
3438
service = ""
3539
if source_arn := self.request.get("EventSourceArn"):
3640
parsed_arn = parse_arn(source_arn)
3741
service = get_standardized_service_name(parsed_arn["service"])
3842

43+
uuid = long_uid()
44+
3945
default_source_parameters = {}
40-
default_source_parameters["UUID"] = long_uid()
46+
default_source_parameters["UUID"] = uuid
47+
default_source_parameters["EventSourceMappingArn"] = lambda_event_source_mapping_arn(
48+
uuid, self.context.account_id, self.context.region
49+
)
4150
default_source_parameters["StateTransitionReason"] = EsmStateReason.USER_ACTION
51+
4252
if service == "sqs":
4353
default_source_parameters["BatchSize"] = 10
4454
default_source_parameters["MaximumBatchingWindowInSeconds"] = 0

localstack-core/localstack/services/lambda_/provider.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,10 @@
220220
from localstack.services.lambda_.urlrouter import FunctionUrlRouter
221221
from localstack.services.plugins import ServiceLifecycleHook
222222
from localstack.state import StateVisitor
223-
from localstack.utils.aws.arns import extract_service_from_arn, get_partition
223+
from localstack.utils.aws.arns import (
224+
extract_service_from_arn,
225+
get_partition,
226+
)
224227
from localstack.utils.bootstrap import is_api_enabled
225228
from localstack.utils.collections import PaginatedList
226229
from localstack.utils.files import load_file
@@ -1859,7 +1862,7 @@ def create_event_source_mapping_v2(
18591862
# Validations
18601863
function_arn, function_name, state = self.validate_event_source_mapping(context, request)
18611864

1862-
esm_config = EsmConfigFactory(request, function_arn).get_esm_config()
1865+
esm_config = EsmConfigFactory(request, context, function_arn).get_esm_config()
18631866

18641867
# Copy esm_config to avoid a race condition with potential async update in the store
18651868
state.event_source_mappings[esm_config["UUID"]] = esm_config.copy()

localstack-core/localstack/utils/aws/arns.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,11 @@ def lambda_code_signing_arn(code_signing_id: str, account_id: str, region_name:
267267
return _resource_arn(code_signing_id, pattern, account_id=account_id, region_name=region_name)
268268

269269

270+
def lambda_event_source_mapping_arn(uuid: str, account_id: str, region_name: str) -> str:
271+
pattern = "arn:%s:lambda:%s:%s:event-source-mapping:%s"
272+
return _resource_arn(uuid, pattern, account_id=account_id, region_name=region_name)
273+
274+
270275
def lambda_function_or_layer_arn(
271276
type: str,
272277
entity_name: str,

tests/aws/services/cloudformation/resources/test_lambda.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ def _assert_single_lambda_call():
5858

5959

6060
@markers.snapshot.skip_snapshot_verify(
61-
["$..EventSourceMappings..FunctionArn", "$..EventSourceMappings..LastProcessingResult"]
61+
[
62+
"$..EventSourceMappings..FunctionArn",
63+
"$..EventSourceMappings..LastProcessingResult",
64+
]
6265
)
6366
@markers.aws.validated
6467
def test_lambda_w_dynamodb_event_filter_update(deploy_cfn_template, snapshot, aws_client):

tests/aws/services/cloudformation/resources/test_lambda.snapshot.json

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -825,7 +825,7 @@
825825
}
826826
},
827827
"tests/aws/services/cloudformation/resources/test_lambda.py::TestCfnLambdaIntegrations::test_cfn_lambda_dynamodb_source": {
828-
"recorded-date": "09-04-2024, 07:34:20",
828+
"recorded-date": "12-10-2024, 10:46:17",
829829
"recorded-content": {
830830
"stack_resources": {
831831
"StackResources": [
@@ -846,7 +846,7 @@
846846
"StackResourceDriftStatus": "NOT_CHECKED"
847847
},
848848
"LogicalResourceId": "fnDynamoDBEventSourceLambdaDynamodbSourceStacktable153BBA79064FDF1D",
849-
"PhysicalResourceId": "<uuid:1>",
849+
"PhysicalResourceId": "<resource:6>",
850850
"ResourceStatus": "CREATE_COMPLETE",
851851
"ResourceType": "AWS::Lambda::EventSourceMapping",
852852
"StackId": "arn:<partition>:cloudformation:<region>:111111111111:stack/<stack-name:1>/<resource:1>",
@@ -882,7 +882,7 @@
882882
"StackResourceDriftStatus": "NOT_CHECKED"
883883
},
884884
"LogicalResourceId": "table8235A42E",
885-
"PhysicalResourceId": "<resource:6>",
885+
"PhysicalResourceId": "<resource:7>",
886886
"ResourceStatus": "CREATE_COMPLETE",
887887
"ResourceType": "AWS::DynamoDB::Table",
888888
"StackId": "arn:<partition>:cloudformation:<region>:111111111111:stack/<stack-name:1>/<resource:1>",
@@ -912,7 +912,7 @@
912912
"dynamodb:GetShardIterator"
913913
],
914914
"Effect": "Allow",
915-
"Resource": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:6>/stream/<resource:2>"
915+
"Resource": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:7>/stream/<resource:2>"
916916
}
917917
],
918918
"Version": "2012-10-17"
@@ -949,7 +949,7 @@
949949
},
950950
"MemorySize": 128,
951951
"PackageType": "Zip",
952-
"RevisionId": "<uuid:2>",
952+
"RevisionId": "<uuid:1>",
953953
"Role": "arn:<partition>:iam::111111111111:role/<resource:4>",
954954
"Runtime": "python3.9",
955955
"RuntimeVersionConfig": {
@@ -982,7 +982,8 @@
982982
"DestinationConfig": {
983983
"OnFailure": {}
984984
},
985-
"EventSourceArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:6>/stream/<resource:2>",
985+
"EventSourceArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:7>/stream/<resource:2>",
986+
"EventSourceMappingArn": "arn:<partition>:lambda:<region>:111111111111:event-source-mapping:<resource:6>",
986987
"FunctionArn": "arn:<partition>:lambda:<region>:111111111111:function:<resource:3>",
987988
"FunctionResponseTypes": [],
988989
"LastModified": "datetime",
@@ -995,7 +996,7 @@
995996
"State": "Enabled",
996997
"StateTransitionReason": "User action",
997998
"TumblingWindowInSeconds": 0,
998-
B41A "UUID": "<uuid:1>",
999+
"UUID": "<resource:6>",
9991000
"ResponseMetadata": {
10001001
"HTTPHeaders": {},
10011002
"HTTPStatusCode": 200
@@ -1018,7 +1019,7 @@
10181019
"KeyType": "HASH"
10191020
}
10201021
],
1021-
"LatestStreamArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:6>/stream/<resource:2>",
1022+
"LatestStreamArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:7>/stream/<resource:2>",
10221023
"LatestStreamLabel": "<resource:2>",
10231024
"ProvisionedThroughput": {
10241025
"NumberOfDecreasesToday": 0,
@@ -1029,9 +1030,9 @@
10291030
"StreamEnabled": true,
10301031
"StreamViewType": "NEW_AND_OLD_IMAGES"
10311032
},
1032-
"TableArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:6>",
1033-
"TableId": "<uuid:3>",
1034-
"TableName": "<resource:6>",
1033+
"TableArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:7>",
1034+
"TableId": "<uuid:2>",
1035+
"TableName": "<resource:7>",
10351036
"TableSizeBytes": 0,
10361037
"TableStatus": "ACTIVE"
10371038
},
@@ -1057,11 +1058,11 @@
10571058
"ShardId": "shard-id"
10581059
}
10591060
],
1060-
"StreamArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:6>/stream/<resource:2>",
1061+
"StreamArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<resource:7>/stream/<resource:2>",
10611062
"StreamLabel": "<resource:2>",
10621063
"StreamStatus": "ENABLED",
10631064
"StreamViewType": "NEW_AND_OLD_IMAGES",
1064-
"TableName": "<resource:6>"
1065+
"TableName": "<resource:7>"
10651066
},
10661067
"ResponseMetadata": {
10671068
"HTTPHeaders": {},
@@ -1122,7 +1123,7 @@
11221123
}
11231124
},
11241125
"tests/aws/services/cloudformation/resources/test_lambda.py::TestCfnLambdaIntegrations::test_cfn_lambda_kinesis_source": {
1125-
"recorded-date": "09-04-2024, 07:37:55",
1126+
"recorded-date": "12-10-2024, 10:52:28",
11261127
"recorded-content": {
11271128
"stack_resources": {
11281129
"StackResources": [
@@ -1143,7 +1144,7 @@
11431144
"StackResourceDriftStatus": "NOT_CHECKED"
11441145
},
11451146
"LogicalResourceId": "fnKinesisEventSourceLambdaKinesisSourceStackstream996A3395ED86A30E",
1146-
"PhysicalResourceId": "<uuid:1>",
1147+
"PhysicalResourceId": "<resource:6>",
11471148
"ResourceStatus": "CREATE_COMPLETE",
11481149
"ResourceType": "AWS::Lambda::EventSourceMapping",
11491150
"StackId": "arn:<partition>:cloudformation:<region>:111111111111:stack/<stack-name:1>/<resource:1>",
@@ -1250,7 +1251,7 @@
12501251
},
12511252
"MemorySize": 128,
12521253
"PackageType": "Zip",
1253-
"RevisionId": "<uuid:2>",
1254+
"RevisionId": "<uuid:1>",
12541255
"Role": "arn:<partition>:iam::111111111111:role/<resource:4>",
12551256
"Runtime": "python3.9",
12561257
"RuntimeVersionConfig": {
@@ -1284,6 +1285,7 @@
12841285
"OnFailure": {}
12851286
},
12861287
"EventSourceArn": "arn:<partition>:kinesis:<region>:111111111111:stream/<resource:2>",
1288+
"EventSourceMappingArn": "arn:<partition>:lambda:<region>:111111111111:event-source-mapping:<resource:6>",
12871289
"FunctionArn": "arn:<partition>:lambda:<region>:111111111111:function:<resource:3>",
12881290
"FunctionResponseTypes": [],
12891291
"LastModified": "datetime",
@@ -1296,7 +1298,7 @@
12961298
"State": "Enabled",
12971299
"StateTransitionReason": "User action",
12981300
"TumblingWindowInSeconds": 0,
1299-
"UUID": "<uuid:1>",
1301+
"UUID": "<resource:6>",
13001302
"ResponseMetadata": {
13011303
"HTTPHeaders": {},
13021304
"HTTPStatusCode": 200
@@ -1446,7 +1448,7 @@
14461448
}
14471449
},
14481450
"tests/aws/services/cloudformation/resources/test_lambda.py::test_lambda_w_dynamodb_event_filter_update": {
1449-
"recorded-date": "11-04-2024, 18:29:12",
1451+
"recorded-date": "12-10-2024, 10:42:00",
14501452
"recorded-content": {
14511453
"source_mappings": {
14521454
"EventSourceMappings": [
@@ -1457,6 +1459,7 @@
14571459
"OnFailure": {}
14581460
},
14591461
"EventSourceArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<table_name>/stream/<resource:1>",
1462+
"EventSourceMappingArn": "arn:<partition>:lambda:<region>:111111111111:event-source-mapping:<resource:2>",
14601463
"FilterCriteria": {
14611464
"Filters": [
14621465
{
@@ -1468,7 +1471,7 @@
14681471
}
14691472
]
14701473
},
1471-
"FunctionArn": "arn:<partition>:lambda:<region>:111111111111:function:<resource:2>",
1474+
"FunctionArn": "arn:<partition>:lambda:<region>:111111111111:function:<resource:3>",
14721475
"FunctionResponseTypes": [],
14731476
"LastModified": "datetime",
14741477
"LastProcessingResult": "No records processed",
@@ -1480,7 +1483,7 @@
14801483
"State": "Enabled",
14811484
"StateTransitionReason": "User action",
14821485
"TumblingWindowInSeconds": 0,
1483-
"UUID": "<uuid:1>"
1486+
"UUID": "<resource:2>"
14841487
}
14851488
],
14861489
"ResponseMetadata": {
@@ -1497,6 +1500,7 @@
14971500
"OnFailure": {}
14981501
},
14991502
"EventSourceArn": "arn:<partition>:dynamodb:<region>:111111111111:table/<table_name>/stream/<resource:1>",
1503+
"EventSourceMappingArn": "arn:<partition>:lambda:<region>:111111111111:event-source-mapping:<resource:2>",
15001504
"FilterCriteria": {
15011505
"Filters": [
15021506
{
@@ -1508,7 +1512,7 @@
15081512
}
15091513
]
15101514
},
1511-
"FunctionArn": "arn:<partition>:lambda:<region>:111111111111:function:<resource:2>",
1515+
"FunctionArn": "arn:<partition>:lambda:<region>:111111111111:function:<resource:3>",
15121516
"FunctionResponseTypes": [],
15131517
"LastModified": "datetime",
15141518
"LastProcessingResult": "No records processed",
@@ -1520,7 +1524,7 @@
15201524
"State": "Enabled",
15211525
"StateTransitionReason": "User action",
15221526
"TumblingWindowInSeconds": 0,
1523-
"UUID": "<uuid:1>"
1527+
"UUID": "<resource:2>"
15241528
}
15251529
],
15261530
"ResponseMetadata": {

tests/aws/services/cloudformation/resources/test_lambda.validation.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"tests/aws/services/cloudformation/resources/test_lambda.py::TestCfnLambdaIntegrations::test_cfn_lambda_dynamodb_source": {
3-
"last_validated_date": "2024-04-09T07:34:20+00:00"
3+
"last_validated_date": "2024-10-12T10:46:17+00:00"
44
},
55
"tests/aws/services/cloudformation/resources/test_lambda.py::TestCfnLambdaIntegrations::test_cfn_lambda_kinesis_source": {
6-
"last_validated_date": "2024-04-09T07:37:55+00:00"
6+
"last_validated_date": "2024-10-12T10:52:28+00:00"
77
},
88
"tests/aws/services/cloudformation/resources/test_lambda.py::TestCfnLambdaIntegrations::test_cfn_lambda_permissions": {
99
"last_validated_date": "2024-04-09T07:26:03+00:00"
@@ -39,7 +39,7 @@
3939
"last_validated_date": "2024-04-09T07:21:37+00:00"
4040
},
4141
"tests/aws/services/cloudformation/resources/test_lambda.py::test_lambda_w_dynamodb_event_filter_update": {
42-
"last_validated_date": "2024-04-11T18:29:12+00:00"
42+
"last_validated_date": "2024-10-12T10:42:00+00:00"
4343
},
4444
"tests/aws/services/cloudformation/resources/test_lambda.py::test_multiple_lambda_permissions_for_singlefn": {
4545
"last_validated_date": "2024-04-09T07:25:05+00:00"

tests/aws/services/lambda_/event_source_mapping/test_lambda_integration_dynamodbstreams.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ def _get_lambda_logs_event(function_name, expected_num_events, retries=30):
6464
return _get_lambda_logs_event
6565

6666

67+
@markers.snapshot.skip_snapshot_verify(
68+
condition=is_old_esm,
69+
# Only match EventSourceMappingArn field if ESM v2 and above
70+
paths=["$..EventSourceMappingArn"],
71+
)
6772
@markers.snapshot.skip_snapshot_verify(
6873
condition=is_v2_esm,
6974
paths=[
@@ -979,7 +984,6 @@ def test_dynamodb_report_batch_item_failure_scenarios(
979984
):
980985
snapshot.add_transformer(snapshot.transform.key_value("MD5OfBody"))
981986
snapshot.add_transformer(snapshot.transform.key_value("ReceiptHandle"))
982-
snapshot.add_transformer(snapshot.transform.key_value("startSequenceNumber"))
983987

984988
function_name = f"lambda_func-{short_uid()}"
985989
table_name = f"test-table-{short_uid()}"

0 commit comments

Comments
 (0)
0