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:
@@ -1,8 +1,17 @@
|
||||
{{- if .Values.alembic.enabled }}
|
||||
{{- if and .Values.alembic.enabled .Values.database.enabled }}
|
||||
# Job pre-upgrade que corre Alembic antes de rolar el Deployment.
|
||||
# - backoffLimit: 0 → si falla, NO reintenta; Helm aborta el upgrade.
|
||||
# - hook-delete-policy hook-succeeded → solo se borra al éxito; en fallo queda
|
||||
# visible para `kubectl logs job/...` y diagnóstico.
|
||||
# - backoffLimit: 0 -> si falla, NO reintenta; Helm aborta el upgrade.
|
||||
# - hook-delete-policy hook-succeeded -> solo se borra al exito; en fallo queda
|
||||
# visible para `kubectl logs job/...` y diagnostico.
|
||||
#
|
||||
# InitContainer `wait-for-secret`:
|
||||
# - El AddonClaim (hook weight -10) ya fue aplicado, pero 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
|
||||
# latencia del reconcile dentro del pod, sin tocar el operator ni
|
||||
# bajar RECONCILE_INTERVAL global.
|
||||
# - Loop: 48 iteraciones x 5s = 240s — encaja dentro del helm timeout 7m.
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
@@ -28,6 +37,42 @@ spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
initContainers:
|
||||
- name: wait-for-secret
|
||||
image: bitnami/kubectl:1.30
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -eu
|
||||
# Espera hasta que addon-provisioner publique el Secret tras
|
||||
# reconciliar el AddonClaim (hook weight -10). 48 x 5s = 240s.
|
||||
SECRET="{{ .Values.database.secretName }}"
|
||||
i=1
|
||||
while [ "$i" -le 48 ]; do
|
||||
if kubectl get secret "$SECRET" -n "$NAMESPACE" >/dev/null 2>&1; then
|
||||
echo "Secret $SECRET encontrado en intento $i"
|
||||
exit 0
|
||||
fi
|
||||
echo "[$i/48] Secret $SECRET aun no publicado, esperando 5s..."
|
||||
sleep 5
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo "ERROR: Secret $SECRET no aparecio tras 240s — addon-provisioner stuck?"
|
||||
exit 1
|
||||
env:
|
||||
- name: NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
resources:
|
||||
requests: {cpu: 10m, memory: 32Mi}
|
||||
limits: {cpu: 100m, memory: 64Mi}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
containers:
|
||||
- name: alembic
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
@@ -37,7 +82,7 @@ spec:
|
||||
name: {{ .Values.database.secretName }}
|
||||
env:
|
||||
# Si el Secret expone MIGRATION_DATABASE_URL, lo usa; si no, env.py
|
||||
# cae a DATABASE_URL automáticamente (ver settings.migration_url).
|
||||
# cae a DATABASE_URL automaticamente (ver settings.migration_url).
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
|
||||
Reference in New Issue
Block a user