Saltar al contenido
cloud-webv0.1.0
Documentación

Protocolo agente–plano de control

Contrato entre el plano de control (cloud-web) y el agente de nodo (cloud-web-node). Los dos viven en repositorios y se publican por separado, así que este documento es lo que tienen en común.

Versión de protocolo vigente: 1.

Transporte y autenticación

  • HTTPS en el puerto del listener de agentes (por defecto :8443), TLS 1.3.
  • POST /agent/v1/join solo exige TLS de servidor: el agente fija la CA por huella (--ca-sha256).
  • El resto de las rutas exigen certificado de cliente emitido por la CA del clúster para un nodo que siga registrado. Eliminar un nodo revoca su acceso.
  • Las órdenes se entregan por una cola persistente con confirmación (ack); el stream SSE solo avisa de que hay algo nuevo.

Endpoints (protocolo 1)

Método y ruta Uso
POST /agent/v1/join Canjea un token de unión; devuelve certificado de cliente, CA y peers
POST /agent/v1/heartbeat Latido y capacidad actual del nodo
GET /agent/v1/peers Peers de la malla WireGuard
GET /agent/v1/commands Órdenes pendientes (deploy, stop)
POST /agent/v1/commands/{id}/ack Confirma una orden
POST /agent/v1/leave[?force=1] El nodo pide su propia baja (desinstalación); 409 si aún corre apps, salvo con force=1: el nodo, que está vivo y es quien se borra, puede forzarla y sus despliegues pasan a fallidos
POST /agent/v1/report Estado y logs de despliegues
GET /agent/v1/routes Tabla de rutas para el ingress
GET /agent/v1/registry Configuración del registro de imágenes interno
GET /agent/v1/stream SSE de avisos

Los esquemas JSON son los de apps/control-plane/internal/api/agent.go y apps/node-agent/internal/cpclient/cpclient.go; deben coincidir.

Latido y salud (campo opcional)

POST /agent/v1/heartbeat lleva la capacidad del nodo en el nivel superior (cpu_cores, mem_bytes, disk_bytes) y, desde la versión de agente 0.2, un campo opcional health:

{"cpu_cores": 8, "mem_bytes": 0, "disk_bytes": 0,
 "health": {"registry": {"configured": true, "reachable": false, "error": "i/o timeout"},
            "mesh": {"peers": 1, "connected": 0}}}
  • registry.reachable es si el nodo alcanza el registro de imágenes del clúster; solo se informa false tras dos chequeos seguidos fallidos. registry.configured=false significa que no hay registro que evaluar.
  • mesh.connected son los peers con un handshake de los últimos 3 minutos.
  • Es aditivo: no sube el entero de protocolo. Un servidor anterior ignora el campo y un agente anterior no lo envía; en ese caso la salud del nodo es desconocida y el nodo sigue siendo elegible.
  • El plano de control trata una salud de más de 60 s como desconocida y marca un nodo degradado solo si el registro está configurado y el nodo no lo alcanza (la malla se muestra pero no decide). Un nodo degradado no recibe instancias nuevas y se recupera solo cuando vuelve a alcanzar el registro.

Capacidades del agente (health.features)

Desde la versión de agente 0.3 el mismo campo health puede llevar features, la lista de capacidades opcionales de ese agente:

{"health": {"registry": {...}, "mesh": {...}, "features": ["static-sites"]}}
  • static-sites: sabe construir y servir un sitio estático a partir de los ajustes de build de la orden de despliegue.
  • Es aditivo, igual que health: no sube el entero de protocolo. Un agente anterior no lo envía y un plano de control anterior lo ignora.
  • Como la salud caduca a los 60 s, una capacidad sin salud vigente no se supone.
  • El plano de control solo exige una capacidad cuando la app lo necesita (ver abajo); para todo lo demás un agente sin features sigue siendo un nodo elegible.

Docker Desktop

Un nodo cuyo motor de Docker es Docker Desktop informa registry.configured=true, reachable=false con el motivo Docker Desktop is not supported on a node…: el chequeo del registro lo hace el agente desde su red, pero quien sube las imágenes es el daemon, que en Docker Desktop corre en otra VM y no ve la malla. El nodo queda degradado y no recibe despliegues.

Orden de despliegue y sitios estáticos (build)

La orden deploy puede llevar un bloque opcional build con los ajustes de build que el usuario fijó para un sitio estático:

{"deployment_id": "dep_…", "source": {…}, "env": {…}, "port": 0,
 "build": {"install_cmd": "npm ci", "build_cmd": "npm run build:prod", "output_dir": "dist", "spa": "yes"}}
  • Todos los campos son opcionales; spa es auto, yes o no. Sin el bloque, el agente detecta todo.
  • Solo se envía en despliegues desde el código (no en un rollback ni en un reprogramado, que reutilizan una imagen ya construida).
  • El plano de control valida los valores y el agente vuelve a validarlos (largo, sin saltos de línea, directorio relativo y dentro del proyecto): no confía en el servidor.
  • Un agente anterior ignora el bloque. Para que no construya por buildpacks algo que no puede construirse, el planificador solo envía una app con ajustes explícitos a nodos que informan static-sites; si no hay ninguno, el despliegue falla diciendo qué nodos hay que actualizar. Una app sin ajustes se envía a cualquier nodo y el agente decide la estrategia (Dockerfile, sitio estático o buildpacks).
  • Solo las variables de la app con prefijo público (VITE_, REACT_APP_, NEXT_PUBLIC_, PUBLIC_, GATSBY_) entran al build de un sitio estático; el resto no.

Versionado

Cada petición del agente lleva dos cabeceras:

Cabecera Valor
X-Cloudweb-Agent-Version versión del binario (p. ej. 0.1.0)
X-Cloudweb-Protocol entero de protocolo que habla el agente
  • El entero de protocolo solo sube ante cambios incompatibles (quitar o cambiar el significado de un campo, de una ruta o de un flujo). Agregar campos opcionales o rutas nuevas no lo sube.
  • Un agente que no envía la cabecera es anterior al versionado y se trata como protocolo 1.
  • El plano de control declara el rango que entiende (MinAgentProtocol–MaxAgentProtocol).

Incompatibilidad

Si el protocolo del agente queda fuera del rango, el plano de control responde antes de ejecutar cualquier acción (no cambia estado, no consume el token de unión):

HTTP/1.1 426 Upgrade Required
{
  "error": "protocol 2 is not supported: this control plane supports protocols 1 to 1; upgrade the control-plane",
  "agent_version": "0.3.0",
  "agent_protocol": 2,
  "supported_min": 1,
  "supported_max": 1,
  "upgrade": "control-plane"
}

upgrade indica qué lado debe actualizarse (agent si el agente es más viejo que el mínimo, control-plane si es más nuevo que el máximo). Una cabecera mal formada da 400.

El agente, al recibir 426, lo registra como error con el rango soportado y baja el ritmo de reintento a uno cada 5 minutos; vuelve al ritmo normal en cuanto el plano de control lo acepta.

Cómo evolucionar el protocolo

  1. Cambios compatibles: se hacen en cualquier lado, sin subir el entero.
  2. Cambios incompatibles: subir MaxAgentProtocol del servidor para que acepte ambos protocolos durante una ventana de transición, publicar el servidor, y recién después el agente que habla el nuevo. Retirar el protocolo viejo (MinAgentProtocol) en una versión posterior, avisándolo en las notas.
  3. Cada repositorio prueba su pipeline contra la última versión publicada del otro (ver tests/e2e/ y el pipeline de cloud-web-node).