fix(local-lambda): accept documented Lambda DurableExecutionArn shape#9040
Conversation
aws-sam-cli-bot
left a comment
There was a problem hiding this comment.
Code Review Results
Reviewed: 3c1153a..05039de
Files: 3
Comments: 1
Comments on lines outside the diff:
[tests/integration/durable_integ_base.py:155] [BUG] The new regex narrows the ARN capture to [a-f0-9-]+, which does not include /. With the new <uuid>/<invocation-id> ARN format that this PR is explicitly enabling, this regex truncates the captured value to just the leading UUID and drops the invocation-id segment.
For example, given output:
ARN: 9a1bc86c-40b9-4688-86b4-d7ecaca41579/2a8bc667-bc0b-4b5e-8c78-26db9c5b4e33
- Old regex
\S+→ captures the full<uuid>/<invocation-id>ARN. - New regex
[a-f0-9-]+→ captures only9a1bc86c-40b9-4688-86b4-d7ecaca41579.
execution_arn is then passed to downstream CLI commands such as sam local execution history <execution_arn> (see tests/integration/local/invoke/test_invoke_durable.py:56,88) and sam local execution stop <execution_arn> (see tests/integration/local/execution/test_execution.py:64). Even if those commands still accept UUID-only ARNs for backwards compatibility (as the PR description states), this change actively prevents the integration tests from exercising the new full-ARN form end-to-end through the Flask routes that this PR just fixed — which defeats the regression-coverage value the integration suite was meant to provide here. If the emulator/SDK ever drops UUID-only backwards compatibility, these tests will silently start using a malformed ARN.
The original \S+ already correctly captured both old and new formats. Suggest reverting this hunk, or if a tighter pattern is desired, allow the embedded /:
arn_match = re.search(r"ARN:\s+([a-f0-9/-]+)", stdout_str)05039de to
1556d11
Compare
The Lambda public API documents DurableExecutionArn as
arn:([a-zA-Z0-9-]+):lambda:([a-zA-Z0-9-]+):(\d{12})
:function:([a-zA-Z0-9_-]+)
:(\$LATEST(?:\.PUBLISHED)?|[0-9]+)
/durable-execution/([a-zA-Z0-9_-]+)/([a-zA-Z0-9_-]+)
(https://docs.aws.amazon.com/lambda/latest/api/API_GetDurableExecution.html)
That shape contains characters that boto's REST-JSON serializer
percent-encodes inside non-greedy URI labels: '/' -> %2F, ':' -> %3A,
'$' -> %24. Werkzeug decodes those back to their literal forms before
route matching, so the default <string> converter (which does not match
'/') cannot match the resulting multi-segment path. Every Get / History
/ Stop and every callback succeed / fail / heartbeat 404'd with
PathNotFoundLocally before reaching the handler when sam-cli was given
the ARN shape the API itself documents.
Switch the six DurableExecutionArn / CallbackId URI labels in
LocalLambdaHttpService from <string> to <path:...>. Werkzeug picks the
right rule via trailing-literal specificity (.../<path:arn>/history
beats .../<path:arn>). The existing unquote() calls in handlers stay --
idempotent on already-decoded values. Backwards-compatible with the
legacy UUID-only shape.
Adds TestDurableArnShapeCompatibility, which drives the real Flask app
via test_client across three ARN shapes (the documented one, the
transitional <uuid>/<invocation-id> currently emitted by the
durable-functions emulator, and the legacy bare UUID) for all six
routes plus the three callback actions, asserting routing reaches the
correct handler with the decoded value.
Verified locally:
- make pr (init, schema, black-check, lint, test-all): green on
Python 3.11.11 (9253 unit tests pass, 25 skipped, 28 subtests pass,
coverage 94.16%)
- tests/integration/local/start_lambda/test_start_lambda_durable.py:
all 18 tests pass (was 17 failures pre-fix), end-to-end through
the public.ecr.aws/durable-functions/aws-durable-execution-emulator
Docker image
Fixes aws#9039
aws-sam-cli-bot
left a comment
There was a problem hiding this comment.
Code Review Results
Reviewed: 3c1153a..1556d11
Files: 3
Comments: 1
Comments on lines outside the diff:
[tests/integration/durable_integ_base.py:155] [BUG] The capture group ([a-f0-9-]+) excludes / (and :, $), so it truncates exactly the characters this PR is introducing into DurableExecutionArn.
Per the PR description, ARNs are now minted as <uuid>/<invocation-id> and will eventually take the documented public shape arn:aws:lambda:<region>:<acct>:function:<name>:$LATEST/durable-execution/<name>/<id>. Against the printed line:
ARN: 9a1bc86c-40b9-4688-86b4-d7ecaca41579/2a8bc667-bc0b-4b5e-8c78-26db9c5b4e33
the new regex captures only 9a1bc86c-40b9-4688-86b4-d7ecaca41579 and silently drops the /<invocation-id> segment. The truncated value is then passed as the ARN argument to sam local execution history / stop via get_execution_history_command_list at line 60, which is the exact path this PR is fixing.
The previous regex \S+ already captured the full ARN correctly across all three shapes (legacy UUID-only, transitional uuid/invocation-id, and the documented Lambda public shape). Either keep \S+ or, if a tighter pattern is desired, broaden to include the characters the new ARN format actually uses, e.g.:
arn_match = re.search(r"ARN:\s+(\S+)", stdout_str)This was raised in the prior automated review and has not been addressed.
1556d11 to
d45a481
Compare
|
the two review bots comments were because initially I based this branch on develop from before PR #9038 squash-merged, so the diff against current origin/develop showed rebasing onto current origin/develop (commit d45a481) drops that spurious revert and leaves only the intended local_lambda_http_service.py route changes plus their unit tests. tldr; bot reviews are stale. |
|
I ran this against previously failing integration tests locally: |
Issue #, if available
Fixes #9039
Description of changes
The Lambda public API documents
DurableExecutionArnas:—
API_GetDurableExecution.That shape contains characters that boto's REST-JSON serializer percent-encodes inside non-greedy URI labels:
/→%2F,:→%3A,$→%24. Werkzeug decodes them back to their literal forms before route matching, so the default<string>converter — which does not match/— cannot match the resulting multi-segment path. EveryGet/History/Stopand every callbacksucceed/fail/heartbeatreturnsPathNotFoundLocallybefore reaching the handler when sam-cli is given the ARN shape the public API itself documents. This is independent of any specific upstream emulator behaviour; it is a direct consequence of how<string>rules interact with the documented ARN.Fix: switch the six
DurableExecutionArn/CallbackIdURI labels inLocalLambdaHttpServicefrom<string>to<path:...>. Werkzeug picks the right rule via trailing-literal specificity (…/<path:arn>/historybeats…/<path:arn>). The existingunquote()calls in handlers stay — they are idempotent on already-decoded values. Backwards-compatible with the legacy UUID-only shape.Description of how you validated changes
TestDurableArnShapeCompatibilitydrives the real Flask app viatest_clientacross three ARN shapes (the documented public-API shape, the transitional<uuid>/<invocation-id>currently emitted by thepublic.ecr.aws/durable-functions/aws-durable-execution-emulatorimage, and the legacy bare UUID) for all six durable-execution and callback routes, asserting routing reaches the correct handler with the decoded value.test_create_service_endpointsmock assertions to match the new<path:...>route strings.make pr(init, schema, black-check, lint, test-all): green locally on Python 3.11.11 — 9253 unit tests pass, 25 skipped, 28 subtests pass, coverage 94.16% ≥ 94% gate.tests/integration/local/start_lambda/test_start_lambda_durable.py: all 18 tests pass (vs 17 failures pre-fix), end-to-end through Docker + the durable-functions emulator image.Checklist
make prpassesmake update-reproducible-reqsif dependencies were changed (n/a)By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.