Skip to content

Commit

Permalink
Handle Concurrent Releases (#2236)
Browse files Browse the repository at this point in the history
## Summary
Fixes #1919

### Time to review: __5 mins__

## Changes proposed

This PR makes two changes:

- adds the is-build-published check from
navapbc/template-infra#611
- moves build and publish to a more sensible place
- adds `build_repository.tf` and adjacent terraform configuration, to
bring us in line with the latest infra template code

## Context for reviewers

I used this commit to test:
https://github.com/HHS/simpler-grants-gov/actions/runs/11059113106/job/30727162490
  • Loading branch information
coilysiren authored Sep 27, 2024
1 parent 9720f2b commit bea90ec
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 31 deletions.
15 changes: 12 additions & 3 deletions .github/workflows/build-and-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,23 @@ jobs:
with:
ref: ${{ inputs.ref }}

- name: Build release
run: make APP_NAME=${{ inputs.app_name }} ENVIRONMENT=${{ inputs.environment }} release-build

- name: Configure AWS credentials
uses: ./.github/actions/configure-aws-credentials
with:
app_name: ${{ inputs.app_name }}
environment: shared

- name: Check if image is already published
id: check-image-published
run: |
is_image_published=$(./bin/is-image-published "${{ inputs.app_name }}" "${{ inputs.ref }}")
echo "Is image published: $is_image_published"
echo "is_image_published=$is_image_published" >> "$GITHUB_OUTPUT"
- name: Build release
if: steps.check-image-published.outputs.is_image_published == 'false'
run: make APP_NAME=${{ inputs.app_name }} release-build

- name: Publish release
if: steps.check-image-published.outputs.is_image_published == 'false'
run: make APP_NAME=${{ inputs.app_name }} release-publish
5 changes: 3 additions & 2 deletions .github/workflows/cd-frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ name: Deploy Frontend
# Need to set a default value for when the workflow is triggered from a git push
# which bypasses the default configuration for inputs
run-name: Deploy ${{ github.ref_name }} to Frontend ${{ inputs.environment || (github.event_name == 'release' && 'prod') || 'nonprod' }}

on:
push:
branches:
- main
- "main"
paths:
- "frontend/**"
release:
Expand Down Expand Up @@ -33,7 +34,7 @@ jobs:
uses: ./.github/workflows/deploy.yml
strategy:
matrix:
envs: ${{ github.event_name == 'release' && fromJSON('["prod"]') || github.ref_name == 'main' && fromJSON('["dev"]') || fromJSON('["dev"]') }} # temporarily removing staging from matrix. See: https://github.com/HHS/simpler-grants-gov/issues/1919
envs: ${{ github.event_name == 'release' && fromJSON('["prod"]') || github.ref_name == 'main' && fromJSON('["dev", "staging"]') || fromJSON('["dev"]') }}
with:
app_name: "frontend"
environment: ${{ matrix.envs }}
8 changes: 0 additions & 8 deletions .github/workflows/database-migrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,9 @@ on:
concurrency: database-migrations-${{ inputs.environment }}

jobs:
build-and-publish:
name: Build
uses: ./.github/workflows/build-and-publish.yml
with:
app_name: ${{ inputs.app_name }}
ref: ${{ github.ref }}
environment: ${{ inputs.environment }}
run-migrations:
name: Run migrations
runs-on: ubuntu-latest
needs: [build-and-publish]

permissions:
contents: read
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,20 @@ on:
concurrency: cd-${{ inputs.environment }}

jobs:
# Don't need to call the build-and-publish workflow since the database-migrations
# workflow already calls it
build-and-publish:
name: Build
uses: ./.github/workflows/build-and-publish.yml
with:
app_name: ${{ inputs.app_name }}
ref: ${{ github.ref }}

database-migrations:
name: Database migrations
uses: ./.github/workflows/database-migrations.yml
with:
app_name: ${{ inputs.app_name }}
environment: ${{ inputs.environment }}

deploy:
name: Deploy
runs-on: ubuntu-latest
Expand Down
4 changes: 4 additions & 0 deletions bin/current-region
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
# Print the current AWS region
set -euo pipefail
echo -n "$(aws configure list | grep region | awk '{print $2}')"
25 changes: 25 additions & 0 deletions bin/is-image-published
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
# Checks if an image tag has already been published to the container repository
# Prints "true" if so, "false" otherwise

set -euox pipefail

app_name="$1"
git_ref="$2"

# Get commit hash
image_tag=$(git rev-parse "${git_ref}")

# Need to init module when running in CD since GitHub actions does a fresh checkout of repo
terraform -chdir="infra/${app_name}/app-config" init >/dev/null
terraform -chdir="infra/${app_name}/app-config" apply -auto-approve >/dev/null
image_repository_name="$(terraform -chdir="infra/${app_name}/app-config" output -json build_repository_config | jq -r ".name")"
region=$(./bin/current-region)

result=""
result=$(aws ecr describe-images --repository-name "${image_repository_name}" --image-ids "imageTag=${image_tag}" --region "${region}" 2>/dev/null) || true
if [ -n "${result}" ]; then
echo "true"
else
echo "false"
fi
20 changes: 20 additions & 0 deletions infra/analytics/app-config/build_repository.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
data "external" "account_ids_by_name" {
program = ["${path.module}/../../../bin/account-ids-by-name.sh"]
}

locals {
image_repository_name = "${local.project_name}-${local.app_name}"
image_repository_region = module.project_config.default_region
image_repository_account_name = module.project_config.network_configs[local.shared_network_name].account_name
image_repository_account_id = data.external.account_ids_by_name.result[local.image_repository_account_name]

build_repository_config = {
name = local.image_repository_name
region = local.image_repository_region
network_name = local.shared_network_name
account_name = local.image_repository_account_name
account_id = local.image_repository_account_id
repository_arn = "arn:aws:ecr:${local.image_repository_region}:${local.image_repository_account_id}:repository/${local.image_repository_name}"
repository_url = "${local.image_repository_account_id}.dkr.ecr.${local.image_repository_region}.amazonaws.com/${local.image_repository_name}"
}
}
15 changes: 9 additions & 6 deletions infra/analytics/app-config/main.tf
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
locals {
app_name = "analytics"
project_name = module.project_config.project_name
image_repository_name = "${local.project_name}-${local.app_name}"
app_name = "analytics"
project_name = module.project_config.project_name

environment_configs = {
dev = module.dev_config
staging = module.staging_config
prod = module.prod_config
}

build_repository_config = {
region = module.project_config.default_region
}
# Map from environment name to the account name for the AWS account that
# contains the resources for that environment. Resources that are shared
# across environments use the key "shared".
Expand Down Expand Up @@ -47,6 +43,13 @@ locals {
staging = "simpler-grants-gov"
prod = "simpler-grants-gov"
}

# The name of the network that contains the resources shared across all
# application environments, such as the build repository.
# The list of networks can be found in /infra/networks
# by looking for the backend config files of the form:
# <NETWORK_NAME>.s3.tfbackend
shared_network_name = "prod"
}

module "project_config" {
Expand Down
20 changes: 20 additions & 0 deletions infra/api/app-config/build_repository.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
data "external" "account_ids_by_name" {
program = ["${path.module}/../../../bin/account-ids-by-name.sh"]
}

locals {
image_repository_name = "${local.project_name}-${local.app_name}"
image_repository_region = module.project_config.default_region
image_repository_account_name = module.project_config.network_configs[local.shared_network_name].account_name
image_repository_account_id = data.external.account_ids_by_name.result[local.image_repository_account_name]

build_repository_config = {
name = local.image_repository_name
region = local.image_repository_region
network_name = local.shared_network_name
account_name = local.image_repository_account_name
account_id = local.image_repository_account_id
repository_arn = "arn:aws:ecr:${local.image_repository_region}:${local.image_repository_account_id}:repository/${local.image_repository_name}"
repository_url = "${local.image_repository_account_id}.dkr.ecr.${local.image_repository_region}.amazonaws.com/${local.image_repository_name}"
}
}
11 changes: 7 additions & 4 deletions infra/api/app-config/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ locals {
app_name = "api"
environments = ["dev", "prod"]
project_name = module.project_config.project_name
image_repository_name = "${local.project_name}-${local.app_name}"
has_database = true
has_incident_management_service = false

Expand All @@ -12,9 +11,6 @@ locals {
prod = module.prod_config
}

build_repository_config = {
region = module.project_config.default_region
}
# Map from environment name to the account name for the AWS account that
# contains the resources for that environment. Resources that are shared
# across environments use the key "shared".
Expand Down Expand Up @@ -50,6 +46,13 @@ locals {
staging = "simpler-grants-gov"
prod = "simpler-grants-gov"
}

# The name of the network that contains the resources shared across all
# application environments, such as the build repository.
# The list of networks can be found in /infra/networks
# by looking for the backend config files of the form:
# <NETWORK_NAME>.s3.tfbackend
shared_network_name = "dev"
}

module "project_config" {
Expand Down
20 changes: 20 additions & 0 deletions infra/frontend/app-config/build_repository.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
data "external" "account_ids_by_name" {
program = ["${path.module}/../../../bin/account-ids-by-name.sh"]
}

locals {
image_repository_name = "${local.project_name}-${local.app_name}"
image_repository_region = module.project_config.default_region
image_repository_account_name = module.project_config.network_configs[local.shared_network_name].account_name
image_repository_account_id = data.external.account_ids_by_name.result[local.image_repository_account_name]

build_repository_config = {
name = local.image_repository_name
region = local.image_repository_region
network_name = local.shared_network_name
account_name = local.image_repository_account_name
account_id = local.image_repository_account_id
repository_arn = "arn:aws:ecr:${local.image_repository_region}:${local.image_repository_account_id}:repository/${local.image_repository_name}"
repository_url = "${local.image_repository_account_id}.dkr.ecr.${local.image_repository_region}.amazonaws.com/${local.image_repository_name}"
}
}
12 changes: 7 additions & 5 deletions infra/frontend/app-config/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ locals {
app_name = "frontend"
environments = ["dev", "staging", "prod"]
project_name = module.project_config.project_name
image_repository_name = "${local.project_name}-${local.app_name}"
has_database = false
has_incident_management_service = false
enable_autoscaling = true
hostname = "0.0.0.0"

build_repository_config = {
region = module.project_config.default_region
}

environment_configs = {
dev = module.dev_config
staging = module.staging_config
Expand Down Expand Up @@ -52,6 +47,13 @@ locals {
staging = "simpler-grants-gov"
prod = "simpler-grants-gov"
}

# The name of the network that contains the resources shared across all
# application environments, such as the build repository.
# The list of networks can be found in /infra/networks
# by looking for the backend config files of the form:
# <NETWORK_NAME>.s3.tfbackend
shared_network_name = "staging"
}

module "project_config" {
Expand Down
7 changes: 6 additions & 1 deletion infra/project-config/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,23 @@ locals {
aws_services_security_group_name_prefix = "aws-service-vpc-endpoints"

network_configs = {
# TODO(https://github.com/HHS/simpler-grants-gov/issues/1051) deploy to a non-default VPC in every environment
dev = {
account_name = "simpler-grants-gov"
database_subnet_group_name = "dev"
vpc_name = "dev"
second_octet = 0 # The second octet our the VPC CIDR block
grants_gov_oracle_cidr_block = "10.220.0.0/16" # MicroHealth managed CIDR block where the dev origin Oracle database for Grants.gov is located
}
staging = {
account_name = "simpler-grants-gov"
database_subnet_group_name = "staging"
vpc_name = "staging"
second_octet = 1 # The second octet our the VPC CIDR block
grants_gov_oracle_cidr_block = "10.220.0.0/16" # MicroHealth managed CIDR block where the dev origin Oracle database for Grants.gov is located
}
prod = {
account_name = "simpler-grants-gov"
database_subnet_group_name = "prod"
vpc_name = "prod"
second_octet = 3 # The second octet our the VPC CIDR block
grants_gov_oracle_cidr_block = "10.250.0.0/16" # MicroHealth managed CIDR block where the prod origin Oracle database for Grants.gov is located
Expand Down

0 comments on commit bea90ec

Please sign in to comment.