Sincroniza desde el monorepo IDP la corrección del hook-order del ServiceAccount (hook pre-install/pre-upgrade, weight -5) que destraba el helm install del golden-path sin --no-hooks. Cierre del 4º gap del pipeline tenant hands-off: el smoke E2E valida ahora la cadena completa (addon-provisioner materializa MariaDB; AddonClaim publica DATABASE_URL en apps). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
169 lines
6.1 KiB
Markdown
169 lines
6.1 KiB
Markdown
# __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 hook Alembic pre-upgrade
|
|
├── claim-mariadb.yaml ← AddonClaim de la BD (ver §Base de datos)
|
|
└── .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 |
|
|
|---------|----------|---------------|--------|
|
|
| `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 |
|
|
|
|
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` y el Secret `<APP_NAME>-db-creds` que referencia el Job los
|
|
provee la plataforma (stack-deployer / addon), no el chart.
|
|
|
|
---
|
|
|
|
## 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` y `MIGRATION_DATABASE_URL`. El Secret lo provisiona la
|
|
plataforma:
|
|
|
|
- **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).
|
|
|
|
---
|
|
|
|
## 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:
|
|
|
|
```bash
|
|
cd code
|
|
alembic revision --autogenerate -m "agregar tabla users"
|
|
```
|
|
|
|
---
|
|
|
|
## Desarrollo local
|
|
|
|
```bash
|
|
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-038-ci-estandar-reusable-workflow.md` — pipeline Python
|