Skip to content

Commit ec4557e

Browse files
committed
Add preconditions for required configuration attrs
1 parent 3cca4b4 commit ec4557e

24 files changed

Lines changed: 293 additions & 95 deletions

File tree

.github/workflows/main.yml

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,23 +167,41 @@ jobs:
167167
sed -i 's/base_domain = ""/base_domain = "infra.serverwolken.de"/g' *_cluster.tf
168168
169169
# AKS: set resource_group
170-
sed -i 's/resource_group = ""/resource_group = "terraform-kubestack-testing"/g' aks_zero_cluster.tf || true
170+
sed -i 's/# resource_group = "my-resource-group"/resource_group = "terraform-kubestack-testing"/g' aks_zero_cluster.tf || true
171+
172+
# AKS: set availability_zones
173+
sed -i 's/# availability_zones = \["1", "2", "3"\]/availability_zones = ["1", "2", "3"]/g' aks_zero_cluster.tf || true
174+
175+
# AKS: set vm_size (multi-cloud quickstart example)
176+
sed -i 's/# vm_size = "Standard_B2s"/vm_size = "Standard_B2s"/g' aks_zero_cluster.tf || true
177+
178+
# AKS: set vm_size (aks quickstart example)
179+
sed -i 's/# vm_size = "Standard_D2s_v3"/vm_size = "Standard_B2s"/g' aks_zero_cluster.tf || true
171180
172181
# EKS: set region
173182
sed -i 's/region = ""/region = "eu-west-1"/g' eks_zero_providers.tf || true
174183
175-
# EKS: set cluster_availability_zones
176-
sed -i 's/cluster_availability_zones = \[\]/cluster_availability_zones = ["eu-west-1a", "eu-west-1b"]/g' eks_zero_cluster.tf || true
184+
# EKS: set cluster_availability_zones (apps)
185+
sed -i 's/# cluster_availability_zones = \["eu-west-1a", "eu-west-1b", "eu-west-1c"\]/cluster_availability_zones = ["eu-west-1a", "eu-west-1b", "eu-west-1c"]/g' eks_zero_cluster.tf || true
186+
187+
# EKS: set cluster_availability_zones (ops)
188+
sed -i 's/# cluster_availability_zones = \["eu-west-1a", "eu-west-1b"\]/cluster_availability_zones = ["eu-west-1a", "eu-west-1b"]/g' eks_zero_cluster.tf || true
177189
178190
# GKE: set project_id
179-
sed -i 's/project_id = ""/project_id = "terraform-kubestack-testing"/g' gke_zero_cluster.tf || true
191+
sed -i 's/# project_id = "my-gcp-project"/project_id = "terraform-kubestack-testing"/g' gke_zero_cluster.tf || true
180192
181193
# GKE: set region
182-
sed -i 's/region = ""/region = "europe-west1"/g' gke_zero_cluster.tf || true
194+
sed -i 's/# region = "europe-west1"/region = "europe-west1"/g' gke_zero_cluster.tf || true
183195
184196
# GKE: set cluster_node_locations
185197
sed -i 's/cluster_node_locations = \[\]/cluster_node_locations = ["europe-west1-b", "europe-west1-c", "europe-west1-d"]/g' gke_zero_cluster.tf || true
186198
199+
# SCW: set region
200+
sed -i 's/# region = "fr-par"/region = "fr-par"/g' scw_zero_cluster.tf || true
201+
202+
# SCW: set zones
203+
sed -i 's/# zones = \["fr-par-1", "fr-par-2", "fr-par-3"\]/zones = ["fr-par-1", "fr-par-2", "fr-par-3"]/g' scw_zero_cluster.tf || true
204+
187205
- name: "OpenTofu init"
188206
working-directory: ./kubestack-starter-${{ matrix.starter }}
189207
run: |

AGENTS.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Before considering any task complete, check each row of the table below and appl
2525
| Any module code or `tests/` configuration | Run `make validate`. |
2626
| Any divergence between implementation and rules discovered or resolved | Add or remove the entry in `DEVIATIONS.md`. |
2727
| Any permanent exception to the standard module contract added or removed | Add or remove the entry in `DEVIATIONS.md`. |
28+
| New required configuration attribute added to a module (guarded by a `lifecycle precondition`) | Add a commented-out example line for it in every affected quickstart file. Add a matching `sed` line to the **"Configure Kubestack"** step in `.github/workflows/main.yml`. See the Quickstart Placeholder and CI Injection Pattern section. |
2829
| New cloud provider added | Add provider-specific CLI build and dist targets to `oci/Dockerfile`, following the pattern of existing providers. Add auth instructions to the shared quickstart `README.md`. |
2930
| New quickstart added | Symlink shared files (`README.md`, `.gitignore`, `.user/`) to `quickstart/src/configurations/_shared/` instead of duplicating them. |
3031

@@ -60,7 +61,7 @@ When a provider does not support a required behaviour (e.g. private nodes), appl
6061

