generated from coulomb/repo-seed
Fixing bridge to haskelseed
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""End-to-end tunnel diagnostics for OpsBridge."""
|
||||
from __future__ import annotations
|
||||
|
||||
import socket
|
||||
import subprocess
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
@@ -13,6 +14,38 @@ from bridge.models import BridgeState, TunnelConfig
|
||||
from bridge.state import StateManager, _pid_alive
|
||||
|
||||
|
||||
def _remote_port_probe_command(remote_port: int) -> str:
|
||||
"""Build a portable remote shell probe for a listening TCP port."""
|
||||
return (
|
||||
f"port={remote_port}; "
|
||||
"if command -v ss >/dev/null 2>&1; then "
|
||||
"ss -tnlp 2>/dev/null | grep -q \":$port \" && echo ok || echo closed; "
|
||||
"elif command -v netstat >/dev/null 2>&1; then "
|
||||
"netstat -tnlp 2>/dev/null | "
|
||||
"grep -q \"[.:]$port[[:space:]]\" && echo ok || echo closed; "
|
||||
"else "
|
||||
"hex=$(printf '%04X' \"$port\"); "
|
||||
"awk -v p=\":$hex\" "
|
||||
"'NR > 1 && $4 == \"0A\" && index($2, p) { found = 1 } "
|
||||
"END { print found ? \"ok\" : \"closed\" }' "
|
||||
"/proc/net/tcp /proc/net/tcp6 2>/dev/null; "
|
||||
"fi"
|
||||
)
|
||||
|
||||
|
||||
def _probe_local_port(local_port: int) -> str:
|
||||
"""Check whether the local side of an SSH -L tunnel is accepting TCP."""
|
||||
try:
|
||||
with socket.create_connection(("127.0.0.1", local_port), timeout=5):
|
||||
return "listening"
|
||||
except ConnectionRefusedError:
|
||||
return "closed"
|
||||
except socket.timeout:
|
||||
return "error:timeout"
|
||||
except OSError as e:
|
||||
return f"error:{e}"
|
||||
|
||||
|
||||
@dataclass
|
||||
class TunnelCheckResult:
|
||||
tunnel: str
|
||||
@@ -52,35 +85,38 @@ def check_tunnel(cfg: TunnelConfig, state_mgr: StateManager) -> TunnelCheckResul
|
||||
and ssh_process != "ok"
|
||||
)
|
||||
|
||||
# 3. SSH probe for remote port
|
||||
key_path = str(Path(cfg.ssh_key).expanduser())
|
||||
cmd = [
|
||||
"ssh",
|
||||
"-i", key_path,
|
||||
"-o", "BatchMode=yes",
|
||||
"-o", "ConnectTimeout=5",
|
||||
"-o", "StrictHostKeyChecking=accept-new",
|
||||
f"{cfg.ssh_user}@{cfg.host}",
|
||||
f"ss -tnlp 2>/dev/null | grep -q ':{cfg.remote_port} ' && echo ok || echo closed",
|
||||
]
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
output = proc.stdout.strip()
|
||||
if output == "ok":
|
||||
remote_port = "listening"
|
||||
elif output == "closed":
|
||||
remote_port = "closed"
|
||||
else:
|
||||
remote_port = f"error:{proc.stderr.strip() or 'unknown'}"
|
||||
except subprocess.TimeoutExpired:
|
||||
remote_port = "error:timeout"
|
||||
except Exception as e:
|
||||
remote_port = f"error:{e}"
|
||||
# 3. Port probe: reverse tunnels listen remotely; local tunnels listen here.
|
||||
if cfg.direction == "local":
|
||||
remote_port = _probe_local_port(cfg.local_port)
|
||||
else:
|
||||
key_path = str(Path(cfg.ssh_key).expanduser())
|
||||
cmd = [
|
||||
"ssh",
|
||||
"-i", key_path,
|
||||
"-o", "BatchMode=yes",
|
||||
"-o", "ConnectTimeout=5",
|
||||
"-o", "StrictHostKeyChecking=accept-new",
|
||||
f"{cfg.ssh_user}@{cfg.host}",
|
||||
_remote_port_probe_command(cfg.remote_port),
|
||||
]
|
||||
try:
|
||||
proc = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
output = proc.stdout.strip()
|
||||
if output == "ok":
|
||||
remote_port = "listening"
|
||||
elif output == "closed":
|
||||
remote_port = "closed"
|
||||
else:
|
||||
remote_port = f"error:{proc.stderr.strip() or 'unknown'}"
|
||||
except subprocess.TimeoutExpired:
|
||||
remote_port = "error:timeout"
|
||||
except Exception as e:
|
||||
remote_port = f"error:{e}"
|
||||
|
||||
# 4. Local API health check (optional)
|
||||
local_api: Optional[str] = None
|
||||
|
||||
Reference in New Issue
Block a user