diff --git a/.github/workflows/.deploy_stack.yml b/.github/workflows/.deploy_stack.yml new file mode 100644 index 00000000..510724b1 --- /dev/null +++ b/.github/workflows/.deploy_stack.yml @@ -0,0 +1,106 @@ +name: .Deploy Stack + +on: + workflow_call: + inputs: + ### Required + environment_name: + description: 'The name of the environment to deploy to' + required: true + default: 'dev' + type: string + command: + description: 'The terragrunt command to run' + required: true + default: 'apply' + type: string + tag: + description: 'The tag of the containers to deploy' + default: 'latest' + type: string + required: false + app_env: + required: false + type: string + description: 'The APP env separates between AWS ENV and Actual APP, since AWS dev is where PR, and TEST is deployed' + outputs: + API_GW_URL: + value: ${{ jobs.deploy-api.outputs.API_GW_URL }} + S3_BUCKET_ARN: + value: ${{ jobs.deploy-cloudfront.outputs.S3_BUCKET_ARN }} + CF_DOMAIN: + value: ${{ jobs.deploy-cloudfront.outputs.CF_DOMAIN }} + CF_DISTRIBUTION_ID: + value: ${{ jobs.deploy-cloudfront.outputs.CF_DISTRIBUTION_ID }} +env: + AWS_REGION: ca-central-1 +jobs: + stack-prefix: + name: Stack Prefix + uses: ./.github/workflows/.stack-prefix.yml + deploy-db: + name: Deploys Database + needs: [stack-prefix] + uses: ./.github/workflows/.deployer.yml + with: + environment_name: ${{ inputs.environment_name }} + command: ${{ inputs.command }} + working_directory: database + app_env: ${{ inputs.app_env }} + stack_prefix: ${{ needs.stack-prefix.outputs.stack_prefix }} + secrets: inherit + deploy-api: + name: Deploys API + needs: [deploy-db, stack-prefix] + uses: ./.github/workflows/.deployer.yml + with: + environment_name: ${{ inputs.environment_name }} + command: ${{ inputs.command }} + tag: ${{ inputs.tag }} + app_env: ${{ inputs.app_env }} + working_directory: api + stack_prefix: ${{ needs.stack-prefix.outputs.stack_prefix }} + secrets: inherit + deploy-cloudfront: + name: Deploys Cloudfront + needs: [stack-prefix] + uses: ./.github/workflows/.deployer.yml + with: + environment_name: ${{ inputs.environment_name }} + command: ${{ inputs.command }} + tag: ${{ inputs.tag }} + app_env: ${{ inputs.app_env }} + working_directory: frontend + stack_prefix: ${{ needs.stack-prefix.outputs.stack_prefix }} + secrets: inherit + build-ui: + name: Build And upload UI to s3 ${{ inputs.environment_name }} + environment: ${{ inputs.environment_name }} + if: ${{ inputs.command == 'apply' }} + needs: [deploy-api, deploy-cloudfront] + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: setup node + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + - name: Build And Update UI (CF) + working-directory: frontend + env: + VITE_API_BASE_URL: ${{ needs.deploy-api.outputs.API_GW_URL }}/api + S3_BUCKET_ARN: ${{ needs.deploy-cloudfront.outputs.S3_BUCKET_ARN }} + CF_DISTRIBUTION_ID: ${{ needs.deploy-cloudfront.outputs.CF_DISTRIBUTION_ID }} + run: | + npm run deploy + aws s3 sync --delete ./dist s3://$(echo "$S3_BUCKET_ARN" | cut -d: -f6) + aws cloudfront create-invalidation --distribution-id $CF_DISTRIBUTION_ID --paths "/*" diff --git a/.github/workflows/.deployer.yml b/.github/workflows/.deployer.yml index 91b1e2b7..45e481f6 100644 --- a/.github/workflows/.deployer.yml +++ b/.github/workflows/.deployer.yml @@ -28,6 +28,10 @@ on: required: false type: string description: 'The APP env separates between AWS ENV and Actual APP, since AWS dev is where PR, and TEST is deployed' + stack_prefix: + required: true + type: string + description: 'The stack prefix to use for the resources' outputs: API_GW_URL: value: ${{ jobs.infra.outputs.API_GW_URL }} @@ -80,6 +84,7 @@ jobs: flyway_image: ghcr.io/${{github.repository}}/migrations:${{inputs.tag}} api_image: ghcr.io/${{github.repository}}/backend:${{inputs.tag}} app_env: ${{inputs.app_env}} + stack_prefix: ${{ inputs.stack_prefix }} run: | # Run terraform terragrunt run-all ${{inputs.command}} --terragrunt-non-interactive @@ -93,6 +98,7 @@ jobs: flyway_image: ghcr.io/${{github.repository}}/migrations:${{inputs.tag}} api_image: ghcr.io/${{github.repository}}/backend:${{inputs.tag}} app_env: ${{inputs.app_env}} + stack_prefix: ${{ inputs.stack_prefix }} run: | terragrunt output -json > outputs.json #print the output @@ -109,6 +115,7 @@ jobs: flyway_image: ghcr.io/${{github.repository}}/migrations:${{inputs.tag}} api_image: ghcr.io/${{github.repository}}/backend:${{inputs.tag}} app_env: ${{inputs.app_env}} + stack_prefix: ${{ inputs.stack_prefix }} run: | terragrunt output -json > outputs.json #print the output diff --git a/.github/workflows/.e2e.yml b/.github/workflows/.e2e.yml new file mode 100644 index 00000000..b8517e14 --- /dev/null +++ b/.github/workflows/.e2e.yml @@ -0,0 +1,63 @@ +name: .E2E +on: + workflow_call: + inputs: + frontend_url: + description: 'The URL of the frontend to test' + required: true + type: string + tag: + description: 'The tag of the containers to test' + default: 'latest' + type: string + required: false +jobs: + e2e-tests: + name: E2E Tests + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Run Docker compose + if: ${{ inputs.frontend_url == 'http://localhost:3000' }} + env: + BACKEND_IMAGE: ghcr.io/${{ github.repository }}/backend:${{ inputs.tag }} + FLYWAY_IMAGE: ghcr.io/${{ github.repository }}/migrations:${{ inputs.tag }} + FRONTEND_IMAGE: ghcr.io/${{ github.repository }}/frontend:${{ inputs.tag }} + run: docker compose up -d --wait + continue-on-error: true + - name: Docker Compose Logs + if: ${{ runner.debug == '1' && inputs.frontend_url == 'http://localhost:3000' }} + run: docker compose logs + - name: Cache Playwright Browsers + uses: actions/cache@v4 + id: playwright-cache + with: + path: | + ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }} + - uses: actions/setup-node@v4 + name: Setup Node + with: + node-version: 22 + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + - name: Fix permissions + run: sudo chown -R $USER:$USER frontend + - name: Install dependencies + working-directory: frontend + run: | + npm ci + - run: npx @playwright/test install --with-deps + if: steps.playwright-cache.outputs.cache-hit != 'true' + working-directory: ./frontend + - run: npx @playwright/test install-deps + if: steps.playwright-cache.outputs.cache-hit == 'true' + working-directory: ./frontend + - name: Run Tests + working-directory: frontend + env: + E2E_BASE_URL: ${{ inputs.frontend_url }} + CI: 'true' + run: | + npx playwright test --project="chromium" --reporter=blob \ No newline at end of file diff --git a/.github/workflows/.stack-prefix.yml b/.github/workflows/.stack-prefix.yml new file mode 100644 index 00000000..06242a9e --- /dev/null +++ b/.github/workflows/.stack-prefix.yml @@ -0,0 +1,52 @@ +name: .Stack Prefix +on: + workflow_call: + outputs: + STACK_PREFIX: + description: 'The Stack Prefix' + value: ${{ jobs.prefix.outputs.STACK_PREFIX }} +jobs: + prefix: + name: Stack Prefix + runs-on: ubuntu-24.04 + outputs: + STACK_PREFIX: ${{ steps.stack-prefix.outputs.STACK_PREFIX }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Generate Stack Prefix + id: stack-prefix + shell: bash + run: | + # Get repository name + REPO_NAME="${{ github.event.repository.name }}" + + # If repo name is less than 20 characters, use it directly + if [[ ${#REPO_NAME} -lt 20 ]]; then + STACK_PREFIX="${REPO_NAME}" + else + # Split by hyphen or underscore and get first letter of each word + PREFIX=$(echo "$REPO_NAME" | + awk -v RS='[-_]' '{printf "%s", tolower(substr($0,1,1))}' | + tr -d '\n') + + # Ensure at least 4 characters without repetition + while [[ ${#PREFIX} -lt 4 ]]; do + # Concatenate with the next letter in the sequence (avoiding randomness) + SUFFIX="${PREFIX: -1}" # Get the last character of the current PREFIX + INDEX=$(( $(echo "$PREFIX" | grep -o "$SUFFIX" | wc -l) + 1 )) # Get the index of the next character + NEXT_CHAR=$(echo "$PREFIX" | cut -c $INDEX) # Get the next character + PREFIX="${PREFIX}${NEXT_CHAR}" + done + + # Truncate if prefix exceeds 10 characters + if [[ ${#PREFIX} -gt 10 ]]; then + PREFIX="${PREFIX:0:10}" + fi + + STACK_PREFIX="${PREFIX}" + fi + + # Set output + echo "STACK_PREFIX=$STACK_PREFIX" >> $GITHUB_OUTPUT + echo "Generated prefix: $STACK_PREFIX" \ No newline at end of file diff --git a/.github/workflows/.tests.yml b/.github/workflows/.tests.yml index f242df08..4cab9397 100644 --- a/.github/workflows/.tests.yml +++ b/.github/workflows/.tests.yml @@ -76,49 +76,7 @@ jobs: sarif_file: "trivy-results.sarif" e2e: name: E2E Tests - runs-on: ubuntu-24.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Run Docker compose - env: - BACKEND_IMAGE: ghcr.io/${{ github.repository }}/backend:${{ inputs.tag }} - FLYWAY_IMAGE: ghcr.io/${{ github.repository }}/migrations:${{ inputs.tag }} - FRONTEND_IMAGE: ghcr.io/${{ github.repository }}/frontend:${{ inputs.tag }} - run: docker compose up -d --wait - continue-on-error: true - - name: Docker Compose Logs - if: ${{ runner.debug == '1' }} - run: docker compose logs - - name: Cache Playwright Browsers - uses: actions/cache@v4 - id: playwright-cache - with: - path: | - ~/.cache/ms-playwright - key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }} - - uses: actions/setup-node@v4 - name: Setup Node - with: - node-version: 22 - cache: 'npm' - cache-dependency-path: frontend/package-lock.json - - name: Fix permissions - run: sudo chown -R $USER:$USER frontend - - name: Install dependencies - working-directory: frontend - run: | - npm ci - - run: npx @playwright/test install --with-deps - if: steps.playwright-cache.outputs.cache-hit != 'true' - working-directory: ./frontend - - run: npx @playwright/test install-deps - if: steps.playwright-cache.outputs.cache-hit == 'true' - working-directory: ./frontend - - name: Run Tests - working-directory: frontend - env: - E2E_BASE_URL: http://localhost:3000 - CI: 'true' - run: | - npx playwright test --project="chromium" --reporter=blob \ No newline at end of file + uses: ./.github/workflows/.e2e.yml + with: + FRONTEND_URL: http://localhost:3000 + tag: ${{ inputs.tag }} \ No newline at end of file diff --git a/.github/workflows/merge.yml b/.github/workflows/merge.yml index 9f86d049..1206d6ce 100644 --- a/.github/workflows/merge.yml +++ b/.github/workflows/merge.yml @@ -28,64 +28,30 @@ jobs: if: ${{ github.event_name != 'workflow_dispatch' }} id: pr uses: bcgov-nr/action-get-pr@v0.0.1 - deploy-db: + + deploy_stack_dev: + name: Deploy Stack Dev needs: [vars] - name: Deploys Database - uses: ./.github/workflows/.deployer.yml + uses: ./.github/workflows/.deploy_stack.yml with: environment_name: dev command: apply - working_directory: database - app_env: dev - secrets: inherit - deploy-api: - name: Deploys API - needs: [vars,deploy-db] - uses: ./.github/workflows/.deployer.yml - with: - environment_name: dev - command: apply - working_directory: api tag: ${{ needs.vars.outputs.pr }} app_env: dev secrets: inherit - deploy-cloudfront: - name: Deploys Cloudfront - needs: [vars] - uses: ./.github/workflows/.deployer.yml + e2e: + name: E2E Tests + needs: [deploy_stack_dev] + uses: ./.github/workflows/.e2e.yml with: - environment_name: dev + frontend_url: https://${{ needs.deploy_stack_dev.outputs.CF_DOMAIN }} + deploy_stack_test: + name: Deploy Stack Test + needs: [vars, e2e] + uses: ./.github/workflows/.deploy_stack.yml + with: + environment_name: test command: apply - working_directory: frontend - app_env: dev + tag: ${{ needs.vars.outputs.pr }} + app_env: test secrets: inherit - build-ui: - name: Builds UI - needs: [deploy-api, deploy-cloudfront] - runs-on: ubuntu-24.04 - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: setup node - uses: actions/setup-node@v4 - with: - node-version: '22' - cache: 'npm' - cache-dependency-path: frontend/package-lock.json - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} - aws-region: ${{ env.AWS_REGION }} - - name: Build And Update UI (CF) - working-directory: frontend - env: - VITE_API_BASE_URL: ${{ needs.deploy-api.outputs.API_GW_URL }}/api - S3_BUCKET_ARN: ${{ needs.deploy-cloudfront.outputs.S3_BUCKET_ARN }} - CF_DISTRIBUTION_ID: ${{ needs.deploy-cloudfront.outputs.CF_DISTRIBUTION_ID }} - run: | - npm run deploy - aws s3 sync --delete ./dist s3://$(echo "$S3_BUCKET_ARN" | cut -d: -f6) - aws cloudfront create-invalidation --distribution-id $CF_DISTRIBUTION_ID --paths "/*" - \ No newline at end of file diff --git a/.github/workflows/pause-resources.yml b/.github/workflows/pause-resources.yml index 890be53d..fc6fe4cb 100644 --- a/.github/workflows/pause-resources.yml +++ b/.github/workflows/pause-resources.yml @@ -8,7 +8,12 @@ permissions: id-token: write # This is required for requesting the JWT contents: write # This is required for actions/checkout jobs: + stack-prefix: + name: Stack Prefix + uses: ./.github/workflows/.stack-prefix.yml pause-resources-dev: + name: Pause Resources Dev + needs: [stack-prefix] runs-on: ubuntu-24.04 steps: - name: Checkout @@ -22,11 +27,36 @@ jobs: - name: Pause AWS Resources shell: bash run: | - aws application-autoscaling register-scalable-target --service-namespace ecs --resource-id service/ecs-cluster-node-api-dev/node-api-dev-service --scalable-dimension ecs:service:DesiredCount --min-capacity 0 --max-capacity 0 --no-cli-pager --output json - aws ecs update-service --cluster ecs-cluster-node-api-dev --service node-api-dev-service --desired-count 0 --no-cli-pager --output json - DB_CLUSTER_STATUS=$(aws rds describe-db-clusters --db-cluster-identifier qsawsc-aurora-cluster-dev --query 'DBClusters[0].Status' --output text) + aws application-autoscaling register-scalable-target --service-namespace ecs --resource-id service/ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev/${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev-service --scalable-dimension ecs:service:DesiredCount --min-capacity 0 --max-capacity 0 --no-cli-pager --output json + aws ecs update-service --cluster ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev --service ${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev-service --desired-count 0 --no-cli-pager --output json + DB_CLUSTER_STATUS=$(aws rds describe-db-clusters --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-dev --query 'DBClusters[0].Status' --output text) if [ "$DB_CLUSTER_STATUS" = "available" ]; then - aws rds stop-db-cluster --db-cluster-identifier qsawsc-aurora-cluster-dev --no-cli-pager --output json + aws rds stop-db-cluster --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-dev --no-cli-pager --output json else echo "DB cluster is not in an available state. Current state: $DB_CLUSTER_STATUS" fi + pause-resources-test: + name: Pause Resources Test + environment: test + needs: [stack-prefix] + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} + role-session-name: gha-pause-resources + aws-region: ca-central-1 + - name: Pause AWS Resources + shell: bash + run: | + aws application-autoscaling register-scalable-target --service-namespace ecs --resource-id service/ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test/${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test-service --scalable-dimension ecs:service:DesiredCount --min-capacity 0 --max-capacity 0 --no-cli-pager --output json + aws ecs update-service --cluster ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test --service ${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test-service --desired-count 0 --no-cli-pager --output json + DB_CLUSTER_STATUS=$(aws rds describe-db-clusters --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-test --query 'DBClusters[0].Status' --output text) + if [ "$DB_CLUSTER_STATUS" = "available" ]; then + aws rds stop-db-cluster --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-test --no-cli-pager --output json + else + echo "DB cluster is not in an available state. Current state: $DB_CLUSTER_STATUS" + fi \ No newline at end of file diff --git a/.github/workflows/pr-open.yml b/.github/workflows/pr-open.yml index 0eab930f..94c13f90 100644 --- a/.github/workflows/pr-open.yml +++ b/.github/workflows/pr-open.yml @@ -31,41 +31,17 @@ jobs: tag: ${{ github.event.number }} tag_fallback: latest triggers: ('${{ matrix.package }}/') - - uses: shrink/actions-docker-registry-tag@v4 - with: - registry: ghcr.io - repository: ${{ github.repository }}/${{ matrix.package }} - target: ${{ github.event.number }} - tags: ${{ github.event.number }}-${{ github.run_number }} - plan-db: - name: Plan Database - uses: ./.github/workflows/.deployer.yml - with: - environment_name: dev - command: plan - working_directory: database - app_env: ephermal-${{ github.event.number }} # ephermal, prefixed for easy clean up of PR resources in s3 and dynamodb generated by terraform - secrets: inherit - plan-api: - name: Plan API - needs: [plan-db] - uses: ./.github/workflows/.deployer.yml + + plan-stack: + name: Plan Stack + uses: ./.github/workflows/.deploy_stack.yml with: environment_name: dev command: plan - working_directory: api tag: ${{ github.event.number }} - app_env: ephermal-${{ github.event.number }} - secrets: inherit - plan-cloudfront: - name: Plan Cloudfront - uses: ./.github/workflows/.deployer.yml - with: - environment_name: dev - command: plan - working_directory: frontend - app_env: ephermal-${{ github.event.number }} + app_env: ephermal-${{ github.event.number }} # ephermal, prefixed for easy clean up of PR resources in s3 and dynamodb generated by terraform secrets: inherit + tests: name: Tests @@ -76,7 +52,7 @@ jobs: results: name: PR Results - needs: [builds, plan-db, plan-api, plan-cloudfront, tests] + needs: [builds, plan-stack, tests] if: always() runs-on: ubuntu-24.04 steps: diff --git a/.github/workflows/prune-env.yml b/.github/workflows/prune-env.yml index 0c807aaf..f84b91c7 100644 --- a/.github/workflows/prune-env.yml +++ b/.github/workflows/prune-env.yml @@ -6,40 +6,37 @@ on: description: 'The name of the environment to destroy resources from dev or test or prod(BE CAREFUL HERE)' required: true default: 'dev' - type: string + type: choice + options: + - dev + - test app_env: required: true - type: string + type: choice description: 'The APP env separates between AWS ENV and Actual APP, since AWS dev is where PR, and TEST is deployed' + options: + - dev + - test permissions: id-token: write # This is required for requesting the JWT contents: write # This is required for actions/checkout packages: write jobs: - cleanup-aws-database: - name: Cleanup AWS Database - uses: ./.github/workflows/.deployer.yml + destroy-dev: + name: Destroy Stack Dev + if: ${{ github.event.inputs.app_env == 'dev' }} + uses: ./.github/workflows/.deploy_stack.yml secrets: inherit with: environment_name: ${{ github.event.inputs.environment_name }} command: destroy - working_directory: database app_env: ${{ github.event.inputs.app_env }} - cleanup-aws-api: - name: Cleanup AWS API - uses: ./.github/workflows/.deployer.yml - secrets: inherit - with: - environment_name: ${{ github.event.inputs.environment_name }} - command: destroy - working_directory: api - app_env: ${{ github.event.inputs.app_env }} - cleanup-aws-cf: - name: Cleanup AWS CF - uses: ./.github/workflows/.deployer.yml - secrets: inherit - with: - environment_name: ${{ github.event.inputs.environment_name }} - command: destroy - working_directory: frontend - app_env: ${{ github.event.inputs.app_env }} + destroy-test: + name: Destroy Stack Test + if: ${{ github.event.inputs.app_env == 'test' }} + uses: ./.github/workflows/.deploy_stack.yml + secrets: inherit + with: + environment_name: ${{ github.event.inputs.environment_name }} + command: destroy + app_env: ${{ github.event.inputs.app_env }} \ No newline at end of file diff --git a/.github/workflows/resume-resources.yml b/.github/workflows/resume-resources.yml index 11d822be..65f975e9 100644 --- a/.github/workflows/resume-resources.yml +++ b/.github/workflows/resume-resources.yml @@ -8,7 +8,12 @@ permissions: id-token: write # This is required for requesting the JWT contents: write # This is required for actions/checkout jobs: + stack-prefix: + name: Stack Prefix + uses: ./.github/workflows/.stack-prefix.yml resume-resources-dev: + name: Resume Resources Dev + needs: [stack-prefix] runs-on: ubuntu-24.04 steps: - name: Checkout @@ -17,16 +22,41 @@ jobs: uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} - role-session-name: gha-pause-resources + role-session-name: gha-resume-resources aws-region: ca-central-1 - name: Resume AWS Resources shell: bash run: | - DB_CLUSTER_STATUS=$(aws rds describe-db-clusters --db-cluster-identifier qsawsc-aurora-cluster-dev --query 'DBClusters[0].Status' --output text) + aws application-autoscaling register-scalable-target --service-namespace ecs --resource-id service/ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev/${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev-service --scalable-dimension ecs:service:DesiredCount --min-capacity 1 --max-capacity 2 --no-cli-pager --output json + aws ecs update-service --cluster ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev --service ${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-dev-service --desired-count 1 --no-cli-pager --output json + DB_CLUSTER_STATUS=$(aws rds describe-db-clusters --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-dev --query 'DBClusters[0].Status' --output text) if [ "$DB_CLUSTER_STATUS" = "stopped" ]; then - aws rds start-db-cluster --db-cluster-identifier qsawsc-aurora-cluster-dev --no-cli-pager --output json + aws rds start-db-cluster --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-dev --no-cli-pager --output json else - echo "DB cluster is not in an stopped state. Current state: $DB_CLUSTER_STATUS" + echo "DB cluster is not in a stopped state. Current state: $DB_CLUSTER_STATUS" fi - aws ecs update-service --cluster ecs-cluster-node-api-dev --service node-api-dev-service --desired-count 1 --no-cli-pager --output json - aws application-autoscaling register-scalable-target --service-namespace ecs --resource-id service/ecs-cluster-node-api-dev/node-api-dev-service --scalable-dimension ecs:service:DesiredCount --min-capacity 1 --max-capacity 3 --no-cli-pager --output json + resume-resources-test: + name: Resume Resources Test + environment: test + needs: [stack-prefix] + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }} + role-session-name: gha-resume-resources + aws-region: ca-central-1 + - name: Resume AWS Resources + shell: bash + run: | + aws application-autoscaling register-scalable-target --service-namespace ecs --resource-id service/ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test/${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test-service --scalable-dimension ecs:service:DesiredCount --min-capacity 1 --max-capacity 2 --no-cli-pager --output json + aws ecs update-service --cluster ecs-cluster-${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test --service ${{ needs.stack-prefix.outputs.stack_prefix }}-node-api-test-service --desired-count 1 --no-cli-pager --output json + DB_CLUSTER_STATUS=$(aws rds describe-db-clusters --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-test --query 'DBClusters[0].Status' --output text) + if [ "$DB_CLUSTER_STATUS" = "stopped" ]; then + aws rds start-db-cluster --db-cluster-identifier ${{ needs.stack-prefix.outputs.stack_prefix }}-aurora-test --no-cli-pager --output json + else + echo "DB cluster is not in a stopped state. Current state: $DB_CLUSTER_STATUS" + fi \ No newline at end of file diff --git a/infrastructure/api/ecs.tf b/infrastructure/api/ecs.tf index ad7a0bb4..e14d212b 100644 --- a/infrastructure/api/ecs.tf +++ b/infrastructure/api/ecs.tf @@ -1,13 +1,13 @@ locals { container_name = "${var.app_name}" - rds_app_env = (contains(["dev", "test", "prod"], var.app_env) ? var.app_env : "dev") # if app_env is not dev, test, or prod, default to dev + } data "aws_secretsmanager_secret" "db_master_creds" { - name = "aurora-pg-db-master-creds-${var.target_env}_${local.rds_app_env}" + name = "${var.db_cluster_name}" } data "aws_rds_cluster" "rds_cluster" { - cluster_identifier = "qsawsc-aurora-cluster-${local.rds_app_env}" + cluster_identifier = "${var.db_cluster_name}" } data "aws_secretsmanager_secret_version" "db_master_creds_version" { diff --git a/infrastructure/api/vars.tf b/infrastructure/api/vars.tf index 2ce7594a..8602e614 100644 --- a/infrastructure/api/vars.tf +++ b/infrastructure/api/vars.tf @@ -154,4 +154,8 @@ variable "tags" { description = "A set of of one or more tags to provide some metadata for the provisioned resources." type = map(string) default = {} +} +variable "db_cluster_name"{ + description = "Name of the database cluster" + type = string } \ No newline at end of file diff --git a/infrastructure/database/aurora-v2.tf b/infrastructure/database/aurora-v2.tf index 0aa30acf..0838991c 100644 --- a/infrastructure/database/aurora-v2.tf +++ b/infrastructure/database/aurora-v2.tf @@ -48,7 +48,7 @@ resource "aws_rds_cluster_parameter_group" "db_postgresql" { resource "aws_secretsmanager_secret" "db_mastercreds_secret" { - name = "aurora-pg-db-master-creds-${var.target_env}_${var.app_env}" + name = "${var.db_cluster_name}" tags = { managed-by = "terraform" @@ -101,11 +101,11 @@ module "aurora_postgresql_v2" { } instance_class = "db.serverless" - instances = { + instances = var.ha_enabled ? { one = {} - two = var.ha_enabled ? {}:null - } - + two = {} + }: {one = {}} + tags = { managed-by = "terraform" } @@ -113,5 +113,8 @@ module "aurora_postgresql_v2" { enabled_cloudwatch_logs_exports = ["postgresql"] backup_retention_period = "${var.backup_retention_period}" } +output "ha_enabled" { + value = var.ha_enabled +} diff --git a/infrastructure/database/vars.tf b/infrastructure/database/vars.tf index 5f1e669b..2b79173e 100644 --- a/infrastructure/database/vars.tf +++ b/infrastructure/database/vars.tf @@ -3,11 +3,6 @@ variable "target_env" { type = string } -variable "app_env" { - description = "The environment for the app, since multiple instances can be deployed to same dev environment of AWS, this represents whether it is PR or dev or test" - type = string -} - variable "db_cluster_name" { description = "Name for the database cluster -- must be unique" type = string @@ -36,4 +31,3 @@ variable "ha_enabled" { type = bool default = true } - diff --git a/terraform/api/dev/terragrunt.hcl b/terraform/api/dev/terragrunt.hcl index 60b9c13e..5e5e94a7 100644 --- a/terraform/api/dev/terragrunt.hcl +++ b/terraform/api/dev/terragrunt.hcl @@ -19,6 +19,5 @@ generate "dev_tfvars" { flyway_image="${local.flyway_image}" api_image="${local.api_image}" app_env="${local.app_env}" - app_name="node-api-${local.app_env}" EOF } \ No newline at end of file diff --git a/terraform/api/terragrunt.hcl b/terraform/api/terragrunt.hcl index 8f127f27..e7be5913 100644 --- a/terraform/api/terragrunt.hcl +++ b/terraform/api/terragrunt.hcl @@ -8,15 +8,17 @@ locals { region = "ca-central-1" # Terraform remote S3 config + stack_prefix = get_env("stack_prefix") tf_remote_state_prefix = "terraform-remote-state" # Do not change this, given by cloud.pathfinder. target_env = get_env("target_env") aws_license_plate = get_env("aws_license_plate") app_env = get_env("app_env") statefile_bucket_name = "${local.tf_remote_state_prefix}-${local.aws_license_plate}-${local.target_env}" - statefile_key = "${local.app_env}/api/terraform.tfstate" + statefile_key = "${local.stack_prefix}/${local.app_env}/api/terraform.tfstate" statelock_table_name = "${local.tf_remote_state_prefix}-lock-${local.aws_license_plate}" flyway_image = get_env("flyway_image") api_image = get_env("api_image") + rds_app_env = (contains(["dev", "test", "prod"], "${local.app_env}") ? "${local.app_env}" : "dev") # if app_env is not dev, test, or prod, default to dev } @@ -43,6 +45,8 @@ generate "tfvars" { if_exists = "overwrite" disable_signature = true contents = <<-EOF + app_name="${local.stack_prefix}-node-api-${local.app_env}" + db_cluster_name = "${local.stack_prefix}-aurora-${local.rds_app_env}" EOF } diff --git a/terraform/api/test/terragrunt.hcl b/terraform/api/test/terragrunt.hcl index fbcee610..95c931bc 100644 --- a/terraform/api/test/terragrunt.hcl +++ b/terraform/api/test/terragrunt.hcl @@ -19,6 +19,5 @@ generate "dev_tfvars" { flyway_image="${local.flyway_image}" api_image="${local.api_image}" app_env="${local.app_env}" - app_name="node-api-${local.app_env}" EOF } \ No newline at end of file diff --git a/terraform/database/dev/terragrunt.hcl b/terraform/database/dev/terragrunt.hcl index 524d5908..892a9c05 100644 --- a/terraform/database/dev/terragrunt.hcl +++ b/terraform/database/dev/terragrunt.hcl @@ -12,8 +12,7 @@ generate "dev_tfvars" { disable_signature = true contents = <<-EOF target_env = "dev" - db_cluster_name = "qsawsc-aurora-cluster-${local.app_env}" - app_env="${local.app_env}" backup_retention_period=1 + ha_enabled=false EOF } \ No newline at end of file diff --git a/terraform/database/terragrunt.hcl b/terraform/database/terragrunt.hcl index e7afa230..d212e731 100644 --- a/terraform/database/terragrunt.hcl +++ b/terraform/database/terragrunt.hcl @@ -6,15 +6,16 @@ terraform { locals { region = "ca-central-1" - + stack_prefix = get_env("stack_prefix") # Terraform remote S3 config tf_remote_state_prefix = "terraform-remote-state" # Do not change this, given by cloud.pathfinder. target_env = get_env("target_env") # this is the target environment of AWS, like dev, test, prod aws_license_plate = get_env("aws_license_plate") app_env = get_env("app_env") # this is the environment for the app, like PR, dev, test, since same AWS dev can be reused for both dev and test statefile_bucket_name = "${local.tf_remote_state_prefix}-${local.aws_license_plate}-${local.target_env}" - statefile_key = "${local.app_env}/database/aurora-v2/terraform.tfstate" + statefile_key = "${local.stack_prefix}/${local.app_env}/database/aurora-v2/terraform.tfstate" statelock_table_name = "${local.tf_remote_state_prefix}-lock-${local.aws_license_plate}" + rds_app_env = (contains(["dev", "test", "prod"], "${local.app_env}") ? "${local.app_env}" : "dev") # if app_env is not dev, test, or prod, default to dev } # Remote S3 state for Terraform. @@ -40,6 +41,7 @@ generate "tfvars" { if_exists = "overwrite" disable_signature = true contents = <<-EOF + db_cluster_name = "${local.stack_prefix}-aurora-${local.rds_app_env}" EOF } diff --git a/terraform/database/test/terragrunt.hcl b/terraform/database/test/terragrunt.hcl index 4e86bab2..1a4cab3b 100644 --- a/terraform/database/test/terragrunt.hcl +++ b/terraform/database/test/terragrunt.hcl @@ -12,8 +12,7 @@ generate "dev_tfvars" { disable_signature = true contents = <<-EOF target_env = "test" - db_cluster_name = "qsawsc-aurora-cluster-${local.app_env}" - app_env="${local.app_env}" backup_retention_period=1 + ha_enabled=false EOF } \ No newline at end of file diff --git a/terraform/frontend/dev/terragrunt.hcl b/terraform/frontend/dev/terragrunt.hcl index b892ac3b..c3f84384 100644 --- a/terraform/frontend/dev/terragrunt.hcl +++ b/terraform/frontend/dev/terragrunt.hcl @@ -14,7 +14,5 @@ generate "dev_tfvars" { disable_signature = true contents = <<-EOF target_env = "dev" - app_env="${local.app_env}" - app_name="frontend-${local.app_env}" EOF } \ No newline at end of file diff --git a/terraform/frontend/terragrunt.hcl b/terraform/frontend/terragrunt.hcl index e715ccbc..5e2611bf 100644 --- a/terraform/frontend/terragrunt.hcl +++ b/terraform/frontend/terragrunt.hcl @@ -6,13 +6,14 @@ terraform { locals { region = "ca-central-1" - app_env = get_env("app_env") + app_env = get_env("app_env") + stack_prefix = get_env("stack_prefix") # Terraform remote S3 config tf_remote_state_prefix = "terraform-remote-state" # Do not change this, given by cloud.pathfinder. target_env = get_env("target_env") - aws_license_plate = get_env("aws_license_plate") + aws_license_plate = get_env("aws_license_plate") statefile_bucket_name = "${local.tf_remote_state_prefix}-${local.aws_license_plate}-${local.target_env}" - statefile_key = "${local.app_env}/frontend/terraform.tfstate" + statefile_key = "${local.stack_prefix}/${local.app_env}/frontend/terraform.tfstate" statelock_table_name = "${local.tf_remote_state_prefix}-lock-${local.aws_license_plate}" } @@ -39,6 +40,8 @@ generate "tfvars" { if_exists = "overwrite" disable_signature = true contents = <<-EOF + app_env="${local.app_env}" + app_name="${local.stack_prefix}-frontend-${local.app_env}" EOF } diff --git a/terraform/frontend/test/terragrunt.hcl b/terraform/frontend/test/terragrunt.hcl index bee13375..80db7ba3 100644 --- a/terraform/frontend/test/terragrunt.hcl +++ b/terraform/frontend/test/terragrunt.hcl @@ -14,7 +14,5 @@ generate "dev_tfvars" { disable_signature = true contents = <<-EOF target_env = "test" - app_env="${local.app_env}" - app_name="frontend-${local.app_env}" EOF } \ No newline at end of file