Merge _configs/ into config/ for single configuration directory. Update all path references. Changes: - Move _configs/* to config/ - Update .gitignore for new patterns - No code references to _configs/ found Impact: -1 root directory (layout_conventions.md compliance)
956 lines
26 KiB
Markdown
956 lines
26 KiB
Markdown
# 🔗 Validación Multi-Capas: KCL → Terraform → Ansible
|
|
|
|
**Fecha**: 2025-11-20
|
|
**Clasificación**: Estrategia de validación end-to-end
|
|
**Enfoque**: Aprovechar validaciones nativas de cada herramienta
|
|
|
|
---
|
|
|
|
## 📋 Tabla de Contenidos
|
|
|
|
1. [Arquitectura Multi-Capas](#arquitectura-multi-capas)
|
|
2. [Validación KCL (Definitions)](#validación-kcl-definitions)
|
|
3. [Validación Terraform (IaC)](#validación-terraform-iac)
|
|
4. [Validación Ansible (Configuration)](#validación-ansible-configuration)
|
|
5. [Orquestación NuShell](#orquestación-nushell)
|
|
6. [Flujos de Validación End-to-End](#flujos-de-validación-end-to-end)
|
|
|
|
---
|
|
|
|
## 🏗️ Arquitectura Multi-Capas
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ CAPA 0: DEFINICIONES (KCL) │
|
|
│ ═══════════════════════════════════════════════════════ │
|
|
│ • services.k (definiciones de servicios) │
|
|
│ • patterns.k (patrones de despliegue) │
|
|
│ • policies.k (políticas de validación) │
|
|
│ │
|
|
│ VALIDACIÓN: kcl compile │
|
|
│ ✅ Tipos correctos │
|
|
│ ✅ Sin ciclos de dependencias │
|
|
│ ✅ Referencias válidas │
|
|
│ ✅ Esquema completo │
|
|
└──────────────────────────────────────────────────────────┘
|
|
↓ (genera)
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ CAPA 1: INFRAESTRUCTURA AS CODE (Terraform) │
|
|
│ ═══════════════════════════════════════════════════════ │
|
|
│ • kubernetes.tf (generado desde KCL) │
|
|
│ • docker-compose.tf (generado desde KCL) │
|
|
│ • networking.tf (generado desde KCL) │
|
|
│ • variables.tf (configuración) │
|
|
│ │
|
|
│ VALIDACIÓN: terraform validate │
|
|
│ ✅ Sintaxis HCL correcta │
|
|
│ ✅ Recursos válidos │
|
|
│ ✅ Dependencias de recursos │
|
|
│ ✅ Variables requeridas │
|
|
└──────────────────────────────────────────────────────────┘
|
|
↓ (planifica)
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ CAPA 2: PLAN DE INFRASTRUCTURE (Terraform Plan) │
|
|
│ ═══════════════════════════════════════════════════════ │
|
|
│ • terraform.tfplan (plan de cambios) │
|
|
│ • impacto en recursos │
|
|
│ • dependencias detectadas │
|
|
│ │
|
|
│ VALIDACIÓN: terraform plan -json │
|
|
│ ✅ Cambios permitidos │
|
|
│ ✅ Sin errores de aplicación │
|
|
│ ✅ Impacto predecible │
|
|
└──────────────────────────────────────────────────────────┘
|
|
↓ (genera)
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ CAPA 3: CONFIGURACIÓN (Ansible) │
|
|
│ ═══════════════════════════════════════════════════════ │
|
|
│ • site.yml (playbook principal) │
|
|
│ • roles/
|
|
│ │ ├─ api-server/ │
|
|
│ │ ├─ database/ │
|
|
│ │ └─ monitoring/ │
|
|
│ • group_vars/
|
|
│ │ └─ production.yml (variables generadas) │
|
|
│ │
|
|
│ VALIDACIÓN: ansible-playbook --syntax-check │
|
|
│ ✅ Sintaxis YAML correcta │
|
|
│ ✅ Tasks bien formados │
|
|
│ ✅ Roles válidos │
|
|
│ ✅ Variables resuelven │
|
|
└──────────────────────────────────────────────────────────┘
|
|
↓ (genera)
|
|
┌──────────────────────────────────────────────────────────┐
|
|
│ CAPA 4: DESPLIEGUE FINAL │
|
|
│ ═══════════════════════════════════════════════════════ │
|
|
│ • Infrastructure aplicada │
|
|
│ • Servicios configurados │
|
|
│ • Monitoreo activo │
|
|
└──────────────────────────────────────────────────────────┘
|
|
|
|
FLUJO: Cada capa valida su propio dominio usando herramientas nativas
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Validación KCL (Definitions)
|
|
|
|
### 1. KCL Native Validation
|
|
|
|
```bash
|
|
# Validación nativa - sin código personalizado
|
|
$ kcl compile provisioning/services.k
|
|
|
|
# Detecta automáticamente:
|
|
# ❌ Errores de tipo
|
|
services = [
|
|
{
|
|
id: 123 # ERROR: Esperaba str
|
|
}
|
|
]
|
|
|
|
# ❌ Campos requeridos
|
|
service = {
|
|
id: "api"
|
|
# ERROR: name requerido
|
|
}
|
|
|
|
# ❌ Referencias inválidas
|
|
depends_on: ["service-inexistente"] # ERROR
|
|
|
|
# ❌ Ciclos de dependencias
|
|
api = { depends_on: ["db"] }
|
|
db = { depends_on: ["api"] } # ERROR: CICLO
|
|
|
|
# ❌ Puertos duplicados
|
|
ports: [3000, 3000] # ERROR
|
|
```
|
|
|
|
### 2. Script NuShell: Ejecutar Validación KCL
|
|
|
|
```nushell
|
|
#!/usr/bin/env nu
|
|
|
|
# kcl-validate.nu - Validar definiciones KCL
|
|
|
|
def validate_kcl [kcl_file: path] {
|
|
print "🔍 Validando KCL: $kcl_file"
|
|
|
|
let result = (
|
|
kcl compile $kcl_file 2>&1
|
|
)
|
|
|
|
if ($result | str contains "Error:") {
|
|
print "❌ Errores encontrados:"
|
|
print $result
|
|
return {
|
|
success: false
|
|
error: ($result | str trim)
|
|
}
|
|
}
|
|
|
|
print "✅ KCL válido"
|
|
return {
|
|
success: true
|
|
output: $result
|
|
}
|
|
}
|
|
|
|
# Uso
|
|
validate_kcl "provisioning/services.k"
|
|
```
|
|
|
|
### 3. Qué Valida KCL Automáticamente
|
|
|
|
```
|
|
✅ TIPO-SEGURIDAD
|
|
└─ id: str, port: int, replicas: int
|
|
└─ Rechaza valores con tipo incorrecto
|
|
|
|
✅ REQUERIMIENTOS
|
|
└─ Campos obligatorios deben estar presentes
|
|
└─ Rechaza estructuras incompletas
|
|
|
|
✅ CICLOS DE DEPENDENCIAS
|
|
└─ Función validate_no_dependency_cycles en policies.k
|
|
└─ Rechaza A→B→A
|
|
|
|
✅ REFERENCIAS
|
|
└─ Los servicios en depends_on deben existir
|
|
└─ Rechaza referencias a servicios inexistentes
|
|
|
|
✅ UNICIDAD
|
|
└─ Puertos únicos por host/namespace
|
|
└─ IDs únicos en catálogo
|
|
|
|
✅ RESTRICCIONES DE RECURSOS
|
|
└─ CPU/Memory en formato válido (100m, 512Mi)
|
|
└─ Límites >= requests
|
|
|
|
✅ RELACIONES
|
|
└─ Service debe existir antes de referenciar
|
|
└─ Patterns referencian servicios existentes
|
|
```
|
|
|
|
---
|
|
|
|
## 🏗️ Validación Terraform (IaC)
|
|
|
|
### 1. Terraform Native Validation
|
|
|
|
```bash
|
|
# Validación nativa de HCL
|
|
$ terraform validate
|
|
|
|
# Detecta automáticamente:
|
|
# ❌ Sintaxis HCL inválida
|
|
resource "kubernetes_service" "api" {
|
|
metadata {
|
|
name = "api"
|
|
# ERROR: bloque mal formado
|
|
}
|
|
}
|
|
|
|
# ❌ Tipos incorrectos
|
|
resource "kubernetes_deployment" "api" {
|
|
spec {
|
|
replicas = "2" # ERROR: Esperaba número
|
|
}
|
|
}
|
|
|
|
# ❌ Variables no definidas
|
|
resource "kubernetes_service" "api" {
|
|
metadata {
|
|
namespace = var.undefined_var # ERROR
|
|
}
|
|
}
|
|
|
|
# ❌ Referencias inválidas
|
|
resource "kubernetes_service" "api" {
|
|
spec {
|
|
selector {
|
|
app = kubernetes_deployment.undefined.spec.selector.match_labels.app # ERROR
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Terraform Plan Validation
|
|
|
|
```bash
|
|
# Validación de plan
|
|
$ terraform plan -json
|
|
|
|
# Analiza:
|
|
# ✅ Cambios válidos
|
|
# ✅ Dependencias correctas
|
|
# ✅ Recursos creables
|
|
# ❌ Cambios destructivos
|
|
# ❌ Errores de aplicación
|
|
# ❌ Conflictos de estado
|
|
```
|
|
|
|
### 3. Script NuShell: Validar Terraform
|
|
|
|
```nushell
|
|
#!/usr/bin/env nu
|
|
|
|
# terraform-validate.nu - Validar generado Terraform
|
|
|
|
def validate_terraform [tf_dir: path] {
|
|
print "🔍 Validando Terraform: $tf_dir"
|
|
|
|
# Cambiar a directorio
|
|
cd $tf_dir
|
|
|
|
# Validar sintaxis
|
|
let validate_result = (
|
|
terraform validate -json 2>&1 | from json
|
|
)
|
|
|
|
if not $validate_result.valid {
|
|
print "❌ Errores de sintaxis Terraform:"
|
|
print $validate_result
|
|
return {
|
|
success: false
|
|
errors: $validate_result.diagnostics
|
|
}
|
|
}
|
|
|
|
print "✅ Sintaxis Terraform válida"
|
|
|
|
# Validar plan
|
|
let plan_result = (
|
|
terraform plan -json 2>&1 | from json
|
|
)
|
|
|
|
# Buscar cambios destructivos
|
|
let destructive = (
|
|
$plan_result.resource_changes
|
|
| filter { |rc| $rc.change.actions | any { |a| $a == "delete" } }
|
|
)
|
|
|
|
if ($destructive | length) > 0 {
|
|
print "⚠️ CAMBIOS DESTRUCTIVOS DETECTADOS:"
|
|
$destructive | each { |rc|
|
|
print $" - ($rc.type).($rc.name) será eliminado"
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true
|
|
plan_summary: $plan_result.summary
|
|
destructive_changes: ($destructive | length)
|
|
}
|
|
}
|
|
|
|
# Uso
|
|
validate_terraform "generated/terraform"
|
|
```
|
|
|
|
### 4. Ejemplo: Terraform Generado desde KCL
|
|
|
|
```hcl
|
|
# Generado automáticamente desde services.k
|
|
|
|
variable "namespace" {
|
|
type = string
|
|
default = "production"
|
|
}
|
|
|
|
variable "docker_registry" {
|
|
type = string
|
|
default = "docker.io"
|
|
}
|
|
|
|
# Kubernetes Deployment - Generado desde service.id="api"
|
|
resource "kubernetes_deployment" "api" {
|
|
metadata {
|
|
name = "api-server"
|
|
namespace = var.namespace
|
|
labels = {
|
|
app = "syntaxis"
|
|
component = "api"
|
|
tier = "backend"
|
|
}
|
|
}
|
|
|
|
spec {
|
|
replicas = 2 # Desde service.replicas
|
|
|
|
selector {
|
|
match_labels = {
|
|
app = "api-server"
|
|
}
|
|
}
|
|
|
|
template {
|
|
metadata {
|
|
labels = {
|
|
app = "api-server"
|
|
}
|
|
}
|
|
|
|
spec {
|
|
container {
|
|
name = "api"
|
|
image = "${var.docker_registry}/syntaxis:api-latest" # Desde service.image
|
|
port {
|
|
name = "http"
|
|
container_port = 3000 # Desde service.port
|
|
}
|
|
|
|
# Recursos - Desde service.resources
|
|
resources {
|
|
requests = {
|
|
cpu = "100m"
|
|
memory = "256Mi"
|
|
}
|
|
limits = {
|
|
cpu = "500m"
|
|
memory = "512Mi"
|
|
}
|
|
}
|
|
|
|
# Health check - Desde service.healthCheck
|
|
liveness_probe {
|
|
http_get {
|
|
path = "/health"
|
|
port = "http"
|
|
}
|
|
initial_delay_seconds = 10
|
|
period_seconds = 10
|
|
}
|
|
|
|
# Env vars - Desde service.env
|
|
env {
|
|
name = "RUST_LOG"
|
|
value = "info"
|
|
}
|
|
env {
|
|
name = "DATABASE_URL"
|
|
value = "postgresql://postgres:5432/syntaxis"
|
|
}
|
|
}
|
|
|
|
# Depends on - Desde service.depends_on
|
|
init_container {
|
|
name = "wait-for-db"
|
|
image = "${var.docker_registry}/busybox:latest"
|
|
command = ["sh", "-c", "until nc -z database 5432; do sleep 1; done"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Kubernetes Service - Generado automáticamente
|
|
resource "kubernetes_service" "api" {
|
|
metadata {
|
|
name = "api-service"
|
|
namespace = var.namespace
|
|
}
|
|
|
|
spec {
|
|
selector = {
|
|
app = "api-server"
|
|
}
|
|
|
|
port {
|
|
name = "http"
|
|
port = 80
|
|
target_port = "http"
|
|
}
|
|
|
|
type = "LoadBalancer"
|
|
}
|
|
}
|
|
|
|
# Validación que Terraform hace automáticamente:
|
|
# ✅ name es string requerido
|
|
# ✅ namespace referencia variable existente
|
|
# ✅ replicas es número (2)
|
|
# ✅ image es string válido
|
|
# ✅ port es número (3000)
|
|
# ✅ resources.requests.cpu es formato válido
|
|
# ✅ init_container referencias imagen válida
|
|
# ✅ selector.app coincide con deployment.labels
|
|
```
|
|
|
|
### 5. Qué Valida Terraform Automáticamente
|
|
|
|
```
|
|
✅ SINTAXIS HCL
|
|
└─ Bloques, atributos, tipos correctos
|
|
└─ Comillas, llaves, punto y coma
|
|
|
|
✅ TIPOS DE DATOS
|
|
└─ Numbers, strings, booleans correctos
|
|
└─ Listas y objetos bien formados
|
|
|
|
✅ REFERENCIAS
|
|
└─ Variables referenciadas existen
|
|
└─ Recursos referenciados están definidos
|
|
└─ Módulos válidos
|
|
|
|
✅ DEPENDENCIAS
|
|
└─ Orden de creación correcto
|
|
└─ Ciclos detectados
|
|
|
|
✅ PROVEEDORES
|
|
└─ Proveedor Kubernetes configurado
|
|
└─ Versiones compatibles
|
|
|
|
✅ PLAN
|
|
└─ Cambios aplicables
|
|
└─ Sin conflictos de estado
|
|
└─ Dependencias resuelven
|
|
|
|
✅ VALIDACIONES CUSTOMIZADAS
|
|
└─ Nombre debe contener patrón
|
|
└─ Replicas dentro de rango
|
|
└─ CPU requests < limits
|
|
```
|
|
|
|
---
|
|
|
|
## ⚙️ Validación Ansible (Configuration)
|
|
|
|
### 1. Ansible Native Validation
|
|
|
|
```bash
|
|
# Validación de sintaxis YAML
|
|
$ ansible-playbook site.yml --syntax-check
|
|
|
|
# Detecta automáticamente:
|
|
# ❌ YAML inválido
|
|
- hosts: all
|
|
tasks:
|
|
- name: Start service
|
|
systemd:
|
|
name: api # ERROR: indentación incorrecta
|
|
|
|
# ❌ Tasks malformados
|
|
- name: Invalid task
|
|
copy:
|
|
src: file.txt
|
|
dest: /tmp/
|
|
# ERROR: falta 'dest' o 'src'
|
|
|
|
# ❌ Roles inexistentes
|
|
- import_role:
|
|
name: undefined_role # ERROR: rol no existe
|
|
|
|
# ❌ Variables sin resolver
|
|
- name: Use variable
|
|
debug:
|
|
msg: "{{ undefined_var }}" # ERROR: variable no existe
|
|
```
|
|
|
|
### 2. Script NuShell: Validar Ansible
|
|
|
|
```nushell
|
|
#!/usr/bin/env nu
|
|
|
|
# ansible-validate.nu - Validar playbooks Ansible
|
|
|
|
def validate_ansible [playbook_file: path] {
|
|
print "🔍 Validando Ansible: $playbook_file"
|
|
|
|
# Validar sintaxis
|
|
let syntax_check = (
|
|
ansible-playbook $playbook_file --syntax-check 2>&1
|
|
)
|
|
|
|
if ($syntax_check | str contains "ERROR") {
|
|
print "❌ Errores de sintaxis Ansible:"
|
|
print $syntax_check
|
|
return {
|
|
success: false
|
|
error: ($syntax_check | str trim)
|
|
}
|
|
}
|
|
|
|
print "✅ Sintaxis Ansible válida"
|
|
|
|
# Validar roles
|
|
let roles_dir = "roles"
|
|
let roles = (
|
|
ls $roles_dir | get name | map { |r| $r | path basename }
|
|
)
|
|
|
|
print $"📦 Roles detectados: ($roles | str join ', ')"
|
|
|
|
# Validar references en playbook
|
|
let playbook_content = (open $playbook_file)
|
|
for role in $roles {
|
|
if not ($playbook_content | str contains $role) {
|
|
print $"⚠️ Rol ($role) no utilizado"
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true
|
|
roles_count: ($roles | length)
|
|
roles: $roles
|
|
}
|
|
}
|
|
|
|
# Uso
|
|
validate_ansible "site.yml"
|
|
```
|
|
|
|
### 3. Ejemplo: Playbook Generado desde KCL
|
|
|
|
```yaml
|
|
# Generado automáticamente desde services.k
|
|
|
|
---
|
|
- name: Deploy syntaxis services
|
|
hosts: all
|
|
become: yes
|
|
vars_files:
|
|
- group_vars/{{ environment }}.yml
|
|
vars:
|
|
# Variables generadas desde KCL
|
|
services:
|
|
- name: api-server
|
|
port: 3000
|
|
image: "{{ docker_registry }}/syntaxis:api-latest"
|
|
replicas: 2
|
|
- name: database
|
|
port: 5432
|
|
image: "{{ docker_registry }}/postgres:15-alpine"
|
|
replicas: 1
|
|
|
|
pre_tasks:
|
|
- name: Validate Docker is installed
|
|
command: docker --version
|
|
register: docker_version
|
|
changed_when: false
|
|
|
|
- name: Validate connectivity to services
|
|
wait_for:
|
|
host: "{{ item.host }}"
|
|
port: "{{ item.port }}"
|
|
timeout: 5
|
|
loop:
|
|
- { host: database, port: 5432 }
|
|
|
|
roles:
|
|
# Generado desde service.id="api-server"
|
|
- role: api-server
|
|
vars:
|
|
service_port: 3000
|
|
service_image: "{{ docker_registry }}/syntaxis:api-latest"
|
|
service_replicas: 2
|
|
service_env: # Desde service.env
|
|
RUST_LOG: info
|
|
DATABASE_URL: "postgresql://postgres:5432/syntaxis"
|
|
service_resources: # Desde service.resources
|
|
cpu: 100m
|
|
memory: 256Mi
|
|
|
|
# Generado desde service.id="database"
|
|
- role: database
|
|
vars:
|
|
service_port: 5432
|
|
service_image: "{{ docker_registry }}/postgres:15-alpine"
|
|
service_replicas: 1
|
|
postgresql_databases:
|
|
- name: syntaxis
|
|
user: postgres
|
|
|
|
post_tasks:
|
|
- name: Verify all services are running
|
|
uri:
|
|
url: "http://api-server:3000/health"
|
|
method: GET
|
|
status_code: 200
|
|
retries: 3
|
|
delay: 10
|
|
```
|
|
|
|
### 4. Qué Valida Ansible Automáticamente
|
|
|
|
```
|
|
✅ SINTAXIS YAML
|
|
└─ Indentación correcta
|
|
└─ Estructura válida
|
|
|
|
✅ TASKS VÁLIDOS
|
|
└─ Modules existen
|
|
└─ Parámetros correctos
|
|
└─ Handlers referencian nombres válidos
|
|
|
|
✅ ROLES
|
|
└─ Directorio structure correcto
|
|
└─ Tasks, handlers, templates presentes
|
|
|
|
✅ VARIABLES
|
|
└─ Sintaxis de variable correcta {{ var }}
|
|
└─ Tipos de dato correctos
|
|
|
|
✅ INVENTARIOS
|
|
└─ Hosts válidos
|
|
└─ Grupos existentes
|
|
|
|
✅ HANDLERS
|
|
└─ Listeners referencian names válidos
|
|
└─ Sin handlers huérfanos
|
|
|
|
✅ IMPORTS/INCLUDES
|
|
└─ Archivos referencian existen
|
|
└─ Variables presentes
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 Orquestación NuShell
|
|
|
|
### 1. validate-all.nu - Orquestación Completa
|
|
|
|
```nushell
|
|
#!/usr/bin/env nu
|
|
|
|
# validate-all.nu - Validación multi-capa completa
|
|
|
|
const GREEN = "[32m"
|
|
const RED = "[91m"
|
|
const YELLOW = "[33m"
|
|
const BLUE = "[34m"
|
|
const RESET = "[0m"
|
|
|
|
def separator [title: string] {
|
|
print ""
|
|
print $"($BLUE)════════════════════════════════════════($RESET)"
|
|
print $"($BLUE)($title)($RESET)"
|
|
print $"($BLUE)════════════════════════════════════════($RESET)"
|
|
}
|
|
|
|
# CAPA 0: KCL VALIDATION
|
|
separator "CAPA 0: Validación KCL (Definiciones)"
|
|
|
|
let kcl_result = (
|
|
kcl compile "provisioning/services.k" 2>&1
|
|
)
|
|
|
|
if ($kcl_result | str contains "Error:") {
|
|
print $"($RED)❌ KCL inválido($RESET)"
|
|
print $kcl_result
|
|
exit 1
|
|
} else {
|
|
print $"($GREEN)✅ KCL válido($RESET)"
|
|
print $" - Tipos correctos"
|
|
print $" - Sin ciclos"
|
|
print $" - Referencias válidas"
|
|
}
|
|
|
|
print ""
|
|
|
|
# CAPA 1: TERRAFORM VALIDATION
|
|
separator "CAPA 1: Validación Terraform (IaC)"
|
|
|
|
# Generar Terraform desde KCL si no existe
|
|
if not (path exists "generated/terraform/main.tf") {
|
|
print "📝 Generando Terraform desde KCL..."
|
|
# Ejecutar generador
|
|
nu generate-terraform.nu
|
|
}
|
|
|
|
cd "generated/terraform"
|
|
|
|
let tf_validate = (
|
|
terraform validate -json 2>&1 | from json
|
|
)
|
|
|
|
if not $tf_validate.valid {
|
|
print $"($RED)❌ Terraform inválido($RESET)"
|
|
print $tf_validate.diagnostics
|
|
exit 1
|
|
} else {
|
|
print $"($GREEN)✅ Terraform válido($RESET)"
|
|
print $" - Sintaxis HCL correcta"
|
|
print $" - Variables resuelven"
|
|
print $" - Recursos válidos"
|
|
}
|
|
|
|
# Plan
|
|
let plan_result = (
|
|
terraform plan -json 2>&1
|
|
| from json
|
|
)
|
|
|
|
# Analizar cambios
|
|
let changes = (
|
|
$plan_result.resource_changes
|
|
| map { |rc| {type: $rc.type, name: $rc.name, actions: $rc.change.actions} }
|
|
)
|
|
|
|
print $" Plan summary:"
|
|
for change in $changes {
|
|
print $" - ($change.type).($change.name): ($change.actions | str join '→')"
|
|
}
|
|
|
|
cd - # volver al directorio anterior
|
|
print ""
|
|
|
|
# CAPA 2: ANSIBLE VALIDATION
|
|
separator "CAPA 2: Validación Ansible (Configuration)"
|
|
|
|
# Generar Ansible desde KCL si no existe
|
|
if not (path exists "generated/ansible/site.yml") {
|
|
print "📝 Generando Ansible desde KCL..."
|
|
# Ejecutar generador
|
|
nu generate-ansible.nu
|
|
}
|
|
|
|
let ansible_check = (
|
|
ansible-playbook "generated/ansible/site.yml" --syntax-check 2>&1
|
|
)
|
|
|
|
if ($ansible_check | str contains "ERROR") {
|
|
print $"($RED)❌ Ansible inválido($RESET)"
|
|
print $ansible_check
|
|
exit 1
|
|
} else {
|
|
print $"($GREEN)✅ Ansible válido($RESET)"
|
|
print $" - Sintaxis YAML correcta"
|
|
print $" - Roles válidos"
|
|
print $" - Variables resuelven"
|
|
}
|
|
|
|
print ""
|
|
|
|
# RESUMEN FINAL
|
|
separator "📊 RESUMEN DE VALIDACIÓN"
|
|
|
|
print $"($GREEN)TODAS LAS CAPAS VALIDADAS EXITOSAMENTE($RESET)"
|
|
print ""
|
|
print "✅ Capa 0 - KCL (Definiciones)"
|
|
print "✅ Capa 1 - Terraform (Infraestructura)"
|
|
print "✅ Capa 2 - Ansible (Configuración)"
|
|
print ""
|
|
print $"($GREEN)Listo para deployment($RESET)"
|
|
print ""
|
|
```
|
|
|
|
### 2. Flujo en CI/CD
|
|
|
|
```nushell
|
|
# .github/workflows/validate-all.yml equivalent
|
|
|
|
print "🚀 Iniciando validación multi-capas..."
|
|
|
|
# 1. KCL validation
|
|
if (!(kcl compile provisioning/services.k)) {
|
|
print "❌ KCL failed"
|
|
exit 1
|
|
}
|
|
|
|
# 2. Terraform validation
|
|
if (!(terraform validate)) {
|
|
print "❌ Terraform failed"
|
|
exit 1
|
|
}
|
|
|
|
# 3. Ansible validation
|
|
if (!(ansible-playbook site.yml --syntax-check)) {
|
|
print "❌ Ansible failed"
|
|
exit 1
|
|
}
|
|
|
|
# 4. All passed
|
|
print "✅ All layers validated"
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 Flujos de Validación End-to-End
|
|
|
|
### Flujo 1: Cambio en Service Definition
|
|
|
|
```
|
|
Developer modifica services.k:
|
|
├─ Agrega nuevo servicio "monitoring"
|
|
|
|
Git commit + push
|
|
↓
|
|
GitHub CI Pipeline (validate-all.nu):
|
|
├─ CAPA 0: kcl compile
|
|
│ └─ ✅ KCL válido
|
|
│
|
|
├─ Generar Terraform desde KCL (actualizadas)
|
|
│
|
|
├─ CAPA 1: terraform validate
|
|
│ └─ ✅ Nuevo recurso válido
|
|
│ └─ ✅ Sin conflictos
|
|
│
|
|
├─ Generar Ansible desde KCL (actualizadas)
|
|
│
|
|
├─ CAPA 2: ansible-playbook --syntax-check
|
|
│ └─ ✅ Nuevo role incorporado
|
|
│ └─ ✅ Variables resuelven
|
|
│
|
|
└─ ✅ APROBADO para merge
|
|
|
|
Resultado:
|
|
└─ Validación hecha por herramientas nativas
|
|
└─ Sin código personalizado de validación
|
|
└─ Confianza en cada capa
|
|
```
|
|
|
|
### Flujo 2: Validación de Compatibilidad
|
|
|
|
```
|
|
Cambio detectado: Se rompe dependencia
|
|
├─ database → service inexistente
|
|
|
|
Pipeline CI:
|
|
├─ CAPA 0: kcl compile
|
|
│ └─ ❌ ERROR: "service-b depends on non-existent service-c"
|
|
│
|
|
└─ ❌ RECHAZADO inmediatamente
|
|
|
|
Developer notificado:
|
|
└─ Corrije el error
|
|
└─ Reintenta
|
|
```
|
|
|
|
### Flujo 3: Validación de Impacto
|
|
|
|
```
|
|
Cambio: Puerto de database 5432 → 5433
|
|
|
|
Pipeline CI:
|
|
├─ CAPA 0: kcl compile
|
|
│ └─ ✅ KCL válido
|
|
│
|
|
├─ Generar Terraform
|
|
│
|
|
├─ CAPA 1: terraform plan
|
|
│ └─ Detecta: "RDS port change" (breaking)
|
|
│ └─ ⚠️ Manual approval required
|
|
│
|
|
└─ ⚠️ ESPERANDO APROBACIÓN MANUAL
|
|
|
|
DevOps revisa:
|
|
└─ "Esto requiere downtime"
|
|
└─ Aprueba manualmente
|
|
└─ Deployment continúa
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Comparación: Validación Manual vs Multi-Capa
|
|
|
|
| Aspecto | Manual | Multi-Capa (Nativo) |
|
|
|---------|--------|-------------------|
|
|
| **Errores de tipo KCL** | Descubiertos al deploy | Rechazados al commit |
|
|
| **Sintaxis Terraform** | Descubiertos al deploy | Rechazados al commit |
|
|
| **Errores Ansible** | Descubiertos al deploy | Rechazados al commit |
|
|
| **Ciclos de dependencias** | Requiere revisión manual | Detectado automáticamente |
|
|
| **Conflictos de puerto** | Requiere revisión manual | Detectado automáticamente |
|
|
| **Cambios destructivos** | Riesgo en producción | Detectado antes de apply |
|
|
| **Tiempo de descubrimiento** | Horas (en deploy) | Segundos (en commit) |
|
|
| **Costo de error** | Alto (downtime) | Bajo (cambio rechazado) |
|
|
|
|
---
|
|
|
|
## 🎯 Resumen: Cada Herramienta Valida Su Dominio
|
|
|
|
```
|
|
KCL → Valida definiciones (servicios, dependencias)
|
|
↓
|
|
Terraform → Valida infraestructura (HCL, recursos, plan)
|
|
↓
|
|
Ansible → Valida configuración (playbooks, roles)
|
|
↓
|
|
NuShell → Orquesta validación + reporta resultados
|
|
```
|
|
|
|
**Beneficio**:
|
|
- ✅ Cada herramienta hace lo que sabe hacer mejor
|
|
- ✅ Validaciones nativas = confiables
|
|
- ✅ Sin duplicación de lógica
|
|
- ✅ Errores descubiertos temprano (en commit, no en deploy)
|
|
- ✅ Confianza en cada capa del pipeline
|
|
|
|
---
|
|
|
|
## 📋 Checklist de Implementación
|
|
|
|
- [ ] Crear `validate-all.nu` script principal
|
|
- [ ] Crear `kcl-validate.nu` para KCL
|
|
- [ ] Crear `terraform-validate.nu` para Terraform
|
|
- [ ] Crear `ansible-validate.nu` para Ansible
|
|
- [ ] Integrar en `.github/workflows/validate.yml`
|
|
- [ ] Setup pre-commit hooks para validación local
|
|
- [ ] Documentar flujos de validación para DevOps
|
|
- [ ] Crear dashboard que muestre validaciones
|
|
- [ ] Test con cambios válidos e inválidos
|
|
|
|
---
|
|
|
|
**Conclusión**: Las herramientas modernas (KCL, Terraform, Ansible) ya incluyen validación nativa de sus dominios. La clave es orquestarlas inteligentemente con NuShell para crear un pipeline de validación completo end-to-end.
|