Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 101 additions & 103 deletions .github/workflows/terraform-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ env:
TERRAFORM_AWS_MODULES_DIR: modules/terraform/aws

jobs:
terraform-test:
terraform-integration:
environment: terraform-privileged
permissions:
contents: read
Expand Down Expand Up @@ -59,119 +59,117 @@ jobs:
env:
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

setup-matrix:
runs-on: ubuntu-latest
name: Setup terraform plan matrix for test scenarios
steps:
- name: Checkout PR head
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
persist-credentials: false

- name: Setup test matrix scenarios
id: setup-matrix-scenarios
- name: Compute scenarios to plan
id: scenarios
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
FULL_MATRIX_QUERY: |
find "$GITHUB_WORKSPACE/scenarios/" -name '*.tfvars' | grep 'azure' | awk -F'/' '{
split($11, file_name, ".");
split(file_name[1], cloud_region, "-");
region = (length(cloud_region) > 1) ? substr($11, index($11, "-") + 1) : "";
cloud = cloud_region[1];
gsub(".json", "", region);
printf "{\"cloud\": \"%s\", \"file_name\": \"%s\", \"scenario_type\": \"%s\", \"scenario_name\": \"%s\"}\n", cloud, file_name[1], $8, $9;
}' | sort -u | jq -s 'unique'
Comment on lines +68 to +76
run: |
set -eux
matrix=$(find $GITHUB_WORKSPACE/scenarios/ -name "*.tfvars" | grep 'azure' | awk -F'/' '{split($11, file_name, "."); split(file_name[1], cloud_region, "-");region= (length(cloud_region) > 1) ? substr($11, index($11, "-") + 1) : ""; cloud=cloud_region[1]; gsub(".json", "", region); print "{\"cloud\": \"" cloud "\", \"file_name\": \"" file_name[1] "\", " (region != "" ? "\"region\": \"" region "\", " : "") "\"scenario_type\": \"" $8 "\", \"scenario_name\": \"" $9 "\"},"}' | sort | uniq | sed 's/,$/,/')
matrix_array="[${matrix%,}]"
set -eu
matrix_array=$(bash -c "$FULL_MATRIX_QUERY")

file_changes=$(git diff --name-only -r origin/main HEAD)
echo "PR file changes: $file_changes"
# Ask the GitHub API for the PR's changed files. This avoids depending
# on git remote refs (fork PRs don't have origin/main pointing at the
# base branch), and doesn't require fetch-depth: 0.
file_changes=$(gh api --paginate "/repos/$REPO/pulls/$PR_NUMBER/files" --jq '.[].filename')
echo "PR file changes:"
echo "$file_changes"

if [ -z "$file_changes" ]; then
matrix_combinations=null
echo "matrix_combinations=$matrix_combinations" >> "$GITHUB_OUTPUT"
else
cloud_changes=$(echo "$file_changes" | grep -E '\.tf$' | awk -F'/' '{print $3}' | uniq)
echo "File changes related to cloud: $cloud_changes"

run_all_tests=false
module_matrix='[]'
scenario_matrix='[]'
if [ $(echo "$cloud_changes" | wc -w) -eq 1 ]; then
cloud=$(echo "$cloud_changes" | cut -d' ' -f1)
module_matrix=$(echo "$matrix_array" | jq --arg cloud_value "$cloud" '. | map(select(.cloud == $cloud_value))')
elif [ $(echo "$cloud_changes" | wc -w) -eq 0 ]; then
run_all_tests=false
else
run_all_tests=true
module_matrix=$(echo "$matrix_array")
fi
echo "module_matrix: $module_matrix"
if [ "$run_all_tests" = false ]; then
changed_scenario_names=$(echo "$file_changes" | grep -E '^scenarios/' | awk -F'/' '{print $3}' | tr ' ' '\n' | sort -u)
echo "Test scenarios changed: $changed_scenario_names"
for scenario_name in $changed_scenario_names; do
filtered_objects=$(echo "$matrix_array" | jq --arg scenario_value "$scenario_name" '. | map(select(.scenario_name == $scenario_value))')
scenario_matrix=$(echo "$scenario_matrix $filtered_objects" | jq -s 'flatten | unique')
done
fi
updated_matrix=$(echo "$module_matrix $scenario_matrix" | jq -s 'flatten | unique')
echo "Test scenarios to run: $updated_matrix"
updated_matrix="${updated_matrix//$'\n'/''}"
matrix_combinations="{\"include\": ${updated_matrix}}"
echo "matrix_combinations=$matrix_combinations" >> "$GITHUB_OUTPUT"
echo "scenarios=[]" >> "$GITHUB_OUTPUT"
echo "No file changes -> skipping plans."
exit 0
fi
echo "matrix_combinations: $matrix_combinations"
outputs:
matrix-combinations: ${{ steps.setup-matrix-scenarios.outputs.matrix_combinations }}

terraform-plan:
needs: setup-matrix
environment: terraform-privileged
permissions:
id-token: write
contents: read
if: ${{ needs.setup-matrix.result == 'success' && needs.setup-matrix.outputs.matrix-combinations != '' && needs.setup-matrix.outputs.matrix-combinations != 'null' }}
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix-combinations) }}
runs-on: ubuntu-latest
name: ${{ matrix.cloud }}-${{ matrix.scenario_type }}-${{ matrix.scenario_name }} ${{ matrix.region }}
cloud_changes=$(echo "$file_changes" | grep -E '\.tf$' | awk -F'/' '{print $3}' | uniq || true)
echo "File changes related to cloud: $cloud_changes"

