diff --git a/.agents/skills/e2e-test/SKILL.md b/.agents/skills/e2e-test/SKILL.md index 891602b2..6013c79e 100644 --- a/.agents/skills/e2e-test/SKILL.md +++ b/.agents/skills/e2e-test/SKILL.md @@ -99,10 +99,22 @@ Conventions that keep this clean and correct: `test_context` forces the variant to be chosen before `tofu test` starts, which pushes a test-matrix concern out of the hub and into whatever invokes it. +- **Never read a fixture from the environment.** If `test_context` can carry the value, it carries + it — a `TF_VAR_*` has to be wired in the harness repo too (an Actions variable, a workflow line, a + `setup-env.sh` line), and nothing checks that those still agree. That is exactly how the + `meshstack/noop` runner test broke silently when one of two wiring lines was removed. The harness + side of this rule, including where a new fixture value comes from, is the four-branch rule in + `meshstack-smoke-test`'s `AGENTS.md`. + ### Provider authentication secrets Provider authentication secrets should come via standard environment variables expected by these providers, not grab-bag fields in `test_context`. +Secrets are the one exception to the rule above: GitHub masks values only one by one, so a secret +cannot ride in the `test_context` grab-bag. It arrives as a scalar `TF_VAR_` and the module +declares a matching top-level `sensitive` variable (`nullable`, `default = null`, so foundation mode +can omit it). + --- ## `e2e/main.tf` conventions @@ -464,6 +476,7 @@ source setup-override-provider.sh - [ ] `fixtures` is `optional()` with its inner shape fully required (no half-populated fixtures) - [ ] Always-shared fields (`workspace`, `name_suffix`, `hub_git_ref`) are required, not `optional()` - [ ] Cloud resource IDs sourced from `var.test_context.fixtures.*` (not flat `test_context` fields) +- [ ] No fixture read from the environment — only secrets arrive as `TF_VAR_*` - [ ] Scalar secrets are top-level `nullable` vars with `default = null` (foundation mode omits them) - [ ] Module sourced via relative path (not a GitHub URL), gated with `count = var.test_context.bbd_version_ref == null ? 1 : 0` - [ ] `hub.git_ref = var.test_context.hub_git_ref` — no hardcoded `"main"` diff --git a/AGENTS.md b/AGENTS.md index 2c5cec47..b3948d42 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -414,6 +414,10 @@ the diagram checklist. Modules that can be smoke-tested against a live meshStack instance should include an `e2e/` directory alongside the module root. +An `e2e/` module takes every environment fact from `var.test_context` and **never from the +environment** — only secrets arrive as scalar `TF_VAR_*`. A `TF_VAR_` fixture has to be wired in the +harness repo as well, which is a second place for it to go missing. + See [.agents/skills/e2e-test/SKILL.md](.agents/skills/e2e-test/SKILL.md) (the `e2e-test` skill) for the full e2e testing conventions, including the `e2e/` structure, `test_context` wiring, `e2e/main.tf` and `*.tftest.hcl` conventions, the new-test checklist, and how to run and debug tests via the smoke-test runner. diff --git a/modules/meshstack/noop/e2e/runner/main.tf b/modules/meshstack/noop/e2e/runner/main.tf index dd468f56..008440c4 100644 --- a/modules/meshstack/noop/e2e/runner/main.tf +++ b/modules/meshstack/noop/e2e/runner/main.tf @@ -2,8 +2,8 @@ module "backplane" { source = "../../backplane" meshstack_workspace_identifier = var.test_context.workspace - meshstack_endpoint = var.meshstack_endpoint - gcp_project_id = var.gcp_project_id + meshstack_endpoint = var.test_context.meshstack_endpoint + gcp_project_id = var.test_context.fixtures.gcp.project_id gcp_region = var.gcp_region gcp_resource_name_prefix = "noop-runner-${var.test_context.name_suffix}" runner_display_name = "smoke-test-noop-runner-${var.test_context.name_suffix}" diff --git a/modules/meshstack/noop/e2e/runner/provider.tf b/modules/meshstack/noop/e2e/runner/provider.tf index 7e8eebd1..cf5cded0 100644 --- a/modules/meshstack/noop/e2e/runner/provider.tf +++ b/modules/meshstack/noop/e2e/runner/provider.tf @@ -1,4 +1,4 @@ provider "google" { - project = var.gcp_project_id + project = var.test_context.fixtures.gcp.project_id region = var.gcp_region } diff --git a/modules/meshstack/noop/e2e/runner/variables.tf b/modules/meshstack/noop/e2e/runner/variables.tf index 6b9240f5..eadaf58e 100644 --- a/modules/meshstack/noop/e2e/runner/variables.tf +++ b/modules/meshstack/noop/e2e/runner/variables.tf @@ -4,22 +4,22 @@ variable "test_context" { workspace = string project = string name_suffix = string + + # Base URL of the meshStack API, written into the runner config for API polling. + meshstack_endpoint = string + + # Project the runner's Cloud Run service and Secret Manager secrets live in. + fixtures = object({ + gcp = object({ + project_id = string + }) + }) }) nullable = false } -variable "gcp_project_id" { - type = string - description = "GCP project ID for the runner Cloud Run service and Secret Manager secrets." -} - variable "gcp_region" { type = string default = "europe-west1" description = "GCP region for the Cloud Run service and Secret Manager replicas." } - -variable "meshstack_endpoint" { - type = string - description = "Base URL of the meshStack API. Written into the runner config for API polling." -}