6162
- All environments inherit their configuration from the base environment. The base environment key defaults to `apps` (set via `configuration_base_key`). An environment-specific value always overrides the inherited base value.
6263
- The inheritance logic is implemented in the `common/configuration` module. All Kubestack modules MUST use it. See the Variables and Outputs section for the required usage pattern.
63-
- Because all `configuration` object attributes must be marked `optional(...)` to support inheritance, values that have no module default and must always be user-provided (region, zones, instance type, and `min`/`max` node counts — see Multi-Zone Resilience and Node Pool Lifecycle sections) cannot be enforced by the type system alone. For each such attribute, modules MUST add a `lifecycle` `precondition` block on the primary resource (the cluster resource in a cluster module, the node-pool resource in a node-pool module). The condition MUST check that `local.cfg.<attribute_name>` is not `null`. The error message MUST name the attribute and state that it is required, e.g.: `"<attribute_name> must be set — Kubestack does not provide a default for this value."`.
64+
- Because all `configuration` object attributes must be marked `optional(...)` to support inheritance, values that have no module default and must always be user-provided (region, zones, instance type, and `min`/`max` node counts — see Multi-Zone Resilience and Node Pool Lifecycle sections) cannot be enforced by the type system alone. For each such attribute, modules MUST add a `lifecycle` `precondition` block on the primary resource (the cluster resource in a cluster module, the node-pool resource in a node-pool module). The condition MUST check that `local.cfg.<attribute_name>` is not `null`. The error message MUST name the attribute and state that it is required, e.g.: `"missing required configuration attribute: <attribute_name>"`.
6465

6566
### Multi-Zone Resilience
6667

@@ -399,4 +400,34 @@ The Kubestack framework is released as three asset types. Refer to the Mandatory
399400
|---|---|
400401
| **Versioned modules** | Consumed via `module` blocks using GitHub URLs. |
401402
| **Container image** | Base image for CI/CD pipelines and manual tasks, bundling cloud provider CLIs for IAM authentication and debugging, and Kustomize, OpenTofu/Terraform binaries at the correct version (`oci/Dockerfile`). |
402-
| **Quickstarts** | Example directory layouts for bootstrapping new user repositories. Quickstart examples MUST include `name_prefix`, `base_domain`, region, zones, instance type, and autoscaling `min`/`max` — all values that have no module default and must always be user-provided. Leave all other values absent to rely on module and provider defaults. Common files (`README.md`, `.gitignore`, `.user/`) are shared via symlinks pointing to `quickstart/src/configurations/_shared/`. |
403+
| **Quickstarts** | Example directory layouts for bootstrapping new user repositories. Quickstart examples MUST include `name_prefix`, `base_domain`, region, zones, instance type, and autoscaling `min`/`max` — all values that have no module default and must always be user-provided. Required attributes that are guarded by a `lifecycle precondition` MUST be represented as a commented-out example line (see Quickstart Placeholder and CI Injection Pattern below) so that users who forget to set them receive the helpful precondition error message rather than a cryptic provider error. Leave all other values absent to rely on module and provider defaults. Common files (`README.md`, `.gitignore`, `.user/`) are shared via symlinks pointing to `quickstart/src/configurations/_shared/`. |
404+
405+
### Quickstart Placeholder and CI Injection Pattern
406+
407+
Attributes guarded by a `lifecycle precondition` — those with no module default that must always be user-provided — MUST be represented in quickstart files as a **commented-out example line**, not as an active assignment with an empty value. This ensures that a user who clones a quickstart and runs `tofu plan` without filling in required values receives the helpful precondition error message (e.g. `missing required configuration attribute: region`) rather than a cryptic provider-level error caused by an empty string or empty list being passed through.
408+
409+
**Quickstart file convention for precondition-guarded attributes:**
410+
411+
```hcl
412+
# region = "eu-west-1"
413+
```
414+
415+
Because the key is absent (commented out), OpenTofu evaluates it as `null`, which triggers the precondition. The comment serves as inline documentation showing the user exactly what format the value must take.
416+
417+
**Non-precondition-guarded required values** (e.g. `name_prefix`, `base_domain`) continue to use an active assignment with an empty string placeholder (`= ""`), because they are not null-checked by a precondition and the empty value is intentional as a prompt to the user.
418+
419+
**CI injection:** The CI pipeline injects real values before running `tofu validate` and `tofu plan` using `sed` substitution in the **"Configure Kubestack"** step of `.github/workflows/main.yml`. For commented-out example lines the `sed` command removes the leading `# ` to activate the line:
420+
421+
```bash
422+
# SCW: set region
423+
sed -i 's/# region = "fr-par"/region = "fr-par"/g' scw_zero_cluster.tf || true
424+
```
425+
426+
**Rules:**
427+
428+
- Every precondition-guarded attribute MUST have a commented-out example line in every affected quickstart file, using a realistic placeholder value that matches the expected format.
429+
- Every such commented-out line MUST have a corresponding `sed` line in the **"Configure Kubestack"** CI step that activates it by removing the `# ` prefix, following the comment convention `# <PROVIDER>: set <attribute_name>`.
430+
- The `|| true` suffix on `sed` commands targeting provider-specific files (e.g. `aks_zero_cluster.tf`) is required so that the step does not fail when that file is absent in a single-provider quickstart.
431+
- When a new precondition-guarded attribute is added to a module, you MUST:
432+
1. Add a commented-out example line for it in every affected quickstart file.
433+
2. Add a matching `sed` line to the **"Configure Kubestack"** step in `.github/workflows/main.yml`.

0 commit comments

Comments
 (0)