Skip to content
The last release, 0.1.0, predates this site: this is the documentation of main.

Catalog module standard

The contract every module under oci/catalog/ meets; the why is in The Flux catalog. Models: hello for structure and tests, argocd for chart values.

PartPathChecked by
The templateoci/catalog/<kebab-name>/resourceset.yaml, one ResourceSet named after the folder, in flux-systemcheck-catalog-clouds.sh; pr-static.yaml (render, strict kubeconform)
The schema entrylocal.catalog in opentofu/bootstrap/catalog.tf, key <snake_name>, every attribute with its default, enabled includedcheck-catalog-clouds.sh; the kube validations
Per-attribute ruleskube validations in opentofu/bootstrap/variables.tf, each failing case in tests/validations.tftest.hcltofu test
The overlays../../catalog/<kebab-name>/resourceset.yaml in each offering oci/clusters/<cloud>/kustomization.yamlcheck-catalog-clouds.sh, both directions
What a cloud addsoci/clusters/<cloud>/<kebab-name>/patch.yaml, applied by that cloud’s overlay to the module’s ResourceSet: a step cloud inserted before workload behind a test op, steps appended after it, a dependsOn; no file on a cloud with nothing of its own (SOCLE-32)check-catalog-clouds.sh: applied, aimed at its module, nothing else in the folder, no inputs.cloud under oci/catalog/; pr-static.yaml renders each patched module
Cloud binding<snake_name> = ["aws", …] in catalog_clouds, one per line, when not on every cloudcheck-catalog-clouds.sh; refused at plan elsewhere
Monitoring, for every module with a workloadIts pods annotated prometheus.io/scrape on a declared port (vmagent’s scrape of annotated pods), or a VMServiceScrape when it serves HTTPS only; oci/catalog/<kebab-name>/monitoring/: awesome-prometheus-alerts’ file when it has one, the project’s dashboard, and the socle’s rules.yaml for what awesome-prometheus-alerts lacks; applied by a Kustomization <kebab-name>-monitoring with retryInterval: 1m, on only while the module and monitoring both are (MONITORING-12)upstream-monitoring.sh check (copies unchanged), check-rules.sh (vmalert -dryRun), both in pr-static.yaml; the module’s e2e asserts the Kustomization Ready
The e2e proofoci/catalog/<kebab-name>/tests/e2e/chainsaw-test.yaml, optional values.yamlcheck-catalog-clouds.sh; e2e.yaml runs it
The pagedocs/catalog/<kebab-name>.md, from _template.mdrule 6 of CONTRIBUTING; the site build
The decisionsdocs/decisions/<kebab-name>.md, from _template.md; not published on the sitereview
  • inputsFrom the ResourceSetInputProvider socle; no inline inputs.
  • fluxcd.controlplane.io/reconcile: << if inputs.modules.<m>.enabled >>enabled<< else >>disabled<< end >> on each object’s own annotations, never in commonMetadata.
  • One namespace, rendered by the module, carrying the toggle.
  • Templates test values, never presence.
  • Nothing that is one cloud’s under oci/catalog/, no inputs.cloud there: it goes in the cloud’s patch.yaml; never a new OpenTofu input.
  • Steps namespace, then workload (the chart, its values, its monitoring), so a cloud can insert cloud between them.
  • The chart pinned exactly (OCI tag, or HelmRepository version), listed in Compatibility.
  • No inline spec.values; valuesFrom: <module>-socle-values, <module>-cloud-values (optional: true, when a cloud has values for the chart), <module>-client-values, then the values_secret Secret (optional: true); ConfigMaps labelled reconcile.fluxcd.io/watch: Enabled.
  • values and values_secret in the schema; secret-bearing paths refused at plan, each with a failing tofu test.
  • resourcesTemplate when a block appears under a condition.
  • Cloud access as Crossplane resources in the cloud’s cloud step, with a dependsOn on crossplane and an empty <module>-access ResourceSet that dependsOn the role with a readyExpr, so the workload step waits for it; nothing under opentofu/<cloud>/.
  • CRDs that hold your objects kept when off, or the page says not (CRDs when a module is off).
  • New attributes in oci/.ci/inputs-sample.yaml; flux-operator build rset renders, kubeconform -strict passes.

e2e.yaml runs one job per (module, cloud) whose overlay lists the module, each on its own floci with the fixture root .github/e2e/<cloud>/; the real root is applied once, in root (<cloud>). chainsaw-test.yaml holds one Test per phase, selected by label:

LabelRunsHolds
phase: healthevery job, the real root’s includedthe ResourceSet Ready, the workload Available, the socle’s values live
phase: modulethe module’s job, after healthmutations, each a patch of the input provider socle; the last restores it
phase: destroyedafter the uninstallwhat must survive it
cloud: any / cloud: <cloud>every cloud, or onea per-cloud assertion is its own Test
platform: any / platform: flocieverywhere, or floci onlywhat only floci can state
platform: gkethe GKE sandbox, run by hand; the floci CI never selects itwhat only a real GKE can state; the Test carries its own spec.timeouts, at least apply 30s, assert 180s, error 60s, delete 60s, cleanup 120s and exec 30s: Chainsaw’s defaults are too short for calls through the DNS endpoint and Autopilot’s restarts (measured 2026-10-16)

The module phase proves at least:

  • every named attribute reaching its live object;
  • the valuesFrom order on a key the socle sets: values over it, the values_secret Secret (labelled reconcile.fluxcd.io/watch: Enabled) over that, both cleared;
  • off: everything garbage-collected, and the effect on dependent modules; on again;
  • what it does against the cloud, where floci can show it.

Every step has a catch (describe, events, podLogs), and the off step waits for an idle release (Ready, Released, no Reconciling).

Under the hood

Bindings: tag, cloud, cluster, floci_ip (and resourceset for the generic suite); tests/e2e/values.yaml adds others through --values. After the module’s tests the job sets kube.<module>.enabled = false (the disabled suite asserts nothing rendered), restores the defaults, re-runs root and health, requires an empty tofu plan -detailed-exitcode, then uninstalls and runs the destroyed suites. Chainsaw operations come first; a script only for what the cluster cannot show (a record in floci’s Route 53) or a catch that prints the node’s state. What floci cannot serve (Pod Identity, today) is a step of its own, named as such.

  • Folder oci/catalog/<kebab-name>/, key <snake_name>, enabled in the entry.
  • .github/scripts/check-catalog-clouds.sh passes.
  • Every object, the namespace included, carries the toggle.
  • The chart pinned exactly, its version in docs/reference/compatibility.md.
  • valuesFrom socle, cloud, client, Secret; the watch label on every ConfigMap.
  • Every refusal and validation has a failing tofu test case.
  • No inputs.cloud under oci/catalog/; a cloud’s additions in its patch.yaml, its access before the workload.
  • Kept CRDs, or the page says they go.
  • oci/.ci/inputs-sample.yaml updated; the render kubeconforms strictly.
  • health and module tests, a catch on every step, an idle-release gate on off.
  • The module test overrides a socle key through values, then values_secret.
  • Page and decisions file exist; the page has title, description, category, requires.
  • The page has a kube-start block and a kube-full block matching catalog.tf.