mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-30 13:10:47 +00:00
deploy
This commit is contained in:
parent
c52cd37a74
commit
5db4d2cfbe
8 changed files with 124 additions and 93 deletions
27
.github/workflows/_build_image.yml
vendored
27
.github/workflows/_build_image.yml
vendored
|
|
@ -21,16 +21,20 @@ on:
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
image_digest:
|
image_digest:
|
||||||
description: "Pushed image digest"
|
description: "Pushed image digest (sha256:...)"
|
||||||
value: ${{ jobs.build.outputs.image_digest }}
|
value: ${{ jobs.build.outputs.image_digest }}
|
||||||
|
|
||||||
|
ecr_repo_url:
|
||||||
|
description: "ECR repository URL (no tag, no digest)"
|
||||||
|
value: ${{ jobs.build.outputs.ecr_repo_url }}
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
AWS_ACCESS_KEY_ID:
|
AWS_ACCESS_KEY_ID:
|
||||||
required: true
|
required: true
|
||||||
AWS_SECRET_ACCESS_KEY:
|
AWS_SECRET_ACCESS_KEY:
|
||||||
required: true
|
required: true
|
||||||
AWS_REGION:
|
AWS_REGION:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
@ -38,6 +42,7 @@ jobs:
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
image_digest: ${{ steps.digest.outputs.image_digest }}
|
image_digest: ${{ steps.digest.outputs.image_digest }}
|
||||||
|
ecr_repo_url: ${{ steps.repo.outputs.ecr_repo_url }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
@ -49,16 +54,20 @@ jobs:
|
||||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
aws-region: ${{ secrets.AWS_REGION }}
|
aws-region: ${{ secrets.AWS_REGION }}
|
||||||
|
|
||||||
- uses: aws-actions/amazon-ecr-login@v2
|
- name: Login to ECR
|
||||||
|
uses: aws-actions/amazon-ecr-login@v2
|
||||||
|
|
||||||
|
- name: Resolve ECR repo URL
|
||||||
|
id: repo
|
||||||
|
run: |
|
||||||
|
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
|
||||||
|
ECR_REPO_URL="${AWS_ACCOUNT_ID}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/${{ inputs.ecr_repo }}"
|
||||||
|
echo "ecr_repo_url=$ECR_REPO_URL" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Build & push image
|
- name: Build & push image
|
||||||
run: |
|
run: |
|
||||||
IMAGE_TAG=${GITHUB_SHA}
|
IMAGE_TAG=${GITHUB_SHA}
|
||||||
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
|
IMAGE_URI="${{ steps.repo.outputs.ecr_repo_url }}:${IMAGE_TAG}"
|
||||||
|
|
||||||
IMAGE_URI=${AWS_ACCOUNT_ID}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/${{ inputs.ecr_repo }}:${IMAGE_TAG}
|
|
||||||
|
|
||||||
echo "Using IMAGE_URI=$IMAGE_URI"
|
|
||||||
|
|
||||||
docker build \
|
docker build \
|
||||||
-f ${{ inputs.dockerfile_path }} \
|
-f ${{ inputs.dockerfile_path }} \
|
||||||
|
|
@ -76,4 +85,4 @@ jobs:
|
||||||
--query 'imageDetails[0].imageDigest' \
|
--query 'imageDetails[0].imageDigest' \
|
||||||
--output text)
|
--output text)
|
||||||
|
|
||||||
echo "image_digest=$DIGEST" >> $GITHUB_OUTPUT
|
echo "image_digest=$DIGEST" >> "$GITHUB_OUTPUT"
|
||||||
|
|
|
||||||
20
.github/workflows/_deploy_lambda.yml
vendored
20
.github/workflows/_deploy_lambda.yml
vendored
|
|
@ -6,13 +6,17 @@ on:
|
||||||
lambda_name:
|
lambda_name:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
lambda_path:
|
lambda_path:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
stage:
|
stage:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
image_digest:
|
|
||||||
|
image_uri:
|
||||||
|
description: "Full ECR image URI including digest"
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
|
@ -22,8 +26,7 @@ on:
|
||||||
AWS_SECRET_ACCESS_KEY:
|
AWS_SECRET_ACCESS_KEY:
|
||||||
required: true
|
required: true
|
||||||
AWS_REGION:
|
AWS_REGION:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
|
|
@ -55,14 +58,11 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
terraform plan \
|
terraform plan \
|
||||||
-var="stage=${{ inputs.stage }}" \
|
-var="stage=${{ inputs.stage }}" \
|
||||||
-var="image_digest=${{ inputs.image_digest }}" \
|
-var="lambda_name=${{ inputs.lambda_name }}" \
|
||||||
|
-var="image_uri=${{ inputs.image_uri }}" \
|
||||||
-out=lambdaplan
|
-out=lambdaplan
|
||||||
|
|
||||||
|
# Uncomment when ready
|
||||||
# - name: Terraform Apply
|
# - name: Terraform Apply
|
||||||
# working-directory: ${{ inputs.lambda_path }}
|
# working-directory: ${{ inputs.lambda_path }}
|
||||||
# run: |
|
# run: terraform apply -auto-approve lambdaplan
|
||||||
# terraform apply \
|
|
||||||
# -auto-approve \
|
|
||||||
# -var="stage=${{ inputs.stage }}" \
|
|
||||||
# -var="image_digest=${{ inputs.image_digest }}" \
|
|
||||||
# lambdaplan
|
|
||||||
|
|
|
||||||
38
.github/workflows/deploy_terraform.yml
vendored
38
.github/workflows/deploy_terraform.yml
vendored
|
|
@ -5,7 +5,6 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- "**"
|
- "**"
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine_stage:
|
determine_stage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -27,9 +26,8 @@ jobs:
|
||||||
echo "stage=dev" >> "$GITHUB_OUTPUT"
|
echo "stage=dev" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Resolved STAGE=$BRANCH → $(cat $GITHUB_OUTPUT)"
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 1️⃣ Shared Terraform (plan only for now)
|
# 1️⃣ Shared Terraform (infra)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
shared_terraform:
|
shared_terraform:
|
||||||
needs: determine_stage
|
needs: determine_stage
|
||||||
|
|
@ -38,39 +36,35 @@ jobs:
|
||||||
STAGE: ${{ needs.determine_stage.outputs.stage }}
|
STAGE: ${{ needs.determine_stage.outputs.stage }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Configure AWS credentials
|
- uses: aws-actions/configure-aws-credentials@v4
|
||||||
uses: aws-actions/configure-aws-credentials@v4
|
|
||||||
with:
|
with:
|
||||||
# This will need to be changed to env imports when we have different env to dynamically allocate prod, staging etc
|
|
||||||
aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
|
aws-access-key-id: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
|
||||||
aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
|
aws-secret-access-key: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
|
||||||
aws-region: ${{ secrets.DEV_AWS_REGION }}
|
aws-region: ${{ secrets.DEV_AWS_REGION }}
|
||||||
|
|
||||||
- name: Setup Terraform
|
- uses: hashicorp/setup-terraform@v3
|
||||||
uses: hashicorp/setup-terraform@v3
|
|
||||||
|
|
||||||
- name: Terraform Init (shared)
|
- name: Terraform Init
|
||||||
working-directory: infrastructure/terraform/shared
|
working-directory: infrastructure/terraform/shared
|
||||||
run: terraform init -reconfigure
|
run: terraform init -reconfigure
|
||||||
|
|
||||||
- name: Terraform Workspace (shared)
|
- name: Terraform Workspace
|
||||||
working-directory: infrastructure/terraform/shared
|
working-directory: infrastructure/terraform/shared
|
||||||
run: terraform workspace select ${STAGE} || terraform workspace new ${STAGE}
|
run: terraform workspace select ${STAGE} || terraform workspace new ${STAGE}
|
||||||
|
|
||||||
- name: Terraform Plan (shared)
|
- name: Terraform Plan
|
||||||
working-directory: infrastructure/terraform/shared
|
working-directory: infrastructure/terraform/shared
|
||||||
run: terraform plan -var-file=${STAGE}.tfvars -out=tfplan
|
run: terraform plan -var-file=${STAGE}.tfvars -out=tfplan
|
||||||
|
|
||||||
- name: Terraform Apply (shared)
|
- name: Terraform Apply
|
||||||
if: env.STAGE == 'prod'
|
if: env.STAGE == 'prod'
|
||||||
working-directory: infrastructure/terraform/shared
|
working-directory: infrastructure/terraform/shared
|
||||||
run: terraform apply -auto-approve -var-file=${STAGE}.tfvars tfplan
|
run: terraform apply -auto-approve tfplan
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 2️⃣ Build Docker image (tag = GitHub SHA, digest resolved)
|
# 2️⃣ Build image
|
||||||
# ============================================================
|
# ============================================================
|
||||||
address2uprn_image:
|
address2uprn_image:
|
||||||
needs: [determine_stage, shared_terraform]
|
needs: [determine_stage, shared_terraform]
|
||||||
|
|
@ -84,17 +78,17 @@ jobs:
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_REGION: ${{ secrets.DEV_AWS_REGION }}
|
AWS_REGION: ${{ secrets.DEV_AWS_REGION }}
|
||||||
|
|
||||||
# # ============================================================
|
# ============================================================
|
||||||
# # 3️⃣ Deploy Lambda (Terraform, immutable digest)
|
# 3️⃣ Deploy Lambda
|
||||||
# # ============================================================
|
# ============================================================
|
||||||
address2uprn_lambda:
|
address2uprn_lambda:
|
||||||
needs: [address2uprn_image, determine_stage]
|
needs: [address2uprn_image, determine_stage]
|
||||||
uses: ./.github/workflows/_deploy_lambda.yml
|
uses: ./.github/workflows/_deploy_lambda.yml
|
||||||
with:
|
with:
|
||||||
lambda_name: address2UPRN
|
lambda_name: address2uprn
|
||||||
lambda_path: infrastructure/terraform/lambda/address2UPRN
|
lambda_path: infrastructure/terraform/lambda/address2uprn
|
||||||
stage: ${{ needs.determine_stage.outputs.stage }}
|
stage: ${{ needs.determine_stage.outputs.stage }}
|
||||||
image_digest: ${{ needs.image.outputs.image_digest }}
|
image_uri: ${{ needs.address2uprn_image.outputs.ecr_repo_url }}@${{ needs.address2uprn_image.outputs.image_digest }}
|
||||||
secrets:
|
secrets:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.DEV_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.DEV_AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,68 @@
|
||||||
### Checklist for a new lambda
|
## Checklist for adding a new Lambda
|
||||||
|
|
||||||
- [ ] Copy cp -r lambda/_template lambda/<name>
|
### 1. Create the Lambda scaffold
|
||||||
- [ ] Add ECR repo in shared/main.tf
|
- Copy the template:
|
||||||
- [ ] Set bucket name in provider.tf
|
|
||||||
- [ ] Add shared output for repo name/url
|
|
||||||
- [ ] Push to GitHub (CI will deploy)
|
|
||||||
|
|
||||||
|
cp -r lambda/_template lambda/<lambda_name>
|
||||||
|
|
||||||
Note: By default this does a sqs to lamba. Configure the terraform file for other connections between sqs->lambda
|
- Update `variables.tf` if required
|
||||||
|
- Ensure the Terraform module accepts:
|
||||||
|
- `lambda_name`
|
||||||
|
- `stage`
|
||||||
|
- `image_uri`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Add infrastructure prerequisites (shared stack)
|
||||||
|
- Add a new ECR repository in:
|
||||||
|
|
||||||
|
infrastructure/terraform/shared/main.tf
|
||||||
|
|
||||||
|
- Apply the shared stack (or let CI apply it on `prod`)
|
||||||
|
- Verify the ECR repository exists in AWS
|
||||||
|
|
||||||
|
Note:
|
||||||
|
No Terraform outputs are required for the ECR repo.
|
||||||
|
The CI pipeline resolves the repository URL dynamically at build time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Add Docker build configuration
|
||||||
|
- Create a `Dockerfile` for the Lambda
|
||||||
|
- Verify the Dockerfile path and build context
|
||||||
|
- Add a new image build job in `deploy_terraform.yml` using `_build_image.yml`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Wire the Lambda deploy job (CI)
|
||||||
|
- Add a deploy job using `_deploy_lambda.yml`
|
||||||
|
- Pass the following inputs:
|
||||||
|
- `lambda_name`
|
||||||
|
- `lambda_path`
|
||||||
|
- `stage`
|
||||||
|
- `image_uri` (constructed as `repo@sha256:digest`)
|
||||||
|
- Ensure the deploy job depends on the image build job
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Deploy
|
||||||
|
- Push changes to GitHub
|
||||||
|
- CI will:
|
||||||
|
1. Build and push the Docker image
|
||||||
|
2. Resolve the image digest
|
||||||
|
3. Deploy the Lambda using the immutable `image_uri`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Terraform remote state is not used for image wiring
|
||||||
|
- Image tags are not used; deployments are digest-based
|
||||||
|
- By default, the template wires SQS → Lambda
|
||||||
|
To change this, update the Terraform in `lambda/<lambda_name>`
|
||||||
|
(e.g. EventBridge, API Gateway, direct invoke)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rule of thumb
|
||||||
|
CI decides what image to deploy.
|
||||||
|
Terraform only deploys it.
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,11 @@
|
||||||
data "terraform_remote_state" "shared" {
|
|
||||||
backend = "s3"
|
|
||||||
config = {
|
|
||||||
bucket = "assessment-model-terraform-state"
|
|
||||||
key = "terraform.tfstate"
|
|
||||||
region = "eu-west-2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module "lambda" {
|
module "lambda" {
|
||||||
source = "../modules/lambda_with_sqs"
|
source = "../modules/lambda_with_sqs"
|
||||||
|
|
||||||
name = REPLACE ME #"address2uprn" for example
|
name = REPLACE ME #"address2uprn" for example
|
||||||
stage = var.stage
|
stage = var.stage
|
||||||
|
|
||||||
image_uri = "${data.terraform_remote_state.shared.outputs.REPLACE_ME_repository_url}@${var.image_digest}"
|
image_uri = var.image_uri
|
||||||
|
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
STAGE = var.stage
|
STAGE = var.stage
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
variable "region" {
|
variable "lambda_name" {
|
||||||
type = string
|
type = string
|
||||||
default = "eu-west-2"
|
description = "Logical name of the lambda (e.g. address2uprn)"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "stage" {
|
variable "image_uri" {
|
||||||
type = string
|
type = string
|
||||||
}
|
description = "Full ECR image URI including digest"
|
||||||
|
|
||||||
variable "image_digest" {
|
|
||||||
type = string
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,26 +1,10 @@
|
||||||
############################################
|
|
||||||
# Read shared state to get outputs
|
|
||||||
############################################
|
|
||||||
data "terraform_remote_state" "shared" {
|
|
||||||
backend = "s3"
|
|
||||||
|
|
||||||
config = {
|
|
||||||
bucket = "assessment-model-terraform-state"
|
|
||||||
key = "terraform.tfstate"
|
|
||||||
region = "eu-west-2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
############################################
|
|
||||||
# Address2UPRN Lambda (via reusable module)
|
|
||||||
############################################
|
|
||||||
module "address2uprn" {
|
module "address2uprn" {
|
||||||
source = "../modules/lambda_with_sqs"
|
source = "../modules/lambda_with_sqs"
|
||||||
|
|
||||||
name = "address2uprn"
|
name = "address2uprn"
|
||||||
stage = var.stage
|
stage = var.stage
|
||||||
|
|
||||||
image_uri = "${data.terraform_remote_state.shared.outputs.address2uprn_repository_url}@${var.image_digest}"
|
image_uri = var.image_uri
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
STAGE = var.stage
|
STAGE = var.stage
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
variable "region" {
|
variable "lambda_name" {
|
||||||
type = string
|
type = string
|
||||||
default = "eu-west-2"
|
description = "Logical name of the lambda (e.g. address2uprn)"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "stage" {
|
variable "image_uri" {
|
||||||
type = string
|
type = string
|
||||||
}
|
description = "Full ECR image URI including digest"
|
||||||
|
|
||||||
variable "image_digest" {
|
|
||||||
type = string
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue