feat(ssh-gate): wire gate into DockerBottlePlan, prepare, launch

PRD 0007: thread the DockerSSHGate through the bottle lifecycle.

- DockerBottlePlan gains gate_plan: SSHGatePlan.
- prepare.resolve_plan accepts a gate and renders its entrypoint
  script next to the pipelock yaml.
- launch.launch starts the gate sidecar after pipelock (so it's on
  the same internal + egress networks) and registers its stop in
  the ExitStack. Skipped when the bottle has no ssh entries.
- DockerBottleBackend instantiates DockerSSHGate alongside the
  pipelock proxy.
- bottle_plan.print + to_dict surface the upstream table so
  --dry-run shows the per-host listen-port mapping.

ssh_config provisioning still points at pipelock; that swap lands
in the next commit so this one stays a pure wiring change.
This commit is contained in:
2026-05-12 16:03:55 -04:00
parent c05d1ddcdb
commit 2533f8a00b
5 changed files with 46 additions and 2 deletions
+1
View File
@@ -80,6 +80,7 @@ class TestDryRunPlan(unittest.TestCase):
"runsc isn't available on the CI runner")
self.assertEqual([], plan["skills"])
self.assertEqual([], plan["ssh_hosts"])
self.assertEqual([], plan["ssh_gate"])
self.assertEqual(False, plan["remote_control"])
self.assertEqual(0, plan["prompt"]["length"])