feat: AddonClaim + InitContainer wait-for-secret en chart helm
Cierra GOLDEN-PATH-ADDONCLAIM-MISSING (P0 hands-off pipeline). Cambios: - helm/APP_NAME/templates/addonclaim.yaml (NEW): hook pre-install/pre-upgrade weight -10, SIN delete-policy (sobrevive upgrades, evita disparar finalizer cleanup-logical). Gated por .Values.database.enabled (default true). - helm/APP_NAME/templates/role-db-secret-reader.yaml (NEW): Role + RoleBinding weight -7 que permite al SA leer el Secret <release>-db-creds. Lo consume el InitContainer wait-for-secret del Job Alembic. - helm/APP_NAME/templates/job-alembic-upgrade.yaml: gated por database.enabled, anade InitContainer wait-for-secret (48x5s=240s) que espera a que addon-provisioner publique el Secret tras reconciliar el AddonClaim. Absorbe la latencia del @kopf.timer (RECONCILE_INTERVAL 300s + initial_delay 60s) sin tocar el operator. - helm/APP_NAME/values.yaml: bloque database completo (enabled, addonType, databaseName, user.permissions, deletionPolicy Retain). Opt-out con database.enabled=false + alembic.enabled=false. - .gitea/workflows/APP_NAME-build.yaml: helm timeout 5m -> 7m (margen para reconcile addon-provisioner + StatefulSet ready + DDL Job + Alembic). - claim-mariadb.yaml (DELETED): obsoleto - estaba en la raiz del template y nunca se renderizaba por helm. Reemplazado por addonclaim.yaml dentro del chart helm. - README.md: documenta nueva tabla de hook weights, opt-out database, y trade-off del orphan AddonClaim post-uninstall (cleanup explicito). Decision arquitectural: Opcion D modificada (Opus 4.6 consult). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
59
README.md
59
README.md
@@ -26,8 +26,7 @@ tenant, los placeholders en mayúsculas se sustituyen automáticamente.
|
||||
│ ├── pyproject.toml
|
||||
│ ├── requirements.txt
|
||||
│ └── Dockerfile
|
||||
├── helm/__APP_NAME__/ ← Helm chart con hook Alembic pre-upgrade
|
||||
├── claim-mariadb.yaml ← AddonClaim de la BD (ver §Base de datos)
|
||||
├── helm/__APP_NAME__/ ← Helm chart con AddonClaim + hook Alembic
|
||||
└── .gitea/workflows/ ← CI: tests + build + deploy
|
||||
```
|
||||
|
||||
@@ -56,8 +55,22 @@ el orden de creación. El `helm upgrade --install` funciona **sin `--no-hooks`**
|
||||
|
||||
| Recurso | Template | `hook-weight` | Motivo |
|
||||
|---------|----------|---------------|--------|
|
||||
| `AddonClaim` | `addonclaim.yaml` | `-10` | Declara la intención de la app de tener una BD — el primero en aplicarse para que `addon-provisioner` empiece a reconciliar lo antes posible |
|
||||
| `Role` + `RoleBinding` | `role-db-secret-reader.yaml` | `-7` | Permite al SA leer el Secret `<APP_NAME>-db-creds` (lo consume el InitContainer `wait-for-secret` del Job Alembic) |
|
||||
| `ServiceAccount` | `serviceaccount.yaml` | `-5` | Lo referencia el pod del Job Alembic — debe existir antes |
|
||||
| `Job` Alembic | `job-alembic-upgrade.yaml` | `0` | Corre `alembic upgrade head` antes de rolar el Deployment |
|
||||
| `Job` Alembic | `job-alembic-upgrade.yaml` | `0` | InitContainer `wait-for-secret` espera al Secret publicado por `addon-provisioner` (hasta 240s), luego corre `alembic upgrade head` |
|
||||
|
||||
El `AddonClaim` (hook weight `-10`) NO declara `hook-delete-policy` intencionalmente:
|
||||
así sobrevive entre `helm upgrade` (re-apply idempotente) y se evita disparar el
|
||||
finalizer `cleanup-logical` del controller en cada upgrade (que dropearía el
|
||||
user de la BD y regeneraría el password, causando ventana de downtime).
|
||||
Trade-off: `helm uninstall` deja el AddonClaim huérfano y MariaDB sigue
|
||||
aprovisionada. Es coherente con `deletionPolicy: Retain` — borrar BD productiva
|
||||
debe ser decisión explícita, no side-effect del uninstall. Cleanup explícito:
|
||||
|
||||
```bash
|
||||
kubectl delete addonclaim <release>-db -n apps
|
||||
```
|
||||
|
||||
El `ServiceAccount` se materializa como hook (no en la fase principal de Helm)
|
||||
porque el Job Alembic, que también es hook, declara `serviceAccountName`. Si el
|
||||
@@ -68,8 +81,9 @@ El SA-hook usa `hook-delete-policy: before-hook-creation` (no `hook-succeeded`):
|
||||
el Deployment lo necesita vivo en runtime, así que solo se borra/recrea al
|
||||
inicio del siguiente hook, manteniéndolo idempotente entre upgrades.
|
||||
|
||||
El `imagePullSecret` y el Secret `<APP_NAME>-db-creds` que referencia el Job los
|
||||
provee la plataforma (stack-deployer / addon), no el chart.
|
||||
El `imagePullSecret` lo provee la plataforma (stack-deployer), no el chart.
|
||||
El Secret `<APP_NAME>-db-creds` lo publica `addon-provisioner` tras reconciliar
|
||||
el `AddonClaim` (ver §Base de datos).
|
||||
|
||||
---
|
||||
|
||||
@@ -90,14 +104,34 @@ provee la plataforma (stack-deployer / addon), no el chart.
|
||||
## Base de datos
|
||||
|
||||
Tu app espera un Secret `__APP_NAME__-db-creds` en su namespace con las
|
||||
claves `DATABASE_URL` y `MIGRATION_DATABASE_URL`. El Secret lo provisiona la
|
||||
plataforma:
|
||||
claves `DATABASE_URL` / `DATABASE_HOST` / `DATABASE_PORT` / `DATABASE_NAME` /
|
||||
`DATABASE_USER` / `DATABASE_PASSWORD` (y opcionalmente `MIGRATION_DATABASE_URL`).
|
||||
|
||||
- **PoC / MVP** — Coralware lo crea manualmente siguiendo el runbook
|
||||
`docs/runbook-mariadb-poc.md` en el repo IDP. Tú no haces nada.
|
||||
- **Producción (Fase 5.5+)** — `claim-mariadb.yaml` lo materializa
|
||||
automáticamente cuando esté disponible el controller `addon-provisioner`
|
||||
(ver ADR-052).
|
||||
El Secret lo materializa automáticamente la plataforma vía el flujo
|
||||
`AddonClaim` → `addon-provisioner` (ADR-052 + ADR-064):
|
||||
|
||||
1. El chart aplica `helm/__APP_NAME__/templates/addonclaim.yaml` como hook
|
||||
`pre-install,pre-upgrade` weight `-10` — declara `addonType: mariadb`,
|
||||
`databaseName` derivado del Release name y el `secretName` esperado.
|
||||
2. `addon-provisioner` (corriendo en el plano de control IDP) reconcilia el
|
||||
`AddonClaim`: crea una BD lógica + user + grant en la instancia MariaDB
|
||||
compartida del tenant (ns `addons` del cluster tenant) vía Job DDL efímero.
|
||||
3. Publica el Secret `__APP_NAME__-db-creds` en el ns `apps`.
|
||||
4. El InitContainer `wait-for-secret` del Job Alembic espera al Secret (hasta
|
||||
240s), Alembic ejecuta `upgrade head`, y el Deployment rola.
|
||||
|
||||
### Opt-out — apps stateless
|
||||
|
||||
Si tu app no necesita BD, en `values.yaml`:
|
||||
|
||||
```yaml
|
||||
database:
|
||||
enabled: false # no se aplica AddonClaim ni Role/RoleBinding
|
||||
alembic:
|
||||
enabled: false # no corre el Job Alembic
|
||||
```
|
||||
|
||||
Con `database.enabled=false` la app se despliega sin BD ni migraciones.
|
||||
|
||||
---
|
||||
|
||||
@@ -165,4 +199,5 @@ Sin lock-in técnico.
|
||||
|
||||
- `docs/adr-053-shape-web-backend-python.md` — anatomía del shape
|
||||
- `docs/adr-052-tenant-addon-claim-model.md` — modelo de add-ons (BD)
|
||||
- `docs/adr-064-free-trial-allows-mariadb.md` — free-trial permite MariaDB
|
||||
- `docs/adr-038-ci-estandar-reusable-workflow.md` — pipeline Python
|
||||
|
||||
Reference in New Issue
Block a user