feat(restart): route reverse tunnels through stale-forward cleanup
bridge restart now means blank-slate recovery: reverse tunnels run should_cleanup_tunnel and clear orphan remote listeners before reconnecting; healthy forwards are left running. Local-direction tunnels keep stop/start only. CLI and MCP report per-tunnel actions (healthy, cleaned_and_restarted, restarted, error) and exit non-zero on cleanup failure. Closes BRIDGE-WP-0005.
This commit is contained in:
parent
8c11acc00c
commit
10c6fdaec9
8 changed files with 220 additions and 60 deletions
|
|
@ -13,10 +13,13 @@ import typer
|
|||
|
||||
from bridge.audit import AuditLogger
|
||||
from bridge.cleanup import (
|
||||
CleanupAction,
|
||||
build_cron_line,
|
||||
cleanup_all_tunnels,
|
||||
install_cleanup_cron,
|
||||
read_installed_cron,
|
||||
restart_all_tunnels,
|
||||
restart_tunnel,
|
||||
uninstall_cleanup_cron,
|
||||
)
|
||||
from bridge.config import ConfigError, load_config
|
||||
|
|
@ -153,27 +156,37 @@ def down(
|
|||
raise typer.Exit(2)
|
||||
|
||||
|
||||
def _emit_restart_actions(actions: list[CleanupAction]) -> None:
|
||||
any_error = False
|
||||
for action in actions:
|
||||
typer.echo(f"{action.tunnel}: {action.action} — {action.detail}")
|
||||
if action.action == "error":
|
||||
any_error = True
|
||||
if any_error:
|
||||
raise typer.Exit(1)
|
||||
|
||||
|
||||
@app.command()
|
||||
def restart(
|
||||
tunnel: Optional[str] = typer.Argument(None, help="Tunnel name (omit for all inline)"),
|
||||
):
|
||||
"""Restart one or all tunnels."""
|
||||
"""Restart one or all tunnels.
|
||||
|
||||
Reverse tunnels run conditional remote stale-forward cleanup before
|
||||
reconnecting; healthy forwards are left running. Local-direction tunnels
|
||||
use local stop/start only.
|
||||
"""
|
||||
cfg = _load_or_exit()
|
||||
sd = _state_dir()
|
||||
state_mgr = StateManager(state_dir=sd)
|
||||
|
||||
if tunnel:
|
||||
tcfg = _resolve_tunnel(cfg, tunnel)
|
||||
mgr = TunnelManager(tcfg, state_dir=sd)
|
||||
mgr.stop()
|
||||
mgr.start()
|
||||
typer.echo(f"Restarted tunnel '{tunnel}'.")
|
||||
actions = [restart_tunnel(tcfg, state_mgr)]
|
||||
else:
|
||||
for name in _all_tunnel_names(cfg):
|
||||
tcfg = cfg.tunnels[name]
|
||||
mgr = TunnelManager(tcfg, state_dir=sd)
|
||||
mgr.stop()
|
||||
mgr.start()
|
||||
typer.echo(f"Restarted tunnel '{name}'.")
|
||||
actions = restart_all_tunnels(cfg, state_mgr)
|
||||
|
||||
_emit_restart_actions(actions)
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue