-
Notifications
You must be signed in to change notification settings - Fork 18
Description
Expected Behaviour
Deployment of the generated zip should be compatible with localstack development (which allow to run AWS locally)
Actual Behaviour
Current "lambda" handler is not working while running in localstack due to few differences in the event object structure.
It returns a message like "Request with GET/HEAD method cannot have body" for a simple GET request.
Reproduce Scenario (including but not limited to)
a bit complex, you need to use localstack in docker, and setup a few services (api gateway, s3, secresmanager etc.. as it would run in a real AWS)
version: '3.1'
services:
localstack:
image: localstack/localstack:latest
environment:
- AWS_DEFAULT_REGION=us-east-1
- SERVICES=s3,apigateway,sqs,lambda,secretsmanager
ports:
- '4566:4566'
- '4567:4567'
volumes:
- "${TEMPDIR:-/tmp/localstack}:/tmp/localstack"
- ./test/aws:/etc/localstack/init/ready.d
- ./dist:/dist
- "/var/run/docker.sock:/var/run/docker.sock"
define a bootstrap script in test/aws/apigateway.sh (you will also need some for s3 and secretsmanager) but that one is the most important
#!/usr/bin/env bash
API_NAME=myapp
REGION=us-east-1
STAGE=dev
function fail() {
echo $2
exit $1
}
awslocal lambda create-function \
--region ${REGION} \
--function-name ${API_NAME} \
--runtime nodejs16.x \
--handler index.lambda \
--memory-size 1024 \
--zip-file fileb:///dist/helix-services/yourservice@x.y.z.zip \
--role arn:aws:iam::123456123456:role/irrelevant
[ $? == 0 ] || fail 1 "Failed: AWS / lambda / create-function"
LAMBDA_ARN=$(awslocal lambda list-functions --query "Functions[?FunctionName==\`${API_NAME}\`].FunctionArn" --output text --region ${REGION})
echo LAMBDA_ARN=${LAMBDA_ARN}
awslocal apigateway create-rest-api \
--region ${REGION} \
--name ${API_NAME}
[ $? == 0 ] || fail 2 "Failed: AWS / apigateway / create-rest-api"
API_ID=$(awslocal apigateway get-rest-apis --query "items[?name==\`${API_NAME}\`].id" --output text --region ${REGION})
PARENT_RESOURCE_ID=$(awslocal apigateway get-resources --rest-api-id ${API_ID} --query 'items[?path==`/`].id' --output text --region ${REGION})
awslocal apigateway create-resource \
--region ${REGION} \
--rest-api-id ${API_ID} \
--parent-id ${PARENT_RESOURCE_ID} \
--path-part "endpoint"
[ $? == 0 ] || fail 2 "Failed: AWS / apigateway / create-resource /endpoint"
RESOURCE_ID=$(awslocal apigateway get-resources --rest-api-id ${API_ID} --query 'items[?path==`/endpoint`].id' --output text --region ${REGION})
awslocal apigateway put-method \
--region ${REGION} \
--rest-api-id ${API_ID} \
--resource-id ${RESOURCE_ID} \
--http-method ANY \
--authorization-type "NONE"
[ $? == 0 ] || fail 4 "Failed: AWS / apigateway / put-method"
awslocal apigateway put-integration \
--region ${REGION} \
--rest-api-id ${API_ID} \
--resource-id ${RESOURCE_ID} \
--http-method ANY \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:${REGION}:lambda:path/2015-03-31/functions/${LAMBDA_ARN}/invocations \
--passthrough-behavior WHEN_NO_MATCH \
[ $? == 0 ] || fail 5 "Failed: AWS / apigateway / put-integration"
awslocal apigateway create-deployment \
--region ${REGION} \
--rest-api-id ${API_ID} \
--stage-name ${STAGE} \
[ $? == 0 ] || fail 6 "Failed: AWS / apigateway / create-deployment"
ENDPOINT=http://localhost:4566/restapis/${API_ID}/${STAGE}/_user_request_/endpoint
echo "API available at: ${ENDPOINT}"
Steps to Reproduce
Once you setup to make the generated zip (dist/helix-services/yourservice@x.y.z.zip) as the lambda function to be used by the apigateway endpoint of your choice. Call the service url .i.e http://localhost:4566/restapis/hrbmpo1ifh/dev/_user_request_/endpoint, it will returns a 500 with "Request with GET/HEAD method cannot have body"
Platform and Version
AWS via Localstack
Sample Code that illustrates the problem
Logs taken while reproducing problem
The main reason seems that the main function expect some specific structure for the requestContent, which is not 100% the same in the localstack
main differences is that there is no event.requestContent.http.method, instead it is under event.requestContent.httpMethod
and second main differences is that event.body = "" instead of being null in case of "GET" request.
one fix could be to create a small adapter that fix the event object to be like the default "lambda" adapter expect (mainly copy what is needed under event.requestContext.http and fix the body value to be null in get of "GET|HEAD" request.