chart 0.3.0: AddonClaim FUERA del chart Helm (ADR-066 del IDP)
Bug en chart 0.2.x: AddonClaim era Helm hook pre-install,pre-upgrade weight -10 SIN hook-delete-policy. El default Helm 3 es before-hook-creation → borra el claim en cada upgrade → finalizer cleanup-logical rotaba credenciales + atoraba release en pending-upgrade. Cambios: - helm/APP_NAME/templates/addonclaim.yaml eliminado. - manifests/addonclaim.yaml creado con placeholders sed. - scripts/pre-helm-detach.sh idempotente para migracion v1.0.0 → 1.1.0. - .gitea/workflows/APP_NAME-build.yaml: 2 nuevos steps detach + apply AddonClaim entre kubeconfig y helm upgrade. - Chart.yaml bump 0.2.0 → 0.3.0. - README reescrito (3 pasos workflow + tabla hooks sin AddonClaim + seccion Por que FUERA del chart + opt-out actualizado). - Comentarios stale del AddonClaim como hook -10 limpiados en networkpolicy.yaml, role-db-secret-reader.yaml, job-alembic-upgrade.yaml. ADR-066 del IDP documenta la decision arquitectural completa.
This commit is contained in:
@@ -13,6 +13,8 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- 'code/**'
|
- 'code/**'
|
||||||
- 'helm/**'
|
- 'helm/**'
|
||||||
|
- 'manifests/**'
|
||||||
|
- 'scripts/**'
|
||||||
- '.gitea/workflows/__APP_NAME__-build.yaml'
|
- '.gitea/workflows/__APP_NAME__-build.yaml'
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
@@ -125,6 +127,36 @@ jobs:
|
|||||||
echo "$KUBECONFIG_B64" | base64 -d > "${HOME}/.kube/config"
|
echo "$KUBECONFIG_B64" | base64 -d > "${HOME}/.kube/config"
|
||||||
chmod 600 "${HOME}/.kube/config"
|
chmod 600 "${HOME}/.kube/config"
|
||||||
|
|
||||||
|
# ── ADR-066: AddonClaim vive FUERA del chart Helm ───────────────────
|
||||||
|
# El claim lo gestiona addon-provisioner via finalizer cleanup-logical;
|
||||||
|
# no debe acoplarse al lifecycle del release Helm para no rotar
|
||||||
|
# credenciales en cada upgrade.
|
||||||
|
|
||||||
|
- name: De-adoptar AddonClaim legacy (migracion chart 1.0.0 -> 1.1.0)
|
||||||
|
# Idempotente: skip silencioso si AddonClaim no existe (primera
|
||||||
|
# instalacion) o si ya esta de-adoptado (segundo run post-migracion).
|
||||||
|
# Si tiene annotations Helm de un release v1.0.0, las quita para que
|
||||||
|
# el siguiente helm upgrade no intente borrarlo (no esta en el chart).
|
||||||
|
# Sunset del step tras una rotacion completa (~2 semanas).
|
||||||
|
run: |
|
||||||
|
RELEASE_NAME=__APP_NAME__ bash scripts/pre-helm-detach.sh
|
||||||
|
|
||||||
|
- name: Aplicar AddonClaim
|
||||||
|
# Sustitucion de placeholders con sed (evita dependencia de envsubst,
|
||||||
|
# que no esta garantizado en la imagen del runner). kubectl apply es
|
||||||
|
# idempotente: primer push lo crea, push subsiguientes son no-op si
|
||||||
|
# el spec no cambia. helm upgrade --install nunca lo trackea ni lo borra.
|
||||||
|
env:
|
||||||
|
RELEASE_NAME: __APP_NAME__
|
||||||
|
run: |
|
||||||
|
# databaseName = release name con guiones a underscores + sufijo _db.
|
||||||
|
# Coincide con el default del chart 1.0.0 (.Release.Name | replace
|
||||||
|
# "-" "_" | printf "%s_db").
|
||||||
|
DATABASE_NAME="$(echo "${RELEASE_NAME}" | tr '-' '_')_db"
|
||||||
|
sed -e "s|\${RELEASE_NAME}|${RELEASE_NAME}|g" \
|
||||||
|
-e "s|\${DATABASE_NAME}|${DATABASE_NAME}|g" \
|
||||||
|
manifests/addonclaim.yaml | kubectl apply -f -
|
||||||
|
|
||||||
- name: Helm upgrade --install
|
- name: Helm upgrade --install
|
||||||
env:
|
env:
|
||||||
TENANT_ID: ${{ vars.TENANT_ID }}
|
TENANT_ID: ${{ vars.TENANT_ID }}
|
||||||
@@ -144,8 +176,8 @@ jobs:
|
|||||||
--wait --timeout 7m
|
--wait --timeout 7m
|
||||||
# 7m absorbe el peor caso del reconcile de addon-provisioner
|
# 7m absorbe el peor caso del reconcile de addon-provisioner
|
||||||
# (RECONCILE_INTERVAL 300s + initial_delay 60s) + StatefulSet MariaDB
|
# (RECONCILE_INTERVAL 300s + initial_delay 60s) + StatefulSet MariaDB
|
||||||
# ready (~60s) + Job DDL (~10s) + InitContainer wait-for-secret (~5s)
|
# ready (~60s) + Job DDL (~10s) + InitContainer wait-for-secret + Alembic.
|
||||||
# + Alembic upgrade. Ver helm/<APP_NAME>/templates/addonclaim.yaml.
|
# AddonClaim ya fue aplicado en el step previo (ADR-066).
|
||||||
|
|
||||||
- name: Verificar rollout
|
- name: Verificar rollout
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
84
README.md
84
README.md
@@ -26,7 +26,11 @@ tenant, los placeholders en mayúsculas se sustituyen automáticamente.
|
|||||||
│ ├── pyproject.toml
|
│ ├── pyproject.toml
|
||||||
│ ├── requirements.txt
|
│ ├── requirements.txt
|
||||||
│ └── Dockerfile
|
│ └── Dockerfile
|
||||||
├── helm/__APP_NAME__/ ← Helm chart con AddonClaim + hook Alembic
|
├── helm/__APP_NAME__/ ← Helm chart (Deployment, Job Alembic, NP, RBAC)
|
||||||
|
├── manifests/
|
||||||
|
│ └── addonclaim.yaml ← AddonClaim fuera del chart (ADR-066)
|
||||||
|
├── scripts/
|
||||||
|
│ └── pre-helm-detach.sh ← migración v1.0.0 → v1.1.0 (idempotente)
|
||||||
└── .gitea/workflows/ ← CI: tests + build + deploy
|
└── .gitea/workflows/ ← CI: tests + build + deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -48,30 +52,57 @@ tenant, los placeholders en mayúsculas se sustituyen automáticamente.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Orden de hooks Helm
|
## Orden de aplicación — AddonClaim + hooks Helm
|
||||||
|
|
||||||
|
El workflow del tenant sigue este orden en cada `git push`:
|
||||||
|
|
||||||
|
| Paso | Acción | Motivo |
|
||||||
|
|------|--------|--------|
|
||||||
|
| 1 | `bash scripts/pre-helm-detach.sh` | Migración chart 1.0.0 → 1.1.0: si el AddonClaim tiene labels/annotations de un release Helm previo, las quita. Idempotente: skip si no aplica. Sunset tras ~2 semanas. |
|
||||||
|
| 2 | `kubectl apply -f manifests/addonclaim.yaml` (con `sed` para placeholders) | Crea o actualiza el `AddonClaim`. Idempotente. `addon-provisioner` empieza a reconciliar en paralelo. |
|
||||||
|
| 3 | `helm upgrade --install <release> ./helm/<APP_NAME> --wait --timeout 7m` | Aplica el chart Helm. Los hooks `pre-install,pre-upgrade` corren en orden de `hook-weight` (ver tabla abajo). El Job Alembic espera al Secret. |
|
||||||
|
|
||||||
El chart usa hooks `pre-install,pre-upgrade` con `hook-weight` para garantizar
|
El chart usa hooks `pre-install,pre-upgrade` con `hook-weight` para garantizar
|
||||||
el orden de creación. El `helm upgrade --install` funciona **sin `--no-hooks`**:
|
el orden de creación de SUS recursos (sin AddonClaim — ese vive fuera).
|
||||||
|
`helm upgrade --install` funciona **sin `--no-hooks`**:
|
||||||
|
|
||||||
| Recurso | Template | `hook-weight` | Motivo |
|
| 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 |
|
| `NetworkPolicy` | `networkpolicy.yaml` | `-8` | Bajo el `default-deny` del namespace `apps`, esta NP permite DNS + apiserver + addons:3306. Debe existir antes que el InitContainer del Job Alembic intente conectarse al apiserver. |
|
||||||
| `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) |
|
| `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 |
|
| `ServiceAccount` | `serviceaccount.yaml` | `-5` | Lo referencia el pod del Job Alembic — debe existir antes |
|
||||||
| `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` |
|
| `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:
|
### Por qué el `AddonClaim` vive FUERA del chart (ADR-066)
|
||||||
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
|
En chart 0.2.x el `AddonClaim` era hook `pre-install,pre-upgrade` weight `-10`.
|
||||||
user de la BD y regeneraría el password, causando ventana de downtime).
|
El default de Helm 3 (`hook-delete-policy: before-hook-creation`) borraba el
|
||||||
Trade-off: `helm uninstall` deja el AddonClaim huérfano y MariaDB sigue
|
claim previo antes de aplicar el nuevo en cada `helm upgrade --install`. El
|
||||||
aprovisionada. Es coherente con `deletionPolicy: Retain` — borrar BD productiva
|
finalizer `addon-provisioner.idp.coralware.cloud/cleanup-logical` ejecutaba
|
||||||
debe ser decisión explícita, no side-effect del uninstall. Cleanup explícito:
|
entonces `REVOKE` + `DROP USER` + delete del Secret en cada upgrade → rotación
|
||||||
|
involuntaria de credenciales + release atorado en `pending-upgrade`.
|
||||||
|
|
||||||
|
Convertir el `AddonClaim` a manifest principal del chart (no hook) también
|
||||||
|
falla: los hooks `pre-install,pre-upgrade` corren ANTES del manifest principal,
|
||||||
|
así que el Job Alembic intentaría leer el Secret antes de que el AddonClaim
|
||||||
|
exista — chicken-and-egg fatal.
|
||||||
|
|
||||||
|
La solución es desacoplar el lifecycle del CRD del lifecycle del release Helm:
|
||||||
|
el `AddonClaim` lo aplica el workflow con `kubectl apply` (idempotente, sin
|
||||||
|
ownership Helm), y Helm gobierna sólo la app + sus hooks Alembic/NP/RBAC.
|
||||||
|
|
||||||
|
`helm uninstall` no toca el AddonClaim — desacoplamiento garantizado.
|
||||||
|
Borrar la BD lógica es decisión explícita:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
kubectl delete addonclaim <release>-db -n apps
|
kubectl delete addonclaim <release>-db -n apps
|
||||||
```
|
```
|
||||||
|
|
||||||
|
(Con `deletionPolicy: Retain` — default — preserva la BD lógica; el operador
|
||||||
|
emite Event `OrphanLogicalDatabase`. Cambiar a `Delete` previo para `DROP DATABASE`.)
|
||||||
|
|
||||||
|
### Por qué el ServiceAccount es hook
|
||||||
|
|
||||||
El `ServiceAccount` se materializa como hook (no en la fase principal de Helm)
|
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
|
porque el Job Alembic, que también es hook, declara `serviceAccountName`. Si el
|
||||||
SA viviera en la fase principal se crearía **después** del Job → el pod del Job
|
SA viviera en la fase principal se crearía **después** del Job → el pod del Job
|
||||||
@@ -108,28 +139,33 @@ claves `DATABASE_URL` / `DATABASE_HOST` / `DATABASE_PORT` / `DATABASE_NAME` /
|
|||||||
`DATABASE_USER` / `DATABASE_PASSWORD` (y opcionalmente `MIGRATION_DATABASE_URL`).
|
`DATABASE_USER` / `DATABASE_PASSWORD` (y opcionalmente `MIGRATION_DATABASE_URL`).
|
||||||
|
|
||||||
El Secret lo materializa automáticamente la plataforma vía el flujo
|
El Secret lo materializa automáticamente la plataforma vía el flujo
|
||||||
`AddonClaim` → `addon-provisioner` (ADR-052 + ADR-064):
|
`AddonClaim` → `addon-provisioner` (ADR-052 + ADR-064 + ADR-066):
|
||||||
|
|
||||||
1. El chart aplica `helm/__APP_NAME__/templates/addonclaim.yaml` como hook
|
1. El workflow del tenant aplica `manifests/addonclaim.yaml` con
|
||||||
`pre-install,pre-upgrade` weight `-10` — declara `addonType: mariadb`,
|
`kubectl apply -f -` (placeholders sustituidos con `sed`). El claim declara
|
||||||
`databaseName` derivado del Release name y el `secretName` esperado.
|
`addonType: mariadb`, `databaseName` derivado del nombre del release y el
|
||||||
|
`secretName` esperado.
|
||||||
2. `addon-provisioner` (corriendo en el plano de control IDP) reconcilia el
|
2. `addon-provisioner` (corriendo en el plano de control IDP) reconcilia el
|
||||||
`AddonClaim`: crea una BD lógica + user + grant en la instancia MariaDB
|
`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.
|
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`.
|
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
|
4. El workflow corre `helm upgrade --install`. El InitContainer
|
||||||
240s), Alembic ejecuta `upgrade head`, y el Deployment rola.
|
`wait-for-secret` del Job Alembic (hook `pre-install,pre-upgrade`) espera al
|
||||||
|
Secret (hasta 240s), Alembic ejecuta `upgrade head`, y el Deployment rola.
|
||||||
|
|
||||||
### Opt-out — apps stateless
|
### Opt-out — apps stateless
|
||||||
|
|
||||||
Si tu app no necesita BD, en `values.yaml`:
|
Si tu app no necesita BD:
|
||||||
|
|
||||||
```yaml
|
1. En `values.yaml`:
|
||||||
database:
|
```yaml
|
||||||
enabled: false # no se aplica AddonClaim ni Role/RoleBinding
|
database:
|
||||||
alembic:
|
enabled: false # no se aplica Role/RoleBinding al SA
|
||||||
enabled: false # no corre el Job Alembic
|
alembic:
|
||||||
```
|
enabled: false # no corre el Job Alembic
|
||||||
|
```
|
||||||
|
2. Elimina el step "Aplicar AddonClaim" del workflow `.gitea/workflows/__APP_NAME__-build.yaml`
|
||||||
|
(junto con `manifests/addonclaim.yaml` y el step `pre-helm-detach`).
|
||||||
|
|
||||||
Con `database.enabled=false` la app se despliega sin BD ni migraciones.
|
Con `database.enabled=false` la app se despliega sin BD ni migraciones.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
apiVersion: v2
|
apiVersion: v2
|
||||||
name: __APP_NAME__
|
name: __APP_NAME__
|
||||||
# Helm chart del Golden Path web-backend/python.
|
# Helm chart del Golden Path web-backend/python.
|
||||||
|
# 0.3.0 (2026-05-25): ADR-066 — AddonClaim sale del chart y vive en
|
||||||
|
# manifests/addonclaim.yaml. El workflow del tenant lo aplica con
|
||||||
|
# kubectl apply ANTES de helm upgrade --install. Razon: en chart 0.2.x el
|
||||||
|
# AddonClaim era hook pre-install,pre-upgrade weight -10 → el default Helm
|
||||||
|
# `before-hook-creation` borraba el claim en cada upgrade → el finalizer
|
||||||
|
# cleanup-logical rotaba credenciales y atoraba el release en
|
||||||
|
# pending-upgrade. Desacoplar el lifecycle del CRD del lifecycle del
|
||||||
|
# release Helm resuelve el bug de raiz.
|
||||||
# 0.2.0 (2026-05-19): cambio Ingress controller Traefik (IngressRoute CRD)
|
# 0.2.0 (2026-05-19): cambio Ingress controller Traefik (IngressRoute CRD)
|
||||||
# → rke2-ingress-nginx (networking.k8s.io/v1 Ingress). Razón:
|
# → rke2-ingress-nginx (networking.k8s.io/v1 Ingress). Razón:
|
||||||
# TENANT-INGRESS-CONTROLLER-MISMATCH (APERTURA COVE Sesión 3). El cluster
|
# TENANT-INGRESS-CONTROLLER-MISMATCH (APERTURA COVE Sesión 3). El cluster
|
||||||
@@ -8,7 +16,7 @@ name: __APP_NAME__
|
|||||||
# adicional. tlsSecretName por default = apps-wildcard-tls (Gap 3).
|
# adicional. tlsSecretName por default = apps-wildcard-tls (Gap 3).
|
||||||
description: Helm chart del Golden Path web-backend/python
|
description: Helm chart del Golden Path web-backend/python
|
||||||
type: application
|
type: application
|
||||||
version: 0.2.0
|
version: 0.3.0
|
||||||
appVersion: "0.1.0"
|
appVersion: "0.1.0"
|
||||||
keywords:
|
keywords:
|
||||||
- fastapi
|
- fastapi
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
{{- if .Values.database.enabled }}
|
|
||||||
# AddonClaim — declara la intencion de la app de tener una BD (default MariaDB).
|
|
||||||
# Lo materializa addon-provisioner (CRD idp.coralware.cloud/v1alpha1):
|
|
||||||
# - reconcilia este claim cada RECONCILE_INTERVAL (default 300s) por TenantProfile.
|
|
||||||
# - crea BD logica + user + grant via Job DDL efimero en el cluster tenant.
|
|
||||||
# - publica el Secret `{{ .Values.database.secretName }}` en este ns (`apps`)
|
|
||||||
# con DATABASE_URL / DATABASE_HOST / DATABASE_PORT / DATABASE_USER /
|
|
||||||
# DATABASE_PASSWORD / DATABASE_NAME (contrato §2.3 del OpenSpec).
|
|
||||||
#
|
|
||||||
# Ordering con los hooks del chart (clave para que Alembic encuentre el Secret):
|
|
||||||
# weight -10 (este AddonClaim)
|
|
||||||
# -> weight -5 (ServiceAccount)
|
|
||||||
# -> weight 0 (Job Alembic — espera al Secret via InitContainer wait-for-secret)
|
|
||||||
#
|
|
||||||
# delete-policy: NO se declara intencionalmente.
|
|
||||||
# - Asi el AddonClaim sobrevive entre `helm upgrade` (re-apply idempotente).
|
|
||||||
# - Evita disparar el finalizer `cleanup-logical` en cada upgrade (que dropearia
|
|
||||||
# el user y regeneraria el password, causando ventana de downtime).
|
|
||||||
# - Trade-off documentado: `helm uninstall` deja el AddonClaim orfano y MariaDB
|
|
||||||
# sigue aprovisionada. Es coherente con `deletionPolicy: Retain` (default):
|
|
||||||
# borrar BD productiva debe ser decision explicita, no side-effect del uninstall.
|
|
||||||
# Cleanup explicito: `kubectl delete addonclaim {{ .Release.Name }}-db -n apps`.
|
|
||||||
apiVersion: idp.coralware.cloud/v1alpha1
|
|
||||||
kind: AddonClaim
|
|
||||||
metadata:
|
|
||||||
name: {{ .Release.Name }}-db
|
|
||||||
labels:
|
|
||||||
{{- include "app.labels" . | nindent 4 }}
|
|
||||||
annotations:
|
|
||||||
"helm.sh/hook": pre-install,pre-upgrade
|
|
||||||
"helm.sh/hook-weight": "-10"
|
|
||||||
spec:
|
|
||||||
addonType: {{ .Values.database.addonType | default "mariadb" | quote }}
|
|
||||||
databaseName: {{ .Values.database.databaseName | default (printf "%s_db" (.Release.Name | replace "-" "_")) | quote }}
|
|
||||||
user:
|
|
||||||
permissions: {{ .Values.database.user.permissions | default "rw" | quote }}
|
|
||||||
secretName: {{ .Values.database.secretName | quote }}
|
|
||||||
deletionPolicy: {{ .Values.database.deletionPolicy | default "Retain" | quote }}
|
|
||||||
{{- end }}
|
|
||||||
@@ -5,9 +5,11 @@
|
|||||||
# visible para `kubectl logs job/...` y diagnostico.
|
# visible para `kubectl logs job/...` y diagnostico.
|
||||||
#
|
#
|
||||||
# InitContainer `wait-for-secret`:
|
# InitContainer `wait-for-secret`:
|
||||||
# - El AddonClaim (hook weight -10) ya fue aplicado, pero addon-provisioner es
|
# - El AddonClaim ya fue aplicado por el workflow del tenant ANTES del
|
||||||
# timer-based (RECONCILE_INTERVAL 300s, initial_delay 60s) y materializa el
|
# `helm upgrade --install` (ADR-066, vive FUERA del chart en
|
||||||
# Secret `{{ .Values.database.secretName }}` de forma asincrona.
|
# manifests/addonclaim.yaml). addon-provisioner es timer-based
|
||||||
|
# (RECONCILE_INTERVAL 300s, initial_delay 60s) y materializa el Secret
|
||||||
|
# `{{ .Values.database.secretName }}` de forma asincrona.
|
||||||
# - El InitContainer hace busy-wait hasta que el Secret existe — absorbe la
|
# - El InitContainer hace busy-wait hasta que el Secret existe — absorbe la
|
||||||
# latencia del reconcile dentro del pod, sin tocar el operator ni
|
# latencia del reconcile dentro del pod, sin tocar el operator ni
|
||||||
# bajar RECONCILE_INTERVAL global.
|
# bajar RECONCILE_INTERVAL global.
|
||||||
|
|||||||
@@ -12,7 +12,10 @@
|
|||||||
# NetworkPolicy nunca se aplica en la fase principal.
|
# NetworkPolicy nunca se aplica en la fase principal.
|
||||||
#
|
#
|
||||||
# Weight ordering (mas chico = antes):
|
# Weight ordering (mas chico = antes):
|
||||||
# -10 AddonClaim -> -8 NetworkPolicy -> -7 Role/RoleBinding -> -5 SA -> 0 Job Alembic
|
# -8 NetworkPolicy -> -7 Role/RoleBinding -> -5 SA -> 0 Job Alembic
|
||||||
|
# (El AddonClaim vive FUERA del chart desde 0.3.0 — ADR-066. Lo aplica el
|
||||||
|
# workflow del tenant ANTES de `helm upgrade --install`, asi que en la fase
|
||||||
|
# de hooks ya existe y addon-provisioner ya esta reconciliando.)
|
||||||
#
|
#
|
||||||
# delete-policy: before-hook-creation (NO hook-succeeded): la fase principal del
|
# delete-policy: before-hook-creation (NO hook-succeeded): la fase principal del
|
||||||
# Deployment necesita la NetworkPolicy viva en runtime; solo se borra/recrea al
|
# Deployment necesita la NetworkPolicy viva en runtime; solo se borra/recrea al
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
# (hook pre-install/pre-upgrade weight 0) que hace busy-wait hasta que
|
# (hook pre-install/pre-upgrade weight 0) que hace busy-wait hasta que
|
||||||
# addon-provisioner publica el Secret tras reconciliar el AddonClaim.
|
# addon-provisioner publica el Secret tras reconciliar el AddonClaim.
|
||||||
#
|
#
|
||||||
# Hook weight "-7" — entre AddonClaim (-10) y SA (-5):
|
# Hook weight "-7" — entre NetworkPolicy (-8) y SA (-5):
|
||||||
# - Garantiza que RBAC exista antes de que el SA del hook -5 se materialice
|
# - Garantiza que RBAC exista antes de que el SA del hook -5 se materialice
|
||||||
# y antes de que el Job -0 intente leer el Secret.
|
# y antes de que el Job -0 intente leer el Secret.
|
||||||
# - delete-policy: before-hook-creation para que sea idempotente entre upgrades
|
# - delete-policy: before-hook-creation para que sea idempotente entre upgrades
|
||||||
# sin dejar Roles huerfanos.
|
# sin dejar Roles huerfanos.
|
||||||
|
# (El AddonClaim ya no es hook del chart desde 0.3.0 — ADR-066. Lo aplica el
|
||||||
|
# workflow del tenant ANTES del `helm upgrade --install`.)
|
||||||
#
|
#
|
||||||
# Scope minimo: solo lectura (get/watch) del Secret `<release>-db-creds`, no
|
# Scope minimo: solo lectura (get/watch) del Secret `<release>-db-creds`, no
|
||||||
# secrets cluster-wide ni del ns `addons`. El root Secret de MariaDB vive en
|
# secrets cluster-wide ni del ns `addons`. El root Secret de MariaDB vive en
|
||||||
|
|||||||
34
manifests/addonclaim.yaml
Normal file
34
manifests/addonclaim.yaml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# AddonClaim — vive FUERA del chart Helm (ADR-066).
|
||||||
|
#
|
||||||
|
# El workflow del tenant sustituye los placeholders con `sed` y aplica con
|
||||||
|
# `kubectl apply -f -` ANTES de `helm upgrade --install`. helm nunca lo
|
||||||
|
# trackea — al `helm uninstall` no lo borra; al `helm upgrade --install`
|
||||||
|
# repetido no lo toca. Idempotente.
|
||||||
|
#
|
||||||
|
# Lifecycle gobernado por addon-provisioner (CRD idp.coralware.cloud/v1alpha1):
|
||||||
|
# - reconcilia cada RECONCILE_INTERVAL (default 300s) por TenantProfile.
|
||||||
|
# - crea BD logica + user + GRANT via Job DDL efimero en cluster tenant.
|
||||||
|
# - publica Secret `${RELEASE_NAME}-db-creds` en ns `apps`.
|
||||||
|
# - agrega finalizer `addon-provisioner.idp.coralware.cloud/cleanup-logical`.
|
||||||
|
#
|
||||||
|
# Cleanup explicito post `helm uninstall`:
|
||||||
|
# kubectl delete addonclaim ${RELEASE_NAME}-db -n apps
|
||||||
|
# (con deletionPolicy: Retain — default — preserva la BD; Event
|
||||||
|
# OrphanLogicalDatabase. Cambiar a Delete previo para DROP DATABASE.)
|
||||||
|
apiVersion: idp.coralware.cloud/v1alpha1
|
||||||
|
kind: AddonClaim
|
||||||
|
metadata:
|
||||||
|
name: ${RELEASE_NAME}-db
|
||||||
|
namespace: apps
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: ${RELEASE_NAME}
|
||||||
|
app.kubernetes.io/part-of: ${RELEASE_NAME}
|
||||||
|
idp.coralware.cloud/golden-path: web-backend
|
||||||
|
idp.coralware.cloud/golden-path-lang: python
|
||||||
|
spec:
|
||||||
|
addonType: mariadb
|
||||||
|
databaseName: ${DATABASE_NAME}
|
||||||
|
user:
|
||||||
|
permissions: rw
|
||||||
|
secretName: ${RELEASE_NAME}-db-creds
|
||||||
|
deletionPolicy: Retain
|
||||||
65
scripts/pre-helm-detach.sh
Executable file
65
scripts/pre-helm-detach.sh
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Script de migracion v1.0.0 -> v1.1.0 del chart web-backend-python (ADR-066).
|
||||||
|
#
|
||||||
|
# Las apps desplegadas con chart 1.0.0 tienen el AddonClaim creado como Helm
|
||||||
|
# hook con annotations `meta.helm.sh/release-name`, `app.kubernetes.io/managed-by=Helm`
|
||||||
|
# y `helm.sh/hook`. El chart 1.1.0 mueve el AddonClaim FUERA del chart; el
|
||||||
|
# workflow del tenant lo aplica con `kubectl apply -f` despues de este script.
|
||||||
|
#
|
||||||
|
# Sin de-adopcion, el siguiente `helm upgrade --install` veria el AddonClaim
|
||||||
|
# en el state previo (release v_n) pero no en el manifest nuevo (chart 1.1.0)
|
||||||
|
# → helm intentaria delete del recurso → el finalizer dropearia user/Secret.
|
||||||
|
#
|
||||||
|
# Este script:
|
||||||
|
# - skip silencioso si el AddonClaim no existe (primera instalacion).
|
||||||
|
# - skip silencioso si el AddonClaim NO tiene annotations Helm (ya de-adoptado).
|
||||||
|
# - de-adopta si encuentra annotations Helm — quita las labels/annotations
|
||||||
|
# que hacen que Helm lo reconozca como recurso del release.
|
||||||
|
#
|
||||||
|
# Idempotente. Sunset del script tras una rotacion completa de releases
|
||||||
|
# desplegadas con chart 1.0.0 (~2 semanas).
|
||||||
|
#
|
||||||
|
# Variables de entorno requeridas:
|
||||||
|
# - RELEASE_NAME: nombre del release Helm (== nombre de la app del tenant)
|
||||||
|
# - KUBECONFIG: apuntando al cluster tenant
|
||||||
|
#
|
||||||
|
# Uso (desde el workflow del tenant):
|
||||||
|
# RELEASE_NAME=__APP_NAME__ bash scripts/pre-helm-detach.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
: "${RELEASE_NAME:?ERROR: RELEASE_NAME no esta seteado}"
|
||||||
|
NAMESPACE="${NAMESPACE:-apps}"
|
||||||
|
CLAIM_NAME="${RELEASE_NAME}-db"
|
||||||
|
|
||||||
|
# Comprobar existencia. kubectl get devuelve exit 1 si NotFound — capturamos
|
||||||
|
# con `|| true` para no romper bajo `set -e`.
|
||||||
|
if ! kubectl get addonclaim "${CLAIM_NAME}" -n "${NAMESPACE}" >/dev/null 2>&1; then
|
||||||
|
echo "[pre-helm-detach] AddonClaim ${CLAIM_NAME} no existe en ns ${NAMESPACE} — skip (primera instalacion)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detectar si tiene annotations Helm — si no, ya esta de-adoptado.
|
||||||
|
RELEASE_ANNOTATION=$(kubectl get addonclaim "${CLAIM_NAME}" -n "${NAMESPACE}" \
|
||||||
|
-o jsonpath='{.metadata.annotations.meta\.helm\.sh/release-name}' 2>/dev/null || echo "")
|
||||||
|
|
||||||
|
if [ -z "${RELEASE_ANNOTATION}" ]; then
|
||||||
|
echo "[pre-helm-detach] AddonClaim ${CLAIM_NAME} sin annotations Helm — skip (ya de-adoptado)."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[pre-helm-detach] AddonClaim ${CLAIM_NAME} con release=${RELEASE_ANNOTATION} — de-adoptando…"
|
||||||
|
|
||||||
|
# Quitar annotations Helm — `-` sufijo significa "remove".
|
||||||
|
kubectl annotate addonclaim "${CLAIM_NAME}" -n "${NAMESPACE}" \
|
||||||
|
meta.helm.sh/release-name- \
|
||||||
|
meta.helm.sh/release-namespace- \
|
||||||
|
--overwrite
|
||||||
|
|
||||||
|
# Quitar label de managed-by — el hook tambien deja la annotation `helm.sh/hook`
|
||||||
|
# en algunos casos; la dejamos viva porque no afecta el ownership tracking.
|
||||||
|
kubectl label addonclaim "${CLAIM_NAME}" -n "${NAMESPACE}" \
|
||||||
|
app.kubernetes.io/managed-by- \
|
||||||
|
--overwrite
|
||||||
|
|
||||||
|
echo "[pre-helm-detach] De-adopcion completa. El siguiente helm upgrade --install no tocara este AddonClaim."
|
||||||
Reference in New Issue
Block a user