feat(infra): auto-commit con 56 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
---
|
||||
name: discover_local_services
|
||||
kind: function
|
||||
lang: py
|
||||
domain: infra
|
||||
version: "1.1.0"
|
||||
purity: impure
|
||||
signature: "discover_local_services(manifest_path: str, include_registry: bool = True) -> list[dict]"
|
||||
description: "Descubre los servicios locales del sistema local_hub expuestos como subdominios *.localhost. Lee el manifiesto YAML, normaliza la metadata de cada servicio, opcionalmente añade los servicios del registry con puerto via fn doctor, y comprueba up/down por chequeo de puerto TCP en 127.0.0.1. Robusta: no lanza por servicio caido (up=False) ni por fallo de fn doctor."
|
||||
tags: [local-hub, infra, services, discovery, caddy, glance, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [json, os, socket, subprocess, sys, yaml]
|
||||
params:
|
||||
- name: manifest_path
|
||||
desc: "ruta al manifiesto YAML de servicios (apps/local_hub/local_services.yaml) con claves dashboard_subdomain, glance_port y services[]"
|
||||
- name: include_registry
|
||||
desc: "si True, añade los servicios del registry con port>0 que no esten ya en el manifiesto (dedup por port y por subdomain), obtenidos de fn doctor services-spec --json"
|
||||
output: "lista de dicts normalizados, cada uno con las claves name, subdomain, port, health_path, title, icon, category, rewrite_host (bool, passthrough del manifiesto; False para servicios del registry; lo consume render_caddyfile para reescribir el header Host), up (bool de estado vivo por puerto TCP)"
|
||||
tested: true
|
||||
tests:
|
||||
- "test_golden_service_up_with_all_keys"
|
||||
- "test_edge_closed_port_is_down"
|
||||
- "test_defaults_derived_for_missing_fields"
|
||||
- "test_empty_manifest_returns_empty_list"
|
||||
- "test_rewrite_host_passthrough_desde_manifiesto"
|
||||
test_file_path: "python/functions/infra/discover_local_services_test.py"
|
||||
file_path: "python/functions/infra/discover_local_services.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from discover_local_services import discover_local_services
|
||||
|
||||
# Solo manifiesto (sin tocar el registry):
|
||||
servicios = discover_local_services(
|
||||
"apps/local_hub/local_services.yaml",
|
||||
include_registry=False,
|
||||
)
|
||||
for s in servicios:
|
||||
estado = "UP" if s["up"] else "DOWN"
|
||||
print(f'{s["title"]:<16} {s["subdomain"]}.localhost -> :{s["port"]} [{estado}]')
|
||||
|
||||
# Manifiesto + servicios del registry con puerto:
|
||||
todos = discover_local_services("apps/local_hub/local_services.yaml")
|
||||
print(len([s for s in todos if s["up"]]), "servicios vivos")
|
||||
```
|
||||
|
||||
Como script (imprime JSON a stdout):
|
||||
|
||||
```bash
|
||||
python/.venv/bin/python3 python/functions/infra/discover_local_services.py apps/local_hub/local_services.yaml
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Úsala como fase de descubrimiento del sistema `local_hub` antes de renderizar el
|
||||
Caddyfile o la config de Glance: cuando necesites la lista normalizada de servicios
|
||||
locales (`*.localhost`) con su estado up/down resuelto. También cuando quieras un
|
||||
inventario unificado de servicios manuales (contenedores, daemons de terceros) más
|
||||
los servicios del registry con puerto, deduplicados.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- `up` se decide por **conexión TCP** a `127.0.0.1:<port>` con timeout 0.5s, NO por
|
||||
GET HTTP. Un servicio puede aceptar la conexión y devolver 404/500 en `/` y aun
|
||||
así marcar `up=True`. Es intencional: solo valida que el puerto esté escuchando.
|
||||
- Solo comprueba `127.0.0.1` (loopback). Servicios que bindean únicamente a otra
|
||||
interfaz se reportan como `down`.
|
||||
- `include_registry=True` ejecuta `fn doctor services-spec --json` (fallback a
|
||||
`services --json`) como subproceso desde la raíz del repo. Si `fn` no está, falla,
|
||||
tarda más de 20s o devuelve JSON inválido, la función **no lanza**: sigue solo con
|
||||
el manifiesto. Por eso el resultado puede variar según el entorno.
|
||||
- La raíz del repo se resuelve por `FN_REGISTRY_ROOT` o subiendo directorios hasta
|
||||
encontrar `registry.db`. Si no la encuentra, usa el cwd.
|
||||
- El dedup del registry es por `port` Y por `subdomain`: un servicio del registry
|
||||
cuyo puerto o subdominio derivado ya esté en el manifiesto se omite.
|
||||
- El subdominio de un servicio del registry se deriva por una tabla de alias
|
||||
(`dag_engine`->`dag`, `registry_api`->`registry`, `sqlite_api`->`sqlite`,
|
||||
`osint_db`->`osint`, ...) y, para el resto, el primer token antes de `_`.
|
||||
- Lanza `RuntimeError` solo si el manifiesto no se puede leer o parsear (path
|
||||
inexistente, YAML inválido). Eso sí es un error duro.
|
||||
- La clave `rewrite_host` es passthrough del manifiesto (default `False`); para
|
||||
los servicios añadidos desde el registry siempre es `False`. La consume
|
||||
`render_caddyfile` para emitir `header_up Host` en el bloque del servicio.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v1.1.0 (2026-06-20) — añade clave rewrite_host (passthrough del manifiesto) para que render_caddyfile reescriba el Host
|
||||
Reference in New Issue
Block a user