# Spec — Golden Path `rest-api/python` **Versión del shape:** 1.0.0 **Status:** Activo (free-trial entry point) **ADR de referencia:** ADR-027 (Golden Paths Fase 5), ADR-053 (anatomía del shape web-backend/python — referencia para decisiones de empaque), **ADR-060 Fase 1 (`GATEWAY-API-MIGRATION`): ingress = Traefik Proxy + Gateway API (`HTTPRoute`), reemplaza el `rke2-ingress-nginx` de ADR-053 §11.1**, ADR-061 (workflow CI self-contained — sin `uses:` cross-repo; `helm-deploy` inline sin `container:`) **Visibilidad:** interna de Coralware — este spec NO se sincroniza al repo Gitea ni al repo del cliente. --- ## 1. Propósito Definir el comportamiento del shape `rest-api/python` — scaffold de API HTTP stateless en FastAPI, sin persistencia. Es el shape **entry point** para tier `free-trial`: el cliente arranca rápido con un endpoint REST funcional sin tener que decidir BD, migraciones ni hooks. Cuando un cliente necesita persistencia, debería **migrar a `web-backend/python`** (ADR-053), no extender este shape para incluir DB. --- ## 2. Contrato del shape ### 2.1 Entradas | Entrada | Origen | Descripción | |---------|--------|-------------| | `__APP_NAME__` | placeholder | Sustituido server-side por `repo-provisioner` | | `__TENANT_ID__` | placeholder | UUID del tenant | | `__TENANT_ID_NODASHES__` | placeholder | UUID sin guiones, para DNS y namespaces | | `__TIER__` | placeholder | `free-trial` | `startup` | `business` | `enterprise` | | `KUBECONFIG_TENANT_B64` | secret de proyecto Gitea | Provisto por `tenant-manager` | | `vars.TENANT_ID`, `vars.TENANT_ID_NODASHES`, `vars.TENANT_TIER` | vars de proyecto | Provisto por `tenant-manager` | ### 2.2 Salidas observables | Salida | Forma | |--------|-------| | Imagen Docker | `registry.coralsafety.com/t-/:` — proyecto Harbor dedicado del tenant (ADR-019 D-03) | | Helm release | `` en namespace `apps` del cluster tenant (ADR-054) | | App reachable | `https://..apps.coralware.cloud/health` retorna 200 (FQDN subdominio DOT, ADR-067/ADR-082 — reconciliado drift previo que usaba el host plano con guion) | | Ingress controller | **Traefik Proxy + Gateway API** en `traefik-system` (bundle Fleet `traefik-bootstrap`) — ADR-060 Fase 1 (`GATEWAY-API-MIGRATION`); reemplaza `rke2-ingress-nginx` (EOL marzo 2026). El shape emite un `HTTPRoute` adjunto al `Gateway` `tenant-gateway` (ns `apps`, stack-deployer §3c). Paridad con web-backend/python | | TLS | Wildcard per-tenant `*..apps.coralware.cloud` vía Secret `tenant-wildcard-tls` (cert-manager local del cluster, ADR-067 — reconciliado drift previo que citaba el `apps-wildcard-tls` compartido de Fase 0) | | Endpoints obligatorios | `/health`, `/docs`, `/openapi.json`, `/metrics` | ### 2.3 Invariantes - App stateless — ningún componente persistente requerido. - `/health` retorna 200 con `{"status":"ok"}` siempre que el proceso esté vivo. - `helm upgrade --install` del chart funciona **sin `--no-hooks`**. El `ServiceAccount` se materializa como hook `pre-install,pre-upgrade` (`hook-weight: -5`, `hook-delete-policy: before-hook-creation`) por paridad con el shape `web-backend/python`. Este shape no tiene Job de migración, así que el hook no es estrictamente necesario — se mantiene para coherencia entre ambos charts. - SecurityContext: `runAsNonRoot=true`, `readOnlyRootFilesystem=true`, `drop ALL caps`. - Sin IPs hardcoded; sin credenciales en código. - Errores de servicios upstream **NO crashean** la app — se reportan en el endpoint que los invoca, el proceso sigue. --- ## 3. Flujo principal ```mermaid flowchart TD dev_push([Dev hace git push\nal repo Gitea del tenant]) --> quality_gate[quality_gate\nruff + black + bandit + pytest --cov 80] quality_gate -- fail --> pipeline_failed([pipeline_failed\nHelm no se invoca]) quality_gate -- ok --> image_build[image_build\nbuildx push a Harbor t-short] image_build -- fail --> pipeline_failed image_build -- ok --> helm_deploy[helm_deploy\nhelm upgrade --install --wait] helm_deploy --> rollout_apply[rollout_apply\nDeployment con nuevo image tag] rollout_apply --> rollout_status_check[rollout_status_check\nkubectl rollout status timeout 3m] rollout_status_check -- fail --> helm_rollback_manual([helm_rollback_manual\ndev decide rollback\npipeline_failed]) rollout_status_check -- ok --> smoke_health_check[smoke_health_check\nwget /health 200] smoke_health_check -- fail --> investigate([investigate\nlogs + events\npipeline_failed]) smoke_health_check -- ok --> app_running([app_running]) ``` **Sin nodo `alembic_hook`** — diferencia clave con `web-backend/python`. Los node IDs son labels Prometheus futuros. --- ## 4. Estados terminales | Estado | Significado | Acción del dev | |--------|-------------|----------------| | `app_running` | Todo OK — app desplegada, /health 200 | Ninguna | | `pipeline_failed` | Quality o build falló | Inspeccionar logs CI; corregir; push | | `helm_rollback_manual` | Rollout no estabilizó en 3 min | `helm rollback`; investigar pod events | | `investigate` | Smoke falla aunque rollout terminó | Logs Deployment + cluster events | --- ## 5. Comportamiento fail-safe - **Servicio upstream caído:** endpoint responde 503 con detalle, app sigue. - **Imagen no disponible:** `ImagePullBackOff` visible en Rancher; rollout no avanza. - **Variable de entorno faltante (ej. `LOG_LEVEL`):** default seguro, no crash. --- ## 6. Pruebas negativas | ID | Prueba | Estado terminal esperado | |----|--------|--------------------------| | N1 | Test fallando | `pipeline_failed` en `quality_gate` | | N2 | Imagen tag inexistente | `ImagePullBackOff` visible en Rancher | | N3 | Probe `/health` removido del pod | Rollout falla → `helm_rollback_manual` | (Set reducido vs. web-backend/python; sin pruebas de DB porque no hay BD.) --- ## 7. Versionado del shape `coralware-shape.yaml` declara `shapeVersion` (semver). Mismas reglas que `web-backend/python` (ver ADR-053 §7). --- ## 8. Sincronización a Gitea `templates/rest-api-python/` (este repo IDP) → `idp-templates/template-rest-api-python` (Gitea). Exclusiones del sync: - `openspec/` — gobernanza interna del golden path. - `.ruff_cache/`, `__pycache__/`, `.pytest_cache/`. Mecanismo: `rsync --exclude='openspec/' --exclude-from=.gitignore` desde el scaffold local hacia el clone temporal del repo Gitea. --- ## 9. Promoción a web-backend/python Si la app crece y necesita persistencia: 1. Cliente clona el template `web-backend/python` en un repo nuevo. 2. Migra rutas de su app `rest-api` al nuevo scaffold. 3. Coralware provisiona el `AddonClaim` MariaDB (manual via runbook hasta ADR-052). No hay path de migración in-place — son shapes distintos por diseño.