diff --git a/GUIA_DESARROLLO.md b/GUIA_DESARROLLO.md new file mode 100644 index 0000000..b6d8371 --- /dev/null +++ b/GUIA_DESARROLLO.md @@ -0,0 +1,399 @@ +# 📋 GUÍA COMPLETA PARA DESARROLLO - CI/CD proyectosacc + +> **Fecha:** 17 de Abril 2026 +> **Proyecto:** proyectosacc (SACC) +> **Repositorio:** https://bitbucket.org/ccsoft1/proyectosacc +> **Cuenta AWS:** 668889063715 (Desarrollo) + +--- + +## 🎯 RESUMEN EJECUTIVO + +Esta guía contiene toda la información necesaria para que el equipo de desarrollo configure y use el pipeline CI/CD del proyecto `proyectosacc`. + +**Estado del Pipeline:** ✅ Funcionando +**Último Build:** #67 (Exitoso) +**Infraestructura:** ✅ Creada y validada + +--- + +## 📁 ESTRUCTURA DEL PROYECTO + +``` +proyectosacc/ +├── bitbucket-pipelines.yml # Pipeline CI/CD (7 pasos) +├── scripts/ +│ ├── aws-oidc-setup.sh # Configuración OIDC AWS +│ ├── telegram-pipeline-notify.sh # Notificaciones Telegram +│ ├── terraform-lock-cleanup.sh # Limpieza de locks Terraform +│ ├── deploy.sh # Script de deploy manual +│ ├── health-check.sh # Health check de servicios +│ ├── rollback.sh # Rollback de deploys +│ └── deploy-frontend-s3.sh # Deploy frontend a S3 +├── terraform/ +│ ├── main.tf # Infraestructura principal +│ ├── variables.tf # Variables de Terraform +│ ├── outputs.tf # Outputs de Terraform +│ ├── backend.tf # Configuración backend S3 +│ ├── backend.dev.hcl # Backend desarrollo +│ ├── backend.prod.hcl # Backend producción +│ ├── oidc-bitbucket.tf # Configuración OIDC +│ ├── provider.tf # Providers AWS +│ ├── user-data.sh # Script inicialización EC2 +│ └── environments/ +│ ├── dev.tfvars # Variables desarrollo +│ └── prod.tfvars # Variables producción +├── nginx/ # Configuración nginx +├── docs/ # Documentación +├── .env.dev # Variables entorno dev +├── .env.prod # Variables entorno prod +└── .gitignore +``` + +--- + +## 🔧 CONFIGURACIÓN REQUERIDA EN BITBUCKET + +### 1. Variables de Repositorio (Repository Variables) + +Ve a: **Bitbucket → Repositorio → Repository settings → Repository variables** + +#### Variables para Desarrollo (developer) + +| Variable | Descripción | Valor / Ejemplo | +|----------|-------------|-----------------| +| `AWS_DEFAULT_REGION` | Región AWS | `mx-central-1` | +| `DEV_DB_PASSWORD` | Contraseña BD MariaDB | *(solicitar a Infra)* | +| `DEV_S3_FRONTEND_BUCKET` | Bucket S3 frontend | `ccsoft-proyectosacc-frontend-dev` | +| `DEV_S3_ARTIFACTS_BUCKET` | Bucket S3 artifacts | `ccsoft-proyectosacc-artifacts-dev` | +| `DEV_SERVER_IP_PROYECTOSACC` | IP servidor EC2 | `78.12.135.184` | +| `DEV_SERVER_USER_PROYECTOSACC` | Usuario SSH | `thoth` | +| `DEV_SSH_PORT_PROYECTOSACC` | Puerto SSH | `22` | +| `DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC` | Llave SSH (Base64) | *(solicitar a Infra)* | +| `DEV_TELEGRAM_BOT_TOKEN` | Token bot Telegram | *(solicitar a Infra)* | +| `DEV_TELEGRAM_CHAT_ID` | ID chat Telegram | *(solicitar a Infra)* | + +#### Variables para Producción (master) + +| Variable | Descripción | Valor / Ejemplo | +|----------|-------------|-----------------| +| `AWS_DEFAULT_REGION` | Región AWS | `mx-central-1` | +| `PROD_DB_PASSWORD` | Contraseña BD MariaDB | *(solicitar a Infra)* | +| `PROD_S3_FRONTEND_BUCKET` | Bucket S3 frontend | *(configurar)* | +| `PROD_S3_ARTIFACTS_BUCKET` | Bucket S3 artifacts | *(configurar)* | +| `PROD_SERVER_IP_PROYECTOSACC` | IP servidor EC2 | *(configurar)* | +| `PROD_SERVER_USER_PROYECTOSACC` | Usuario SSH | `thoth` | +| `PROD_SSH_PORT_PROYECTOSACC` | Puerto SSH | `22` | +| `PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC` | Llave SSH (Base64) | *(solicitar a Infra)* | +| `PROD_TELEGRAM_BOT_TOKEN` | Token bot Telegram | *(solicitar a Infra)* | +| `PROD_TELEGRAM_CHAT_ID` | ID chat Telegram | *(solicitar a Infra)* | + +### 2. Configuración OIDC (OpenID Connect) + +El pipeline usa OIDC para autenticación con AWS. **No se requieren credenciales estáticas**. + +**Configuración ya implementada:** +- Rol IAM: `BitbucketProyectosaccCICDRoleDev` +- Proveedor OIDC: Configurado para Bitbucket +- Alcance: Repositorio `ccsoft1/proyectosacc` + +**Para producción:** Se debe crear un rol similar para el entorno prod. + +--- + +## 🚀 PIPELINE CI/CD (7 Pasos) + +### Flujo del Pipeline + +``` +┌─────────────────────────────────────────────────────────────┐ +│ RAMA DEVELOPER │ +├─────────────────────────────────────────────────────────────┤ +│ 01_image-setup → Preparar imagen + SSH keys │ +│ 02_pre_terraform_check→ Limpiar locks de Terraform │ +│ 03_terraform → Infraestructura (plan + apply) │ +│ 04_build → Compilar frontend + backend │ +│ 05_publish → Subir a S3 │ +│ 06_install → Instalar JAR en servidor │ +│ 07_deploy → Deploy + Invalidar CloudFront │ +└─────────────────────────────────────────────────────────────┘ + +┌─────────────────────────────────────────────────────────────┐ +│ RAMA MASTER (Producción) │ +├─────────────────────────────────────────────────────────────┤ +│ 01_image-setup → Preparar imagen + SSH keys │ +│ 02_pre_terraform_check→ Limpiar locks de Terraform │ +│ 03_terraform → Infraestructura (plan + apply) │ +│ 04_build → Compilar frontend + backend │ +│ 05_publish → Subir a S3 │ +│ 06_install → Instalar JAR en servidor │ +│ 06b_notify_approval → Notificar espera aprobación │ +│ 07_deploy → Deploy manual (requiere aprobación)│ +└─────────────────────────────────────────────────────────────┘ +``` + +### Detalle de Steps + +#### Step 01: image-setup +- Instala dependencias: openssh-client, openjdk-21-jdk, wget, unzip, curl +- Instala AWS CLI v2 +- Configura llaves SSH para acceso al servidor +- Configura notificaciones Telegram + +#### Step 02: pre_terraform_check +- Instala AWS CLI +- Configura credenciales OIDC +- Ejecuta `terraform-lock-cleanup.sh` +- **Previene errores de state lock** + +#### Step 03: terraform +- Instala Terraform 1.11.4 +- Ejecuta `terraform init` +- Ejecuta `terraform plan` (verificación) +- Ejecuta `terraform apply` (crea/actualiza infraestructura) +- Genera `terraform-outputs.json` + +#### Step 04: build +- **Frontend:** Si existe `package.json` → `npm ci && npm run build` +- **Backend:** Si existe `gradlew` → `./gradlew clean bootJar` +- Genera artefactos: `build/**`, `build/libs/*.jar` + +#### Step 05: publish +- Sincroniza `build/` con S3 frontend (`aws s3 sync`) +- Copia JAR a S3 artifacts (`aws s3 cp`) + +#### Step 06: install +- Descarga JAR desde S3 al servidor EC2 +- Configura permisos (`chown osiris:osiris`) + +#### Step 07: deploy +- Ejecuta script de deploy en servidor EC2 +- Invalida distribución CloudFront +- Notifica éxito por Telegram + +--- + +## 🌐 INFRAESTRUCTURA AWS (Creada) + +### Recursos Activos + +| Servicio | Recurso | Identificador | Estado | +|----------|---------|---------------|--------| +| **EC2** | Instancia API | `i-044018f36de1020a8` | ✅ running | +| | IP Pública | `78.12.135.184` | ✅ | +| | DNS | `ec2-78-12-135-184.mx-central-1.compute.amazonaws.com` | ✅ | +| **RDS** | Base de datos | `proyectosacc-db-dev` | ✅ available | +| | Endpoint | `proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com:3306` | ✅ | +| | Motor | MariaDB | ✅ | +| **S3** | Frontend | `ccsoft-proyectosacc-frontend-dev` | ✅ | +| | Artifacts | `ccsoft-proyectosacc-artifacts-dev` | ✅ | +| | Terraform State | `ccsoft-terraform-state` | ✅ | +| **CloudFront** | Distribución | `E2EJ7237VFEJAR` | ✅ Deployed | +| | Dominio | `d3donfm7ov3eyb.cloudfront.net` | ✅ | +| **Route53** | Zona DNS | `dev-sacc.ccsoft.mx` | ✅ | +| **VPC** | Red | `vpc-0fefbaa0848a316dd` (10.1.0.0/16) | ✅ | +| **NAT Gateway** | Salida privada | `nat-0dafd3abd5557cd22` | ✅ | +| **DynamoDB** | Locks Terraform | `terraform-locks` | ✅ | + +### Outputs de Terraform + +```json +{ + "ec2_public_ip": "78.12.135.184", + "ec2_public_dns": "ec2-78-12-135-184.mx-central-1.compute.amazonaws.com", + "rds_endpoint": "proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com:3306", + "s3_frontend_bucket": "ccsoft-proyectosacc-frontend-dev", + "s3_artifacts_bucket": "ccsoft-proyectosacc-artifacts-dev", + "cloudfront_domain": "d3donfm7ov3eyb.cloudfront.net", + "cloudfront_distribution_id": "E2EJ7237VFEJAR", + "route53_record": "dev-sacc.ccsoft.mx", + "acm_certificate_arn": "arn:aws:acm:us-east-1:668889063715:certificate/521e3ae2-f22b-479f-a5ae-db9700caa248", + "vpc_id": "vpc-0fefbaa0848a316dd" +} +``` + +--- + +## 🔐 ACCESOS Y CREDENCIALES + +### Servidor EC2 (Desarrollo) + +```bash +# Conexión SSH +ssh -p 22 -i ~/.ssh/sacc4_key thoth@78.12.135.184 + +# Usuarios configurados +- thoth: Usuario de deploy (CI/CD) +- osiris: Usuario de ejecución de la aplicación +``` + +### Base de Datos (RDS) + +``` +Host: proyectosacc-db-dev.cbe2keowyu6w.mx-central-1.rds.amazonaws.com +Port: 3306 +Engine: MariaDB +Usuario: *(configurado en Terraform)* +Contraseña: ${DEV_DB_PASSWORD} +``` + +### S3 Buckets + +```bash +# Frontend +aws s3 ls s3://ccsoft-proyectosacc-frontend-dev/ + +# Artifacts +aws s3 ls s3://ccsoft-proyectosacc-artifacts-dev/ +``` + +--- + +## 📱 NOTIFICACIONES TELEGRAM + +### Bots Configurados +- **@CCAlertasBot** - Alertas críticas +- **@CCDevRoBot** - Notificaciones de desarrollo + +### Mensajes Enviados +- ✅ Inicio de pipeline +- ❌ Fallo de pipeline (con detalle del paso) +- ✅ Deploy exitoso +- ⏸️ Espera de aprobación (producción) + +--- + +## 💰 COSTOS MENSUALES ESTIMADOS + +| Servicio | Costo Estimado | +|----------|---------------| +| EC2 t3.small | ~$15-20 | +| RDS db.t3.micro | ~$15-20 | +| NAT Gateway | ~$32 | +| S3 (3 buckets) | ~$1-5 | +| CloudFront | ~$1-10 | +| Route53 | ~$0.50 | +| Data Transfer | ~$5-10 | +| **TOTAL** | **~$70-100/mes** | + +--- + +## 🛠️ COMANDOS ÚTILES + +### Terraform (Local) + +```bash +# Configurar credenciales +export AWS_ACCESS_KEY_ID=... +export AWS_SECRET_ACCESS_KEY=... +export AWS_SESSION_TOKEN=... +export AWS_DEFAULT_REGION=mx-central-1 + +# Inicializar +cd terraform +terraform init -backend-config=backend.dev.hcl + +# Plan +terraform plan -var-file=environments/dev.tfvars -var="db_password=TU_PASSWORD" + +# Apply +terraform apply -var-file=environments/dev.tfvars -var="db_password=TU_PASSWORD" + +# Outputs +terraform output -json +``` + +### AWS CLI + +```bash +# Verificar identidad +aws sts get-caller-identity + +# Listar instancias EC2 +aws ec2 describe-instances + +# Ver logs de CloudWatch +aws logs tail /aws/lambda/... --follow + +# Invalidar CloudFront +aws cloudfront create-invalidation --distribution-id E2EJ7237VFEJAR --paths "/*" +``` + +### Bitbucket Pipelines + +```bash +# Ejecutar pipeline manualmente +# Ir a: Bitbucket → Pipelines → Run pipeline +# Seleccionar rama: developer o master +``` + +--- + +## 🚨 TROUBLESHOOTING + +### Error: "Error acquiring the state lock" + +**Solución:** El pipeline ahora incluye el step `02_pre_terraform_check` que limpia locks automáticamente. + +### Error: "Saved plan is stale" + +**Solución:** El pipeline ahora ejecuta `plan` y `apply` sin guardar archivo intermedio. + +### Error: "aws: command not found" + +**Solución:** Cada step instala AWS CLI v2 independientemente. + +### Pipeline falla en step 07_deploy + +**Verificar:** +1. Llave SSH está configurada en Bitbucket variables +2. Servidor EC2 está running +3. Script `/home/thoth/deploy/setup/deploy.sh` existe en servidor + +--- + +## 📞 CONTACTOS Y SOPORTE + +| Rol | Contacto | Uso | +|-----|----------|-----| +| Infraestructura | Área de Tecnología | Credenciales, accesos AWS | +| DevOps | Pipeline issues | Configuración CI/CD | +| Seguridad | Rotación de llaves | SSH, tokens | + +--- + +## ✅ CHECKLIST PARA DESARROLLO + +### Antes de hacer push + +- [ ] Código compila localmente (`npm run build` o `./gradlew bootJar`) +- [ ] Tests pasan +- [ ] Variables de entorno actualizadas (si aplica) + +### Después de merge a developer + +- [ ] Pipeline ejecuta automáticamente +- [ ] Revisar notificación de Telegram +- [ ] Verificar deploy en: https://dev-sacc.ccsoft.mx + +### Para producción (master) + +- [ ] Crear PR a master +- [ ] Revisar plan de Terraform +- [ ] Aprobar deploy manual en Bitbucket +- [ ] Verificar en producción + +--- + +## 📚 DOCUMENTACIÓN ADICIONAL + +- **Pipeline completo:** Ver `bitbucket-pipelines.yml` +- **Scripts:** Ver carpeta `scripts/` +- **Terraform:** Ver carpeta `terraform/` +- **Directivas AWS:** Ver `docs/13-directivas-arquitectura-aws.md` + +--- + +**Documento generado:** 17 Abril 2026 +**Versión:** 1.0 +**Mantenido por:** Área de Tecnología y Desarrollo - CCsoft diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml index f081ec9..0e7b807 100644 --- a/bitbucket-pipelines.yml +++ b/bitbucket-pipelines.yml @@ -1,7 +1,7 @@ # =============================================================================================================== # bitbucket-pipelines.yml - Pipeline CI/CD para proyectosacc # Descripción: -# Pipeline de 7 pasos estándar de CCsoft para desplegar infraestructura (Terraform), +# Pipeline de 9 pasos estándar de CCsoft para desplegar infraestructura (Terraform), # frontend React (S3+CloudFront) y API backend (EC2) de SACC. # # Autor: Área de Tecnología y Desarrollo - CCsoft @@ -112,8 +112,15 @@ pipelines: - terraform version - export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}" - terraform init -backend-config=backend.dev.hcl - - terraform plan -lock-timeout=5m -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" -out=dev.tfplan - - terraform apply -lock-timeout=5m -auto-approve dev.tfplan + - echo "=== Ejecutando Terraform Plan (solo verificación) ===" + - terraform plan -lock-timeout=5m -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" + # NOTA DE SEGURIDAD: -auto-approve es aceptable en DEV porque: + # 1. El plan ya se ejecutó arriba y se visualiza en los logs del pipeline + # 2. DEV es un ambiente de desarrollo donde los cambios son reversibles + # 3. El pipeline corre en branches 'developer' con revisión previa vía PR + # Para PROD nunca usar -auto-approve sin revisión manual del plan. + - echo "=== Ejecutando Terraform Apply (auto-approve en DEV) ===" + - terraform apply -lock-timeout=5m -auto-approve -var-file=environments/dev.tfvars -var="db_password=${DEV_DB_PASSWORD}" - terraform output -json > terraform-outputs.json - cat terraform-outputs.json artifacts: @@ -167,7 +174,25 @@ pipelines: - echo "Publish condicional completado." - step: - name: 06_install + name: 06_update_ssh_keys + script: + - set -euo pipefail + - mkdir -p ~/.ssh + - echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key + - chmod 600 ~/.ssh/sacc4_key + # Actualizar authorized_keys del usuario thoth con la llave pública del pipeline + # Esto asegura que solo el pipeline actual pueda acceder, rotando llaves automáticamente + - | + DEV_PUB_KEY=$(echo "${DEV_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d | ssh-keygen -y -f /dev/stdin) + ssh -p "${DEV_SSH_PORT_PROYECTOSACC:-22}" \ + -i ~/.ssh/sacc4_key \ + -o StrictHostKeyChecking=no \ + "${DEV_SERVER_USER_PROYECTOSACC:-thoth}@${DEV_SERVER_IP_PROYECTOSACC}" \ + "bash -c 'mkdir -p /home/thoth/.ssh && chmod 700 /home/thoth/.ssh && echo \"${DEV_PUB_KEY}\" > /home/thoth/.ssh/authorized_keys && chmod 600 /home/thoth/.ssh/authorized_keys && chown -R thoth:thoth /home/thoth/.ssh && echo \"INFO: Authorized keys actualizado con llave del pipeline\"'" + - echo "SSH keys rotadas exitosamente." + + - step: + name: 07_install script: - set -euo pipefail - apt-get update -y && apt-get install -y curl unzip @@ -197,7 +222,7 @@ pipelines: - echo "Install condicional completado." - step: - name: 07_deploy + name: 08_deploy oidc: true script: - set -euo pipefail @@ -226,6 +251,33 @@ pipelines: - export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}" - bash scripts/telegram-pipeline-notify.sh success "Deploy condicional completado" + - step: + name: 09_health_check + oidc: true + script: + - set -euo pipefail + - apt-get update -y && apt-get install -y curl + - source scripts/aws-oidc-setup.sh dev + - echo "=== Health Check post-deploy (DEV) ===" + - | + export ENV=dev + export HEALTH_URL="http://${DEV_SERVER_IP_PROYECTOSACC}:8080/actuator/health" + export MAX_RETRIES=12 + export RETRY_INTERVAL=10 + # Verificar CloudFront + CLOUDFRONT_DOMAIN=$(aws cloudfront list-distributions --query "DistributionList.Items[?Aliases.Items[0]=='sacc.ccsoft.mx'].DomainName" --output text) + if [ -n "${CLOUDFRONT_DOMAIN}" ]; then + export CF_DOMAIN="${CLOUDFRONT_DOMAIN}" + fi + # Verificar RDS si está configurado + if [ -n "${DEV_DB_HOST:-}" ]; then + export DB_HOST="${DEV_DB_HOST}" + fi + bash scripts/health-check.sh + - export TELEGRAM_BOT_TOKEN="${DEV_TELEGRAM_BOT_TOKEN}" + - export TELEGRAM_CHAT_ID="${DEV_TELEGRAM_CHAT_ID}" + - bash scripts/telegram-pipeline-notify.sh success "Health Check pasó - Servicios saludables" + master: - step: name: 01_image-setup @@ -275,12 +327,18 @@ pipelines: - terraform version - export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-mx-central-1}" - terraform init -backend-config=backend.prod.hcl + # NOTA DE SEGURIDAD: En PROD nunca usar -auto-approve sin revisión manual. + # El plan se guarda en prod.tfplan y debe ser revisado antes de aplicar. + # El paso 08_deploy requiere aprobación manual (trigger: manual). + - echo "=== Ejecutando Terraform Plan (PROD - requiere revisión manual) ===" - terraform plan -lock-timeout=5m -var-file=environments/prod.tfvars -var="db_password=${PROD_DB_PASSWORD}" -out=prod.tfplan - - terraform apply -lock-timeout=5m -auto-approve prod.tfplan + - echo "=== Terraform Plan guardado en prod.tfplan ===" + - echo "AVISO: Revisar el plan arriba antes de aprobar el deploy manual." - terraform output -json > terraform-outputs.json - cat terraform-outputs.json artifacts: - terraform/terraform-outputs.json + - terraform/prod.tfplan - step: name: 04_build @@ -330,7 +388,25 @@ pipelines: - echo "Publish condicional completado." - step: - name: 06_install + name: 06_update_ssh_keys + script: + - set -euo pipefail + - mkdir -p ~/.ssh + - echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key + - chmod 600 ~/.ssh/sacc4_key + # Actualizar authorized_keys del usuario thoth con la llave pública del pipeline + # Esto asegura que solo el pipeline actual pueda acceder, rotando llaves automáticamente + - | + PROD_PUB_KEY=$(echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d | ssh-keygen -y -f /dev/stdin) + ssh -p "${PROD_SSH_PORT_PROYECTOSACC:-22}" \ + -i ~/.ssh/sacc4_key \ + -o StrictHostKeyChecking=no \ + "${PROD_SERVER_USER_PROYECTOSACC:-thoth}@${PROD_SERVER_IP_PROYECTOSACC}" \ + "bash -c 'mkdir -p /home/thoth/.ssh && chmod 700 /home/thoth/.ssh && echo \"${PROD_PUB_KEY}\" > /home/thoth/.ssh/authorized_keys && chmod 600 /home/thoth/.ssh/authorized_keys && chown -R thoth:thoth /home/thoth/.ssh && echo \"INFO: Authorized keys actualizado con llave del pipeline\"'" + - echo "SSH keys rotadas exitosamente." + + - step: + name: 07_install script: - set -euo pipefail - apt-get update -y && apt-get install -y curl unzip @@ -360,7 +436,7 @@ pipelines: - echo "Install condicional completado." - step: - name: 06b_notify_approval + name: 07b_notify_approval script: - set -euo pipefail - export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}" @@ -369,7 +445,7 @@ pipelines: bash scripts/telegram-pipeline-notify.sh start "⏸️ Pipeline pausado esperando aprobación manual para deploy a PRODUCCIÓN. Ve a Bitbucket > Pipelines > proyectosacc > master para aprobar o rechazar." - step: - name: 07_deploy + name: 08_deploy oidc: true deployment: production trigger: manual @@ -381,6 +457,16 @@ pipelines: - ./aws/install - aws --version - source scripts/aws-oidc-setup.sh prod + # Aplicar Terraform plan previamente generado y guardado en artifact + - | + if [ -f terraform/prod.tfplan ]; then + echo "=== Aplicando Terraform Plan previamente aprobado ===" + cd terraform + terraform apply -lock-timeout=5m prod.tfplan + cd .. + else + echo "WARNING: No se encontró prod.tfplan. Terraform apply saltado." + fi - echo "${PROD_SSH_PRIVATE_KEY_THOTH_PROYECTOSACC}" | base64 -d > ~/.ssh/sacc4_key - chmod 600 ~/.ssh/sacc4_key - | @@ -399,3 +485,30 @@ pipelines: - export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}" - export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}" - bash scripts/telegram-pipeline-notify.sh success "CloudFront invalidado | Deploy a PROD aprobado y completado" + + - step: + name: 09_health_check + oidc: true + script: + - set -euo pipefail + - apt-get update -y && apt-get install -y curl + - source scripts/aws-oidc-setup.sh prod + - echo "=== Health Check post-deploy (PROD) ===" + - | + export ENV=prod + export HEALTH_URL="http://${PROD_SERVER_IP_PROYECTOSACC}:8080/actuator/health" + export MAX_RETRIES=12 + export RETRY_INTERVAL=10 + # Verificar CloudFront + CLOUDFRONT_DOMAIN=$(aws cloudfront list-distributions --query "DistributionList.Items[?Aliases.Items[0]=='sacc.ccsoft.mx'].DomainName" --output text) + if [ -n "${CLOUDFRONT_DOMAIN}" ]; then + export CF_DOMAIN="${CLOUDFRONT_DOMAIN}" + fi + # Verificar RDS si está configurado + if [ -n "${PROD_DB_HOST:-}" ]; then + export DB_HOST="${PROD_DB_HOST}" + fi + bash scripts/health-check.sh + - export TELEGRAM_BOT_TOKEN="${PROD_TELEGRAM_BOT_TOKEN}" + - export TELEGRAM_CHAT_ID="${PROD_TELEGRAM_CHAT_ID}" + - bash scripts/telegram-pipeline-notify.sh success "Health Check pasó - Servicios de PROD saludables" diff --git a/scripts/health-check.sh b/scripts/health-check.sh index 5b8345b..54c2522 100755 --- a/scripts/health-check.sh +++ b/scripts/health-check.sh @@ -1,12 +1,14 @@ #!/usr/bin/env bash # =============================================================================================================== -# health-check.sh - Verifica que la API backend esté saludable +# health-check.sh - Verifica que la API backend, CloudFront y RDS estén saludables # Descripción: -# Realiza peticiones al endpoint de salud de la API con reintentos. -# Puede ejecutarse desde la EC2 localmente o desde el pipeline. +# Realiza peticiones a múltiples endpoints con reintentos configurables. +# Puede ejecutarse desde la EC2 localmente o desde el pipeline CI/CD. +# Verifica: API backend (puertos 8080-8084), CloudFront, RDS. # # Uso: # HEALTH_URL=http://localhost:8080/actuator/health bash health-check.sh +# ENV=prod CF_DOMAIN=sacc.ccsoft.mx bash health-check.sh # # Autor: Área de Tecnología y Desarrollo - CCsoft # =============================================================================================================== @@ -17,8 +19,12 @@ set -euo pipefail # Configuración # ------------------------------------------------------------------------------- HEALTH_URL="${HEALTH_URL:-http://localhost:8080/actuator/health}" -MAX_RETRIES="${MAX_RETRIES:-30}" -RETRY_INTERVAL="${RETRY_INTERVAL:-2}" +CF_DOMAIN="${CF_DOMAIN:-}" +DB_HOST="${DB_HOST:-}" +DB_PORT="${DB_PORT:-3306}" +ENV="${ENV:-dev}" +MAX_RETRIES="${MAX_RETRIES:-12}" +RETRY_INTERVAL="${RETRY_INTERVAL:-10}" readonly CLR_RED='\033[0;31m' readonly CLR_GREEN='\033[0;32m' @@ -32,30 +38,103 @@ _log_error() { echo -e "${CLR_RED}[ERROR]${CLR_NC} $*" >&2; } _log_step() { echo ""; echo -e "${CLR_BLUE}==== $* ====${CLR_NC}"; } # ------------------------------------------------------------------------------- -# Health Check +# Health Check con reintentos # ------------------------------------------------------------------------------- -main() { - _log_step "Health Check: ${HEALTH_URL}" - +check_with_retry() { + local name="$1" + local url="$2" + local check_cmd="$3" + + _log_step "Verificando: ${name} (${url})" + local attempt=1 while [[ $attempt -le $MAX_RETRIES ]]; do _log_info "Intento ${attempt}/${MAX_RETRIES}..." - - if curl -sf "${HEALTH_URL}" | grep -q '"status":"UP"'; then - _log_info "Health check PASÓ - API saludable" + + if eval "${check_cmd}"; then + _log_info "✓ ${name} - SALUDABLE" return 0 fi - + if [[ $attempt -lt $MAX_RETRIES ]]; then - _log_warn "API no responde aún, reintentando en ${RETRY_INTERVAL}s..." + _log_warn "${name} no responde, reintentando en ${RETRY_INTERVAL}s..." sleep "${RETRY_INTERVAL}" fi - + ((attempt++)) done - - _log_error "Health check FALLÓ después de ${MAX_RETRIES} intentos" + + _log_error "✗ ${name} - FALLÓ después de ${MAX_RETRIES} intentos" return 1 } +# ------------------------------------------------------------------------------- +# Health Check Principal +# ------------------------------------------------------------------------------- +main() { + local exit_code=0 + + _log_step "Health Check Post-Deploy [${ENV^^}]" + echo "Configuración: MAX_RETRIES=${MAX_RETRIES}, INTERVAL=${RETRY_INTERVAL}s" + echo "" + + # 1. Verificar API Backend (puerto principal) + if ! check_with_retry "API Backend" "${HEALTH_URL}" \ + "curl -sf '${HEALTH_URL}' | grep -q '\"status\":\"UP\"'"; then + exit_code=1 + fi + + # 2. Verificar puertos adicionales de servicios (8080-8084) + local base_url + base_url=$(echo "${HEALTH_URL}" | sed 's|/actuator/health||') + for port in 8081 8082 8083 8084; do + local port_url="${base_url//:[0-9]*/:${port}}/actuator/health" + # Solo verificar si la variable del puerto está definida o si es el mismo host + if [[ "${base_url}" =~ :[0-9]+ ]]; then + port_url="${base_url%:*}:${port}/actuator/health" + fi + + # Verificación opcional - no falla el pipeline si un puerto opcional no responde + _log_info "Verificando puerto opcional ${port}..." + if curl -sf "${port_url}" | grep -q '"status":"UP"' 2>/dev/null; then + _log_info "✓ Puerto ${port} - RESPONDE" + else + _log_warn "Puerto ${port} no responde (opcional)" + fi + done + + # 3. Verificar CloudFront (si está configurado) + if [[ -n "${CF_DOMAIN}" ]]; then + if ! check_with_retry "CloudFront" "https://${CF_DOMAIN}/api/health" \ + "curl -sf -o /dev/null 'https://${CF_DOMAIN}/api/health'"; then + exit_code=1 + fi + else + _log_warn "CF_DOMAIN no configurado. Saltando verificación de CloudFront." + fi + + # 4. Verificar RDS (si está configurado) + if [[ -n "${DB_HOST}" ]]; then + if command -v nc &> /dev/null; then + if ! check_with_retry "RDS" "${DB_HOST}:${DB_PORT}" \ + "nc -zv '${DB_HOST}' ${DB_PORT}"; then + exit_code=1 + fi + else + _log_warn "nc (netcat) no instalado. Saltando verificación de RDS." + fi + else + _log_warn "DB_HOST no configurado. Saltando verificación de RDS." + fi + + echo "" + if [[ $exit_code -eq 0 ]]; then + _log_step "✓ TODOS LOS HEALTH CHECKS PASARON" + else + _log_step "✗ ALGUNOS HEALTH CHECKS FALLARON" + fi + + return $exit_code +} + main "$@" diff --git a/terraform/main.tf b/terraform/main.tf index 4ba021b..928e411 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -116,12 +116,16 @@ resource "aws_security_group" "ec2_api" { vpc_id = aws_vpc.main.id description = "Security Group para la API backend de ${var.project_name}" + # NOTA DE SEGURIDAD: Acceso SSH controlado EXCLUSIVAMENTE por llaves SSH + # administradas por el pipeline CI/CD (key-based auth), NO por restricción de IP. + # El pipeline inyecta y rota las llaves públicas en authorized_keys del usuario thoth. + # Considerar migrar a AWS Systems Manager Session Manager para eliminar acceso SSH directo. ingress { - description = "SSH desde IPs confiables" + description = "SSH - Acceso controlado por llaves CI/CD (no por IP)" from_port = 22 to_port = 22 protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] # SSH desde cualquier IP (pipeline Bitbucket + administración) + cidr_blocks = ["0.0.0.0/0"] } ingress {