diff --git a/.github/actions/deploy-environment-aks/action.yml b/.github/actions/deploy-environment-aks/action.yml deleted file mode 100644 index 0a62d165..00000000 --- a/.github/actions/deploy-environment-aks/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Deploy environment to AKS -description: Deploys an application environment to AKS - -inputs: - environment: - description: The name of the environment - required: true - image-tag: - description: The image tag to deploy - required: true - azure-credentials: - description: JSON object containing a service principal that can read from Azure Key Vault - required: true - pull-request-number: - description: The pull request number which triggered this deploy. - required: false - -outputs: - environment_url: - description: The base URL for the deployed environment - value: ${{ steps.set_outputs.outputs.ACCESS_URL }} - -runs: - using: composite - - steps: - - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.6.4 - terraform_wrapper: false - - - uses: DFE-Digital/github-actions/set-kubelogin-environment@master - with: - azure-credentials: ${{ inputs.azure-credentials }} - - - name: Terraform Apply - shell: bash - run: | - make ci ${{ inputs.environment }} aks-terraform-apply - env: - DOCKER_IMAGE_TAG: ${{ inputs.image-tag }} - PR_NUMBER: ${{ inputs.pull-request-number }} - - - name: Extract Terraform outputs - shell: bash - id: set_outputs - run: | - access_url=$(terraform -chdir=terraform/application output -json urls | jq -r '.[0]') - echo "ACCESS_URL=$access_url" >> $GITHUB_OUTPUT - - - name: Run smoke tests - shell: bash - run: | - # Parse the JSON array of URLs into a Bash array - urls=$(echo "$HOSTNAMES" | jq -r '.[]') - - # Loop over each URL and perform the curl check on the /health/all.json endpoint - for url in $urls; do - echo "Check health for $url/health/all.json..." - curl -sS --fail "$url/health/all.json" > /dev/null && echo "Health check passed for $url" || echo "Health check failed for $url" - done diff --git a/.github/actions/deploy-environment/action.yml b/.github/actions/deploy-environment/action.yml index 76aa84ef..0a62d165 100644 --- a/.github/actions/deploy-environment/action.yml +++ b/.github/actions/deploy-environment/action.yml @@ -1,188 +1,61 @@ -name: Deploy to Azure +name: Deploy environment to AKS +description: Deploys an application environment to AKS inputs: - environment_name: + environment: description: The name of the environment required: true - image_name_tag: - description: Image name and tag - image_tag: - description: GitHub SHA of the image + image-tag: + description: The image tag to deploy required: true - azure_credentials: + azure-credentials: description: JSON object containing a service principal that can read from Azure Key Vault required: true - site_up_retries: - description: The number of times that the site up test will be retried - default: 60 - startup_command: + pull-request-number: + description: The pull request number which triggered this deploy. required: false outputs: environment_url: description: The base URL for the deployed environment - value: ${{ steps.terraform.outputs.app_fqdn }} - check_service_url: - description: The base URL for the Check service in the deployed environment - value: ${{ steps.terraform.outputs.app_check_service_fqdn }} + value: ${{ steps.set_outputs.outputs.ACCESS_URL }} runs: using: composite steps: - # Extract configuration from tfvars - - id: config - run: | - APP_RESOURCE_GROUP_NAME=$(jq -r '.resource_group_name' $TFVARS) - RESOURCE_PREFIX=$(jq -r '.resource_prefix' $TFVARS) - STORAGE_ACCOUNT_NAME=$(jq -r '.storage_account_name' $TFVARS) - TERRAFORM_VERSION=$(awk '/{/{f=/^terraform/;next}f' terraform.tf | grep -o [0-9\.]*) - - if [ ${{ inputs.environment_name }} == "review" ]; then - DEV_TFVARS=workspace_variables/dev.tfvars.json - KEY_VAULT_NAME=$(jq -r '.key_vault_name' $DEV_TFVARS) - REVIEW_APP_SUFFIX=-pr-${{ github.event.pull_request.number }} - TF_RESOURCE_GROUP_NAME=$(jq -r '.resource_group_name' $DEV_TFVARS) - else - KEY_VAULT_NAME=$(jq -r '.key_vault_name' $TFVARS) - TF_RESOURCE_GROUP_NAME=$(jq -r '.resource_group_name' $TFVARS) - fi - - if [ -z "$APP_RESOURCE_GROUP_NAME" ]; then - echo "::error ::Failed to extract app_resource_group_name from $TFVARS" - exit 1 - fi - - if [ -z "$KEY_VAULT_NAME" ]; then - echo "::error ::Failed to extract key_vault_name from $TFVARS" - exit 1 - fi - - if [ -z "$RESOURCE_PREFIX" ]; then - echo "::error ::Failed to extract resource_prefix from $TFVARS" - exit 1 - fi - - if [ -z "$STORAGE_ACCOUNT_NAME" ]; then - echo "::error ::Failed to extract storage_account_name from $TFVARS" - exit 1 - fi - - if [ -z "$TERRAFORM_VERSION" ]; then - echo "::error ::Failed to extract terraform_version from terraform.tf" - exit 1 - fi - - if [ -z "$TF_RESOURCE_GROUP_NAME" ]; then - echo "::error ::Failed to extract tf_resource_group_name from TFVARS" - exit 1 - fi + - uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.6.4 + terraform_wrapper: false - echo "app_resource_group_name=$APP_RESOURCE_GROUP_NAME" >> $GITHUB_ENV - echo "key_vault_name=$KEY_VAULT_NAME" >> $GITHUB_ENV - echo "resource_prefix=$RESOURCE_PREFIX" >> $GITHUB_ENV - echo "review_app_suffix=$REVIEW_APP_SUFFIX" >> $GITHUB_ENV - echo "storage_account_name=$STORAGE_ACCOUNT_NAME" >> $GITHUB_ENV - echo "terraform_version=$TERRAFORM_VERSION" >> $GITHUB_ENV - echo "tf_resource_group_name=$TF_RESOURCE_GROUP_NAME" >> $GITHUB_ENV + - uses: DFE-Digital/github-actions/set-kubelogin-environment@master + with: + azure-credentials: ${{ inputs.azure-credentials }} + - name: Terraform Apply shell: bash + run: | + make ci ${{ inputs.environment }} aks-terraform-apply env: - TFVARS: workspace_variables/${{ inputs.environment_name }}.tfvars.json - working-directory: terraform - - - uses: Azure/login@v1 - with: - creds: ${{ inputs.azure_credentials }} + DOCKER_IMAGE_TAG: ${{ inputs.image-tag }} + PR_NUMBER: ${{ inputs.pull-request-number }} - - id: deploy-arm-resources - if: ${{ inputs.environment_name != 'review' }} - run: | - make ci ${{ inputs.environment_name }} deploy-azure-resources + - name: Extract Terraform outputs shell: bash + id: set_outputs + run: | + access_url=$(terraform -chdir=terraform/application output -json urls | jq -r '.[0]') + echo "ACCESS_URL=$access_url" >> $GITHUB_OUTPUT - - run: | - TFSTATE_CONTAINER_ACCESS_KEY="$(az storage account keys list -g ${{ env.tf_resource_group_name }} -n ${{ env.storage_account_name }} | jq -r '.[0].value')" - echo "::add-mask::$TFSTATE_CONTAINER_ACCESS_KEY" - echo "TFSTATE_CONTAINER_ACCESS_KEY=$TFSTATE_CONTAINER_ACCESS_KEY" >> $GITHUB_ENV + - name: Run smoke tests shell: bash - - - uses: hashicorp/setup-terraform@v2 - with: - terraform_version: ${{ env.terraform_version }} - terraform_wrapper: false - - - id: terraform run: | - if [ ${{ inputs.environment_name }} == "review" ]; then - make ci ${{ inputs.environment_name }} terraform-apply pr_id=${{ github.event.pull_request.number }} - else - make ci ${{ inputs.environment_name }} terraform-apply - fi - cd terraform - TFOUTPUTS=$(terraform output --json) - OUTPUTS=($(jq -r <<< "$TFOUTPUTS" | jq -r 'keys | @sh' | tr -d \')) - for o in "${OUTPUTS[@]}" - do - echo ${o}=$(jq -r .${o}.value <<< "$TFOUTPUTS") >> $GITHUB_ENV - done - echo "app_fqdn=$(terraform output -raw app_fqdn)" >>$GITHUB_OUTPUT - echo "app_check_service_fqdn=$(terraform output -raw app_check_service_fqdn)" >>$GITHUB_OUTPUT - env: - ARM_ACCESS_KEY: ${{ env.TFSTATE_CONTAINER_ACCESS_KEY }} - TF_VAR_azure_sp_credentials_json: ${{ inputs.azure_credentials }} - TF_VAR_aytq_docker_image: ${{ inputs.image_name_tag }} - shell: bash - - - uses: azure/webapps-deploy@v2 - if: ${{ inputs.environment_name != 'review' }} - with: - app-name: ${{ env.resource_prefix }}-${{ inputs.environment_name}}-app - images: ${{ inputs.image_name_tag }} - slot-name: ${{ env.web_app_slot_name }} + # Parse the JSON array of URLs into a Bash array + urls=$(echo "$HOSTNAMES" | jq -r '.[]') - - uses: azure/webapps-deploy@v2 - if: ${{ inputs.environment_name == 'review' }} - with: - app-name: ${{ env.resource_prefix }}-${{ inputs.environment_name}}${{ env.review_app_suffix }}-app - images: ${{ inputs.image_name_tag }} - slot-name: ${{ env.web_app_slot_name }} - startup-command: ${{ inputs.startup_command }} - - - uses: azure/CLI@v1 - if: ${{ env.web_app_slot_name != 'production' }} - with: - inlineScript: | - az webapp deployment slot swap -g ${{ env.app_resource_group_name }} -n ${{ env.web_app_name }} --slot ${{ env.web_app_slot_name }} --target-slot production - - # Check new site is up - - run: | - echo "Checking new site is up" - attempt_counter=0 - max_attempts=$RETRIES - - HEALTH_URL="${{ env.app_fqdn }}/health/all.json" - HEALTH_RESPONSE=$(curl $HEALTH_URL --silent --connect-timeout 600) - APP_SHA=$(echo $HEALTH_RESPONSE | jq -R '. as $line | try (fromjson | .version.message) catch $line' | grep -Po "Version: \K\w*") - APP_STATUS=$(echo $HEALTH_RESPONSE | jq -R '. as $line | try (fromjson | .default.success) catch $line') - APP_DATABASE_STATUS=$(echo $HEALTH_RESPONSE | jq -R '. as $line | try (fromjson | .database.success) catch $line') - echo "sha: $APP_SHA; app_status: $APP_STATUS; app_database_status: $APP_DATABASE_STATUS" - until [[ "$EXPECTED_SHA" == "$APP_SHA" && "$APP_STATUS" == "true" && "$APP_DATABASE_STATUS" == "true" ]]; do - if [ ${attempt_counter} -eq ${max_attempts} ];then - echo "Max attempts reached" - exit 1 - fi - echo "Attempt $attempt_counter: new site not up, retrying in 5 seconds ..." - sleep 5 - attempt_counter=$(($attempt_counter+1)) - - HEALTH_RESPONSE=$(curl $HEALTH_URL --silent --connect-timeout 600) - APP_SHA=$(echo $HEALTH_RESPONSE | jq -R '. as $line | try (fromjson | .version.message) catch $line' | grep -Po "Version: \K\w*") - APP_STATUS=$(echo $HEALTH_RESPONSE | jq -R '. as $line | try (fromjson | .default.success) catch $line') - APP_DATABASE_STATUS=$(echo $HEALTH_RESPONSE | jq -R '. as $line | try (fromjson | .database.success) catch $line') - echo "sha: $APP_SHA; app_status: $APP_STATUS; app_database_status: $APP_DATABASE_STATUS" + # Loop over each URL and perform the curl check on the /health/all.json endpoint + for url in $urls; do + echo "Check health for $url/health/all.json..." + curl -sS --fail "$url/health/all.json" > /dev/null && echo "Health check passed for $url" || echo "Health check failed for $url" done - shell: bash - env: - EXPECTED_SHA: ${{ inputs.image_tag }} - RETRIES: ${{ inputs.site_up_retries }} diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index a8ca22cf..4f7eedeb 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -20,7 +20,6 @@ on: required: true type: choice options: - - dev - production jobs: @@ -42,34 +41,6 @@ jobs: deploy_review_app: name: Deploy to review environment runs-on: ubuntu-latest - if: contains(github.event.pull_request.labels.*.name, 'deploy') - concurrency: deploy_review_${{ github.event.pull_request.number }} - needs: [build_image] - environment: - name: review - - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/deploy-environment - id: deploy - with: - environment_name: review - image_name_tag: ${{ needs.build_image.outputs.image_name_tag }} - image_tag: ${{ github.sha }} - azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} - site_up_retries: 150 - startup_command: "sh /app/bin/start-review-app.sh" - - - name: Post URL to Pull Request comment - uses: marocchino/sticky-pull-request-comment@v2 - with: - message: | - Review app deployed to <${{ steps.deploy.outputs.environment_url }}> - - deploy_review_app_aks: - name: Deploy to review environment for AKS - runs-on: ubuntu-latest if: contains(github.event.pull_request.labels.*.name, 'deploy-aks') concurrency: deploy_review_${{ github.event.pull_request.number }} needs: [build_image] @@ -80,8 +51,8 @@ jobs: - uses: actions/checkout@v4 - name: Deploy to AKS - uses: ./.github/actions/deploy-environment-aks - id: deploy_aks + uses: ./.github/actions/deploy-environment + id: deploy with: environment: aks-review image-tag: ${{ github.sha }} @@ -115,8 +86,8 @@ jobs: | Access Your Teaching Qualifications | | | Check A Teacher's Record | | - deploy_aks: - name: Deploy to ${{ matrix.environment }} environment for AKS + deploy: + name: Deploy to ${{ matrix.environment }} environment runs-on: ubuntu-latest if: github.event_name == 'push' concurrency: deploy_${{ matrix.environment }} @@ -133,18 +104,18 @@ jobs: - uses: actions/checkout@v4 - name: Deploy to AKS - uses: ./.github/actions/deploy-environment-aks + uses: ./.github/actions/deploy-environment id: deploy with: environment: ${{ matrix.environment }} image-tag: ${{ github.sha }} azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} - deploy_production_aks: - name: Deploy to production environment for AKS + deploy_production: + name: Deploy to production environment runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.event_name == 'push' - needs: [build_image, deploy_aks] + needs: [build_image, deploy] environment: name: aks-production url: ${{ steps.deploy.outputs.environment_url }} @@ -153,146 +124,9 @@ jobs: - uses: actions/checkout@v4 - name: Deploy to AKS - uses: ./.github/actions/deploy-environment-aks + uses: ./.github/actions/deploy-environment id: deploy with: environment: aks-production image-tag: ${{ github.sha }} azure-credentials: ${{ secrets.AZURE_CREDENTIALS }} - - set_matrix: - name: Set deployment matrix - runs-on: ubuntu-latest - needs: [build_image] - outputs: - deployment_matrix: ${{ steps.set-matrix.outputs.deployment_matrix }} - steps: - - id: set-matrix - run: | - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - DEPLOYMENT_MATRIX="{ 'environment': ['${{ github.event.inputs.environment }}'] }" - else - DEPLOYMENT_MATRIX="{ 'environment': ['dev'] }" - fi - echo "deployment_matrix=$DEPLOYMENT_MATRIX" >> $GITHUB_OUTPUT - - deploy_non_prod: - name: Deploy to ${{ matrix.environment }} environment - runs-on: ubuntu-latest - if: (github.ref == 'refs/heads/main' && github.event_name == 'push') || github.event_name == 'workflow_dispatch' - concurrency: deploy_${{ matrix.environment }} - needs: [build_image, set_matrix] - strategy: - fail-fast: false # this is necessary to prevent early termination of terraform deployments that will result in tfstate locks - max-parallel: 3 - matrix: ${{ fromJson(needs.set_matrix.outputs.deployment_matrix) }} - environment: - name: ${{ matrix.environment }} - url: ${{ steps.deploy.outputs.environment_url }} - - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/deploy-environment - id: deploy - with: - environment_name: ${{ matrix.environment }} - image_name_tag: ${{ needs.build_image.outputs.image_name_tag }} - image_tag: ${{ github.sha }} - azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} - site_up_retries: 240 - - - run: | - echo Sleeping... - sleep 600 - echo Finished sleeping - - - uses: ./.github/actions/smoke-test - id: smoke-test - with: - environment: ${{ matrix.environment }} - azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} - url: ${{ steps.deploy.outputs.environment_url }} - check_url: ${{ steps.deploy.outputs.check_service_url }} - - deploy_prod: - name: Deploy to production environment - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - concurrency: deploy_prod - needs: [build_image, deploy_aks] - environment: - name: production - url: ${{ steps.deploy.outputs.environment_url }} - - steps: - - uses: actions/checkout@v4 - - - uses: ./.github/actions/deploy-environment - id: deploy - with: - environment_name: production - image_name_tag: ${{ needs.build_image.outputs.image_name_tag }} - image_tag: ${{ github.sha }} - azure_credentials: ${{ secrets.AZURE_CREDENTIALS }} - - notify_slack_of_failures: - name: Notify Slack of failures - runs-on: ubuntu-latest - needs: [build_image, deploy_review_app, set_matrix, deploy_non_prod, deploy_prod] - environment: ${{ needs.deploy_nonprod.outputs.environment_name || 'dev' }} - env: - ENVIRONMENT_NAME: ${{ needs.deploy_nonprod.outputs.environment_name || 'dev' }} - if: failure() - steps: - - uses: actions/checkout@v4 - - - name: Set Environment variables - shell: bash - working-directory: terraform - run: | - if ${{ needs.build_image.result == 'failure' }} - then - job=build_image - elif ${{ needs.deploy_review_app.result == 'failure' }} - then - job=deploy_review_app - review=true - elif ${{ needs.set_matrix.result == 'failure' }} - then - job=set_matrix - elif ${{ needs.deploy_non_prod.result == 'failure' }} - then - job=deploy_non_prod - elif ${{ needs.deploy_prod.result == 'failure' }} - then - job=deploy_prod - fi - - tf_vars_file=workspace_variables/${{ env.ENVIRONMENT_NAME }}.tfvars.json - echo "KEY_VAULT_NAME=$(jq -r '.key_vault_name' ${tf_vars_file})" >> $GITHUB_ENV - echo "JOB=${job}" >> $GITHUB_ENV - echo "REVIEW=${review}" >> $GITHUB_ENV - - - uses: Azure/login@v1 - with: - creds: ${{ secrets.AZURE_CREDENTIALS }} - - - uses: DfE-Digital/keyvault-yaml-secret@v1 - id: get_monitoring_secret - with: - keyvault: ${{ env.KEY_VAULT_NAME }} - secret: MONITORING - key: SLACK_WEBHOOK - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Notify Slack channel on job failure - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_USERNAME: CI Deployment - SLACK_TITLE: Deployment of access-your-teaching-qualifications ${{ env.REVIEW && 'review' }} failed - SLACK_MESSAGE: Job ${{ env.JOB }} failed - SLACK_WEBHOOK: ${{ steps.get_monitoring_secret.outputs.SLACK_WEBHOOK }} - SLACK_COLOR: failure - SLACK_FOOTER: Sent from Build and Deploy workflow diff --git a/Makefile b/Makefile index f8f6f710..81a952b9 100644 --- a/Makefile +++ b/Makefile @@ -174,6 +174,7 @@ ci: ## Run in automation environment $(eval DISABLE_PASSCODE=true) $(eval AUTO_APPROVE=-auto-approve) $(eval SP_AUTH=true) + $(eval SKIP_AZURE_LOGIN=true) ### AKS ### # Note: AKS-specific files are found at the following locations, and do not conflict diff --git a/terraform/domains/infrastructure/vendor/modules/domains b/terraform/domains/infrastructure/vendor/modules/domains deleted file mode 160000 index 7999271f..00000000 --- a/terraform/domains/infrastructure/vendor/modules/domains +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7999271faa00de5db59e1721d14fa59ef67c15c8