fix(gateway): persist git-gate state across gateway restarts
Per-bottle git-gate state (bare repos under /git/<id>, deploy creds under /git-gate/creds/<id>) was provisioned once at bottle launch and lived only in the gateway's ephemeral storage. A gateway rebuild/restart wiped it and nothing re-provisioned already-running bottles, so their agents 404'd on fetch/push. Same class of bug as the CA (#510); the orchestrator restores only egress tokens, not git-gate declarations. Persist the state on both backends, mirroring the CA-persistence approach: - firecracker: attach a second persistent data drive (/dev/vdc) to the gateway VM and bind-mount its git/ + creds/ subdirs onto /git and /git-gate/creds in the gateway guest init, before the data plane starts. Generalize the VM config to a stable-ordered data_drives tuple (CA=vdb, git=vdc; orchestrator registry stays vdb). - docker: bind-mount host dirs (host_gateway_git_dir / creds_dir, under the never-pruned app-data root) onto /git and /git-gate/creds, with BOT_BOTTLE_DOCKER_GIT_MOUNT / _CREDS_MOUNT env overrides so CI isolates them to per-run volumes it cleans up. Teardown already rm -rf's /git/<id> + creds, so the persistent store self-cleans over the normal lifecycle. Closes #512 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -61,6 +61,16 @@ _GATEWAY_CA_PATH = "/home/mitmproxy/.mitmproxy/mitmproxy-ca-cert.pem"
|
||||
# which outlives the ephemeral rootfs. mitmproxy is tiny; 16M is ample.
|
||||
_CA_VOLUME_SIZE = "16M"
|
||||
|
||||
# The gateway VM's persistent git-gate volume — a (sparse) ext4 file the gateway
|
||||
# init mounts, then bind-mounts onto /git + /git-gate/creds (see
|
||||
# `infra_vm._GATEWAY_GIT_MOUNT`), so per-bottle bare repos and deploy creds
|
||||
# SURVIVE a gateway-VM rebuild. Without it a restart drops every already-running
|
||||
# bottle's git-gate state and its agent 404s on fetch/push (same class as the CA
|
||||
# — issue #512). Bare repos hold upstream history, so this is sized far larger
|
||||
# than the CA volume; mke2fs leaves the file sparse, so the host only stores
|
||||
# blocks actually used.
|
||||
_GIT_VOLUME_SIZE = "8G"
|
||||
|
||||
_CA_FETCH_TIMEOUT_SECONDS = 15.0
|
||||
|
||||
|
||||
@@ -106,15 +116,18 @@ class FirecrackerGateway(Gateway):
|
||||
f"cannot resolve orchestrator guest IP from {self._orchestrator_url!r}"
|
||||
)
|
||||
# Boot on the gateway link from the gateway rootfs, then push the token
|
||||
# the init waits for before starting the data plane. The persistent CA
|
||||
# volume (/dev/vdb, mounted at mitmproxy's confdir by the gateway init)
|
||||
# keeps the CA STABLE across rebuilds so already-running bottles keep
|
||||
# trusting the gateway's TLS interception (issue #450).
|
||||
# the init waits for before starting the data plane. Two persistent
|
||||
# volumes ride along, attached in a FIXED order the gateway init depends
|
||||
# on: the CA volume as /dev/vdb (mounted at mitmproxy's confdir) and the
|
||||
# git-gate volume as /dev/vdc (bind-mounted onto /git + /git-gate/creds).
|
||||
# Both keep gateway-side state STABLE across rebuilds so already-running
|
||||
# bottles keep working — TLS interception (issue #450) and git-gate fetch
|
||||
# /push (issue #512) respectively.
|
||||
vm = infra_vm.boot_vm(
|
||||
name=GATEWAY_NAME, slot=netpool.gw_slot(), run_dir=infra_vm._gw_dir(),
|
||||
role="gateway", mem_mib=_GW_MEM_MIB,
|
||||
extra_boot_args=f"bb_orch={orchestrator_guest_ip}",
|
||||
data_drive=self._ensure_ca_volume(),
|
||||
data_drives=(self._ensure_ca_volume(), self._ensure_git_volume()),
|
||||
)
|
||||
infra_vm.push_secret(
|
||||
vm, self._gateway_token, _GUEST_GATEWAY_JWT_PATH,
|
||||
@@ -152,6 +165,26 @@ class FirecrackerGateway(Gateway):
|
||||
die(f"creating gateway CA volume failed: {proc.stderr.strip()}")
|
||||
return vol
|
||||
|
||||
def _ensure_git_volume(self) -> Path:
|
||||
"""Create the empty ext4 git-gate volume on first use; reuse it after.
|
||||
|
||||
Empty on first boot (the gateway init lays out `git/` + `creds/` subdirs
|
||||
and bind-mounts them); every later boot reuses whatever repos + creds the
|
||||
volume already holds — which is what keeps git-gate state stable across a
|
||||
gateway-VM rebuild (issue #512). Sibling of `_ensure_ca_volume`."""
|
||||
vol = infra_vm._gw_dir() / "gateway-git.ext4"
|
||||
if vol.exists():
|
||||
return vol
|
||||
info(f"creating gateway git-gate volume {vol} ({_GIT_VOLUME_SIZE})")
|
||||
proc = subprocess.run(
|
||||
["mke2fs", "-q", "-t", "ext4", "-F", str(vol), _GIT_VOLUME_SIZE],
|
||||
capture_output=True, text=True, check=False,
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
vol.unlink(missing_ok=True)
|
||||
die(f"creating gateway git-gate volume failed: {proc.stderr.strip()}")
|
||||
return vol
|
||||
|
||||
def address(self) -> str:
|
||||
"""The gateway VM's guest IP — the agent-facing target agent VMs'
|
||||
gateway-port traffic is DNAT'd to."""
|
||||
|
||||
Reference in New Issue
Block a user