---
name: enos

on:
  # Only trigger this working using workflow_call. This workflow requires many
  # secrets that must be inherited from the caller workflow.
  workflow_call:
    inputs:
      # The name of the artifact that we're going to use for testing. This should
      # match exactly to build artifacts uploaded to Github and Artifactory.
      build-artifact-name:
        required: true
        type: string
      # The base name of the file in ./github/enos-run-matrices that we use to
      # determine which scenarios to run for the build artifact.
      #
      # They are named in the format of:
      #   $caller_workflow_name-$artifact_source-$vault_edition-$platform-$arch-$packing_type
      #
      # Where each are:
      #   caller_workflow_name: the Github Actions workflow that is calling
      #     this one
      #   artifact_source: where we're getting the artifact from. Either
      #     "github" or "artifactory"
      #   vault_edition: which edition of vault that we're testing. e.g. "oss"
      #     or "ent"
      #   platform: the vault binary target platform, e.g. "linux" or "macos"
      #   arch: the vault binary target architecture, e.g. "arm64" or "amd64"
      #   packing_type: how vault binary is packaged, e.g. "zip", "deb", "rpm"
      #
      # Examples:
      #   build-github-oss-linux-amd64-zip
      matrix-file-name:
        required: true
        type: string
      # The test group we want to run. This corresponds to the test_group attribute
      # defined in the enos-run-matrices files.
      matrix-test-group:
        default: 0
        type: string
      runs-on:
        # NOTE: The value should be JSON encoded as that's the only way we can
        # pass arrays with workflow_call.
        type: string
        required: false
        default: '"ubuntu-latest"'
      ssh-key-name:
        type: string
        default: ${{ github.event.repository.name }}-ci-ssh-key
      # Which edition of Vault we're using. e.g. "oss", "ent", "ent.hsm.fips1402"
      vault-edition:
        required: true
        type: string
      # The Git commit SHA used as the revision when building vault
      vault-revision:
        required: true
        type: string

