|
| 1 | +# Visión general |
| 2 | + |
| 3 | +Este repositorio implementa una aplicación de registro y validación de tickets con una estrategia de ramas basada en trunk (trunk-based development), dos entornos (`stg` y `prd`), y una automatización CI/CD en GitHub Actions. La infraestructura se gestiona con Terraform sobre GCP (Cloud SQL para PostgreSQL y Cloud Run para el servicio API). |
| 4 | + |
| 5 | +# Estrategia de ramas |
| 6 | + |
| 7 | +- **Trunk-based development**: ramas cortas `feature/**`, `feat/**`, `fix/**` que se integran a `main` vía Pull Request. |
| 8 | +- **Mapeo de entornos**: |
| 9 | + - **staging (`stg`)**: despliegue automático al fusionar a `main`. |
| 10 | + - **producción (`prd`)**: promoción manual y condicionada al éxito de `stg`. |
| 11 | + |
| 12 | +# Entornos |
| 13 | + |
| 14 | +- **Staging (`stg`)**: objetivo de pruebas para commits en `main`. Durante CD se aplica infraestructura, se inicializa la base de datos con SQLs de `src/db/scripts/init`, se ejecutan pruebas de integración y luego se limpian los datos de prueba con `src/db/scripts/cleanup`. |
| 15 | +- **Producción (`prd`)**: despliegue manual; requiere que el último despliegue a `stg` haya sido exitoso. |
| 16 | + |
| 17 | +# Pipelines de CI/CD |
| 18 | + |
| 19 | +Los workflows viven en `.github/workflows/`. |
| 20 | + |
| 21 | +- **Validaciones en ramas de desarrollo** (`ci-dev-branches.yml`) |
| 22 | + - **Disparo**: push a `feature/**`, `feat/**`, `fix/**`. |
| 23 | + - **Propósito**: feedback rápido de calidad. |
| 24 | + - **Flujo**: análisis estático reutilizable (pre-commit, unit tests con cobertura en consola). |
| 25 | + |
| 26 | +- **Validaciones de Pull Request** (`pr-validations.yml`) |
| 27 | + - **Disparo**: `pull_request` → `main`. |
| 28 | + - **Propósito**: proteger integraciones a `main`. |
| 29 | + - **Flujo**: |
| 30 | + - Análisis estático (pre-commit, unit tests, artefacto de cobertura). |
| 31 | + - Pruebas de integración en modo local (Docker Compose). |
| 32 | + - Build y push de la imagen Docker a GHCR (`latest` y `sha`). |
| 33 | + |
| 34 | +- **CD a Staging** (`cd-stg.yml`) |
| 35 | + - **Disparo**: push a `main` y manual. |
| 36 | + - **Propósito**: desplegar imagen verificada a `stg` y validar con pruebas de integración. |
| 37 | + - **Flujo**: llama al workflow base `cd-base.yml` con `deployment_env=stg` e imagen `ghcr.io/.../register-ticket-api:latest`. |
| 38 | + |
| 39 | +- **CD a Producción** (`cd-prod.yml`) |
| 40 | + - **Disparo**: manual. |
| 41 | + - **Propósito**: promover a `prd` sólo si el último despliegue a `stg` fue exitoso. |
| 42 | + - **Flujo**: verifica último run de `cd-stg.yml` y, si fue exitoso, llama a `cd-base.yml` con `deployment_env=prd`. |
| 43 | + |
| 44 | +- **Base de CD reutilizable** (`cd-base.yml`) |
| 45 | + - **Propósito**: estandarizar despliegues `stg`/`prd`. |
| 46 | + - **Flujo**: |
| 47 | + 1. Promoción de imagen: pull desde GHCR → re-tag → push a DockerHub. |
| 48 | + 2. Terraform: `init`/`workspace` por entorno, `plan`/`apply` con variables de entorno y `image_uri`. |
| 49 | + 3. Salidas: `db_host` (IP pública de Cloud SQL) y `api_url` (URL de Cloud Run). |
| 50 | + 4. Seed de base de datos: ejecutar `src/db/scripts/init/*.sql`. |
| 51 | + 5. Pruebas de integración contra el entorno desplegado. |
| 52 | + 6. Limpieza: ejecutar `src/db/scripts/cleanup/*.sql` (siempre, salvo entornos locales). |
| 53 | + |
| 54 | +- **Teardown de emergencia** (`teardown.yml`) |
| 55 | + - **Disparo**: manual, requiere escribir “DESTROY”. |
| 56 | + - **Propósito**: destruir toda la infraestructura de `stg` o `prd` con Terraform. |
| 57 | + |
| 58 | +## Estrategia de despliegue |
| 59 | + |
| 60 | +### Despliegue de la API |
| 61 | + |
| 62 | +- La imagen se construye en las validaciones de PR y se publica en GHCR con tags `latest` y el `sha` del commit. |
| 63 | +- Los pipelines de CD promueven esa imagen: se re-etiqueta y publica en DockerHub (p. ej. `<dockerhub_user>/register-ticket-api:<tag>`). |
| 64 | +- Terraform despliega la imagen en Cloud Run, exponiendo una URL pública y enroutando 100% del tráfico a la última revisión. |
| 65 | + |
| 66 | +### Despliegue de la Base de Datos |
| 67 | + |
| 68 | +- Terraform crea una instancia de Cloud SQL (PostgreSQL 15) con IP pública, base de datos y usuario por entorno. |
| 69 | +- Durante el despliegue, se ejecutan los scripts SQL de `src/db/scripts/init` para crear esquema, SPs y datos iniciales. |
| 70 | +- Tras las pruebas de integración, se ejecutan los scripts de `src/db/scripts/cleanup` para eliminar datos de prueba. |
| 71 | + |
| 72 | +## Infraestructura con Terraform |
| 73 | + |
| 74 | +- **Estado**: backend en GCS (`event-access-tfstate`). |
| 75 | +- **Workspaces**: uno por entorno (`stg`, `prd`). |
| 76 | +- **Recursos**: |
| 77 | + - Cloud SQL (PostgreSQL 15), base de datos y usuario. |
| 78 | + - Cloud Run para el servicio `register-ticket-api`, con acceso público (invoker `allUsers`). |
| 79 | + - Salidas: `db_host` (IP pública) y `cloud_run_service_url` (URL del servicio). |
| 80 | + |
| 81 | +Archivos clave en `terraform/`: |
| 82 | + |
| 83 | +- `main.tf`: define Cloud SQL y Cloud Run (inyecta `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`). |
| 84 | +- `variables.tf`: variables requeridas (`project_id`, `environment`, `db_*`, `image_uri`, etc.). |
| 85 | +- `provider.tf`: backend de estado (GCS) y provider `google`. |
| 86 | +- `outputs.tf`: `db_host`, `cloud_run_service_url`. |
| 87 | +- `environments/*.tfvars`: valores por entorno. |
| 88 | + |
| 89 | +## Estrategia de pruebas |
| 90 | + |
| 91 | +### Unit tests |
| 92 | + |
| 93 | +- Se ejecutan en el workflow reutilizable de análisis estático (`static-code-analysis.yml`). |
| 94 | +- Herramientas: `pytest` con cobertura, gestionado con `uv`. |
| 95 | +- Alcance: lógica de servicios, comportamiento de repositorios mediante mocks, validaciones y rutas de error. |
| 96 | +- En PRs se sube artefacto de cobertura. |
| 97 | + |
| 98 | +### Integration tests |
| 99 | + |
| 100 | +- Dos modos de ejecución: |
| 101 | + - **Local (PR)**: levanta dependencias con `docker/docker-compose.yml`, carga variables desde `.env.test`, invoca la API local y valida efectos en DB (psycopg2). |
| 102 | + - **Remoto (CD)**: ejecuta contra `stg`/`prd` usando las salidas de Terraform (`api_url`, `db_host`) y credenciales desde secretos. |
| 103 | +- Casos cubiertos (ejemplos en `tests/integration/`): |
| 104 | + - Registro exitoso de ticket y persistencia en DB. |
| 105 | + - Error al registrar ticket duplicado. |
| 106 | + - Error por payload inválido. |
| 107 | +- Ciclo de datos (detalle): |
| 108 | + 1) Tras el `apply` de Terraform, la instancia de Cloud SQL y la base de datos quedan disponibles. |
| 109 | + 2) El pipeline ejecuta los SQL de `src/db/scripts/init/*.sql` (en orden): |
| 110 | + - `01_create_tables.sql`: crea tablas `users` y `tickets`, incluyendo PKs, restricciones (único seat+gate) y columnas necesarias (seed TOTP, `used_at`, `status`). |
| 111 | + - `02_create_ticket_stored_procedures.sql`: define el `PROCEDURE sp_register_ticket_to_user` para registrar tickets a usuarios, y la `FUNCTION fn_mark_ticket_as_used` para marcar el uso de un ticket. |
| 112 | + - `03_populate_tables.sql`: habilita `pgcrypto` y carga datos de prueba mínimos: dos usuarios (`spuertaf`, `juanperez`) y varios tickets con `seed` aleatorio y estado `valid`. |
| 113 | + 3) Se ejecutan las pruebas de integración contra el ambiente desplegado, verificando conectividad API y DB: |
| 114 | + - La suite (p. ej. `tests/integration/test_tickets_registration.py`) hace llamadas HTTP a la API en Cloud Run (`BASE_URL`) y valida efectos en DB (`db_host`) vía `psycopg2` (fixtures `base_url` y `db_connection`). |
| 115 | + - Casos: alta de ticket para usuario; intentos duplicados; validación de errores. |
| 116 | + 4) Si las pruebas de integración finalizan correctamente, se ejecutan los SQL de `src/db/scripts/cleanup/*.sql` para limpiar datos de prueba: |
| 117 | + - `01_drop_test_records.sql`: elimina tickets y usuarios insertados por los scripts de init (evita residuos entre deployments). |
| 118 | + |
| 119 | + Diagrama breve del ciclo de datos: |
| 120 | + |
| 121 | + ```mermaid |
| 122 | + sequenceDiagram |
| 123 | + participant CD as Workflow CD (cd-base) |
| 124 | + participant TF as Terraform |
| 125 | + participant DB as Cloud SQL (DB) |
| 126 | + participant API as Cloud Run (API) |
| 127 | + participant IT as Pruebas Integración |
| 128 | +
|
| 129 | + CD->>TF: plan/apply (provisiona DB y API) |
| 130 | + TF-->>CD: outputs db_host, api_url |
| 131 | + CD->>DB: ejecutar init: 01_create_tables.sql |
| 132 | + CD->>DB: ejecutar init: 02_create_ticket_stored_procedures.sql |
| 133 | + CD->>DB: ejecutar init: 03_populate_tables.sql |
| 134 | + CD->>IT: correr tests contra API (api_url) y DB (db_host) |
| 135 | + IT-->>CD: resultados OK |
| 136 | + CD->>DB: ejecutar cleanup: 01_drop_test_records.sql |
| 137 | + ``` |
| 138 | + |
| 139 | +## Flujo extremo a extremo (E2E) |
| 140 | + |
| 141 | +1. Push a rama `feature/**` → análisis estático y unit tests. |
| 142 | +2. Pull Request a `main` → análisis estático + integración local + build & push de imagen a GHCR. |
| 143 | +3. Merge a `main` → CD a `stg`: promoción de imagen, Terraform apply, seed DB, integración remota, cleanup. |
| 144 | +4. Despliegue a `prd` (manual) → valida último run de `stg` y aplica el mismo proceso. |
| 145 | + |
| 146 | +### Diagramas de secuencia |
| 147 | + |
| 148 | +#### CI desde ramas/PR + CD a Staging y Producción |
| 149 | + |
| 150 | +```mermaid |
| 151 | +sequenceDiagram |
| 152 | + actor Dev as Developer |
| 153 | + participant GH as GitHub Actions |
| 154 | + participant CI as Workflows CI |
| 155 | + participant PR as PR Validations |
| 156 | + participant GHCR as GitHub Container Registry |
| 157 | + participant DH as DockerHub |
| 158 | + participant CD as Workflows CD |
| 159 | + participant TF as Terraform |
| 160 | + participant GSQL as GCP Cloud SQL (PostgreSQL) |
| 161 | + participant CR as GCP Cloud Run (API) |
| 162 | + participant TRT as Test Runner (pytest) |
| 163 | +
|
| 164 | + Dev->>GH: push a feature/** | feat/** | fix/** |
| 165 | + GH->>CI: ejecutar ci-dev-branches.yml |
| 166 | + CI->>CI: pre-commit + unit tests (coverage consola) |
| 167 | +
|
| 168 | + Dev->>GH: abrir Pull Request -> main |
| 169 | + GH->>PR: ejecutar pr-validations.yml |
| 170 | + PR->>PR: análisis estático + unit tests (coverage artifact) |
| 171 | + PR->>PR: integración local (docker-compose) |
| 172 | + PR->>GHCR: build & push imagen :latest y :sha |
| 173 | +
|
| 174 | + Dev->>GH: merge PR -> main |
| 175 | + GH->>CD: ejecutar cd-stg.yml |
| 176 | + CD->>CD: llamar cd-base.yml (deployment_env=stg, image=GHCR:latest) |
| 177 | + CD->>GHCR: pull imagen |
| 178 | + CD->>DH: re-tag y push a DockerHub |
| 179 | + CD->>TF: init, workspace stg, plan/apply con image_uri |
| 180 | + TF->>GSQL: crear instancia + DB + usuario |
| 181 | + TF->>CR: desplegar servicio Cloud Run (100% tráfico) |
| 182 | + TF-->>CD: outputs db_host, api_url |
| 183 | + CD->>GSQL: ejecutar scripts init/*.sql (seed) |
| 184 | + CD->>TRT: ejecutar tests integración remotos (usa api_url, db_host) |
| 185 | + TRT-->>CD: resultados |
| 186 | + CD->>GSQL: ejecutar scripts cleanup/*.sql |
| 187 | +
|
| 188 | + Note over CD: Despliegue a stg completo |
| 189 | +
|
| 190 | + Dev->>GH: disparo manual cd-prod.yml |
| 191 | + CD->>CD: validar último run exitoso de cd-stg |
| 192 | + CD->>CD: llamar cd-base.yml (deployment_env=prd, image=GHCR:latest) |
| 193 | + CD->>GHCR: pull imagen |
| 194 | + CD->>DH: re-tag y push a DockerHub |
| 195 | + CD->>TF: init, workspace prd, plan/apply con image_uri |
| 196 | + TF->>GSQL: crear instancia + DB + usuario (prd) |
| 197 | + TF->>CR: desplegar servicio Cloud Run (prd) |
| 198 | + TF-->>CD: outputs db_host, api_url (prd) |
| 199 | +``` |
| 200 | + |
| 201 | +#### Teardown / Destroy de infraestructura |
| 202 | + |
| 203 | +```mermaid |
| 204 | +sequenceDiagram |
| 205 | + actor Ops as Operador |
| 206 | + participant GH as GitHub Actions |
| 207 | + participant TD as Teardown Workflow (teardown.yml) |
| 208 | + participant TF as Terraform |
| 209 | + participant GSQL as GCP Cloud SQL |
| 210 | + participant CR as GCP Cloud Run |
| 211 | +
|
| 212 | + Ops->>GH: workflow_dispatch (inputs: env=stg|prd, confirmation="DESTROY") |
| 213 | + GH->>TD: ejecutar teardown.yml |
| 214 | + TD->>TD: validar confirmation == "DESTROY" |
| 215 | + TD->>TF: init, seleccionar workspace (env) |
| 216 | + TF->>GSQL: destruir instancia/DB/usuario |
| 217 | + TF->>CR: destruir servicio Cloud Run/policy |
| 218 | + TF-->>TD: destroy completado |
| 219 | +``` |
| 220 | + |
| 221 | +## Secretos y configuración |
| 222 | + |
| 223 | +En GitHub Actions (por entorno): |
| 224 | + |
| 225 | +- `GCP_SA_KEY`, `GCP_PROJECT_ID` |
| 226 | +- `DB_NAME`, `DB_USER`, `DB_PASSWORD` |
| 227 | +- `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN` |
| 228 | + |
| 229 | +Variables de Terraform por entorno en `terraform/environments/*.tfvars` (p. ej. `environment`, tamaños, etc.). |
0 commit comments