diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1aaed95..8969232 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,7 @@ jobs: run: | npm install -g @localstack/lstk aws-cdk cdk --version + lstk setup aws --force --non-interactive - name: Install Python uses: actions/setup-python@v4 @@ -59,6 +60,9 @@ jobs: lstk cdk bootstrap aws://000000000000/us-east-1 lstk cdk deploy --require-approval never + - name: Run tests + run: make test + - name: Send a Slack notification if: failure() || github.event_name != 'pull_request' uses: ravsamhq/notify-slack-action@v2 diff --git a/Makefile b/Makefile index c9c4d14..ce57411 100644 --- a/Makefile +++ b/Makefile @@ -26,4 +26,7 @@ deploy: ## Deploy the CDK stack lstk cdk bootstrap lstk cdk deploy --require-approval never -.PHONY: usage install start stop logs deploy +test: ## Call the service and check that code changes are picked up live + ./test.sh + +.PHONY: usage install start stop logs deploy test diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..79c5856 --- /dev/null +++ b/test.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Call the Flask service through the load balancer, then change service/main.py +# on the host and check the running ECS tasks pick up the change through the +# mounted volume, without a redeploy. +set -euo pipefail + +STACK=CdkEcsExample +MAIN=service/main.py +MAX_ATTEMPTS=${MAX_ATTEMPTS:-40} + +LB_DNS=$(lstk aws cloudformation describe-stacks --stack-name "$STACK" \ + --query "Stacks[0].Outputs[?starts_with(OutputKey, 'DemoServiceLoadBalancerDNS')].OutputValue | [0]" --output text) +URL="http://$LB_DNS:4566" +echo "Service URL: $URL" + +# Wait until the tasks are up and serving +response="" +for attempt in $(seq 1 "$MAX_ATTEMPTS"); do + response=$(curl -s -m 5 "$URL/" || true) + [ "$response" = "Hello, LocalStack!" ] && break + echo "Attempt $attempt/$MAX_ATTEMPTS: service not ready yet" + sleep 3 +done +if [ "$response" != "Hello, LocalStack!" ]; then + echo "Test failed: GET / returned '$response'" + exit 1 +fi +echo "PASS: GET / returned '$response'" + +response=$(curl -s -m 5 -X POST "$URL/post" -H 'Content-Type: application/json' -d '{"source": "ci"}') +if [[ "$response" != *"Received data: {'source': 'ci'}"* ]]; then + echo "Test failed: POST /post returned '$response'" + exit 1 +fi +echo "PASS: POST /post returned '$response'" + +restore() { + git checkout -- "$MAIN" +} +trap restore EXIT + +echo "Changing $MAIN on the host" +sed -i.bak 's/Hello, LocalStack!/Hello, code mounting!/' "$MAIN" && rm -f "$MAIN.bak" + +# Both tasks mount the same directory, so every response should change +for attempt in $(seq 1 "$MAX_ATTEMPTS"); do + response=$(curl -s -m 5 "$URL/" || true) + if [ "$response" = "Hello, code mounting!" ]; then + echo "PASS: GET / returned '$response' after the change" + echo "Test passed: the service reloaded the mounted code" + exit 0 + fi + echo "Attempt $attempt/$MAX_ATTEMPTS: still getting '$response'" + sleep 3 +done + +echo "Test failed: the code change was not picked up" +exit 1