jobs:
  metadata:
    runs-on: ${{ fromJSON(inputs.runs-on) }}
    outputs:
      build-date: ${{ steps.metadata.outputs.build-date }}
      matrix: ${{ steps.metadata.outputs.matrix }}
      version: ${{ steps.metadata.outputs.version }}
      version-minor: ${{ steps.metadata.outputs.matrix }}
    env:
      # Pass the vault edition as VAULT_METADATA so the CI make targets can create
      # values that consider the edition.
      VAULT_METADATA: ${{ inputs.vault-edition }}
      # Pass in the matrix and matrix group for filtering
      MATRIX_FILE: ./.github/enos-run-matrices/${{ inputs.matrix-file-name }}.json
      MATRIX_TEST_GROUP: ${{ inputs.matrix-test-group }}
    steps:
      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
        with:
          ref: ${{ inputs.vault-revision }}
      - id: metadata
        run: |
          # shellcheck disable=SC2129
          echo "build-date=$(make ci-get-date)" >> "$GITHUB_OUTPUT"
          echo "version=$(make ci-get-version)" >> "$GITHUB_OUTPUT"
          echo "matrix=$(make ci-filter-matrix)" >> "$GITHUB_OUTPUT"

  # Run the Enos test scenarios
  run:
    needs: metadata
    strategy:
      fail-fast: false # don't fail as that can skip required cleanup steps for jobs
      matrix: ${{ fromJson(needs.metadata.outputs.matrix) }}
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
      # Pass in enos variables
      ENOS_VAR_aws_region: ${{ matrix.aws_region }}
      ENOS_VAR_aws_ssh_keypair_name: ${{ inputs.ssh-key-name }}
      ENOS_VAR_aws_ssh_private_key_path: ./support/private_key.pem
      ENOS_VAR_tfc_api_token: ${{ secrets.TF_API_TOKEN }}
      ENOS_VAR_artifactory_username: ${{ secrets.ARTIFACTORY_USER }}
      ENOS_VAR_artifactory_token: ${{ secrets.ARTIFACTORY_TOKEN }}
      ENOS_VAR_terraform_plugin_cache_dir: ./support/terraform-plugin-cache
      ENOS_VAR_vault_artifact_path: ./support/downloads/${{ inputs.build-artifact-name }}
      ENOS_VAR_vault_build_date: ${{ needs.metadata.outputs.build-date }}
      ENOS_VAR_vault_product_version: ${{ needs.metadata.outputs.version }}
      ENOS_VAR_vault_revision: ${{ inputs.vault-revision }}
      ENOS_VAR_vault_license_path: ./support/vault.hclic
      ENOS_DEBUG_DATA_ROOT_DIR: /tmp/enos-debug-data
    steps:
      - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
      - uses: hashicorp/setup-terraform@v2
        with:
          # the Terraform wrapper will break Terraform execution in Enos because
          # it changes the output to text when we expect it to be JSON.
          terraform_wrapper: false
      - uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_CI }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_CI }}
          aws-region: ${{ matrix.aws_region }}
          role-to-assume: ${{ secrets.AWS_ROLE_ARN_CI }}
          role-skip-session-tagging: true
          role-duration-seconds: 3600
      - uses: hashicorp/action-setup-enos@v1
        with:
          github-token: ${{ secrets.ELEVATED_GITHUB_TOKEN }}
      - name: Prepare scenario dependencies
        id: prepare_scenario
        run: |
          mkdir -p "./enos/support/terraform-plugin-cache"
          echo "${{ secrets.SSH_KEY_PRIVATE_CI }}" > "./enos/support/private_key.pem"
          chmod 600 "./enos/support/private_key.pem"
          echo "debug_data_artifact_name=enos-debug-data_$(echo "${{ matrix.scenario }}" | sed -e 's/ /_/g' | sed -e 's/:/=/g')" >> "$GITHUB_OUTPUT"
      - if: contains(inputs.matrix-file-name, 'github')
        uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
        with:
          name: ${{ inputs.build-artifact-name }}
          path: ./enos/support/downloads
      - if: contains(inputs.matrix-file-name, 'ent')
        name: Configure Vault license
        run: echo "${{ secrets.VAULT_LICENSE }}" > ./enos/support/vault.hclic || true
      - name: Run Enos scenario
        id: run
        # Continue once and retry to handle occasional blips when creating
        # infrastructure.
        continue-on-error: true
        run: enos scenario run --timeout 60m0s --chdir ./enos ${{ matrix.scenario }}
      - name: Retry Enos scenario if necessary
        id: run_retry
        if: steps.run.outcome == 'failure'
        continue-on-error: true
        run: enos scenario run --timeout 60m0s --chdir ./enos ${{ matrix.scenario }}
      - name: Upload Debug Data
        if: failure()
        uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
        with:
          # The name of the artifact is the same as the matrix scenario name with the spaces replaced with underscores and colons replaced by equals.
          name: ${{ steps.prepare_scenario.outputs.debug_data_artifact_name }}
          path: ${{ env.ENOS_DEBUG_DATA_ROOT_DIR }}
          retention-days: 30
        continue-on-error: true
      - name: Ensure scenario has been destroyed
        id: destroy
        if: ${{ always() }}
        # With Enos version 0.0.11 the destroy step returns an error if the infrastructure
        # is already destroyed by enos run. So temporarily setting it to continue on error in GHA
        continue-on-error: true
        run: enos scenario destroy --timeout 60m0s --chdir ./enos ${{ matrix.scenario }}
      - name: Clean up Enos runtime directories
        id: cleanup
        if: ${{ always() }}
        continue-on-error: true
        run: |
          rm -rf /tmp/enos*
          rm -rf ./enos/support
          rm -rf ./enos/.enos
      # Send a Slack notification to #feed-vault-enos-failures if the 'run' step fails.
      # There is an incoming webhook set up on the "Enos Vault Failure Bot" Slackbot https://api.slack.com/apps/A05E31CH1LG/incoming-webhooks
      - name: Send Slack notification on Enos run failure
        uses: hashicorp/actions-slack-status@v1
        if: ${{ always() }}
        with:
          failure-message: "An Enos scenario `run` failed. \nTriggering event: `${{ github.event_name }}` \nActor: `${{ github.actor }}`"
          status: ${{ steps.run.outcome }}
          slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
      # Send a Slack notification to #feed-vault-enos-failures if the 'run_retry' step fails.
      - name: Send Slack notification on Enos run_retry failure
        uses: hashicorp/actions-slack-status@v1
        if: ${{ always() }}
        with:
          failure-message: "An Enos scenario `run_retry` failed. \nTriggering event: `${{ github.event_name }}` \nActor: `${{ github.actor }}`"
          status: ${{ steps.run_retry.outcome }}
          slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
      # Send a Slack notification to #feed-vault-enos-failures if the 'destroy' step fails.
      - name: Send Slack notification on Enos destroy failure
        uses: hashicorp/actions-slack-status@v1
        if: ${{ always() }}
        with:
          failure-message: "An Enos scenario `destroy` failed. \nTriggering event: `${{ github.event_name }}` \nActor: `${{ github.actor }}`"
          status: ${{ steps.destroy.outcome }}
          slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
