fix(supervise): apply egress approvals
This commit is contained in:
@@ -1,17 +1,20 @@
|
||||
"""Host-side helper for egress sidecar inspection (issue #198).
|
||||
"""Host-side helper for egress sidecar inspection and live updates.
|
||||
|
||||
`_merge_single_route`, `add_route`, and `apply_routes_change` were
|
||||
removed when the egress-block MCP tool was dropped. The remaining
|
||||
helpers support runtime inspection and validation of the routes file
|
||||
without modifying it at runtime.
|
||||
The approve path uses this module to validate a proposed routes file,
|
||||
write it to the bottle's live egress state dir, and signal the sidecar
|
||||
bundle so the mitmproxy addon reloads it.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from ...bottle_state import egress_state_dir, read_metadata
|
||||
from ...egress import EGRESS_ROUTES_IN_CONTAINER
|
||||
from ...egress_addon_core import load_routes
|
||||
from ...log import warn
|
||||
from .sidecar_bundle import sidecar_bundle_container_name
|
||||
|
||||
|
||||
@@ -29,10 +32,22 @@ def fetch_current_routes(slug: str) -> str:
|
||||
raise EgressApplyError(
|
||||
f"could not read routes.yaml from {container}: "
|
||||
f"{(r.stderr or '').strip() or 'container not running?'}"
|
||||
)
|
||||
)
|
||||
return r.stdout
|
||||
|
||||
|
||||
def apply_routes_change(slug: str, content: str) -> tuple[str, str]:
|
||||
"""Persist `content` to the live routes file and reload egress."""
|
||||
validate_routes_content(content)
|
||||
routes_path = _routes_path(slug)
|
||||
routes_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
before = routes_path.read_text(encoding="utf-8") if routes_path.exists() else ""
|
||||
routes_path.write_text(content, encoding="utf-8")
|
||||
routes_path.chmod(0o600)
|
||||
_signal_bundle_reload(slug)
|
||||
return before, content
|
||||
|
||||
|
||||
def validate_routes_content(content: str) -> None:
|
||||
try:
|
||||
load_routes(content)
|
||||
@@ -42,8 +57,57 @@ def validate_routes_content(content: str) -> None:
|
||||
) from e
|
||||
|
||||
|
||||
def _routes_path(slug: str) -> Path:
|
||||
return egress_state_dir(slug) / "egress_routes.yaml"
|
||||
|
||||
|
||||
def _signal_bundle_reload(slug: str) -> None:
|
||||
container = sidecar_bundle_container_name(slug)
|
||||
backend = ""
|
||||
metadata = read_metadata(slug)
|
||||
if metadata is not None:
|
||||
backend = metadata.backend
|
||||
|
||||
candidates: list[list[str]]
|
||||
if backend == "macos-container":
|
||||
candidates = [["container", "kill", "--signal", "HUP", container]]
|
||||
elif backend:
|
||||
candidates = [["docker", "kill", "--signal", "HUP", container]]
|
||||
else:
|
||||
candidates = [
|
||||
["docker", "kill", "--signal", "HUP", container],
|
||||
["container", "kill", "--signal", "HUP", container],
|
||||
]
|
||||
|
||||
last_error = ""
|
||||
for argv in candidates:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
argv,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
env=os.environ,
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
last_error = str(e)
|
||||
continue
|
||||
if result.returncode == 0:
|
||||
return
|
||||
last_error = (result.stderr or "").strip() or (result.stdout or "").strip()
|
||||
warn(
|
||||
f"egress: routes updated on disk for {slug}, but bundle reload failed: "
|
||||
f"{last_error or 'no reload command succeeded'}"
|
||||
)
|
||||
raise EgressApplyError(
|
||||
f"could not reload egress bundle {container}: "
|
||||
f"{last_error or 'no reload command succeeded'}"
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"EgressApplyError",
|
||||
"apply_routes_change",
|
||||
"fetch_current_routes",
|
||||
"validate_routes_content",
|
||||
]
|
||||
|
||||
@@ -12,7 +12,6 @@ from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from contextlib import ExitStack, contextmanager
|
||||
from pathlib import Path
|
||||
@@ -364,8 +363,8 @@ def _sidecar_mounts(
|
||||
))
|
||||
if ep.routes:
|
||||
mounts.append((
|
||||
str(_stage_routes_dir(plan)),
|
||||
str(Path(EGRESS_ROUTES_IN_CONTAINER).parent),
|
||||
str(ep.routes_path),
|
||||
EGRESS_ROUTES_IN_CONTAINER,
|
||||
True,
|
||||
))
|
||||
|
||||
@@ -375,17 +374,6 @@ def _sidecar_mounts(
|
||||
|
||||
return tuple(mounts)
|
||||
|
||||
|
||||
def _stage_routes_dir(plan: MacosContainerBottlePlan) -> Path:
|
||||
routes_dir = plan.stage_dir / "macos-container-egress"
|
||||
routes_dir.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(
|
||||
plan.egress_plan.routes_path,
|
||||
routes_dir / Path(EGRESS_ROUTES_IN_CONTAINER).name,
|
||||
)
|
||||
return routes_dir
|
||||
|
||||
|
||||
def _mount_spec(host_path: str, container_path: str, read_only: bool) -> str:
|
||||
spec = f"type=bind,source={host_path},target={container_path}"
|
||||
if read_only:
|
||||
|
||||
Reference in New Issue
Block a user