diff --git a/docker/.env b/docker/.env index 3ce413f..41cf0c3 100644 --- a/docker/.env +++ b/docker/.env @@ -1,25 +1,24 @@ # Activate LocalStack Pro: https://docs.localstack.cloud/getting-started/auth-token/ -LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:-} # required for Pro, not processed via template due to security reasons -LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY:-} +# LOCALSTACK_AUTH_TOKEN=${LOCALSTACK_AUTH_TOKEN:-} # required for Pro, not processed via template due to security reasons +# LOCALSTACK_API_KEY=${LOCALSTACK_API_KEY:-} # LocalStack configuration: https://docs.localstack.cloud/references/configuration/ +FLOCI_SERVICES_LAMBDA_HOT_RELOAD_ENABLED=true ACTIVATE_PRO=false DEBUG=false LS_LOG=info PERSISTENCE=false -AWS_ENDPOINT_URL=http://localhost.localstack.cloud:4566 -LOCALSTACK_HOST=localhost.localstack.cloud:4566 +# AWS_ENDPOINT_URL=http://localhost:4566 +# FLOCI_HOSTNAME=floci AUTO_LOAD_POD= ENFORCE_IAM=false -AWS_REGION=eu-west-2 -AWS_DEFAULT_REGION=eu-west-2 -# LocalStack community edition is sufficient -IMAGE_NAME=localstack/localstack:latest +# AWS_REGION=eu-west-2 +# FLOCI_DEFAULT_REGION=eu-west-2 LAMBDA_TIMEOUT=9999999 # Comment the line below and create a new containerised development environment to disable debugging of Lambda functions. # IMPORTANT - If cloning the remote repository into a container volume, the change must be pushed to a pull request # branch from which a new containerised development environment is created. If a new containerised development environment # is not created, running multiple Lambda functions without remote debug limitations will not be possible. -LAMBDA_DOCKER_FLAGS="-e NODE_OPTIONS=--inspect-brk=0.0.0.0:9229 -p 9229:9229" +# LAMBDA_DOCKER_FLAGS="-e NODE_OPTIONS=--inspect-brk=0.0.0.0:9229 -p 9229:9229" # Let Docker select a matching image based on the host operating system and architecture. LAMBDA_IGNORE_ARCHITECTURE=1 diff --git a/docker/infrastructure.yml b/docker/infrastructure.yml index ff88ba3..08535d8 100644 --- a/docker/infrastructure.yml +++ b/docker/infrastructure.yml @@ -1,7 +1,7 @@ services: - localstack: - container_name: "localstack-main" - image: localstack/localstack:4.14.0 + floci: + container_name: "floci-main" + image: floci-floci:latest ports: - "127.0.0.1:4566:4566" # LocalStack Gateway - "127.0.0.1:4510-4559:4510-4559" # external services port range diff --git a/docker/scripts/load-dummy-data.sh b/docker/scripts/load-dummy-data.sh index da2b4d8..97d0646 100755 --- a/docker/scripts/load-dummy-data.sh +++ b/docker/scripts/load-dummy-data.sh @@ -8,7 +8,7 @@ set -e BASE_GUID="4eb3b7350ab7aa443650fc9351f02940E" BASE_AREA="TESTAREA" DATA_FILE="test/lib/functions/data/nws-alert.xml" -LAMBDA_URL=http://$(awslocal apigateway get-rest-apis | jq -r ".items[0].id").execute-api.localhost.localstack.cloud:4566/local/message +LAMBDA_URL=http://localhost:4566/restapis/$(awslocal apigateway get-rest-apis | jq -r ".items[0].id")/local/_user_request_/message # Calculate tomorrow's date TOMORROW=$(date -u -d "+1 day" +"%Y-%m-%dT%H:%M:%S+00:00") diff --git a/docker/scripts/register-api-gateway.sh b/docker/scripts/register-api-gateway.sh index d8d6c33..b6f422f 100755 --- a/docker/scripts/register-api-gateway.sh +++ b/docker/scripts/register-api-gateway.sh @@ -6,8 +6,9 @@ set -e main() { # Reference - https://docs.localstack.cloud/user-guide/aws/apigateway/ echo "Creating API Gateway" + echo $AWS_ENDPOINT_URL - cap_xml_rest_api_id=$(awslocal apigateway create-rest-api --name "FWS API Gateway" | jq -r '.id') + cap_xml_rest_api_id=$(awslocal apigateway create-rest-api --name "CPX API Gateway" | jq -r '.id') cap_xml_rest_api_root_resource_id=$(awslocal apigateway get-resources --rest-api-id $cap_xml_rest_api_id | jq -r '.items[0].id') lambda_functions_dir="lib/functions" @@ -37,9 +38,13 @@ main() { done - awslocal apigateway create-deployment \ - --rest-api-id $cap_xml_rest_api_id \ - --stage-name local + deployment_id=$(awslocal apigateway create-deployment \ + --rest-api-id $cap_xml_rest_api_id | jq -r '.id') + + awslocal apigateway create-stage \ + --rest-api-id $cap_xml_rest_api_id \ + --stage-name local \ + --deployment-id $deployment_id echo "Created API Gateway deployment" return 0 diff --git a/docker/scripts/register-lambda-functions.sh b/docker/scripts/register-lambda-functions.sh index 3e40b79..f39f578 100755 --- a/docker/scripts/register-lambda-functions.sh +++ b/docker/scripts/register-lambda-functions.sh @@ -4,7 +4,7 @@ set -e lambda_functions_dir="lib/functions" -deployed_cpx_agw_url=http://$(awslocal apigateway get-rest-apis | jq -r ".items[0].id").execute-api.localhost.localstack.cloud:4566/local +deployed_cpx_agw_url=http://localhost:4566/restapis/$(awslocal apigateway get-rest-apis | jq -r ".items[0].id")/local/_user_request_ # Prepare a comma separated list of custom environment variables required by # each Lambda function. @@ -24,6 +24,9 @@ node_tls_reject_unauthorized=$(echo NODE_TLS_REJECT_UNAUTHORIZED=$NODE_TLS_REJEC set -- $cpx_db_username $cpx_db_password $cpx_db_name $cpx_db_host $cpx_agw_url $cpx_redis_host $cpx_redis_port $cpx_redis_tls $cpx_meteoalarm_api_url $cpx_meteoalarm_api_username $cpx_meteoalarm_api_password $node_tls_reject_unauthorized $cpx_meteoalarm_disable custom_environment_variables=$(printf '%s,' "$@" | sed 's/,*$//g') +# Create the hot-reload bucket so Floci can serve Lambda code from the local filesystem. +awslocal s3 mb s3://hot-reload 2>/dev/null || true + # Iterate over each file in lambda_functions_dir find "$lambda_functions_dir" -type f -name "*.js" | while read -r lambda_function; do if [ -f "$lambda_function" ]; then @@ -43,7 +46,7 @@ find "$lambda_functions_dir" -type f -name "*.js" | while read -r lambda_functio ;; esac - echo Registering $function_name with LocalStack + echo Registering $function_name with Floci awslocal lambda create-function \ --function-name "$function_name" \ @@ -59,12 +62,12 @@ find "$lambda_functions_dir" -type f -name "*.js" | while read -r lambda_functio fi done -echo "All Lambda functions have been registered with LocalStack." +echo "All Lambda functions have been registered with Floci." awslocal lambda create-function-url-config --function-name archiveMessages --auth-type NONE echo "Created function URL config for archiveMessages function" echo Function URL for archiveMessages is $(awslocal lambda get-function-url-config --function-name archiveMessages | jq -r .FunctionUrl) -echo API Gateway root URL is http://$(awslocal apigateway get-rest-apis | jq -r ".items[0].id").execute-api.localhost.localstack.cloud:4566/local +echo API Gateway base URL is http://localhost:4566/restapis/$(awslocal apigateway get-rest-apis | jq -r ".items[0].id")/local/_user_request_ diff --git a/docker/scripts/test-teardown.sh b/docker/scripts/test-teardown.sh new file mode 100755 index 0000000..130b4a7 --- /dev/null +++ b/docker/scripts/test-teardown.sh @@ -0,0 +1,29 @@ + +export AWS_ENDPOINT_URL=http://localhost:4566 +export AWS_PAGER="" + +# Delete the Lambda function +echo "Deleting Lambda function 'my-function'..." +awslocal lambda delete-function \ + --function-name my-function \ + --endpoint-url $AWS_ENDPOINT_URL 2>/dev/null && echo "Lambda function deleted" || echo "Lambda function not found" + +# Get the API ID (assumes the test API is named "My API") +API_ID=$(awslocal apigateway get-rest-apis \ + --query 'items[?name==`My API`].id' --output text \ + --endpoint-url $AWS_ENDPOINT_URL) + +if [ -z "$API_ID" ]; then + echo "No API found with name 'My API'" + echo "Teardown complete" + exit 0 +fi + +echo "Deleting API: $API_ID" + +# Delete the REST API (removes all resources, methods, integrations, and deployments) +awslocal apigateway delete-rest-api \ + --rest-api-id $API_ID \ + --endpoint-url $AWS_ENDPOINT_URL + +echo "Teardown complete" diff --git a/docker/scripts/test.sh b/docker/scripts/test.sh new file mode 100755 index 0000000..e5dc0b1 --- /dev/null +++ b/docker/scripts/test.sh @@ -0,0 +1,115 @@ +# docker compose -f ../../docker/infrastructure.yml -f ../../docker/networks.yml up floci -d + + +export AWS_ENDPOINT_URL=http://localhost:4566 +export AWS_PAGER="" + +# ============================================================================= +# Lambda function code - update the handler body below to change the response +# ============================================================================= +LAMBDA_CODE=$(cat <<'LAMBDA' +exports.handler = async (event) => { + return { + statusCode: 200, + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ message: "Hello from my-function!", event }), + }; +}; +LAMBDA +) + +# Package and create the Lambda function +LAMBDA_DIR=$(mktemp -d) +echo "$LAMBDA_CODE" > "$LAMBDA_DIR/index.js" +(cd "$LAMBDA_DIR" && zip -q function.zip index.js) + +awslocal lambda create-function \ + --function-name my-function \ + --runtime nodejs20.x \ + --handler index.handler \ + --role arn:aws:iam::000000000000:role/lambda-role \ + --zip-file "fileb://$LAMBDA_DIR/function.zip" \ + --endpoint-url $AWS_ENDPOINT_URL + +rm -rf "$LAMBDA_DIR" +echo "Lambda function 'my-function' created" + +# ============================================================================= +# API Gateway setup +# ============================================================================= + +# Create a REST API +API_ID=$(awslocal apigateway create-rest-api \ + --name "My API" \ + --query id --output text \ + --endpoint-url $AWS_ENDPOINT_URL) +echo "API_ID: $API_ID" + +# Get the root resource +ROOT_ID=$(awslocal apigateway get-resources \ + --rest-api-id $API_ID \ + --query 'items[?path==`/`].id' --output text \ + --endpoint-url $AWS_ENDPOINT_URL) +echo "ROOT_ID: $ROOT_ID" + +# Create a resource +RESOURCE_ID=$(awslocal apigateway create-resource \ + --rest-api-id $API_ID \ + --parent-id $ROOT_ID \ + --path-part users \ + --query id --output text \ + --endpoint-url $AWS_ENDPOINT_URL) +echo "RESOURCE_ID: $RESOURCE_ID" + +# Add a GET method +awslocal apigateway put-method \ + --rest-api-id $API_ID \ + --resource-id $RESOURCE_ID \ + --http-method GET \ + --authorization-type NONE \ + --endpoint-url $AWS_ENDPOINT_URL + +# Add a Lambda integration +awslocal apigateway put-integration \ + --rest-api-id $API_ID \ + --resource-id $RESOURCE_ID \ + --http-method GET \ + --type AWS \ + --integration-http-method POST \ + --uri "arn:aws:apigateway:eu-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-2:000000000000:function:my-function/invocations" \ + --endpoint-url $AWS_ENDPOINT_URL + +# Create a deployment +DEPLOYMENT_ID=$(awslocal apigateway create-deployment \ + --rest-api-id $API_ID \ + --query id --output text \ + --endpoint-url $AWS_ENDPOINT_URL) +echo "DEPLOYMENT_ID: $DEPLOYMENT_ID" + +# Create the stage explicitly +awslocal apigateway create-stage \ + --rest-api-id $API_ID \ + --stage-name dev \ + --deployment-id $DEPLOYMENT_ID \ + --endpoint-url $AWS_ENDPOINT_URL + +echo "" +echo "=== Curl Commands ===" +echo "GET /users:" +echo " curl http://localhost:4566/restapis/$API_ID/dev/_user_request_/users" +echo "" +echo "=== IDs Summary ===" +echo " API_ID: $API_ID" +echo " ROOT_ID: $ROOT_ID" +echo " RESOURCE_ID: $RESOURCE_ID" +echo " Stage: dev" +echo "" + +# Call the deployed API +curl http://localhost:4566/restapis/$API_ID/dev/_user_request_/users + +# awslocal lambda invoke \ +# --function-name my-function \ +# --payload '{}' \ +# --endpoint-url http://localhost:4566 \ +# /dev/stdout diff --git a/package-lock.json b/package-lock.json index e761539..3cd8e5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "cap-xml", - "version": "4.0.1", + "version": "4.1.0", "license": "OGL", "dependencies": { "@aws-sdk/client-sns": "3.1022.0",