Saltar al contenido principal

Clase 04 — Estructuras Anidadas y Patrones Comunes

JSON permite anidar objetos y arrays sin límite (aunque más de 4-5 niveles indica un posible rediseño).

Patrón: Infraestructura

{
"infraestructura": {
"cloud": "AWS",
"region": "us-east-1",
"vpc": {
"id": "vpc-abc123",
"cidr": "10.0.0.0/16",
"subnets": {
"publicas": [
{ "id": "subnet-pub-1", "az": "us-east-1a", "cidr": "10.0.1.0/24",
"recursos": { "instancias": 3, "load_balancers": 1 } },
{ "id": "subnet-pub-2", "az": "us-east-1b", "cidr": "10.0.2.0/24",
"recursos": { "instancias": 2, "load_balancers": 0 } }
],
"privadas": [
{ "id": "subnet-priv-1", "az": "us-east-1a", "cidr": "10.0.10.0/24",
"recursos": { "instancias": 2, "rds": 1 } }
]
}
}
}
}
# Acceso directo a niveles profundos
jq '.infraestructura.vpc.subnets.publicas[0].cidr' infra.json

# Todas las AZs de subnets públicas
jq '[.infraestructura.vpc.subnets.publicas[].az]' infra.json

# Total de instancias en subnets públicas
jq '[.infraestructura.vpc.subnets.publicas[].recursos.instancias] | add' infra.json

# Buscar recursivamente una clave
jq '.. | .cidr? // empty' infra.json

Patrón: Configuración por ambientes

{
"app": { "nombre": "pokemon-api", "version": "2.1.0" },
"ambientes": {
"development": {
"debug": true, "log_level": "debug",
"base_datos": { "host": "localhost", "puerto": 5432, "ssl": false },
"replicas": 1
},
"production": {
"debug": false, "log_level": "warn",
"base_datos": { "host": "prod-db.internal", "puerto": 5432, "ssl": true },
"replicas": 5
}
}
}
# Comparar replicas entre ambientes
jq '.ambientes | to_entries[] | {(.key): .value.replicas}' config.json

# Host de BD por ambiente
jq '.ambientes | to_entries[] | "\(.key): \(.value.base_datos.host)"' config.json

Patrón: Logs estructurados

{
"logs": [
{ "timestamp": "2026-02-20T10:00:00Z", "nivel": "INFO", "servicio": "api",
"mensaje": "Servidor iniciado en puerto 8080", "metadata": { "pid": 12345 } },
{ "timestamp": "2026-02-20T10:05:00Z", "nivel": "ERROR", "servicio": "worker",
"mensaje": "Error al procesar job #42", "metadata": { "error": "TimeoutError", "reintentos": 3 } },
{ "timestamp": "2026-02-20T10:10:00Z", "nivel": "WARN", "servicio": "api",
"mensaje": "Uso de memoria alto: 85%", "metadata": { "memoria_usada_mb": 435 } }
]
}
# Solo errores
jq '[.logs[] | select(.nivel == "ERROR")]' logs.json

# Contar por nivel
jq '.logs | group_by(.nivel) | map({nivel: .[0].nivel, total: length})' logs.json

# Mensajes de un servicio específico
jq '[.logs[] | select(.servicio == "api") | .mensaje]' logs.json

Patrón: Pipeline de CI/CD

{
"pipeline": {
"id": "pipe-789",
"repositorio": "roxsross/pokemon-api",
"estado": "success",
"stages": [
{ "nombre": "lint", "estado": "success", "duracion_s": 15,
"jobs": [{ "nombre": "flake8", "estado": "success" }, { "nombre": "black", "estado": "success" }] },
{ "nombre": "test", "estado": "success", "duracion_s": 120,
"jobs": [{ "nombre": "unit-tests", "estado": "success", "cobertura": 87.5 }] },
{ "nombre": "build", "estado": "success", "duracion_s": 45,
"jobs": [{ "nombre": "docker-build", "estado": "success" }] },
{ "nombre": "deploy", "estado": "success", "duracion_s": 150,
"jobs": [{ "nombre": "deploy-production", "estado": "success" }] }
]
}
}
# Duración total del pipeline
jq '.pipeline.stages | map(.duracion_s) | add' pipeline.json

# Todos los jobs con su estado
jq '.pipeline.stages[].jobs[] | "\(.nombre): \(.estado)"' pipeline.json

# Cobertura promedio de tests
jq '[.pipeline.stages[] | select(.nombre == "test") | .jobs[].cobertura] | add / length' pipeline.json

Patrón: Inventario de contenedores

# Total de contenedores en el cluster
jq '[.nodos[].contenedores | length] | add' cluster.json

# Contenedores por nodo
jq '.nodos[] | {nodo: .nombre, containers: (.contenedores | length)}' cluster.json

# Todas las imágenes únicas
jq '[.nodos[].contenedores[].imagen] | unique' cluster.json