Files
2026-06-16 22:06:50 -05:00

6.7 KiB

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
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-<tenant-id-short>/<APP_NAME>:<tag> — proyecto Harbor dedicado del tenant (ADR-019 D-03)
Helm release <APP_NAME> en namespace apps del cluster tenant (ADR-054)
App reachable https://<APP_NAME>.<tenant-id-con-guiones>.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 *.<tenant-id>.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

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.