diff --git a/CATALOG_REPO_CONTRACT.md b/CATALOG_REPO_CONTRACT.md new file mode 100644 index 0000000..0ea7ef2 --- /dev/null +++ b/CATALOG_REPO_CONTRACT.md @@ -0,0 +1,316 @@ +# BNK Forge Catalog Repo Contract + +This document defines the **contract** every `bnk-forge-catalog-*` repository must follow so that: + +1. BNK Forge can sync them as Module Sources and/or Blueprint Sources without per-repo special-casing. +2. Customers can clone or import any of them and get a consistent experience. +3. Third-party contributors can author new catalog repos confidently. +4. Vendor-refresh automation (where applicable) works the same in every repo. + +If you are creating a new catalog repo — AWS, Azure, GCP, on-prem, or anything else — this file is the canonical reference. Match it. + +## Naming + +Repository names follow the pattern **`bnk-forge-catalog-`** where `` is a short, hyphen-separated identifier for the deployment target: + +| Repo name | Target | +|---|---| +| `bnk-forge-catalog-shared` | This repo — shared cloud-agnostic k8s primitives | +| `bnk-forge-catalog-aws-eks` | AWS EKS | +| `bnk-forge-catalog-azure-aks` | Azure AKS (planned) | +| `bnk-forge-catalog-gcp-gke` | GCP GKE (planned) | +| `bnk-forge-catalog-onprem-k8s` | Generic on-prem Kubernetes (planned) | + +External / community catalogs may use their own naming (e.g. `jgruberf5/bnk-forge-ibm-roks-cluster`) — the `bnk-forge-catalog-*` convention applies to JLCode-tech-owned repos. + +## Branches and BNK release cadence + +Every catalog repo carries one branch per BNK release: `release/2.2`, `release/2.3`, `release/3.0`, etc. The branch matches the BNK product version that the content targets. `main` tracks the most recent release branch. + +- New content for the current BNK release lands on the current `release/X.Y` branch. +- A new BNK release cuts a new `release/X.Y` branch, typically branched from the prior release branch and updated forward. +- Customers point Forge's Module Source / Blueprint Source at the branch (or a tag within it) for the BNK version they want. + +## Required top-level files + +| File | Purpose | +|---|---| +| `README.md` | Repo overview. See "README requirements" below. | +| `.gitignore` | Standard Terraform gitignore (excludes `.terraform/`, `*.tfstate*`, `.terraform.lock.hcl`, etc.). | +| `VENDORED.md` | **Required if the repo vendors modules from another catalog repo.** Documents what is vendored, from where, and the refresh discipline. | +| `VENDORED.pin` | **Required if vendoring.** Machine-readable pin record — auto-generated by `scripts/vendor-refresh.sh`. | +| `scripts/vendor-refresh.sh` | **Required if vendoring.** Portable bash (3.2+ compatible) script that refreshes the vendored modules from a pinned upstream ref. | +| `.github/workflows/vendor-refresh.yml` | **Required if vendoring.** Workflow that runs the script on cron + dispatch and opens a PR if drift detected. | + +### README requirements + +The root README must include: + +1. **One-paragraph summary** of what the repo contains. +2. **Modules table** — every module under `modules/` listed with its path and purpose. Mark "not yet implemented" entries explicitly. +3. **Blueprints table** — every blueprint under `blueprints/` listed with its module chain. +4. **Repo model section** — describe the per-release branching ("This is a long-lived repo with branches per BNK release: `release/2.2`, `release/2.3`, …"). +5. **Vendored content** (if applicable) — reference `VENDORED.md` for the discipline; never duplicate the vendor list in README. +6. **Credential template compatibility** — list the credential template variable names this repo's blueprints expect (e.g. `aws_access_key_id`, `aws_region`). +7. **BNK registration outputs** — list the canonical output fields that cluster-create / cluster-register modules emit for Forge auto-registration (`cluster_name`, `cluster_id`, `cluster_endpoint`, `region`, `kubeconfig`). +8. **Forge import instructions** — how to add this repo as a Module Source and Blueprint Source in Forge. + +## Directory layout + +``` +bnk-forge-catalog-/ +├── README.md +├── .gitignore +├── VENDORED.md (if vendoring) +├── VENDORED.pin (if vendoring; auto-generated) +├── scripts/ +│ └── vendor-refresh.sh (if vendoring) +├── .github/ +│ └── workflows/ +│ └── vendor-refresh.yml (if vendoring) +├── modules/ +│ └── / (one dir per module) +│ ├── bnkforge.pack.json +│ ├── module.json (legacy; required during transition) +│ ├── main.tf +│ ├── variables.tf +│ ├── outputs.tf +│ ├── versions.tf +│ └── README.md +└── blueprints/ + └── / + ├── forge-blueprint.json + └── README.md +``` + +### Module naming + +Modules under `modules/` follow `-` naming: + +- `eks-cluster-create`, `eks-cluster-register` (provisioning) +- `eks-cluster-install-cert-manager`, `eks-cluster-install-flo` (install steps) +- `eks-cluster-cneinstall`, `eks-cluster-license` (BNK-specific) + +The `` prefix scopes the module name to the deployment target so cross-catalog naming collisions don't happen. + +For the shared catalog repo (`bnk-forge-catalog-shared`), modules are organized by family at the top level (`k8s/bnk-prerequisites`, `k8s/cert-manager`, etc.) — they're not deployment-target-scoped because they're cloud-agnostic. + +## Module structure + +### Required files per module + +| File | Purpose | +|---|---| +| `bnkforge.pack.json` | v2alpha1 pack manifest. Forge reads this to populate the module catalog. | +| `module.json` | Legacy metadata. Required during transition while Forge's release-manifest validator still references it. | +| `main.tf`, `variables.tf`, `outputs.tf`, `versions.tf` | Standard Terraform. Skip `.tf` files for pure-manifest modules (engine `kubernetes`). | +| `README.md` | Module-level docs: when to use, inputs, outputs, IAM/cluster permissions required. | +| `manifests/` | YAML manifests for pure-manifest modules (engine `kubernetes`). | +| `scripts/` | Helper scripts invoked by `data.external` blocks in `main.tf`. | +| `examples/` | Usage examples (HCL snippets). Optional but recommended. | + +### `bnkforge.pack.json` schema + +```json +{ + "schema_version": 1, + "module": { + "name": "", + "path": "", + "version": "", + "category": "", + "description": "", + "provider": "", // optional but recommended + "supported_platforms": [""], + "tags": ["", ...] + }, + "deployment_pack": { + "engine": "", + "runner_profile": "", + "working_directory": ".", + "entrypoints": { "module_root": "." }, + "lifecycle": { + "supports_init": true, + "supports_plan": true, + "supports_apply": true, + "supports_destroy": true, + "supports_refresh": true, + "supports_drift": true + } + }, + "credentials": { + "required": [ + { "name": "", "type": "cloud", "description": "..." } + ], + "optional": [] + }, + "dependencies": { + "required": [ + { "module": "", "reason": "..." } + ], + "optional": [] + }, + "inputs": { + "required": [ + { "name": "", "type": "", + "description": "...", "source": "", "sensitive": } + ], + "optional": [ + { "name": "", ..., "default": } + ] + }, + "outputs": { + "key_outputs": [ + { "name": "", "type": "", "description": "..." } + ] + } +} +``` + +**Validation**: The pack JSON must pass `python3 scripts/validate_pack_manifests.py` in this repo (`bnk-forge-catalog-shared`). The validator is the canonical schema enforcement. + +**Forbidden values**: `inputs[*].source` must be one of `user`, `module`, or `auto`. **Do not** use `project_secret` — Forge handles sensitive bindings via the deploy form when `sensitive: true` is set on a `user`-source input. + +## Blueprint structure + +### `forge-blueprint.json` schema + +```json +{ + "schema_version": 1, + "blueprint": { + "id": "", + "version": "", + "name": "", + "description": "" + }, + "bnk_version": "", // the BNK release this targets + "compatibility": { + "supported_platform_profiles": [""], + "required_capabilities": [] + }, + "category": "", // 'bnk' for install blueprints, 'infrastructure' for provisioning + "cloud_provider": "", + "icon": "", + "color": "", + "estimated_time": "<5-10 minutes>", + "estimated_cost": "<...>", + "difficulty": "", + "maturity": "", + "tags": ["", ...], + "outcomes": [ "", ... ], + "prerequisites": [ + { "type": "kubernetes_cluster", "description": "..." }, + { "type": "credential_template", "name": "", "description": "..." }, + { "type": "project_secret", "name": "", "description": "..." } + ], + "input_summary": [ + { "label": "Input guidance", "value": "..." } + ], + "platform_defaults": { + "": { + "container_platform": "", + "storage_class_name": "", + "cloud_provider": "", + "cni_type": "", + "nad_cni_type": "", + "nad_interface_name": "", + "nad_ipvlan_mode": "", + "cert_manager_namespace": "cert-manager", + "flo_namespace": "", + "flo_utils_namespace": "f5-utils" + } + }, + "inputs": { + "required": [ + { "name": "", "type": "string", "description": "...", + "sensitive": , + "source": "", + "source_field": "", // when source is credential_template or project + "order": // form ordering, lower = earlier + } + ], + "optional": [ ..., "default": ] + }, + "modules": [ + { + "id": "", + "name": "", + "description": "", + "module": "modules/", + "version": "", + "depends_on": ["", ...], + "inputs": { + "": "${}" + } + } + ] +} +``` + +### Cross-module wiring + +Forge auto-wires module-to-module dependencies by **name match across `inputs`/`outputs`** when modules share `depends_on`. The blueprint manifest does **not** use `${module..outputs.}` syntax — it only references blueprint-level inputs via `${var-name}`. If a module input has the same name as an upstream module's output and the upstream is in `depends_on`, Forge wires it automatically. + +### Cloud credential pattern + +Two valid patterns: + +**Pattern A (IBM-style, cloud-coupled modules)**: every module accepts the cloud credentials directly. Each module does its own cluster lookup via the cloud provider data sources. The blueprint passes credentials to every module's `inputs` block. + +**Pattern B (cloud-agnostic modules)**: only the cluster-register / cluster-create module accepts cloud credentials. Downstream modules use Forge-injected `forge_kubeconfig_content` (via `local.forge_kubeconfig` in their TF code — Forge generates `bnk_forge_providers.tf` at deploy time). The blueprint only wires credentials to the cluster module. + +Both patterns are valid. The shared `bnk-forge-catalog-shared` modules use **Pattern B** because they're cloud-agnostic by design. Per-cloud-specific install modules (FLO, CNEInstance, License) typically use **Pattern A** because they need cloud APIs for IAM/registry/etc. setup. + +A single blueprint can mix both patterns — wire creds to the modules that need them, omit for the vendored cloud-agnostic ones. + +## Vendoring rules (if applicable) + +If a catalog repo vendors modules from `bnk-forge-catalog-shared` (or any other upstream catalog): + +1. **Vendored files are pristine.** Do not hand-edit anything inside a vendored module's directory. The next refresh will clobber local changes. +2. **AWS/Azure/GCP-specific tuning belongs in a wrapper module**, not in the vendored tree. Write a new module (e.g. `eks-cluster-install-flo`) that uses the vendored content as a submodule or reference. +3. **The pin (`VENDORED.pin`) is auto-generated** by `scripts/vendor-refresh.sh`. Do not edit it by hand. To change the upstream ref, run the script with `UPSTREAM_REF=...`. +4. **The refresh script applies deterministic rewrites** to the vendored pack JSONs: rewriting `module.path` and `dependencies.required[].module` to the local module names. The rewrite rules are encoded in the script's `rewrite_jq` block. +5. **The refresh workflow runs three triggers**: `schedule` (weekly cron), `workflow_dispatch` (manual), `repository_dispatch` (event `bnk-forge-modules-released` fired from this repo on push to `release/*`). + +See [`VENDORED.md`](https://github.com/JLCode-tech/bnk-forge-catalog-aws-eks/blob/release/2.2/VENDORED.md) in `bnk-forge-catalog-aws-eks` for a working example. + +## Validation + +Before opening a PR in any catalog repo, run the equivalent of: + +```bash +# JSON well-formedness +find . -name "bnkforge.pack.json" -exec jq empty {} \; +find . -name "forge-blueprint.json" -exec jq empty {} \; + +# Pack schema (this validator lives in bnk-forge-catalog-shared/scripts/) +python3 scripts/validate_pack_manifests.py + +# Terraform (only for non-vendored, non-Forge-injected modules) +tofu fmt -check -recursive +( cd modules/ && tofu init -backend=false && tofu validate ) +``` + +Vendored TF modules will fail standalone `tofu validate` with "local.forge_kubeconfig not declared" — that's by design, not a bug. Forge injects that local at deploy time. + +## Adding a new catalog repo + +1. Create `JLCode-tech/bnk-forge-catalog-` as a public repo with an auto-generated README and Terraform `.gitignore`. +2. Push an initial commit on `main`, then branch `release/2.X` (matching the current BNK release). +3. Populate the structure above on the release branch via a series of PRs: + - PR 1: scaffold + cluster-register module + initial blueprint stub + - PR 2: vendor the shared k8s layer + extend blueprint to chain prereqs/cert-manager/cert-issuer (if vendoring) + - PR 3+: per-cloud install modules (FLO, CNEInstance, License) + extend blueprint +4. Once content stabilizes, mark blueprint `maturity: "reference"` and document in this contract's compatibility section above. + +## Open questions + +- Should `module.json` be deprecated entirely once Forge stops reading it? Yes — but defer until `bnk-forge-v2`'s release-manifest validator no longer requires it. +- Should vendored modules track a tag (e.g. `v2.2.0`) instead of a branch (`release/2.2`)? Branches give us a moving target; tags give immutability. Defer until the first vendor-refresh after a `release/X.Y+1` cut surfaces the tradeoff in practice. +- Should the contract itself be versioned? If yes, this doc gets `version: 1` at the top. Defer until a backwards-incompatible change is actually needed. + +--- + +Last reviewed: 2026-05-13 diff --git a/VERSION b/VERSION index d5cc7bb..8f2fcfd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2-rev.31 +2.2-rev.32