gateway: converge, don't start/stop/reload
The gateway is a deployment (castle-gateway) — its lifecycle is the same convergence as everything else, so the bespoke start/stop/reload verbs (which still routed through the retired enable/disable path) are gone. - CLI: `castle gateway` is now inspection only — bare or `status` shows the route table. Start/stop/reload it via `castle apply` (render routes + reload) or `castle restart castle-gateway`. Deletes the last users of `_service_enable`/`_service_disable`, so those are removed too. - API: retire `POST /gateway/reload` (making routes live is `POST /apply`); `PUT /gateway/config` message + docstring now say apply, not deploy. - UI: GatewayPanel's "Reload" button → "Apply" (useApply); drop the useGatewayReload hook; GatewaySettings points at `castle apply`. - docs: `castle gateway reload|status` → `castle gateway` (status lens). core 129 / cli 31 / api 59 pass; dashboard builds clean; live `castle gateway` shows routes with no start/stop/reload; /gateway/reload removed from the API.
This commit is contained in:
@@ -2,13 +2,11 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter, HTTPException, status
|
||||
|
||||
from castle_core.config import SPECS_DIR
|
||||
from castle_core.generators.caddyfile import generate_caddyfile_from_registry
|
||||
from castle_core.manifest import (
|
||||
ProgramSpec,
|
||||
@@ -937,7 +935,7 @@ def get_gateway() -> GatewayInfo:
|
||||
def save_gateway_config(request: GatewayConfigRequest) -> dict[str, str]:
|
||||
"""Update the gateway's routing/exposure settings in castle.yaml.
|
||||
|
||||
Saves only; the caller runs deploy to regenerate the Caddyfile + tunnel config.
|
||||
Saves only; the caller runs apply to regenerate the Caddyfile + tunnel config.
|
||||
An empty string clears a field.
|
||||
"""
|
||||
root = get_castle_root()
|
||||
@@ -952,7 +950,7 @@ def save_gateway_config(request: GatewayConfigRequest) -> dict[str, str]:
|
||||
config.gateway.public_domain = norm(request.public_domain)
|
||||
config.gateway.tunnel_id = norm(request.tunnel_id)
|
||||
save_config(config)
|
||||
return {"status": "saved", "message": "Saved. Run deploy to apply."}
|
||||
return {"status": "saved", "message": "Saved. Run apply to converge."}
|
||||
|
||||
|
||||
@router.get("/gateway/caddyfile")
|
||||
@@ -962,35 +960,5 @@ def get_caddyfile() -> dict[str, str]:
|
||||
return {"content": generate_caddyfile_from_registry(registry)}
|
||||
|
||||
|
||||
@router.post("/gateway/reload")
|
||||
async def reload_gateway() -> dict[str, str]:
|
||||
"""Regenerate Caddyfile and reload Caddy."""
|
||||
registry = get_registry()
|
||||
SPECS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
caddyfile_path = SPECS_DIR / "Caddyfile"
|
||||
|
||||
# Include remote registries for cross-node routing
|
||||
remote_regs = {h: n.registry for h, n in mesh_state.all_nodes().items()}
|
||||
caddyfile_path.write_text(
|
||||
generate_caddyfile_from_registry(
|
||||
registry, remote_registries=remote_regs or None
|
||||
)
|
||||
)
|
||||
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
"systemctl",
|
||||
"--user",
|
||||
"reload",
|
||||
"castle-castle-gateway.service",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
_, stderr = await proc.communicate()
|
||||
|
||||
if proc.returncode != 0:
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Reload failed: {(stderr or b'').decode().strip()}",
|
||||
)
|
||||
|
||||
return {"status": "ok"}
|
||||
# Note: gateway reload is not a standalone endpoint. Making routes/config live is
|
||||
# convergence — POST /apply renders the Caddyfile (+ tunnel) and reloads Caddy.
|
||||
|
||||
Reference in New Issue
Block a user