--- name: refresh_local_hub kind: pipeline lang: py domain: pipelines purity: impure version: "1.0.0" signature: "def refresh_local_hub(manifest_path: str | None = None, reload: bool = True) -> dict" description: "Orquesta el refresco del sistema local_hub: descubre los servicios locales, regenera el fragmento de Caddyfile y la config de Glance, recarga Caddy (admin API) y reinicia la user-unit glance. Compone discover_local_services + render_caddyfile + render_glance_config. Lo corre el dag_engine a diario y tambien el usuario a mano." tags: [local-hub, pipelines, pipeline, caddy, glance, infra, dashboard] uses_functions: [discover_local_services_py_infra, render_caddyfile_py_infra, render_glance_config_py_infra] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [pyyaml] tested: true tests: - "compone las tres funciones del registry y escribe ambas configs" - "la config completa de Glance generada es YAML parseable" - "con reload=False no se llama a subprocess.run" - "con reload=True se invoca caddy reload y systemctl --user restart glance" - "el dict de retorno tiene todas las claves del contrato" test_file_path: "python/functions/pipelines/refresh_local_hub_test.py" file_path: "python/functions/pipelines/refresh_local_hub.py" params: - name: manifest_path desc: "Ruta al manifiesto YAML del local_hub. Si es None se usa /apps/local_hub/local_services.yaml (RAIZ derivada de FN_REGISTRY_ROOT o del path del modulo)." - name: reload desc: "Si True recarga Caddy (admin API localhost:2019, sin sudo) y reinicia la user-unit glance (sin sudo). Si False solo regenera las configs y no toca servicios." output: "dict {total, up, down, caddy_path, glance_path, reloaded, caddy_reload_rc, glance_restart_rc, services:[{name,subdomain,port,up}, ...]}" --- ## Ejemplo ```bash ./fn run refresh_local_hub ``` Sin recargar servicios (solo regenera las configs): ```bash $HOME/fn_registry/python/.venv/bin/python3 \ python/functions/pipelines/refresh_local_hub.py --no-reload ``` Desde Python: ```python from pipelines.refresh_local_hub import refresh_local_hub r = refresh_local_hub(reload=True) print(r) # {"total": 8, "up": 6, "down": 2, "caddy_path": "/etc/caddy/conf.d/local_hub.caddy", # "glance_path": ".../apps/local_hub/glance/glance.yml", "reloaded": True, # "caddy_reload_rc": 0, "glance_restart_rc": 0, "services": [...]} ``` ## Cuando usarla Cuando cambien los servicios locales expuestos como subdominios `*.localhost` (alta/baja de un servicio en el manifiesto, o un service nuevo del registry con bloque `service:`) y quieras que Caddy y el dashboard Glance reflejen el estado actual. Es el paso `function:` que el dag_engine corre a diario para mantener el `local_hub` sincronizado, y el comando que lanzas a mano tras editar `apps/local_hub/local_services.yaml`. ## Gotchas - **Impura: escribe en `/etc/caddy/conf.d/local_hub.caddy` via ACL**, no via sudo. El usuario debe tener permiso de escritura ahi (ACL ya configurada en este PC). Sin la ACL, el `open(..., "w")` lanza `PermissionError`. - **Recarga Caddy por su admin API** (`caddy reload` habla con `localhost:2019`), no reinicia el servicio: requiere que Caddy este corriendo con la admin API activa. Si Caddy no esta levantado, `caddy_reload_rc` queda en un valor != 0 (o -1 si el binario falla) pero el pipeline NO lanza. - **Reinicia la user-unit `glance`** (`systemctl --user restart glance`), no la system-unit: requiere que la user-unit `glance` este instalada y el bus de usuario disponible. Si falta, `glance_restart_rc` refleja el fallo sin abortar. - **Valida el YAML de Glance antes de escribir**: si `render_glance_config` produjera YAML invalido, el pipeline lanza `RuntimeError` con mensaje claro y no escribe el archivo (fail-fast). - **Raiz dinamica**: la raiz del registry se deriva de `FN_REGISTRY_ROOT` o del path del modulo; nunca se hardcodea ningun `/home/`. - **`reload=False` no toca ningun servicio**: util para previsualizar las configs generadas sin recargar Caddy ni reiniciar Glance (lo que hace el test). ## Capability growth log (sin entradas — v1.0.0 inicial)