ilopez eae346684f
Some checks failed
__APP_NAME__ — build & deploy / Calidad + build + push (push) Failing after 13s
__APP_NAME__ — build & deploy / Helm upgrade --install (push) Has been skipped
fix(chart): InitContainer curl+REST API en lugar de bitnami/kubectl:1.30
bitnami retiro los tags semanticos minor (1.30/1.29/etc.) de Docker Hub
2026-05; solo quedan latest + digests. El InitContainer fallaba con
ImagePullBackOff en el smoke E2E DO (run 1511/1512 del tenant
91c93c9c060a48febb7874c87c1ce993).

Cambio:
- image: bitnami/kubectl:1.30 -> curlimages/curl:8.20.0 (Docker Official,
  tag estable).
- comando: kubectl get secret -> curl REST API kubernetes.default.svc con
  SA token + CA cert montados en /var/run/secrets/kubernetes.io/serviceaccount.
- Misma logica: 48 iteraciones x 5s = 240s busy-wait.
- El Role/RoleBinding (role-db-secret-reader.yaml) ya da el permiso get
  secrets al SA — sin cambios RBAC necesarios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 09:46:35 -05:00

APP_NAME — Web Backend Python (FastAPI + Alembic)

App generada desde el Golden Path web-backend/python de Coralware Cove IDP.

Este repositorio contiene el scaffold completo: aplicación FastAPI, migraciones Alembic, frontend estático en /demo, Helm chart, workflow de CI y manifiesto de contrato con la plataforma. Cuando se materializa este template para tu tenant, los placeholders en mayúsculas se sustituyen automáticamente.

Contrato del shape: ver coralware-shape.yaml en la raíz. Define los endpoints obligatorios, la BD esperada y la versión del shape. No elimines ese archivo — la plataforma lo lee.


Estructura

.
├── coralware-shape.yaml          ← contrato con la plataforma
├── catalog-info.yaml             ← entidad Backstage
├── code/
│   ├── app/                      ← FastAPI app
│   ├── alembic/                  ← migraciones DB
│   ├── tests/                    ← pytest + cobertura
│   ├── pyproject.toml
│   ├── requirements.txt
│   └── Dockerfile
├── helm/__APP_NAME__/            ← Helm chart con AddonClaim + hook Alembic
└── .gitea/workflows/             ← CI: tests + build + deploy

Ciclo de vida

  1. Tú escribes código — endpoints en code/app/routers/, modelos en code/app/models/, migraciones con alembic revision --autogenerate.
  2. git push a main dispara el workflow Gitea Actions.
  3. El CI corre black, ruff, bandit, pytest --cov. Si algo falla, el pipeline aborta.
  4. Si pasa, builda la imagen linux/amd64 con BuildKit y la publica en el proyecto Harbor de tu tenant.
  5. helm upgrade --install aplica el chart al cluster de tu tenant. Antes del rollout, un Job pre-upgrade corre alembic upgrade head. Si Alembic falla, Helm aborta y la app vieja queda intacta.
  6. Tras rollout exitoso, smoke check valida /health 200.

Orden de hooks Helm

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:

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 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:

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 SA viviera en la fase principal se crearía después del Job → el pod del Job nunca arrancaría (serviceaccount not found) → helm --wait haría timeout.

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 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).


Endpoints obligatorios

Path Auth Propósito
/health none Liveness + readiness; devuelve {status: ok, db: up|down}
/docs configurable (OPENAPI_REQUIRE_AUTH) Swagger UI
/openapi.json igual que /docs Schema OpenAPI
/demo none Frontend estático servido desde code/app/static/demo/
/metrics restringido vía NetworkPolicy Métricas Prometheus

/health, /docs, /demo son contrato del shape — NO renombres.


Base de datos

Tu app espera un Secret __APP_NAME__-db-creds en su namespace con las claves DATABASE_URL / DATABASE_HOST / DATABASE_PORT / DATABASE_NAME / DATABASE_USER / DATABASE_PASSWORD (y opcionalmente MIGRATION_DATABASE_URL).

El Secret lo materializa automáticamente la plataforma vía el flujo AddonClaimaddon-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:

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.


Variables de entorno

Variable Origen Default Descripción
DATABASE_URL Secret Connection string runtime
MIGRATION_DATABASE_URL Secret = DATABASE_URL Connection para Alembic
LOG_LEVEL ConfigMap INFO DEBUG para troubleshooting
CORRELATION_ID_HEADER ConfigMap X-Correlation-ID Header de trazabilidad
OPENAPI_REQUIRE_AUTH ConfigMap false Activar JWT en /docs
KEYCLOAK_ISSUER ConfigMap URL del realm cuando auth activado
STATIC_DEMO_PATH ConfigMap /app/static/demo Path al frontend estático

Migraciones Alembic

Migración base vacía en code/alembic/versions/0001_initial.py. Para crear una nueva tras agregar modelos:

cd code
alembic revision --autogenerate -m "agregar tabla users"

Desarrollo local

cd code
pip install -r requirements-dev.txt

docker run --rm -d --name mariadb-dev \
  -e MARIADB_ROOT_PASSWORD=dev -e MARIADB_DATABASE=app \
  -p 3306:3306 mariadb:11

export DATABASE_URL="mysql+pymysql://root:dev@127.0.0.1:3306/app"
export MIGRATION_DATABASE_URL="$DATABASE_URL"

alembic upgrade head
uvicorn app.main:app --reload --port 8000

Plan de salida

Si decides migrar fuera de Cove, todo lo que necesitas vive en formato estándar:

Componente Cómo lo exportas
Código git remote add github && git push github main
Imagen docker pull desde Harbor + push a tu registry
Manifests Ya están en helm/helm template genera YAML aplicable
Datos Coralware ejecuta mariadb-dump y entrega .sql.gz

Sin lock-in técnico.


Referencias

  • 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
Description
Template Golden Path — Web Backend con Python
Readme 255 KiB
Languages
Python 54.9%
Shell 35.7%
HTML 3.9%
Smarty 3.4%
Dockerfile 2.1%