diff --git a/.github/test-matrix.json b/.github/test-matrix.json new file mode 100644 index 0000000..b0e4319 --- /dev/null +++ b/.github/test-matrix.json @@ -0,0 +1,64 @@ +{ + "arch": [ + { + "runner": "ubuntu-latest", + "rie": "aws-lambda-rie", + "label": "x64" + }, + { + "runner": "ubuntu-24.04-arm", + "rie": "aws-lambda-rie-arm64", + "label": "arm64" + } + ], + "distro_config": [ + { + "distro": "al2023", + "distro_version": "al2023", + "runtime_version": "3.4", + "executable": "/usr/local/bin/aws_lambda_ric" + }, + { + "distro": "al2023", + "distro_version": "al2023", + "runtime_version": "3.3", + "executable": "/usr/local/bin/aws_lambda_ric" + }, + { + "distro": "alpine", + "distro_version": "3.23", + "runtime_version": "3.4", + "executable": "/usr/local/bundle/bin/aws_lambda_ric" + }, + { + "distro": "alpine", + "distro_version": "3.23", + "runtime_version": "3.3", + "executable": "/usr/local/bundle/bin/aws_lambda_ric" + }, + { + "distro": "debian", + "distro_version": "bookworm", + "runtime_version": "3.4", + "executable": "/usr/local/bundle/bin/aws_lambda_ric" + }, + { + "distro": "debian", + "distro_version": "bookworm", + "runtime_version": "3.3", + "executable": "/usr/local/bundle/bin/aws_lambda_ric" + }, + { + "distro": "ubuntu", + "distro_version": "24.04", + "runtime_version": "3.4", + "executable": "/usr/local/bin/aws_lambda_ric" + }, + { + "distro": "ubuntu", + "distro_version": "24.04", + "runtime_version": "3.3", + "executable": "/usr/local/bin/aws_lambda_ric" + } + ] +} diff --git a/.github/workflows/bootstrap-alarms.yml b/.github/workflows/bootstrap-alarms.yml new file mode 100644 index 0000000..c619159 --- /dev/null +++ b/.github/workflows/bootstrap-alarms.yml @@ -0,0 +1,107 @@ +name: bootstrap-alarms + +permissions: + id-token: write + contents: read + +on: + pull_request: + branches: [ '*' ] + workflow_dispatch: + +env: + AWS_REGION: ${{ secrets.AWS_REGION }} + ALARM_NAMESPACE: GitHubActions + +jobs: + bootstrap: + runs-on: ubuntu-latest + env: + COMPOSITE_ALARM_NAME: GitHubActions-${{ github.repository_owner }}-${{ github.event.repository.name }}-integration-tests-aggregate + + steps: + - name: Debug OIDC token + run: | + echo "GitHub ref: ${{ github.ref }}" + echo "GitHub event name: ${{ github.event_name }}" + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0 + with: + role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Create individual metric alarms + run: | + set -euo pipefail + + MATRIX_FILE=".github/test-matrix.json" + ALARM_NAMES=() + + # Iterate over every arch × distro_config permutation from the shared matrix + for row in $(jq -c ' + .arch[] as $a | + .distro_config[] as $d | + { arch: $a.label, distro: $d.distro, distro_version: $d.distro_version, runtime_version: $d.runtime_version } + ' "$MATRIX_FILE"); do + + arch=$(echo "$row" | jq -r '.arch') + distro=$(echo "$row" | jq -r '.distro') + distro_version=$(echo "$row" | jq -r '.distro_version') + runtime_version=$(echo "$row" | jq -r '.runtime_version') + + ALARM_NAME="GitHubActions-ruby-ric-${distro}-${distro_version}-ruby${runtime_version}-${arch}" + + echo "Creating alarm: ${ALARM_NAME}" + + # Alarms if no success metric is received within 3 days + # Uses 1-day periods with 3 evaluation periods for faster state transitions + aws cloudwatch put-metric-alarm \ + --alarm-name "${ALARM_NAME}" \ + --alarm-description "Integration test: ${distro} ${distro_version} / ruby ${runtime_version} (${arch})" \ + --namespace "${ALARM_NAMESPACE}" \ + --metric-name "TestResult" \ + --dimensions "Name=Distro,Value=${distro}" "Name=DistroVersion,Value=${distro_version}" "Name=RuntimeVersion,Value=${runtime_version}" "Name=Arch,Value=${arch}" \ + --statistic Sum \ + --period 86400 \ + --evaluation-periods 3 \ + --datapoints-to-alarm 3 \ + --threshold 1 \ + --comparison-operator LessThanThreshold \ + --treat-missing-data breaching + + ALARM_NAMES+=("${ALARM_NAME}") + done + + # Save alarm names for the composite alarm step + printf '%s\n' "${ALARM_NAMES[@]}" > /tmp/alarm_names.txt + + - name: Create composite aggregate alarm + run: | + set -euo pipefail + + mapfile -t ALARM_NAMES < /tmp/alarm_names.txt + + # Build the composite alarm rule: triggers if ANY sub-alarm is in ALARM or INSUFFICIENT_DATA + RULE="" + for name in "${ALARM_NAMES[@]}"; do + if [ -n "$RULE" ]; then + RULE="${RULE} OR " + fi + RULE="${RULE}(ALARM(\"${name}\") OR INSUFFICIENT_DATA(\"${name}\"))" + done + + echo "Composite alarm rule:" + echo "${RULE}" + + aws cloudwatch put-composite-alarm \ + --alarm-name "${COMPOSITE_ALARM_NAME}" \ + --alarm-description "Aggregate alarm for all Ruby RIC integration test permutations" \ + --alarm-rule "${RULE}" \ + --actions-enabled \ + --alarm-actions "${{ secrets.AWS_ALARM_TARGET_ARN }}" \ + --insufficient-data-actions "${{ secrets.AWS_ALARM_TARGET_ARN }}" + + echo "Composite alarm '${COMPOSITE_ALARM_NAME}' created successfully." diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index f0757d1..a6d72aa 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -1,6 +1,7 @@ name: integration-tests permissions: + id-token: write contents: read on: @@ -8,64 +9,42 @@ on: branches: [main] pull_request: branches: ['*'] + schedule: + - cron: '0 8 * * 1-5' # Every workday (Mon-Fri) at 08:00 UTC workflow_dispatch: jobs: + load-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set.outputs.matrix }} + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Load test matrix + id: set + run: | + MATRIX=$(jq -c '.' .github/test-matrix.json) + echo "matrix=${MATRIX}" >> "$GITHUB_OUTPUT" + integration-test: + needs: load-matrix runs-on: ${{ matrix.arch.runner }} strategy: fail-fast: false - matrix: - arch: - - runner: ubuntu-latest - rie: aws-lambda-rie - label: x64 - - runner: ubuntu-24.04-arm - rie: aws-lambda-rie-arm64 - label: arm64 - distro_config: - # al2023 - - distro: al2023 - distro_version: "al2023" - runtime_version: "3.4" - executable: /usr/local/bin/aws_lambda_ric - - distro: al2023 - distro_version: "al2023" - runtime_version: "3.3" - executable: /usr/local/bin/aws_lambda_ric - # Alpine - - distro: alpine - distro_version: "3.23" - runtime_version: "3.4" - executable: /usr/local/bundle/bin/aws_lambda_ric - - distro: alpine - distro_version: "3.23" - runtime_version: "3.3" - executable: /usr/local/bundle/bin/aws_lambda_ric - # Debian - - distro: debian - distro_version: bookworm - runtime_version: "3.4" - executable: /usr/local/bundle/bin/aws_lambda_ric - - distro: debian - distro_version: bookworm - runtime_version: "3.3" - executable: /usr/local/bundle/bin/aws_lambda_ric - # Ubuntu - - distro: ubuntu - distro_version: "24.04" - runtime_version: "3.4" - executable: /usr/local/bin/aws_lambda_ric - - distro: ubuntu - distro_version: "24.04" - runtime_version: "3.3" - executable: /usr/local/bin/aws_lambda_ric + matrix: ${{ fromJson(needs.load-matrix.outputs.matrix) }} name: "${{ matrix.distro_config.distro }} ${{ matrix.distro_config.distro_version }} / ruby ${{ matrix.distro_config.runtime_version }} (${{ matrix.arch.label }})" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@ec61189d14ec14c8efccab744f656cffd0e33f37 # v6.1.0 + with: + role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + - name: Download RIE run: | mkdir -p .scratch @@ -131,6 +110,16 @@ jobs: echo "=== Tester container logs ===" docker logs "${TEST_NAME}-tester" 2>&1 || true + - name: Publish success metric + if: success() + run: | + aws cloudwatch put-metric-data \ + --namespace "GitHubActions" \ + --metric-name "TestResult" \ + --dimensions "Distro=${{ matrix.distro_config.distro }},DistroVersion=${{ matrix.distro_config.distro_version }},RuntimeVersion=${{ matrix.distro_config.runtime_version }},Arch=${{ matrix.arch.label }}" \ + --value 1 \ + --unit Count + - name: Cleanup if: always() run: |