diff --git a/.github/workflows/alpr-counts-deploy.yml b/.github/workflows/alpr-counts-deploy.yml new file mode 100644 index 0000000..e0f812f --- /dev/null +++ b/.github/workflows/alpr-counts-deploy.yml @@ -0,0 +1,37 @@ +name: ALPR Counts Deploy + +on: + push: + branches: [master] + paths: + - 'serverless/alpr_counts/**' + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-1 + + - name: Login to ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Build and push image + working-directory: serverless/alpr_counts/src + run: | + docker buildx build --platform linux/arm64 \ + -t ${{ steps.login-ecr.outputs.registry }}/alpr_counts-lambda:latest --load . + docker push ${{ steps.login-ecr.outputs.registry }}/alpr_counts-lambda:latest + + - name: Update Lambda function code + run: | + aws lambda update-function-code \ + --function-name alpr_counts \ + --image-uri ${{ steps.login-ecr.outputs.registry }}/alpr_counts-lambda:latest diff --git a/serverless/alpr_counts/README.md b/serverless/alpr_counts/README.md index c8e3d94..7f3f87e 100644 --- a/serverless/alpr_counts/README.md +++ b/serverless/alpr_counts/README.md @@ -3,4 +3,8 @@ Counts total ALPRs reported in the OSM database, hourly. OSM's Overpass API is used to query for counts of all ALPRs and ALPRs in the United States. The counts are stored in a JSON file in an S3 bucket. ## Deploying -This Lambda uses a zip file, so from the `/terraform` directory, you can run `terraform apply -target=module.alpr_counts` after making changes, and it should detect the changes, rebuild the zip, and update the Lambda's function code. +This Lambda runs as a container image. Code changes to `serverless/alpr_counts/**` deploy automatically via GitHub Actions (`.github/workflows/alpr-counts-deploy.yml`) on push to `master` — it builds the image, pushes it to ECR, and updates the Lambda's function code. + +To deploy manually (e.g. for local testing), run `./deploy.sh` with AWS credentials configured. + +Infrastructure changes (IAM, ECR repo, EventBridge schedule, alarms) still go through Terraform: from the `/terraform` directory, run `terraform apply -target=module.alpr_counts`. diff --git a/serverless/alpr_counts/deploy.sh b/serverless/alpr_counts/deploy.sh new file mode 100755 index 0000000..bda051a --- /dev/null +++ b/serverless/alpr_counts/deploy.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +ECR_REPO_URL=912821578123.dkr.ecr.us-east-1.amazonaws.com/alpr_counts-lambda + +set -e + +# check if AWS role is assumed +if ! aws sts get-caller-identity &> /dev/null; then + echo "Error: AWS role is not assumed. Please assume the necessary role and try again." + exit 1 +fi + +cd src + +# login to ECR +aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_REPO_URL + +# build and push Docker image to ECR for ARM64 using legacy format +docker buildx build --platform linux/arm64 -t $ECR_REPO_URL:latest --load . +docker push $ECR_REPO_URL:latest + +# update lambda function +export AWS_PAGER="" +aws lambda update-function-code --function-name alpr_counts --image-uri $ECR_REPO_URL:latest + +echo "Deployed!" diff --git a/serverless/alpr_counts/src/Dockerfile b/serverless/alpr_counts/src/Dockerfile new file mode 100644 index 0000000..5629964 --- /dev/null +++ b/serverless/alpr_counts/src/Dockerfile @@ -0,0 +1,12 @@ +# Use the official AWS Lambda Python 3.13 base image from public ECR +FROM public.ecr.aws/lambda/python:3.13-arm64 + +# Copy function code +COPY alpr_counts.py ${LAMBDA_TASK_ROOT} + +# Install dependencies +COPY requirements.txt . +RUN pip install -r requirements.txt + +# Set the CMD to your handler +CMD ["alpr_counts.lambda_handler"] diff --git a/serverless/alpr_counts/src/alpr_counts.py b/serverless/alpr_counts/src/alpr_counts.py index d4ac7b3..d41143f 100644 --- a/serverless/alpr_counts/src/alpr_counts.py +++ b/serverless/alpr_counts/src/alpr_counts.py @@ -1,4 +1,5 @@ import json +import os import requests import boto3 from concurrent.futures import ThreadPoolExecutor, as_completed @@ -76,8 +77,8 @@ def lambda_handler(event, context): } s3 = boto3.client('s3') - bucket = 'cdn.deflock.me' - key = 'alpr-counts.json' + bucket = os.environ['OUTPUT_BUCKET'] + key = os.environ.get('OUTPUT_KEY', 'alpr-counts.json') s3.put_object( Bucket=bucket, diff --git a/terraform/modules/alpr_counts/main.tf b/terraform/modules/alpr_counts/main.tf index 253497f..ba62ce9 100644 --- a/terraform/modules/alpr_counts/main.tf +++ b/terraform/modules/alpr_counts/main.tf @@ -39,36 +39,24 @@ resource "aws_iam_role_policy_attachment" "lambda_s3_write_attachment" { policy_arn = aws_iam_policy.lambda_s3_write_policy.arn } -resource "null_resource" "pip_install" { - provisioner "local-exec" { - command = <