From 7622e671082151abca55df7046bba9f895e867c7 Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 01:08:54 +0200 Subject: [PATCH 1/9] test: fix ec2 tests --- .github/workflows/terraform.yml | 10 +++++++++- .../aws/ec2/tests/provider.tftest.hcl | 1 - .../multi-runner/tests/config-resolution.tftest.hcl | 8 ++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml index 8aeeba9944..b31321c2b2 100644 --- a/.github/workflows/terraform.yml +++ b/.github/workflows/terraform.yml @@ -54,9 +54,17 @@ env: multi-runner-scale-set external-managed-ssm-secrets TEST_MODULES: | - modules/runners + modules/compute-providers/aws/ec2 + modules/compute-providers/aws/ec2/trust-policy modules/multi-runner modules/orchestration-providers/scale-set + modules/orchestration-providers/webhook + modules/orchestration-providers/webhook/job-retry + modules/orchestration-providers/webhook/pool + modules/orchestration-providers/webhook/scale-runners + modules/runner-config + modules/runner-config/ssm-housekeeper + modules/runners jobs: verify_modules: diff --git a/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl b/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl index a8258163b8..7cfe99e1fa 100644 --- a/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl +++ b/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl @@ -95,7 +95,6 @@ run "separates_control_plane_contract_from_ec2_resources" { command = plan assert { - condition = toset(keys(output.provider)) == toset(["environment_variables", "policies", "resources"]) error_message = "The EC2 provider contract must expose only integration and resource data; its module identity must not be repeated in the output." } diff --git a/modules/multi-runner/tests/config-resolution.tftest.hcl b/modules/multi-runner/tests/config-resolution.tftest.hcl index 9d4e913456..425717f5c6 100644 --- a/modules/multi-runner/tests/config-resolution.tftest.hcl +++ b/modules/multi-runner/tests/config-resolution.tftest.hcl @@ -450,6 +450,10 @@ run "v2_inputs_do_not_require_legacy_arguments" { } } } + runner = { + os = "linux" + architecture = "x64" + } compute_provider = { aws = { ec2 = { @@ -804,6 +808,10 @@ run "scale_set_lane_requires_owner_for_non_enterprise_registration" { name = "scale-missing-owner" } } + runner = { + os = "linux" + architecture = "x64" + } compute_provider = { aws = { ec2 = { From d8f6f414f9f49259fc90a3939f0b9922df07f143 Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 01:37:24 +0200 Subject: [PATCH 2/9] fix: fix test --- .../aws/ec2/tests/provider.tftest.hcl | 7 +++++++ .../aws/ec2/trust-policy/assume-role.tf | 4 +++- .../webhook/job-retry/tests/job-retry.tftest.hcl | 8 ++++++++ .../webhook/pool/tests/provider.tftest.hcl | 8 ++++++-- modules/runner-config/compute-provider.tf | 3 +-- modules/runner-config/tests/pool.tftest.hcl | 1 + modules/runner-config/validations.tf | 8 -------- modules/runner-config/variables.compute-provider.tf | 13 ------------- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl b/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl index 7cfe99e1fa..e1ce0630d5 100644 --- a/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl +++ b/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl @@ -95,6 +95,13 @@ run "separates_control_plane_contract_from_ec2_resources" { command = plan assert { + condition = toset(keys(output.provider)) == toset([ + "capabilities", + "environment_variables", + "policies", + "resources", + "type", + ]) error_message = "The EC2 provider contract must expose only integration and resource data; its module identity must not be repeated in the output." } diff --git a/modules/compute-providers/aws/ec2/trust-policy/assume-role.tf b/modules/compute-providers/aws/ec2/trust-policy/assume-role.tf index bea81c1b9e..7be78e1c17 100644 --- a/modules/compute-providers/aws/ec2/trust-policy/assume-role.tf +++ b/modules/compute-providers/aws/ec2/trust-policy/assume-role.tf @@ -13,6 +13,8 @@ data "aws_iam_policy_document" "default" { data "aws_iam_policy_document" "assume_role" { source_policy_documents = compact([ data.aws_iam_policy_document.default.json, - var.additional_trust_policy_json, + var.additional_trust_policy_json != null && can(jsondecode(var.additional_trust_policy_json)) + ? var.additional_trust_policy_json + : null, ]) } diff --git a/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl b/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl index 0e797b9a04..803d2a6de5 100644 --- a/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl +++ b/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl @@ -230,6 +230,14 @@ run "does_not_enable_partial_vpc_configuration" { command = plan variables { + storage_provider = { + aws = { + ssm = { + kms_key_id = null + } + } + } + config = { prefix = "job-retry-test" aws_partition = "aws" diff --git a/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl b/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl index 25fa6c8a14..84c91cfef9 100644 --- a/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl +++ b/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl @@ -228,8 +228,12 @@ run "omits_optional_kms_statement" { command = plan variables { - config = merge(var.config, { - kms_key_id = null + storage_provider = merge(var.storage_provider, { + aws = merge(var.storage_provider.aws, { + ssm = merge(var.storage_provider.aws.ssm, { + kms_key_id = null + }) + }) }) } diff --git a/modules/runner-config/compute-provider.tf b/modules/runner-config/compute-provider.tf index bffc43b814..d9bd644d6b 100644 --- a/modules/runner-config/compute-provider.tf +++ b/modules/runner-config/compute-provider.tf @@ -3,11 +3,10 @@ locals { aws_ec2 = var.compute_provider.aws.ec2 } - discovered_provider_key = one([ + provider_key = one([ for provider_key, provider_config in local.compute_providers : provider_key if provider_config != null ]) - provider_key = var.compute_provider_key != null ? var.compute_provider_key : local.discovered_provider_key provider_types = { aws_ec2 = "ec2" diff --git a/modules/runner-config/tests/pool.tftest.hcl b/modules/runner-config/tests/pool.tftest.hcl index f0cb0048e4..9269735384 100644 --- a/modules/runner-config/tests/pool.tftest.hcl +++ b/modules/runner-config/tests/pool.tftest.hcl @@ -33,6 +33,7 @@ variables { vpc_id = "vpc-12345678" subnet_ids = ["subnet-12345678"] instance_types = ["m5.large"] + ssm_enabled = true ami = { filter = { state = ["available"] } owners = ["amazon"] diff --git a/modules/runner-config/validations.tf b/modules/runner-config/validations.tf index df0671e93f..023032fafc 100644 --- a/modules/runner-config/validations.tf +++ b/modules/runner-config/validations.tf @@ -77,14 +77,6 @@ resource "terraform_data" "validate_config" { error_message = "Exactly one compute-provider block must be set. Supported compute-provider blocks: aws.ec2." } - precondition { - condition = var.compute_provider_key == null || try( - local.compute_providers[var.compute_provider_key] != null, - false, - ) - error_message = "compute_provider_key must identify the non-null typed compute-provider block." - } - precondition { condition = length([ for provider_name, provider_config in var.orchestration_provider : provider_name diff --git a/modules/runner-config/variables.compute-provider.tf b/modules/runner-config/variables.compute-provider.tf index 954bc18c61..70ace8a0cf 100644 --- a/modules/runner-config/variables.compute-provider.tf +++ b/modules/runner-config/variables.compute-provider.tf @@ -1,16 +1,3 @@ -# Optional plan-known dispatch key supplied by the multi-runner topology layer. -variable "compute_provider_key" { - description = "Optional plan-known compute-provider dispatch key. Null discovers the key from the exactly one populated compute_provider block." - type = string - default = null - - validation { - condition = var.compute_provider_key == null ? true : contains(["aws_ec2"], var.compute_provider_key) - error_message = "compute_provider_key must be null or aws_ec2." - } -} - -# Typed compute-provider input boundary between the common control plane and compute implementations. variable "compute_provider" { description = <<-EOT Typed compute-provider configuration. Provider-owned settings remain inside the selected compute-provider block. From fd60fb00076470919c4d82890ec4c7ab167c8ef6 Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 01:43:37 +0200 Subject: [PATCH 3/9] test: fix test in runner-config --- modules/runner-config/tests/pool.tftest.hcl | 12 ++---------- modules/runner-config/variables.compute-provider.tf | 4 ++++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/modules/runner-config/tests/pool.tftest.hcl b/modules/runner-config/tests/pool.tftest.hcl index 9269735384..9315c7a45d 100644 --- a/modules/runner-config/tests/pool.tftest.hcl +++ b/modules/runner-config/tests/pool.tftest.hcl @@ -606,11 +606,7 @@ run "rejects_empty_compute_provider" { compute_provider = {} } - plan_options { - target = [terraform_data.validate_config] - } - - expect_failures = [terraform_data.validate_config] + expect_failures = [var.compute_provider] } run "rejects_empty_aws_compute_provider_namespace" { @@ -622,11 +618,7 @@ run "rejects_empty_aws_compute_provider_namespace" { } } - plan_options { - target = [terraform_data.validate_config] - } - - expect_failures = [terraform_data.validate_config] + expect_failures = [var.compute_provider] } run "job_retry_uses_common_runner_configuration_identity" { diff --git a/modules/runner-config/variables.compute-provider.tf b/modules/runner-config/variables.compute-provider.tf index 70ace8a0cf..d7aaaafe2a 100644 --- a/modules/runner-config/variables.compute-provider.tf +++ b/modules/runner-config/variables.compute-provider.tf @@ -281,4 +281,8 @@ variable "compute_provider" { }), {}) }) + validation { + condition = var.compute_provider.aws.ec2 != null + error_message = "Exactly one compute-provider block must be set. Supported compute-provider blocks: aws.ec2." + } } From a8501e79a06c2c6eac25d0e7e8c717b9b97591eb Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 02:15:20 +0200 Subject: [PATCH 4/9] ci(workflows): update Lambda, Packer, and Terraform checks --- .github/workflows/lambda.yml | 17 +--- .github/workflows/packer-build.yml | 44 ++++++++- .github/workflows/terraform.yml | 153 +++++++++++++++++------------ 3 files changed, 136 insertions(+), 78 deletions(-) diff --git a/.github/workflows/lambda.yml b/.github/workflows/lambda.yml index 8537e4beaa..9fb7d73123 100644 --- a/.github/workflows/lambda.yml +++ b/.github/workflows/lambda.yml @@ -34,7 +34,7 @@ jobs: persist-credentials: false - name: Install dependencies - run: yarn install --frozen-lockfile + run: yarn install --immutable --mode=skip-build - name: Run prettier run: yarn format-check @@ -77,24 +77,15 @@ jobs: uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 - name: Build scale-set service image - uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 - with: - context: . - file: ./lambdas/services/scale-set/Dockerfile - platforms: linux/amd64,linux/arm64 - push: false - cache-from: type=gha,scope=scale-set-service - cache-to: type=gha,mode=max,scope=scale-set-service - - - name: Build scale-set service image for smoke test uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: context: . file: ./lambdas/services/scale-set/Dockerfile platforms: linux/amd64 + push: false load: true - tags: scale-set-service:smoke-test cache-from: type=gha,scope=scale-set-service + cache-to: type=gha,mode=max,scope=scale-set-service - name: Run scale-set service image smoke test - run: ./tests/scale-set-container-smoke-test.sh + run: ./tests/scale-set-container-smoke-test.sh \ No newline at end of file diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index 8dcff4efb6..b83dda3f8e 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -21,14 +21,54 @@ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: + discover_packer_examples: + name: Discover Packer examples + runs-on: ubuntu-latest + outputs: + images: ${{ steps.discover.outputs.images }} + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Discover Packer template directories + id: discover + shell: bash + run: | + mapfile -t images < <( + find images \ + \( -type d \( -name .git -o -name .terraform \) -prune \) -o \ + \( -type f \( -name '*.pkr.hcl' -o -name '*.pkr.json' \) -print \) | + while IFS= read -r template; do + dirname "${template}" + done | + sed 's#^images/##' | + sort -u + ) + + if [ "${#images[@]}" -eq 0 ]; then + echo "::error::No Packer examples found under images/" + exit 1 + fi + + images_json="$(python3 -c 'import json, sys; print(json.dumps(sys.argv[1:]))' "${images[@]}")" + printf 'images=%s\n' "${images_json}" >> "${GITHUB_OUTPUT}" + verify_packer: - name: Verify packer + name: Verify packer (${{ matrix.image }}) + needs: discover_packer_examples runs-on: ubuntu-latest container: image: index.docker.io/hashicorp/packer@sha256:12c441b8a3994e7df9f0e2692d9298f14c387e70bcc06139420977dbf80a137b # 1.11.2 strategy: matrix: - image: ["linux-al2023", "windows-core-2019", "windows-core-2022", "ubuntu-focal", "ubuntu-jammy", "ubuntu-jammy-arm64"] + image: ${{ fromJSON(needs.discover_packer_examples.outputs.images) }} defaults: run: working-directory: images/${{ matrix.image }} diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml index b31321c2b2..b04006bbba 100644 --- a/.github/workflows/terraform.yml +++ b/.github/workflows/terraform.yml @@ -16,55 +16,6 @@ permissions: env: AWS_REGION: eu-west-1 - MODULES: | - . - modules/ami-housekeeper - modules/compute-providers/aws/ec2 - modules/compute-providers/aws/ec2/trust-policy - modules/download-lambda - modules/lambda - modules/multi-runner - modules/orchestration-providers/scale-set - modules/orchestration-providers/webhook - modules/orchestration-providers/webhook/job-retry - modules/orchestration-providers/webhook/pool - modules/orchestration-providers/webhook/scale-runners - modules/runner-binaries-syncer - modules/runner-config - modules/runner-config/ssm-housekeeper - modules/runners - modules/runners/job-retry - modules/runners/pool - modules/setup-iam-permissions - modules/storage-providers/aws/ssm - modules/termination-watcher - modules/termination-watcher/notification - modules/termination-watcher/termination - modules/webhook - modules/webhook/direct - modules/webhook/eventbridge - modules/webhook-github-app - EXAMPLES: | - default - prebuilt - ephemeral - termination-watcher - multi-runner - multi-runner-v2 - multi-runner-scale-set - external-managed-ssm-secrets - TEST_MODULES: | - modules/compute-providers/aws/ec2 - modules/compute-providers/aws/ec2/trust-policy - modules/multi-runner - modules/orchestration-providers/scale-set - modules/orchestration-providers/webhook - modules/orchestration-providers/webhook/job-retry - modules/orchestration-providers/webhook/pool - modules/orchestration-providers/webhook/scale-runners - modules/runner-config - modules/runner-config/ssm-housekeeper - modules/runners jobs: verify_modules: @@ -135,6 +86,32 @@ jobs: tofu_version: ${{ matrix.iac.version }} tofu_wrapper: false + - name: Discover Terraform modules + shell: bash + run: | + modules="$( + { + printf '.\n' + find modules \ + \( -type d \( -name .git -o -name .terraform -o -name tests \) -prune \) -o \ + \( -type f -name '*.tf' -print \) | + while IFS= read -r tf_file; do + dirname "${tf_file}" + done + } | sort -u + )" + + if [ -z "${modules}" ]; then + echo "::error::No Terraform modules found" + exit 1 + fi + + { + printf 'MODULES<> "${GITHUB_ENV}" + - name: ${{ matrix.iac.name }} init env: IAC_COMMAND: ${{ matrix.iac.command }} @@ -262,58 +239,83 @@ jobs: tofu_version: ${{ matrix.iac.version }} tofu_wrapper: false + - name: Discover examples + shell: bash + run: | + examples="$( + find examples \ + \( -type d \( -name .git -o -name .terraform \) -prune \) -o \ + \( -type f -name '*.tf' -print \) | + while IFS= read -r tf_file; do + dirname "${tf_file}" + done | + sed 's#^examples/##' | + sort -u + )" + + if [ -z "${examples}" ]; then + echo "::error::No Terraform examples found" + exit 1 + fi + + { + printf 'EXAMPLES<> "${GITHUB_ENV}" + - name: Select ${{ matrix.iac.name }} lockfile if: matrix.iac.command == 'tofu' env: IAC_LOCK_FILE: ${{ matrix.iac.lockfile }} run: | - printf '%s\n' "${EXAMPLES}" | while IFS= read -r example; do + while IFS= read -r example; do [ -z "${example}" ] && continue echo "::group::Selecting lockfile for example: ${example}" cp "examples/${example}/${IAC_LOCK_FILE}" \ "examples/${example}/.terraform.lock.hcl" echo "::endgroup::" - done + done <<< "${EXAMPLES}" - name: ${{ matrix.iac.name }} init env: IAC_COMMAND: ${{ matrix.iac.command }} run: | - printf '%s\n' "${EXAMPLES}" | while IFS= read -r example; do + while IFS= read -r example; do [ -z "${example}" ] && continue echo "::group::Running $IAC_COMMAND init for example: ${example}" $IAC_COMMAND -chdir="examples/${example}" init \ -get -backend=false -input=false -lockfile=readonly echo "::endgroup::" - done + done <<< "${EXAMPLES}" - name: Check ${{ matrix.iac.name }} formatting env: IAC_COMMAND: ${{ matrix.iac.command }} run: | - printf '%s\n' "${EXAMPLES}" | while IFS= read -r example; do + while IFS= read -r example; do [ -z "${example}" ] && continue echo "::group::Checking $IAC_COMMAND formatting for example: ${example}" $IAC_COMMAND -chdir="examples/${example}" fmt \ -recursive -check=true -write=false echo "::endgroup::" - done + done <<< "${EXAMPLES}" continue-on-error: ${{ matrix.iac.version == 'latest' }} - name: Validate ${{ matrix.iac.name }} env: IAC_COMMAND: ${{ matrix.iac.command }} run: | - printf '%s\n' "${EXAMPLES}" | while IFS= read -r example; do + while IFS= read -r example; do [ -z "${example}" ] && continue echo "::group::Validating example: ${example}" $IAC_COMMAND -chdir="examples/${example}" validate echo "::endgroup::" - done + done <<< "${EXAMPLES}" - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 name: Cache TFLint plugin dir @@ -330,7 +332,7 @@ jobs: run: | tflint --init -c ${GITHUB_WORKSPACE}/.tflint.hcl - printf '%s\n' "${EXAMPLES}" | while IFS= read -r example; do + while IFS= read -r example; do [ -z "${example}" ] && continue echo "::group::Running TFLint for example: ${example}" @@ -339,7 +341,7 @@ jobs: --var-file ${GITHUB_WORKSPACE}/.github/lint/tflint.tfvars \ --chdir "examples/${example}" echo "::endgroup::" - done + done <<< "${EXAMPLES}" terraform_test: name: ${{ matrix.iac.name }} test @@ -395,27 +397,52 @@ jobs: tofu_version: ${{ matrix.iac.version }} tofu_wrapper: false + - name: Discover Terraform test modules + shell: bash + run: | + test_modules="$( + find . \ + \( -type d \( -name .git -o -name .terraform \) -prune \) -o \ + \( -type f -name '*.tftest.hcl' -print \) | + while IFS= read -r test_file; do + test_dir=$(dirname "${test_file}") + printf '%s\n' "${test_dir%/tests}" + done | + sort -u + )" + + if [ -z "${test_modules}" ]; then + echo "::error::No Terraform test modules found" + exit 1 + fi + + { + printf 'TEST_MODULES<> "${GITHUB_ENV}" + - name: ${{ matrix.iac.name }} init env: IAC_COMMAND: ${{ matrix.iac.command }} run: | - printf '%s\n' "${TEST_MODULES}" | while IFS= read -r module; do + while IFS= read -r module; do [ -z "${module}" ] && continue echo "::group::Running $IAC_COMMAND init for test module: ${module}" $IAC_COMMAND -chdir="${module}" init \ -backend=false -input=false echo "::endgroup::" - done + done <<< "${TEST_MODULES}" - name: ${{ matrix.iac.name }} test env: IAC_COMMAND: ${{ matrix.iac.command }} run: | - printf '%s\n' "${TEST_MODULES}" | while IFS= read -r module; do + while IFS= read -r module; do [ -z "${module}" ] && continue echo "::group::Running $IAC_COMMAND test for module: ${module}" $IAC_COMMAND -chdir="${module}" test -test-directory=tests -compact-warnings echo "::endgroup::" - done + done <<< "${TEST_MODULES}" From 8972ace2c0894122c3bf06cdc8e77ee576cb158b Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 02:23:05 +0200 Subject: [PATCH 5/9] ci: fix jobs --- .github/workflows/lambda.yml | 9 +++++++++ .github/workflows/packer-build.yml | 6 +++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lambda.yml b/.github/workflows/lambda.yml index 9fb7d73123..06c116a0ef 100644 --- a/.github/workflows/lambda.yml +++ b/.github/workflows/lambda.yml @@ -83,6 +83,15 @@ jobs: file: ./lambdas/services/scale-set/Dockerfile platforms: linux/amd64 push: false + cache-from: type=gha,scope=scale-set-service + cache-to: type=gha,mode=max,scope=scale-set-service + + - name: Load scale-set service image + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 + with: + context: . + file: ./lambdas/services/scale-set/Dockerfile + platforms: linux/amd64 load: true cache-from: type=gha,scope=scale-set-service cache-to: type=gha,mode=max,scope=scale-set-service diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index b83dda3f8e..e7e85088d9 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -18,7 +18,6 @@ permissions: env: AWS_REGION: eu-west-1 - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: discover_packer_examples: @@ -72,6 +71,8 @@ jobs: defaults: run: working-directory: images/${{ matrix.image }} + env: + PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 @@ -82,9 +83,12 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + - name: packer init run: packer init . + - name: check packer formatting run: packer fmt -recursive -check=true . + - name: packer validate run: packer validate -evaluate-datasources . From f48c33a50696787c56ccf212e0e60f9aff10c007 Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 02:49:35 +0200 Subject: [PATCH 6/9] fix(packer): authenticate release lookup and check all images --- .github/workflows/packer-build.yml | 82 +++++++++---------- .../linux-al2023/github_agent.linux.pkr.hcl | 20 ++++- .../ubuntu-focal/github_agent.ubuntu.pkr.hcl | 20 ++++- .../github_agent.ubuntu.pkr.hcl | 20 ++++- .../ubuntu-jammy/github_agent.ubuntu.pkr.hcl | 20 ++++- .../github_agent.windows.pkr.hcl | 20 ++++- .../github_agent.windows.pkr.hcl | 20 ++++- 7 files changed, 136 insertions(+), 66 deletions(-) diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index e7e85088d9..dcb118c9dc 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -20,11 +20,13 @@ env: AWS_REGION: eu-west-1 jobs: - discover_packer_examples: - name: Discover Packer examples + verify_packer: + name: Verify packer runs-on: ubuntu-latest - outputs: - images: ${{ steps.discover.outputs.images }} + container: + image: index.docker.io/hashicorp/packer@sha256:12c441b8a3994e7df9f0e2692d9298f14c387e70bcc06139420977dbf80a137b # 1.11.2 + env: + PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Harden the runner (Audit all outbound calls) uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 @@ -37,58 +39,54 @@ jobs: persist-credentials: false - name: Discover Packer template directories - id: discover - shell: bash run: | - mapfile -t images < <( + images="$( find images \ \( -type d \( -name .git -o -name .terraform \) -prune \) -o \ \( -type f \( -name '*.pkr.hcl' -o -name '*.pkr.json' \) -print \) | while IFS= read -r template; do dirname "${template}" done | - sed 's#^images/##' | sort -u - ) + )" - if [ "${#images[@]}" -eq 0 ]; then - echo "::error::No Packer examples found under images/" + if [ -z "${images}" ]; then + echo "::error::No Packer templates found under images/" exit 1 fi - images_json="$(python3 -c 'import json, sys; print(json.dumps(sys.argv[1:]))' "${images[@]}")" - printf 'images=%s\n' "${images_json}" >> "${GITHUB_OUTPUT}" + { + printf 'IMAGES<> "${GITHUB_ENV}" - verify_packer: - name: Verify packer (${{ matrix.image }}) - needs: discover_packer_examples - runs-on: ubuntu-latest - container: - image: index.docker.io/hashicorp/packer@sha256:12c441b8a3994e7df9f0e2692d9298f14c387e70bcc06139420977dbf80a137b # 1.11.2 - strategy: - matrix: - image: ${{ fromJSON(needs.discover_packer_examples.outputs.images) }} - defaults: - run: - working-directory: images/${{ matrix.image }} - env: - PACKER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 - with: - egress-policy: audit + - name: packer init + run: | + printf '%s\n' "${IMAGES}" | while IFS= read -r image; do + [ -z "${image}" ] && continue - - name: "Checkout" - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false + echo "::group::Running packer init for image: ${image}" + (cd "${image}" && packer init .) + echo "::endgroup::" + done - - name: packer init - run: packer init . + - name: Check packer formatting + run: | + printf '%s\n' "${IMAGES}" | while IFS= read -r image; do + [ -z "${image}" ] && continue + + echo "::group::Checking packer formatting for image: ${image}" + (cd "${image}" && packer fmt -recursive -check=true .) + echo "::endgroup::" + done - - name: check packer formatting - run: packer fmt -recursive -check=true . + - name: Validate packer + run: | + printf '%s\n' "${IMAGES}" | while IFS= read -r image; do + [ -z "${image}" ] && continue - - name: packer validate - run: packer validate -evaluate-datasources . + echo "::group::Validating image: ${image}" + (cd "${image}" && packer validate -evaluate-datasources .) + echo "::endgroup::" + done diff --git a/images/linux-al2023/github_agent.linux.pkr.hcl b/images/linux-al2023/github_agent.linux.pkr.hcl index 2876f053c7..d5fbb289cd 100644 --- a/images/linux-al2023/github_agent.linux.pkr.hcl +++ b/images/linux-al2023/github_agent.linux.pkr.hcl @@ -7,6 +7,13 @@ packer { } } +variable "github_api_token" { + description = "Optional GitHub token for the runner release API." + type = string + default = env("PACKER_GITHUB_API_TOKEN") + sensitive = true +} + variable "runner_version" { description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." default = null @@ -101,10 +108,15 @@ variable "temporary_security_group_source_public_ip" { data "http" github_runner_release_json { url = "https://api.github.com/repos/actions/runner/releases/latest" - request_headers = { - Accept = "application/vnd.github+json" - X-GitHub-Api-Version : "2022-11-28" - } + request_headers = merge( + { + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" + }, + var.github_api_token == "" ? {} : { + Authorization = "Bearer ${var.github_api_token}" + } + ) } locals { diff --git a/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl b/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl index 9712417274..2a84637af1 100644 --- a/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl @@ -7,6 +7,13 @@ packer { } } +variable "github_api_token" { + description = "Optional GitHub token for the runner release API." + type = string + default = env("PACKER_GITHUB_API_TOKEN") + sensitive = true +} + variable "runner_version" { description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." default = null @@ -91,10 +98,15 @@ variable "temporary_security_group_source_public_ip" { data "http" github_runner_release_json { url = "https://api.github.com/repos/actions/runner/releases/latest" - request_headers = { - Accept = "application/vnd.github+json" - X-GitHub-Api-Version : "2022-11-28" - } + request_headers = merge( + { + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" + }, + var.github_api_token == "" ? {} : { + Authorization = "Bearer ${var.github_api_token}" + } + ) } locals { diff --git a/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl b/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl index 1536eb5784..69df5554d8 100644 --- a/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl @@ -7,6 +7,13 @@ packer { } } +variable "github_api_token" { + description = "Optional GitHub token for the runner release API." + type = string + default = env("PACKER_GITHUB_API_TOKEN") + sensitive = true +} + variable "runner_version" { description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." default = null @@ -91,10 +98,15 @@ variable "temporary_security_group_source_public_ip" { data "http" github_runner_release_json { url = "https://api.github.com/repos/actions/runner/releases/latest" - request_headers = { - Accept = "application/vnd.github+json" - X-GitHub-Api-Version : "2022-11-28" - } + request_headers = merge( + { + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" + }, + var.github_api_token == "" ? {} : { + Authorization = "Bearer ${var.github_api_token}" + } + ) } locals { diff --git a/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl b/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl index be23a256ca..d8d52ea9d1 100644 --- a/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl @@ -7,6 +7,13 @@ packer { } } +variable "github_api_token" { + description = "Optional GitHub token for the runner release API." + type = string + default = env("PACKER_GITHUB_API_TOKEN") + sensitive = true +} + variable "runner_version" { description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." default = null @@ -91,10 +98,15 @@ variable "temporary_security_group_source_public_ip" { data "http" github_runner_release_json { url = "https://api.github.com/repos/actions/runner/releases/latest" - request_headers = { - Accept = "application/vnd.github+json" - X-GitHub-Api-Version : "2022-11-28" - } + request_headers = merge( + { + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" + }, + var.github_api_token == "" ? {} : { + Authorization = "Bearer ${var.github_api_token}" + } + ) } locals { diff --git a/images/windows-core-2019/github_agent.windows.pkr.hcl b/images/windows-core-2019/github_agent.windows.pkr.hcl index e27ad4a2bc..73de75d3fd 100644 --- a/images/windows-core-2019/github_agent.windows.pkr.hcl +++ b/images/windows-core-2019/github_agent.windows.pkr.hcl @@ -7,6 +7,13 @@ packer { } } +variable "github_api_token" { + description = "Optional GitHub token for the runner release API." + type = string + default = env("PACKER_GITHUB_API_TOKEN") + sensitive = true +} + variable "runner_version" { description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." default = null @@ -56,10 +63,15 @@ variable "temporary_security_group_source_public_ip" { data "http" github_runner_release_json { url = "https://api.github.com/repos/actions/runner/releases/latest" - request_headers = { - Accept = "application/vnd.github+json" - X-GitHub-Api-Version : "2022-11-28" - } + request_headers = merge( + { + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" + }, + var.github_api_token == "" ? {} : { + Authorization = "Bearer ${var.github_api_token}" + } + ) } locals { diff --git a/images/windows-core-2022/github_agent.windows.pkr.hcl b/images/windows-core-2022/github_agent.windows.pkr.hcl index 0a85595ec5..802639f248 100644 --- a/images/windows-core-2022/github_agent.windows.pkr.hcl +++ b/images/windows-core-2022/github_agent.windows.pkr.hcl @@ -7,6 +7,13 @@ packer { } } +variable "github_api_token" { + description = "Optional GitHub token for the runner release API." + type = string + default = env("PACKER_GITHUB_API_TOKEN") + sensitive = true +} + variable "runner_version" { description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." default = null @@ -73,10 +80,15 @@ variable "temporary_security_group_source_public_ip" { data "http" github_runner_release_json { url = "https://api.github.com/repos/actions/runner/releases/latest" - request_headers = { - Accept = "application/vnd.github+json" - X-GitHub-Api-Version : "2022-11-28" - } + request_headers = merge( + { + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" + }, + var.github_api_token == "" ? {} : { + Authorization = "Bearer ${var.github_api_token}" + } + ) } locals { From caa4f4c23960d9d35e25492474c756cc324babec Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 02:52:08 +0200 Subject: [PATCH 7/9] chore(pre-commit): update hooks and enable validation --- .pre-commit-config.yaml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1502b6ab8e..9c63b9a806 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,26 @@ repos: - repo: https://github.com/antonbabenko/pre-commit-terraform - rev: v1.96.2 + rev: 581213484f4262600d17c71e272176d4eb6724a5 # frozen: v1.109.0 hooks: - id: terraform_fmt - id: terraform_tflint args: - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl --var-file __GIT_WORKING_DIR__/.github/lint/tflint.tfvars + - id: terraform_validate + name: Terraform · Validate + always_run: true + args: + - --hook-config=--retry-once-with-cleanup=true + - --tf-init-args=-backend=false + - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - id: check-merge-conflict + + - repo: https://github.com/hadolint/hadolint + rev: 2eece55955ced00200be9729e9728cb7dacca505 # frozen: v2.15.1 + hooks: + - id: hadolint + name: Docker · Linter + exclude: (docs/operations/repo-blueprints/) \ No newline at end of file From 602f8314bf5ecd06fcd73ef75f2ab8b32be3aa48 Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 02:52:32 +0200 Subject: [PATCH 8/9] fix(tests): make module test fixtures self-contained --- .../aws/ec2/tests/provider.tftest.hcl | 15 +- .../job-retry/tests/job-retry.tftest.hcl | 232 ++++++++++++++++-- .../webhook/pool/tests/provider.tftest.hcl | 59 +++-- .../tests/scale-runners.tftest.hcl | 181 ++++++++++++-- .../webhook/tests/webhook.tftest.hcl | 75 +++++- .../tests/computed-iam-inputs.tftest.hcl | 31 +++ modules/runner-config/tests/pool.tftest.hcl | 25 ++ modules/runner-config/tests/tags.tftest.hcl | 25 ++ 8 files changed, 584 insertions(+), 59 deletions(-) diff --git a/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl b/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl index e1ce0630d5..d1165397e8 100644 --- a/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl +++ b/modules/compute-providers/aws/ec2/tests/provider.tftest.hcl @@ -222,11 +222,18 @@ run "includes_managed_ami_read_in_scale_set_contract" { command = plan variables { - config = merge(var.config, { - ami = merge(var.config.ami, { + config = { + vpc_id = "vpc-12345678" + subnet_ids = ["subnet-12345678"] + instance_types = ["m5.large"] + binaries_syncer = { + enabled = false + s3 = null + } + ami = { id_ssm_parameter = null - }) - }) + } + } } assert { diff --git a/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl b/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl index 803d2a6de5..3a1c41abfb 100644 --- a/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl +++ b/modules/orchestration-providers/webhook/job-retry/tests/job-retry.tftest.hcl @@ -11,6 +11,13 @@ mock_provider "aws" { } } + + mock_resource "aws_sqs_queue" { + defaults = { + arn = "arn:aws:sqs:eu-west-1:123456789012:mock-queue" + } + } + } variables { @@ -349,11 +356,76 @@ run "rejects_unsupported_lambda_architecture" { } variables { - config = merge(var.config, { - lambda = merge(var.config.lambda, { - architecture = "unsupported" - }) - }) + config = { + prefix = "job-retry-test" + aws_partition = "aws" + lambda = { + artifact = { + zip = "unused.zip" + s3 = { + bucket = "lambda-artifacts" + key = "job-retry.zip" + } + } + architecture = "unsupported" + runtime = "nodejs24.x" + memory_size = 256 + timeout = 30 + reserved_concurrent_executions = 1 + environment_variables = {} + vpc = { + subnet_ids = [] + security_group_ids = [] + } + role = { + path = "/job-retry-test/" + principals = [] + } + } + runner = { name_prefix = "required-prefix-" } + github = { + organization_runners = false + enterprise_server = { url = null, ssl_verify = false } + app_parameters = { key_base64 = {}, id = {} } + } + queue = { + build = { + url = "https://sqs.eu-west-1.amazonaws.com/123456789012/build-queue" + arn = "arn:aws:sqs:eu-west-1:123456789012:build-queue" + } + event_source_mapping = { + batch_size = 10 + maximum_batching_window_in_seconds = 0 + } + encryption = { sqs_managed_sse_enabled = true } + } + observability = { + logs = { + level = "trace" + retention_in_days = 14 + class = "STANDARD" + } + tracing = { + capture_http_requests = false + capture_error = false + } + metrics = { + enabled = false + namespace = "JobRetryTest" + metric = { + github_app_rate_limit = { enabled = false } + job_retry = { enabled = false } + } + } + } + tags = { + resources = {} + lambda = {} + log_group = {} + queue = {} + event_source_mapping = {} + } + } } expect_failures = [terraform_data.validate_config] @@ -367,13 +439,76 @@ run "rejects_unsupported_log_level" { } variables { - config = merge(var.config, { - observability = merge(var.config.observability, { - logs = merge(var.config.observability.logs, { - level = "verbose" - }) - }) - }) + config = { + prefix = "job-retry-test" + aws_partition = "aws" + lambda = { + artifact = { + zip = "unused.zip" + s3 = { + bucket = "lambda-artifacts" + key = "job-retry.zip" + } + } + architecture = "arm64" + runtime = "nodejs24.x" + memory_size = 256 + timeout = 30 + reserved_concurrent_executions = 1 + environment_variables = {} + vpc = { + subnet_ids = [] + security_group_ids = [] + } + role = { + path = "/job-retry-test/" + principals = [] + } + } + runner = { name_prefix = "required-prefix-" } + github = { + organization_runners = false + enterprise_server = { url = null, ssl_verify = false } + app_parameters = { key_base64 = {}, id = {} } + } + queue = { + build = { + url = "https://sqs.eu-west-1.amazonaws.com/123456789012/build-queue" + arn = "arn:aws:sqs:eu-west-1:123456789012:build-queue" + } + event_source_mapping = { + batch_size = 10 + maximum_batching_window_in_seconds = 0 + } + encryption = { sqs_managed_sse_enabled = true } + } + observability = { + logs = { + level = "verbose" + retention_in_days = 14 + class = "STANDARD" + } + tracing = { + capture_http_requests = false + capture_error = false + } + metrics = { + enabled = false + namespace = "JobRetryTest" + metric = { + github_app_rate_limit = { enabled = false } + job_retry = { enabled = false } + } + } + } + tags = { + resources = {} + lambda = {} + log_group = {} + queue = {} + event_source_mapping = {} + } + } } expect_failures = [terraform_data.validate_config] @@ -387,9 +522,76 @@ run "rejects_resource_prefix_longer_than_aws_limit" { } variables { - config = merge(var.config, { - prefix = "1234567890123456789012345678901234567890123456789012345" - }) + config = { + prefix = "1234567890123456789012345678901234567890123456789012345" + aws_partition = "aws" + lambda = { + artifact = { + zip = "unused.zip" + s3 = { + bucket = "lambda-artifacts" + key = "job-retry.zip" + } + } + architecture = "arm64" + runtime = "nodejs24.x" + memory_size = 256 + timeout = 30 + reserved_concurrent_executions = 1 + environment_variables = {} + vpc = { + subnet_ids = [] + security_group_ids = [] + } + role = { + path = "/job-retry-test/" + principals = [] + } + } + runner = { name_prefix = "required-prefix-" } + github = { + organization_runners = false + enterprise_server = { url = null, ssl_verify = false } + app_parameters = { key_base64 = {}, id = {} } + } + queue = { + build = { + url = "https://sqs.eu-west-1.amazonaws.com/123456789012/build-queue" + arn = "arn:aws:sqs:eu-west-1:123456789012:build-queue" + } + event_source_mapping = { + batch_size = 10 + maximum_batching_window_in_seconds = 0 + } + encryption = { sqs_managed_sse_enabled = true } + } + observability = { + logs = { + level = "trace" + retention_in_days = 14 + class = "STANDARD" + } + tracing = { + capture_http_requests = false + capture_error = false + } + metrics = { + enabled = false + namespace = "JobRetryTest" + metric = { + github_app_rate_limit = { enabled = false } + job_retry = { enabled = false } + } + } + } + tags = { + resources = {} + lambda = {} + log_group = {} + queue = {} + event_source_mapping = {} + } + } } expect_failures = [terraform_data.validate_config] diff --git a/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl b/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl index 84c91cfef9..5d102cf7be 100644 --- a/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl +++ b/modules/orchestration-providers/webhook/pool/tests/provider.tftest.hcl @@ -4,6 +4,19 @@ mock_provider "aws" { json = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"logs:CreateLogStream\",\"Resource\":\"*\"}]}" } } + + mock_resource "aws_iam_role" { + defaults = { + arn = "arn:aws:iam::123456789012:role/pool-test" + } + } + + mock_resource "aws_lambda_function" { + defaults = { + arn = "arn:aws:lambda:eu-west-1:123456789012:function:mock-function" + } + } + } variables { @@ -228,13 +241,18 @@ run "omits_optional_kms_statement" { command = plan variables { - storage_provider = merge(var.storage_provider, { - aws = merge(var.storage_provider.aws, { - ssm = merge(var.storage_provider.aws.ssm, { - kms_key_id = null - }) - }) - }) + storage_provider = { + aws = { + ssm = { + token_path = "/github-runner/tokens" + token_path_arn = "arn:aws:ssm:eu-west-1:123456789012:parameter/github-runner/tokens" + config_path = "/github-runner/config" + config_path_arn = "arn:aws:ssm:eu-west-1:123456789012:parameter/github-runner/config" + kms_key_id = null + parameter_store_tags = "{}" + } + } + } } assert { @@ -257,9 +275,13 @@ run "rejects_empty_compute_provider_type" { } variables { - runner_provider = merge(var.runner_provider, { - type = " " - }) + runner_provider = { + type = " " + environment_variables = { MICROVM_CLUSTER = "runner-cluster" } + iam_policy_json = jsonencode({ Version = "2012-10-17", Statement = [] }) + managed_policy_enabled = true + managed_policy_arn = "arn:aws:iam::123456789012:policy/microvm-pool" + } } expect_failures = [terraform_data.validate_config] @@ -273,9 +295,13 @@ run "rejects_invalid_compute_provider_policy" { } variables { - runner_provider = merge(var.runner_provider, { - iam_policy_json = "not-json" - }) + runner_provider = { + type = "microvm" + environment_variables = { MICROVM_CLUSTER = "runner-cluster" } + iam_policy_json = "not-json" + managed_policy_enabled = true + managed_policy_arn = "arn:aws:iam::123456789012:policy/microvm-pool" + } } expect_failures = [terraform_data.validate_config] @@ -289,10 +315,13 @@ run "requires_enabled_compute_provider_managed_policy_arn" { } variables { - runner_provider = merge(var.runner_provider, { + runner_provider = { + type = "microvm" + environment_variables = { MICROVM_CLUSTER = "runner-cluster" } + iam_policy_json = jsonencode({ Version = "2012-10-17", Statement = [] }) managed_policy_enabled = true managed_policy_arn = null - }) + } } expect_failures = [terraform_data.validate_config] diff --git a/modules/orchestration-providers/webhook/scale-runners/tests/scale-runners.tftest.hcl b/modules/orchestration-providers/webhook/scale-runners/tests/scale-runners.tftest.hcl index 22b695aac2..d4171ca2d7 100644 --- a/modules/orchestration-providers/webhook/scale-runners/tests/scale-runners.tftest.hcl +++ b/modules/orchestration-providers/webhook/scale-runners/tests/scale-runners.tftest.hcl @@ -10,6 +10,19 @@ mock_provider "aws" { arn = "arn:aws:iam::123456789012:role/scale-runners-test" } } + + mock_resource "aws_lambda_function" { + defaults = { + arn = "arn:aws-us-gov:lambda:us-gov-west-1:123456789012:function:mock-function" + } + } + + mock_resource "aws_cloudwatch_event_rule" { + defaults = { + arn = "arn:aws-us-gov:events:us-gov-west-1:123456789012:rule/mock-event-rule" + } + } + } variables { @@ -409,18 +422,87 @@ run "omits_optional_kms_statements" { command = plan variables { - config = merge(var.config, { - queue = merge(var.config.queue, { - kms_key_id = null - }) - }) - storage_provider = merge(var.storage_provider, { - aws = merge(var.storage_provider.aws, { - ssm = merge(var.storage_provider.aws.ssm, { - kms_key_id = null - }) - }) - }) + config = { + prefix = "scale-runners-test" + lambda = { + artifact = { zip = "runners.zip", s3 = { bucket = "lambda-artifacts", key = "runners.zip" } } + runtime = "nodejs24.x" + architecture = "arm64" + vpc = { subnet_ids = [], security_group_ids = [] } + role = { path = "/scale-runners-test/" } + } + runner = { + os = "linux" + auto_update_disabled = false + ephemeral = true + labels = ["self-hosted", "linux"] + group_name = "default" + name_prefix = "test-" + boot_time_in_minutes = 10 + maximum_count = 10 + } + github = { + organization_runners = true + enterprise_server = { url = null, ssl_verify = true } + app_parameters = { + key_base64 = { + name = "/github-runner/key-base64" + arn = "arn:aws-us-gov:ssm:us-gov-west-1:123456789012:parameter/github-runner/key-base64" + } + id = { + name = "/github-runner/app-id" + arn = "arn:aws-us-gov:ssm:us-gov-west-1:123456789012:parameter/github-runner/app-id" + } + } + } + queue = { + build = { arn = "arn:aws-us-gov:sqs:us-gov-west-1:123456789012:build-queue" } + kms_key_id = null + event_source_mapping = { batch_size = 10, maximum_batching_window_in_seconds = 0 } + } + observability = { + logs = { level = "info", retention_in_days = 14, class = "STANDARD" } + tracing = { capture_http_requests = false, capture_error = false } + metrics = { + enabled = false + namespace = "ScaleRunnersTest" + metric = { github_app_rate_limit = { enabled = false } } + } + } + scale_up = { + memory_size = 512 + timeout = 60 + reserved_concurrent_executions = 1 + job_queued_check_enabled = false + tags = { resources = {}, lambda = {}, log_group = {}, event_source_mapping = {} } + } + scale_down = { + memory_size = 512 + timeout = 60 + schedule_expression = "rate(10 minutes)" + idle_config = [] + tags = { resources = {}, lambda = {}, log_group = {} } + } + job_retry = { + enabled = false + max_attempts = 3 + delay_in_seconds = 60 + delay_backoff = 1 + queue = null + } + } + storage_provider = { + aws = { + ssm = { + token_path = "/github-runner/tokens" + token_path_arn = "arn:aws-us-gov:ssm:us-gov-west-1:123456789012:parameter/github-runner/tokens" + config_path = "/github-runner/config" + config_path_arn = "arn:aws-us-gov:ssm:us-gov-west-1:123456789012:parameter/github-runner/config" + parameter_store_tags = "[]" + kms_key_id = null + } + } + } } assert { @@ -448,12 +530,75 @@ run "requires_job_retry_queue_when_enabled" { } variables { - config = merge(var.config, { - job_retry = merge(var.config.job_retry, { - enabled = true - queue = null - }) - }) + config = { + prefix = "scale-runners-test" + lambda = { + artifact = { zip = "runners.zip", s3 = { bucket = "lambda-artifacts", key = "runners.zip" } } + runtime = "nodejs24.x" + architecture = "arm64" + vpc = { subnet_ids = [], security_group_ids = [] } + role = { path = "/scale-runners-test/" } + } + runner = { + os = "linux" + auto_update_disabled = false + ephemeral = true + labels = ["self-hosted", "linux"] + group_name = "default" + name_prefix = "test-" + boot_time_in_minutes = 10 + maximum_count = 10 + } + github = { + organization_runners = true + enterprise_server = { url = null, ssl_verify = true } + app_parameters = { + key_base64 = { + name = "/github-runner/key-base64" + arn = "arn:aws-us-gov:ssm:us-gov-west-1:123456789012:parameter/github-runner/key-base64" + } + id = { + name = "/github-runner/app-id" + arn = "arn:aws-us-gov:ssm:us-gov-west-1:123456789012:parameter/github-runner/app-id" + } + } + } + queue = { + build = { arn = "arn:aws-us-gov:sqs:us-gov-west-1:123456789012:build-queue" } + kms_key_id = null + event_source_mapping = { batch_size = 10, maximum_batching_window_in_seconds = 0 } + } + observability = { + logs = { level = "info", retention_in_days = 14, class = "STANDARD" } + tracing = { capture_http_requests = false, capture_error = false } + metrics = { + enabled = false + namespace = "ScaleRunnersTest" + metric = { github_app_rate_limit = { enabled = false } } + } + } + scale_up = { + memory_size = 512 + timeout = 60 + reserved_concurrent_executions = 1 + job_queued_check_enabled = false + tags = { resources = {}, lambda = {}, log_group = {}, event_source_mapping = {} } + } + scale_down = { + memory_size = 512 + timeout = 60 + schedule_expression = "rate(10 minutes)" + idle_config = [] + tags = { resources = {}, lambda = {}, log_group = {} } + } + job_retry = { + enabled = true + max_attempts = 3 + delay_in_seconds = 60 + delay_backoff = 1 + queue = null + } + } } expect_failures = [terraform_data.validate_config] diff --git a/modules/orchestration-providers/webhook/tests/webhook.tftest.hcl b/modules/orchestration-providers/webhook/tests/webhook.tftest.hcl index 5d8ca6a68a..31f198c2ce 100644 --- a/modules/orchestration-providers/webhook/tests/webhook.tftest.hcl +++ b/modules/orchestration-providers/webhook/tests/webhook.tftest.hcl @@ -10,6 +10,25 @@ mock_provider "aws" { arn = "arn:aws:iam::123456789012:role/webhook-orchestration-test" } } + + mock_resource "aws_lambda_function" { + defaults = { + arn = "arn:aws:lambda:eu-west-1:123456789012:function:mock-function" + } + } + + mock_resource "aws_sqs_queue" { + defaults = { + arn = "arn:aws:sqs:eu-west-1:123456789012:mock-queue" + } + } + + mock_resource "aws_cloudwatch_event_rule" { + defaults = { + arn = "arn:aws:events:eu-west-1:123456789012:rule/mock-event-rule" + } + } + } variables { @@ -305,13 +324,55 @@ run "rejects_conflicting_artifact_sources" { } variables { - config = merge(var.config, { - lambda = merge(var.config.lambda, { - artifact = merge(var.config.lambda.artifact, { - zip = "runners.zip" - }) - }) - }) + config = { + runner = { + boot_time_in_minutes = 11 + ephemeral = true + maximum_count = 10 + } + github = { organization_runners = true } + queue = { + build = { + arn = "arn:aws:sqs:eu-west-1:123456789012:build-queue" + url = "https://sqs.eu-west-1.amazonaws.com/123456789012/build-queue" + } + } + lambda = { + artifact = { zip = "runners.zip", s3 = { key = "runners.zip" } } + scale = { + up = { + memory_size = 512 + timeout = 60 + reserved_concurrent_executions = 1 + event_source_mapping = { batch_size = 10, maximum_batching_window_in_seconds = 0 } + } + down = { + memory_size = 512 + timeout = 60 + schedule_expression = "rate(5 minutes)" + idle_config = [] + } + } + pool = { + memory_size = 512 + timeout = 60 + reserved_concurrent_executions = 1 + config = [] + include_busy_runners = false + } + } + job_retry = { + enabled = false + delay_in_seconds = 60 + delay_backoff = 1 + max_attempts = 3 + lambda = { + memory_size = 256 + reserved_concurrent_executions = 1 + timeout = 30 + } + } + } } expect_failures = [terraform_data.validate_config] diff --git a/modules/runner-config/tests/computed-iam-inputs.tftest.hcl b/modules/runner-config/tests/computed-iam-inputs.tftest.hcl index 3e08eeb84b..c2834a5e63 100644 --- a/modules/runner-config/tests/computed-iam-inputs.tftest.hcl +++ b/modules/runner-config/tests/computed-iam-inputs.tftest.hcl @@ -4,6 +4,37 @@ mock_provider "aws" { json = "{\"Version\":\"2012-10-17\",\"Statement\":[]}" } } + + mock_resource "aws_iam_role" { + defaults = { + arn = "arn:aws:iam::123456789012:role/runner-config-test" + } + } + + mock_resource "aws_iam_policy" { + defaults = { + arn = "arn:aws:iam::123456789012:policy/mock-policy" + } + } + + mock_resource "aws_lambda_function" { + defaults = { + arn = "arn:aws:lambda:eu-west-1:123456789012:function:mock-function" + } + } + + mock_resource "aws_sqs_queue" { + defaults = { + arn = "arn:aws:sqs:eu-west-1:123456789012:mock-queue" + } + } + + mock_resource "aws_cloudwatch_event_rule" { + defaults = { + arn = "arn:aws:events:eu-west-1:123456789012:rule/mock-event-rule" + } + } + } run "computed_external_values_keep_plan_shape_known" { diff --git a/modules/runner-config/tests/pool.tftest.hcl b/modules/runner-config/tests/pool.tftest.hcl index 9315c7a45d..8537f3f553 100644 --- a/modules/runner-config/tests/pool.tftest.hcl +++ b/modules/runner-config/tests/pool.tftest.hcl @@ -16,6 +16,31 @@ mock_provider "aws" { arn = "arn:aws:ssm:eu-west-1:123456789012:parameter/github-runner/ami-id" } } + + mock_resource "aws_iam_policy" { + defaults = { + arn = "arn:aws:iam::123456789012:policy/mock-policy" + } + } + + mock_resource "aws_lambda_function" { + defaults = { + arn = "arn:aws:lambda:eu-west-1:123456789012:function:mock-function" + } + } + + mock_resource "aws_sqs_queue" { + defaults = { + arn = "arn:aws:sqs:eu-west-1:123456789012:mock-queue" + } + } + + mock_resource "aws_cloudwatch_event_rule" { + defaults = { + arn = "arn:aws:events:eu-west-1:123456789012:rule/mock-event-rule" + } + } + } # The runner archive is injected during packaging, so isolate the common diff --git a/modules/runner-config/tests/tags.tftest.hcl b/modules/runner-config/tests/tags.tftest.hcl index 8f9cbb2d0d..fe98663654 100644 --- a/modules/runner-config/tests/tags.tftest.hcl +++ b/modules/runner-config/tests/tags.tftest.hcl @@ -16,6 +16,31 @@ mock_provider "aws" { arn = "arn:aws:ssm:eu-west-1:123456789012:parameter/github-runner/config" } } + + mock_resource "aws_iam_policy" { + defaults = { + arn = "arn:aws:iam::123456789012:policy/mock-policy" + } + } + + mock_resource "aws_lambda_function" { + defaults = { + arn = "arn:aws:lambda:eu-west-1:123456789012:function:mock-function" + } + } + + mock_resource "aws_sqs_queue" { + defaults = { + arn = "arn:aws:sqs:eu-west-1:123456789012:mock-queue" + } + } + + mock_resource "aws_cloudwatch_event_rule" { + defaults = { + arn = "arn:aws:events:eu-west-1:123456789012:rule/mock-event-rule" + } + } + } # The runner archive is injected during packaging, so model the common From e615768920f944e2d0def6d54db59a97018dc19e Mon Sep 17 00:00:00 2001 From: edersonbrilhante Date: Sun, 27 Sep 2026 02:57:14 +0200 Subject: [PATCH 9/9] fix(ci): tag loaded scale-set smoke image --- .github/workflows/lambda.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/lambda.yml b/.github/workflows/lambda.yml index 06c116a0ef..6fa0af7393 100644 --- a/.github/workflows/lambda.yml +++ b/.github/workflows/lambda.yml @@ -83,16 +83,8 @@ jobs: file: ./lambdas/services/scale-set/Dockerfile platforms: linux/amd64 push: false - cache-from: type=gha,scope=scale-set-service - cache-to: type=gha,mode=max,scope=scale-set-service - - - name: Load scale-set service image - uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 - with: - context: . - file: ./lambdas/services/scale-set/Dockerfile - platforms: linux/amd64 load: true + tags: scale-set-service:smoke-test cache-from: type=gha,scope=scale-set-service cache-to: type=gha,mode=max,scope=scale-set-service