# 🔗 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.
