fix(lambda): report resolved executed version - #2026
Conversation
|
🎉 Thanks for your first pull request to Floci! Your CI checks need a maintainer to approve them before they run. That is GitHub's standard gate on first-time contributors, not a problem with your PR — so if the checks look like they are doing nothing, that is why. Once a maintainer approves, CI and the compatibility suite start automatically. Nothing is needed from you in the meantime. While you wait, a couple of things that make review faster:
Come join us in Slack — it is the fastest way to reach maintainers if you get stuck, or want feedback on an approach before investing more time in it. |
|
| Filename | Overview |
|---|---|
| src/main/java/io/github/hectorvent/floci/services/lambda/LambdaService.java | After executor invoke, stamps resolved fn version onto InvokeResult for the HTTP header path. |
| src/main/java/io/github/hectorvent/floci/services/lambda/LambdaController.java | Replaces hardcoded $LATEST with result.getExecutedVersion() on X-Amz-Executed-Version. |
| src/main/java/io/github/hectorvent/floci/services/lambda/model/InvokeResult.java | Adds executedVersion field with getter/setter; controller path always sets it via LambdaService. |
| src/test/java/io/github/hectorvent/floci/services/lambda/LambdaVersionIntegrationTest.java | Adds ordered DryRun test that version-qualified invoke returns X-Amz-Executed-Version 1. |
Sequence Diagram
sequenceDiagram
participant Client
participant LambdaController
participant LambdaService
participant Executor as LambdaExecutorService
Client->>LambdaController: POST .../functions/name:1/invocations
LambdaController->>LambdaService: invoke(region, name:1, ...)
LambdaService->>LambdaService: "resolveInvokeTarget → fn.version=1"
LambdaService->>Executor: invoke(fn, payload, type)
Executor-->>LambdaService: InvokeResult
LambdaService->>LambdaService: setExecutedVersion(fn.getVersion())
LambdaService-->>LambdaController: result
LambdaController-->>Client: X-Amz-Executed-Version: 1
Reviews (1): Last reviewed commit: "fix(lambda): report resolved executed ve..." | Re-trigger Greptile
pgermosen
left a comment
There was a problem hiding this comment.
Looks good
(added a new comments both are minors)
Clean, correctly-layered fix. X-Amz-Executed-Version was hardcoded to "$LATEST" at LambdaController.java:253 regardless of what actually ran; the resolved target now flows through InvokeResult instead. Three lines of real logic, in the one layer that knows the answer.
One process note: the checklist leaves ./mvnw test passes locally unticked, with focused runs of LambdaVersionIntegrationTest and LambdaIntegrationTest instead. I ran the full suite against this head, so that box is covered — see above.
| void invokePublishedVersionReturnsExecutedVersion() { | ||
| given() | ||
| .header("X-Amz-Invocation-Type", "DryRun") | ||
| .when() | ||
| .post(BASE_PATH + "/functions/" + FUNCTION_NAME + ":1/invocations") | ||
| .then() | ||
| .statusCode(204) |
There was a problem hiding this comment.
No $LATEST baseline — which is the case that could actually regress.
The header used to be hardcoded "$LATEST", so an unqualified invoke was accidentally correct before and needs to stay correct now. Nothing asserts it, and it's the cheapest possible regression guard — it also covers the qualifier == null branch of resolveInvokeTarget, which the new test doesn't touch:
@Test
@Order(4)
void invokeUnqualifiedReturnsLatest() {
given()
.header("X-Amz-Invocation-Type", "DryRun")
.when()
.post(BASE_PATH + "/functions/" + FUNCTION_NAME + "/invocations")
.then()
.statusCode(204)
.header("X-Amz-Executed-Version", equalTo("$LATEST"));
}| InvokeResult result = executorService.invoke(fn, payload, type); | ||
| result.setExecutedVersion(fn.getVersion()); |
There was a problem hiding this comment.
Verified this is safe, noting it so the reasoning is on record.
Setting the field here rather than in the executor is the right call — LambdaService is the layer that resolved the qualifier, and LambdaExecutorService never sees it. Two things I checked because they'd have been silent failures:
fn.getVersion()can't be null:LambdaFunction.versiondefaults to"$LATEST"(LambdaFunction.java:42) and is only reassigned on publish (:934). That matters more than it looks — JAX-RSheader(name, null)removes the header rather than sending it empty, so a null here would have silently droppedX-Amz-Executed-Versionfrom every response, a regression from the old always-$LATESTbehavior.executorService.invokereturns a non-nullInvokeResulton every path (DryRun 204, Event 202, and eachexecuteSyncbranch), so this can't NPE.
Minor style point, take it or leave it: this is a set-after-construct on an object that otherwise arrives fully built by its constructor. Threading it through as a constructor arg would keep InvokeResult immutable-ish, but that touches five call sites for little gain — I'd leave it.
|
@pgermosen Thanks for the review, much appreciated. |
|
@hectorvent , is there anything else needs to be done in this ? Let me know. |
hectorvent
left a comment
There was a problem hiding this comment.
Thanks @aniketshukla1
|
🎉 This PR is included in version 1.6.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
# [1.6.0](floci-io/floci@1.5.34...1.6.0) (2026-08-06) ### Bug Fixes * **1790:** better populate describe alarm responses ([floci-io#1792](floci-io#1792)) ([fcb22be](floci-io@fcb22be)) * **apigateway:** fall through to less specific resources on method mi… ([floci-io#1630](floci-io#1630)) ([96c7aa6](floci-io@96c7aa6)), closes [floci-io#1547](floci-io#1547) * **apigateway:** import authorizers and security requirements from OpenAPI ([floci-io#1798](floci-io#1798)) ([91e06eb](floci-io@91e06eb)) * **apigateway:** use last duplicate header value ([floci-io#1806](floci-io#1806)) ([6a214e8](floci-io@6a214e8)) * **athena:** report the result CSV object key as OutputLocation ([floci-io#1895](floci-io#1895)) ([418ec1b](floci-io@418ec1b)), closes [floci-io#1752](floci-io#1752) * **cloudformation:** AWS::IAM::ManagedPolicy exposes PolicyArn for Fn::GetAtt ([floci-io#2056](floci-io#2056)) ([f719033](floci-io@f719033)) * **cloudformation:** keep uniqueness suffix when truncating generated resource names ([floci-io#1802](floci-io#1802)) ([48b2139](floci-io@48b2139)), closes [floci-io#1825](floci-io#1825) * **cloudformation:** preserve subnet public IP setting ([floci-io#2031](floci-io#2031)) ([b62d1e7](floci-io@b62d1e7)) * **cloudformation:** provision AWS::ApiGatewayV2::Authorizer and wire Route.AuthorizerId ([floci-io#1760](floci-io#1760)) ([7f51c74](floci-io@7f51c74)), closes [floci-io#1773](floci-io#1773) * **cloudformation:** resolve Fn::GetAtt [Vpc, DefaultSecurityGroup] ([floci-io#1977](floci-io#1977)) ([bdad066](floci-io@bdad066)), closes [floci-io#1976](floci-io#1976) * **cloudformation:** treat delete of an already-removed DynamoDB table or Lambda function as idempotent ([floci-io#1803](floci-io#1803)) ([5cb26ff](floci-io@5cb26ff)) * **codebuild:** stabilize start and retry responses ([floci-io#2052](floci-io#2052)) ([fa96b79](floci-io@fa96b79)) * **cognito:** reject unconfirmed users in SRP auth ([floci-io#2027](floci-io#2027)) ([733c72d](floci-io@733c72d)) * **dockerfile:** ensure docker-java compatibility ([floci-io#2096](floci-io#2096)) ([7ade290](floci-io@7ade290)) * **dynamodb:** resolve nested document paths in ADD and DELETE update actions ([floci-io#1908](floci-io#1908)) ([c052259](floci-io@c052259)) * **ec2:** DescribeKeyPairs returns InvalidKeyPair.NotFound for missing names/ids ([floci-io#1932](floci-io#1932)) ([7d96106](floci-io@7d96106)), closes [floci-io#1911](floci-io#1911) * **ec2:** return AWS-parity MissingParameter for CreateSubnet without VpcId ([floci-io#2094](floci-io#2094)) ([88408d8](floci-io@88408d8)), closes [floci-io#2089](floci-io#2089) * **ecs:** resolve Secrets Manager JSON-key selectors in task definition secrets ([floci-io#2133](floci-io#2133)) ([e3c180b](floci-io@e3c180b)), closes [floci-io#1912](floci-io#1912) * **lambda:** carry the owning account into published version snapshots ([floci-io#2041](floci-io#2041)) ([f4e8bd8](floci-io@f4e8bd8)), closes [floci-io#2040](floci-io#2040) * **lambda:** report resolved executed version ([floci-io#2026](floci-io#2026)) ([2889fbd](floci-io@2889fbd)) * **lambda:** reset volatile DynamoDB Streams ESM checkpoints on restart ([floci-io#2077](floci-io#2077)) ([847c30a](floci-io@847c30a)) * **pipes:** register kafka-clients reflection metadata for native image ([floci-io#2068](floci-io#2068)) ([ab73379](floci-io@ab73379)) * **s3:** accept a percent-encoded bucket/key separator in copy sources ([floci-io#2060](floci-io#2060)) ([a8ed218](floci-io@a8ed218)), closes [floci-io#2038](floci-io#2038) * **s3:** apply CreateBucketConfiguration tags on bucket creation ([floci-io#2115](floci-io#2115)) ([79031a2](floci-io@79031a2)) * **s3:** include EventBridgeConfiguration in GetBucketNotification response ([floci-io#2072](floci-io#2072)) ([b8edd37](floci-io@b8edd37)) * **s3:** recognize virtual-hosted-style requests over HTTP/2 ([floci-io#1954](floci-io#1954)) ([5f7d4d2](floci-io@5f7d4d2)), closes [floci-io#1866](floci-io#1866) * **ssm:** report invalid batch parameter names ([floci-io#2075](floci-io#2075)) ([04873bf](floci-io@04873bf)) * **tls:** reuse the persisted self-signed certificate across restarts ([floci-io#1799](floci-io#1799)) ([a4356f2](floci-io@a4356f2)) ### Features * Add AWSCloudFormationReadOnlyAccess as a managed policy. ([floci-io#2057](floci-io#2057)) ([0142dc4](floci-io@0142dc4)) * **apigateway:** support floci:override-id for v1 and v2 ([floci-io#2045](floci-io#2045)) ([02385da](floci-io@02385da)), closes [floci-io#1593](floci-io#1593) * **bedrock:** add proxy backend for real LLM responses via OpenAI-compatible API ([floci-io#1789](floci-io#1789)) ([875e0f2](floci-io@875e0f2)) * **cloudcontrol:** include EC2 resource tags ([floci-io#1933](floci-io#1933)) ([205a8f1](floci-io@205a8f1)) * **cloudformation:** provision AWS::EC2::LaunchTemplate ([floci-io#1973](floci-io#1973)) ([4e0a815](floci-io@4e0a815)), closes [floci-io#1971](floci-io#1971) * **cloudformation:** provision AWS::EC2::VPCGatewayAttachment ([floci-io#1972](floci-io#1972)) ([1a99721](floci-io@1a99721)), closes [floci-io#1970](floci-io#1970) * **cloudformation:** provision AWS::SecretsManager::SecretTargetAttachment ([floci-io#1804](floci-io#1804)) ([62a576c](floci-io@62a576c)) * **cloudformation:** support AWS::Events::EventBus and EventBusPolicy ([floci-io#1794](floci-io#1794)) ([76f602f](floci-io@76f602f)) * **cognito:** implement ResendConfirmationCode ([floci-io#2071](floci-io#2071)) ([bb7c45c](floci-io@bb7c45c)), closes [floci-io#2067](floci-io#2067) * **ec2:** added attach and detach volume support ([floci-io#1787](floci-io#1787)) ([2c74329](floci-io@2c74329)) * **ec2:** return an empty set for DescribeVpnGateways ([floci-io#1975](floci-io#1975)) ([3baf4f4](floci-io@3baf4f4)), closes [floci-io#1974](floci-io#1974) * **iam:** list entities attached to managed policies ([floci-io#1808](floci-io#1808)) ([78ba5b3](floci-io@78ba5b3)) * **iam:** seed Amazon Bedrock managed policies ([floci-io#2034](floci-io#2034)) ([5874348](floci-io@5874348)), closes [floci-io#1559](floci-io#1559) [floci-io#1216](floci-io#1216) * **iam:** seed the managed policies CDK bootstrap and the scenario stacks attach ([floci-io#2064](floci-io#2064)) ([43aea09](floci-io@43aea09)), closes [floci-io#2059](floci-io#2059) [floci-io#2057](floci-io#2057) [floci-io#2057](floci-io#2057) * **kms:** implement ListKeyPolicies ([floci-io#2046](floci-io#2046)) ([760115d](floci-io@760115d)), closes [floci-io#1528](floci-io#1528) * **lambda:** implement the Lambda Extensions API ([floci-io#1773](floci-io#1773)) ([9f0dec9](floci-io@9f0dec9)) * **lambda:** support extra /etc/hosts entries on Lambda launch ([floci-io#2073](floci-io#2073)) ([b543aa4](floci-io@b543aa4)) * **mwaa:** add Amazon MWAA emulation backed by real Airflow (LocalExecutor) ([floci-io#2086](floci-io#2086)) ([7e4e3e8](floci-io@7e4e3e8)) * **release:** one-button release cut from main and ECR Public versioned publishing ([floci-io#2127](floci-io#2127)) ([61011c0](floci-io@61011c0)) * **rum:** add CloudWatch RUM app-monitor service ([floci-io#1797](floci-io#1797)) ([aadc93e](floci-io@aadc93e)) * **sqs:** retain MessageGroupId on standard queue messages ([floci-io#1891](floci-io#1891)) ([a01b707](floci-io@a01b707)), closes [floci-io#1496](floci-io#1496) ### Performance Improvements * **docker:** stop duplicating the native binary into a second image layer ([floci-io#2069](floci-io#2069)) ([9e90e5c](floci-io@9e90e5c))
Summary
Closes #1989.
Type of change
AWS Compatibility
Version-qualified and alias-qualified invokes previously reported $LATEST even when another published version ran. The header now reports the resolved version, including aliases that route to a version.
Checklist
Focused verification: ./mvnw -q -Dtest=LambdaVersionIntegrationTest test and ./mvnw -q -Dtest=LambdaIntegrationTest test (JDK 25).