steps:
- name: Checkout PR head
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
persist-credentials: false
run_all_tests=false
module_matrix='[]'
scenario_matrix='[]'
if [ "$(echo "$cloud_changes" | wc -w)" -eq 1 ]; then
cloud=$(echo "$cloud_changes" | cut -d' ' -f1)
module_matrix=$(echo "$matrix_array" | jq --arg c "$cloud" 'map(select(.cloud == $c))')
elif [ "$(echo "$cloud_changes" | wc -w)" -eq 0 ]; then
run_all_tests=false
else
run_all_tests=true
module_matrix=$matrix_array
fi

- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.10.0
if [ "$run_all_tests" = false ]; then
changed_scenario_names=$(echo "$file_changes" | grep -E '^scenarios/' | awk -F'/' '{print $3}' | tr ' ' '\n' | sort -u || true)
for scenario_name in $changed_scenario_names; do
filtered=$(echo "$matrix_array" | jq --arg s "$scenario_name" 'map(select(.scenario_name == $s))')
scenario_matrix=$(echo "$scenario_matrix $filtered" | jq -s 'flatten | unique')
done
fi

- name: Get job id and set env
run: |
echo "TERRAFORM_INPUT_FILE=$GITHUB_WORKSPACE/scenarios/${{ matrix.scenario_type }}/${{ matrix.scenario_name }}/terraform-inputs/${{ matrix.file_name }}.tfvars" >> "$GITHUB_ENV"
echo "TERRAFORM_TEST_INPUT_FILE=$GITHUB_WORKSPACE/scenarios/${{ matrix.scenario_type }}/${{ matrix.scenario_name }}/terraform-test-inputs/${{ matrix.file_name }}.json" >> "$GITHUB_ENV"
echo "TERRAFORM_MODULES_DIR=modules/terraform/${{ matrix.cloud }}" >> "$GITHUB_ENV"
final=$(echo "$module_matrix $scenario_matrix" | jq -s 'flatten | unique')
echo "Scenarios to plan: $final"
echo "scenarios=$(echo "$final" | jq -c '.')" >> "$GITHUB_OUTPUT"

- name: Create JSON Input
- name: Terraform Plan for every scenario
if: steps.scenarios.outputs.scenarios != '[]'
env:
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
SCENARIOS_JSON: ${{ steps.scenarios.outputs.scenarios }}
run: |
INPUT_JSON_STRING=$(jq -c '. + {creation_time: (now | todateiso8601)}' "${{ env.TERRAFORM_TEST_INPUT_FILE }}")
echo "INPUT_JSON=$INPUT_JSON_STRING" >> "$GITHUB_ENV"
set -eu

# Collect failures instead of bailing at the first one, so a single
# bad scenario doesn't hide the rest.
FAILED=""

# Iterate via process substitution so we can mutate FAILED without
# losing it to a subshell.
while IFS=$'\t' read -r cloud scenario_type scenario_name file_name; do
modules_dir="modules/terraform/$cloud"
tfvars="$GITHUB_WORKSPACE/scenarios/$scenario_type/$scenario_name/terraform-inputs/${file_name}.tfvars"
test_input="$GITHUB_WORKSPACE/scenarios/$scenario_type/$scenario_name/terraform-test-inputs/${file_name}.json"

label="$cloud/$scenario_type/$scenario_name/$file_name"
echo "::group::plan $label"

if [ ! -f "$tfvars" ]; then
echo "::error::missing tfvars: $tfvars"
FAILED="${FAILED}${label}\n"
echo "::endgroup::"
continue
fi
if [ ! -f "$test_input" ]; then
echo "::error::missing test input: $test_input"
FAILED="${FAILED}${label}\n"
echo "::endgroup::"
continue
fi

- name: Azure login
if: ${{ matrix.cloud == 'azure' }}
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_MI }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
INPUT_JSON=$(jq -c '. + {creation_time: (now | todateiso8601)}' "$test_input")

- name: Terraform Init
working-directory: ${{ env.TERRAFORM_MODULES_DIR }}
run: terraform init
if ! (
cd "$modules_dir"
terraform init -input=false
terraform plan \
-var-file "$tfvars" \
-var="json_input=$INPUT_JSON"
); then
FAILED="${FAILED}${label}\n"
fi
echo "::endgroup::"
done < <(echo "$SCENARIOS_JSON" | jq -r '.[] | [.cloud, .scenario_type, .scenario_name, .file_name] | @tsv')

- name: Terraform Plan
working-directory: ${{ env.TERRAFORM_MODULES_DIR }}
run: |
terraform plan -var-file "$TERRAFORM_INPUT_FILE" -var="json_input=$INPUT_JSON"
env:
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
CLOUD: ${{ matrix.cloud }}
if [ -n "$FAILED" ]; then
echo "::error::Failed scenarios:"
printf ' %b' "$FAILED"
exit 1
fi
Loading