refactor(gateway): each backend's transport in its own gateway_transport.py
Give every backend a `gateway_transport.py` holding just its `GatewayTransport` impl, named consistently with the backend: `DockerGatewayTransport`, `MacosGatewayTransport` (was `AppleGatewayTransport`), and `FirecrackerGatewayTransport` (was `SshGatewayTransport`). - docker: split `DockerGatewayTransport` out of `gateway_provision.py`, which keeps only the backend-neutral provisioning logic (provision_git_gate / deprovision_git_gate) the other backends share. - macOS: `gateway_provision.py` held only the transport, so it's replaced by `gateway_transport.py`. - firecracker: move the transport out of `gateway.py`. Behaviour-preserving; importers + tests updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -26,7 +26,7 @@ from ...orchestrator.store.secret_store import ENV_VAR_SECRET_NAME
|
|||||||
from ...orchestrator.reprovision import reprovision_bottles
|
from ...orchestrator.reprovision import reprovision_bottles
|
||||||
from ..consolidated_util import provision_bottle
|
from ..consolidated_util import provision_bottle
|
||||||
from ..consolidated_util import teardown_consolidated as _teardown_util
|
from ..consolidated_util import teardown_consolidated as _teardown_util
|
||||||
from .gateway_provision import DockerGatewayTransport
|
from .gateway_transport import DockerGatewayTransport
|
||||||
from .gateway_net import next_free_ip
|
from .gateway_net import next_free_ip
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import time
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from .util import run_docker
|
from .util import run_docker
|
||||||
from .gateway_provision import DockerGatewayTransport
|
from .gateway_transport import DockerGatewayTransport
|
||||||
from ...paths import (
|
from ...paths import (
|
||||||
ORCHESTRATOR_AUTH_JWT_ENV,
|
ORCHESTRATOR_AUTH_JWT_ENV,
|
||||||
host_gateway_ca_dir,
|
host_gateway_ca_dir,
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .util import run_docker
|
|
||||||
from ...git_gate import GitGatePlan, git_gate_render_provision
|
from ...git_gate import GitGatePlan, git_gate_render_provision
|
||||||
from ...gateway import GatewayProvisionError, GatewayTransport
|
from ...gateway import GatewayProvisionError, GatewayTransport
|
||||||
|
|
||||||
@@ -27,27 +26,6 @@ from ...gateway import GatewayProvisionError, GatewayTransport
|
|||||||
_SAFE_BOTTLE_ID = re.compile(r"[A-Za-z0-9_-]+")
|
_SAFE_BOTTLE_ID = re.compile(r"[A-Za-z0-9_-]+")
|
||||||
|
|
||||||
|
|
||||||
class DockerGatewayTransport:
|
|
||||||
"""`GatewayTransport` for the docker gateway container (exec/cp)."""
|
|
||||||
|
|
||||||
def __init__(self, gateway: str) -> None:
|
|
||||||
self.gateway = gateway
|
|
||||||
|
|
||||||
def exec(self, argv: list[str]) -> None:
|
|
||||||
proc = run_docker(["docker", "exec", self.gateway, *argv])
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise GatewayProvisionError(
|
|
||||||
f"gateway exec {argv!r} failed: {proc.stderr.strip()}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def cp_into(self, src: str, dest: str) -> None:
|
|
||||||
proc = run_docker(["docker", "cp", src, f"{self.gateway}:{dest}"])
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise GatewayProvisionError(
|
|
||||||
f"gateway cp {src} -> {dest} failed: {proc.stderr.strip()}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _require_safe(bottle_id: str) -> None:
|
def _require_safe(bottle_id: str) -> None:
|
||||||
if not _SAFE_BOTTLE_ID.fullmatch(bottle_id):
|
if not _SAFE_BOTTLE_ID.fullmatch(bottle_id):
|
||||||
raise GatewayProvisionError(f"unsafe bottle id {bottle_id!r}")
|
raise GatewayProvisionError(f"unsafe bottle id {bottle_id!r}")
|
||||||
@@ -109,5 +87,5 @@ def deprovision_git_gate(transport: GatewayTransport, bottle_id: str) -> None:
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"provision_git_gate", "deprovision_git_gate",
|
"provision_git_gate", "deprovision_git_gate",
|
||||||
"GatewayProvisionError", "GatewayTransport", "DockerGatewayTransport",
|
"GatewayProvisionError", "GatewayTransport",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""The `GatewayTransport` for the docker gateway container (PRD 0070).
|
||||||
|
|
||||||
|
How the launcher stages files + runs commands in the running gateway container:
|
||||||
|
`docker exec` / `docker cp` over the docker socket. The backend-neutral
|
||||||
|
provisioning logic that drives it lives in `gateway_provision`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .util import run_docker
|
||||||
|
from ...gateway import GatewayProvisionError
|
||||||
|
|
||||||
|
|
||||||
|
class DockerGatewayTransport:
|
||||||
|
"""`GatewayTransport` for the docker gateway container (exec/cp)."""
|
||||||
|
|
||||||
|
def __init__(self, gateway: str) -> None:
|
||||||
|
self.gateway = gateway
|
||||||
|
|
||||||
|
def exec(self, argv: list[str]) -> None:
|
||||||
|
proc = run_docker(["docker", "exec", self.gateway, *argv])
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise GatewayProvisionError(
|
||||||
|
f"gateway exec {argv!r} failed: {proc.stderr.strip()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def cp_into(self, src: str, dest: str) -> None:
|
||||||
|
proc = run_docker(["docker", "cp", src, f"{self.gateway}:{dest}"])
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise GatewayProvisionError(
|
||||||
|
f"gateway cp {src} -> {dest} failed: {proc.stderr.strip()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["DockerGatewayTransport"]
|
||||||
@@ -20,22 +20,18 @@ Orchestrator service lands and `infra_vm` is dissolved.
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import os
|
|
||||||
import shlex
|
|
||||||
import stat
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from ...gateway import (
|
from ...gateway import (
|
||||||
DEFAULT_CA_TIMEOUT_SECONDS,
|
DEFAULT_CA_TIMEOUT_SECONDS,
|
||||||
Gateway,
|
Gateway,
|
||||||
GatewayError,
|
GatewayError,
|
||||||
GatewayProvisionError,
|
|
||||||
GatewayTransport,
|
GatewayTransport,
|
||||||
)
|
)
|
||||||
from .. import util as backend_util
|
from .. import util as backend_util
|
||||||
from . import infra_vm, netpool, util
|
from . import infra_vm, netpool, util
|
||||||
|
from .gateway_transport import FirecrackerGatewayTransport
|
||||||
|
|
||||||
# The gateway microVM's name (the `bb_role=gateway` boot tag / run dir). Fixed
|
# The gateway microVM's name (the `bb_role=gateway` boot tag / run dir). Fixed
|
||||||
# per host — one gateway VM, shared by every agent VM.
|
# per host — one gateway VM, shared by every agent VM.
|
||||||
@@ -57,40 +53,6 @@ _GATEWAY_CA_PATH = "/home/mitmproxy/.mitmproxy/mitmproxy-ca-cert.pem"
|
|||||||
_CA_FETCH_TIMEOUT_SECONDS = 15.0
|
_CA_FETCH_TIMEOUT_SECONDS = 15.0
|
||||||
|
|
||||||
|
|
||||||
class SshGatewayTransport:
|
|
||||||
"""`GatewayTransport` for the gateway running in the gateway VM — the docker
|
|
||||||
exec/cp equivalents over SSH (dropbear + the stable infra key)."""
|
|
||||||
|
|
||||||
def __init__(self, private_key: Path, guest_ip: str) -> None:
|
|
||||||
self._key = private_key
|
|
||||||
self._ip = guest_ip
|
|
||||||
|
|
||||||
def exec(self, argv: list[str]) -> None:
|
|
||||||
proc = subprocess.run(
|
|
||||||
util.ssh_base_argv(self._key, self._ip) + [shlex.join(argv)],
|
|
||||||
capture_output=True, text=True, timeout=60, check=False,
|
|
||||||
)
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise GatewayProvisionError(
|
|
||||||
f"infra gateway exec {argv!r} failed: {proc.stderr.strip()}")
|
|
||||||
|
|
||||||
def cp_into(self, src: str, dest: str) -> None:
|
|
||||||
# Preserve the source mode (docker cp does): the access-hook is staged
|
|
||||||
# 0700 and git-http execs it directly — a plain `cat >` would land it
|
|
||||||
# 0644 and the exec fails with EACCES; keys stay 0600.
|
|
||||||
mode = stat.S_IMODE(os.stat(src).st_mode)
|
|
||||||
q = shlex.quote(dest)
|
|
||||||
proc = subprocess.run(
|
|
||||||
util.ssh_base_argv(self._key, self._ip)
|
|
||||||
+ [f"cat > {q} && chmod {mode:o} {q}"],
|
|
||||||
input=Path(src).read_bytes(), capture_output=True, timeout=30, check=False,
|
|
||||||
)
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise GatewayProvisionError(
|
|
||||||
f"infra gateway cp {src} -> {dest} failed: "
|
|
||||||
f"{proc.stderr.decode(errors='replace').strip()}")
|
|
||||||
|
|
||||||
|
|
||||||
class FirecrackerGateway(Gateway):
|
class FirecrackerGateway(Gateway):
|
||||||
"""The consolidated gateway as a Firecracker microVM on the gateway link.
|
"""The consolidated gateway as a Firecracker microVM on the gateway link.
|
||||||
|
|
||||||
@@ -188,8 +150,8 @@ class FirecrackerGateway(Gateway):
|
|||||||
deploy keys through (over SSH to the gateway VM). Needs no live handle —
|
deploy keys through (over SSH to the gateway VM). Needs no live handle —
|
||||||
built from the stable key + the gateway link's guest IP, so teardown can
|
built from the stable key + the gateway link's guest IP, so teardown can
|
||||||
use it too."""
|
use it too."""
|
||||||
return SshGatewayTransport(
|
return FirecrackerGatewayTransport(
|
||||||
infra_vm._infra_dir() / "id_ed25519", netpool.gw_slot().guest_ip)
|
infra_vm._infra_dir() / "id_ed25519", netpool.gw_slot().guest_ip)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["FirecrackerGateway", "SshGatewayTransport", "GATEWAY_NAME"]
|
__all__ = ["FirecrackerGateway", "FirecrackerGatewayTransport", "GATEWAY_NAME"]
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
"""The `GatewayTransport` for the gateway running in the gateway microVM
|
||||||
|
(PRD 0070).
|
||||||
|
|
||||||
|
How the launcher stages files + runs commands in the running gateway: the
|
||||||
|
docker exec/cp equivalents over SSH (dropbear + the stable infra key). The
|
||||||
|
backend-neutral provisioning logic that drives it lives in
|
||||||
|
`backend.docker.gateway_provision`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
import stat
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ...gateway import GatewayProvisionError
|
||||||
|
from . import util
|
||||||
|
|
||||||
|
|
||||||
|
class FirecrackerGatewayTransport:
|
||||||
|
"""`GatewayTransport` for the gateway running in the gateway VM — the docker
|
||||||
|
exec/cp equivalents over SSH (dropbear + the stable infra key)."""
|
||||||
|
|
||||||
|
def __init__(self, private_key: Path, guest_ip: str) -> None:
|
||||||
|
self._key = private_key
|
||||||
|
self._ip = guest_ip
|
||||||
|
|
||||||
|
def exec(self, argv: list[str]) -> None:
|
||||||
|
proc = subprocess.run(
|
||||||
|
util.ssh_base_argv(self._key, self._ip) + [shlex.join(argv)],
|
||||||
|
capture_output=True, text=True, timeout=60, check=False,
|
||||||
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise GatewayProvisionError(
|
||||||
|
f"infra gateway exec {argv!r} failed: {proc.stderr.strip()}")
|
||||||
|
|
||||||
|
def cp_into(self, src: str, dest: str) -> None:
|
||||||
|
# Preserve the source mode (docker cp does): the access-hook is staged
|
||||||
|
# 0700 and git-http execs it directly — a plain `cat >` would land it
|
||||||
|
# 0644 and the exec fails with EACCES; keys stay 0600.
|
||||||
|
mode = stat.S_IMODE(os.stat(src).st_mode)
|
||||||
|
q = shlex.quote(dest)
|
||||||
|
proc = subprocess.run(
|
||||||
|
util.ssh_base_argv(self._key, self._ip)
|
||||||
|
+ [f"cat > {q} && chmod {mode:o} {q}"],
|
||||||
|
input=Path(src).read_bytes(), capture_output=True, timeout=30, check=False,
|
||||||
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
raise GatewayProvisionError(
|
||||||
|
f"infra gateway cp {src} -> {dest} failed: "
|
||||||
|
f"{proc.stderr.decode(errors='replace').strip()}")
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["FirecrackerGatewayTransport"]
|
||||||
@@ -47,7 +47,7 @@ from ..consolidated_util import (
|
|||||||
from . import util as container_mod
|
from . import util as container_mod
|
||||||
from .enumerate import CONTAINER_NAME_PREFIX, EnumerationError, enumerate_active
|
from .enumerate import CONTAINER_NAME_PREFIX, EnumerationError, enumerate_active
|
||||||
from .gateway import GATEWAY_NETWORK
|
from .gateway import GATEWAY_NETWORK
|
||||||
from .gateway_provision import AppleGatewayTransport
|
from .gateway_transport import MacosGatewayTransport
|
||||||
from .infra import MacosInfraService, OrchestratorStartError
|
from .infra import MacosInfraService, OrchestratorStartError
|
||||||
|
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ def register_agent(
|
|||||||
except (OrchestratorClientError, EnumerationError) as e:
|
except (OrchestratorClientError, EnumerationError) as e:
|
||||||
info(f"registry reconciliation skipped: {e}")
|
info(f"registry reconciliation skipped: {e}")
|
||||||
reg = provision_bottle(
|
reg = provision_bottle(
|
||||||
client, source_ip, egress_plan, git_gate_plan, AppleGatewayTransport(),
|
client, source_ip, egress_plan, git_gate_plan, MacosGatewayTransport(),
|
||||||
image_ref=image_ref, tokens=tokens,
|
image_ref=image_ref, tokens=tokens,
|
||||||
env_var_secret=env_var_secret,
|
env_var_secret=env_var_secret,
|
||||||
)
|
)
|
||||||
@@ -193,7 +193,7 @@ def teardown_consolidated(
|
|||||||
"""Deregister the bottle and remove its git-gate state from the gateway.
|
"""Deregister the bottle and remove its git-gate state from the gateway.
|
||||||
Both steps are idempotent so this is safe from a cleanup trap. Does NOT
|
Both steps are idempotent so this is safe from a cleanup trap. Does NOT
|
||||||
stop the gateway — it's a persistent per-host singleton."""
|
stop the gateway — it's a persistent per-host singleton."""
|
||||||
_teardown_util(bottle_id, AppleGatewayTransport(),
|
_teardown_util(bottle_id, MacosGatewayTransport(),
|
||||||
orchestrator_url=orchestrator_url, timeout=timeout)
|
orchestrator_url=orchestrator_url, timeout=timeout)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -184,10 +184,10 @@ class MacosGateway(Gateway):
|
|||||||
def provisioning_transport(self) -> GatewayTransport:
|
def provisioning_transport(self) -> GatewayTransport:
|
||||||
"""The exec/cp transport git-gate provisioning stages per-bottle repos +
|
"""The exec/cp transport git-gate provisioning stages per-bottle repos +
|
||||||
deploy keys through (over the `container` CLI)."""
|
deploy keys through (over the `container` CLI)."""
|
||||||
# Local import: gateway_provision imports GATEWAY_NAME from this module,
|
# Local import: gateway_transport imports GATEWAY_NAME from this module,
|
||||||
# so importing AppleGatewayTransport at module scope would cycle.
|
# so importing MacosGatewayTransport at module scope would cycle.
|
||||||
from .gateway_provision import AppleGatewayTransport
|
from .gateway_transport import MacosGatewayTransport
|
||||||
return AppleGatewayTransport(self.name)
|
return MacosGatewayTransport(self.name)
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
+8
-9
@@ -1,10 +1,9 @@
|
|||||||
"""`GatewayTransport` for the Apple infra container (PRD 0070).
|
"""The `GatewayTransport` for the Apple gateway container (PRD 0070).
|
||||||
|
|
||||||
The provisioning *logic* (per-bottle creds dirs, namespaced repo init) is
|
How the launcher stages files + runs commands in the running gateway container:
|
||||||
backend-neutral and lives in `backend.docker.gateway_provision`; this is only
|
the `container` CLI's exec/cp equivalents. The backend-neutral provisioning
|
||||||
the transport — how files and commands reach the running gateway. Docker uses
|
logic that drives it lives in `backend.docker.gateway_provision`; Docker uses
|
||||||
`docker exec`/`docker cp` and Firecracker uses SSH; Apple uses the `container`
|
`docker exec`/`docker cp` and Firecracker uses SSH.
|
||||||
CLI's equivalents against the infra container that hosts the gateway daemons.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -14,8 +13,8 @@ from . import util as container_mod
|
|||||||
from .gateway import GATEWAY_NAME
|
from .gateway import GATEWAY_NAME
|
||||||
|
|
||||||
|
|
||||||
class AppleGatewayTransport:
|
class MacosGatewayTransport:
|
||||||
"""`GatewayTransport` for the gateway daemons in the Apple infra container."""
|
"""`GatewayTransport` for the gateway daemons in the Apple gateway container."""
|
||||||
|
|
||||||
def __init__(self, gateway: str = GATEWAY_NAME) -> None:
|
def __init__(self, gateway: str = GATEWAY_NAME) -> None:
|
||||||
self.gateway = gateway
|
self.gateway = gateway
|
||||||
@@ -41,4 +40,4 @@ class AppleGatewayTransport:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["AppleGatewayTransport", "GatewayProvisionError"]
|
__all__ = ["MacosGatewayTransport"]
|
||||||
@@ -18,11 +18,14 @@ from bot_bottle.backend.firecracker import infra_vm
|
|||||||
from bot_bottle.backend.firecracker.gateway import (
|
from bot_bottle.backend.firecracker.gateway import (
|
||||||
GATEWAY_NAME,
|
GATEWAY_NAME,
|
||||||
FirecrackerGateway,
|
FirecrackerGateway,
|
||||||
SshGatewayTransport,
|
)
|
||||||
|
from bot_bottle.backend.firecracker.gateway_transport import (
|
||||||
|
FirecrackerGatewayTransport,
|
||||||
)
|
)
|
||||||
from bot_bottle.gateway import GatewayError, GatewayProvisionError
|
from bot_bottle.gateway import GatewayError, GatewayProvisionError
|
||||||
|
|
||||||
_GW = "bot_bottle.backend.firecracker.gateway"
|
_GW = "bot_bottle.backend.firecracker.gateway"
|
||||||
|
_GW_TRANSPORT = "bot_bottle.backend.firecracker.gateway_transport"
|
||||||
|
|
||||||
_ORCH_URL = "http://10.243.255.1:8099"
|
_ORCH_URL = "http://10.243.255.1:8099"
|
||||||
_TOKEN = "gw-jwt-token"
|
_TOKEN = "gw-jwt-token"
|
||||||
@@ -115,24 +118,24 @@ class TestFirecrackerGatewaySurface(unittest.TestCase):
|
|||||||
def test_provisioning_transport_targets_the_gateway_link(self) -> None:
|
def test_provisioning_transport_targets_the_gateway_link(self) -> None:
|
||||||
gw = FirecrackerGateway()
|
gw = FirecrackerGateway()
|
||||||
transport = gw.provisioning_transport()
|
transport = gw.provisioning_transport()
|
||||||
assert isinstance(transport, SshGatewayTransport)
|
assert isinstance(transport, FirecrackerGatewayTransport)
|
||||||
self.assertEqual(infra_vm.netpool.gw_slot().guest_ip, transport._ip)
|
self.assertEqual(infra_vm.netpool.gw_slot().guest_ip, transport._ip)
|
||||||
|
|
||||||
|
|
||||||
class TestSshGatewayTransport(unittest.TestCase):
|
class TestFirecrackerGatewayTransport(unittest.TestCase):
|
||||||
def test_cp_into_preserves_source_mode(self) -> None:
|
def test_cp_into_preserves_source_mode(self) -> None:
|
||||||
with tempfile.NamedTemporaryFile() as f:
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
os.chmod(f.name, 0o700) # like the staged access-hook
|
os.chmod(f.name, 0o700) # like the staged access-hook
|
||||||
t = SshGatewayTransport(Path("/k"), "10.0.0.1")
|
t = FirecrackerGatewayTransport(Path("/k"), "10.0.0.1")
|
||||||
with patch(f"{_GW}.subprocess.run",
|
with patch(f"{_GW_TRANSPORT}.subprocess.run",
|
||||||
return_value=CompletedProcess([], 0)) as run:
|
return_value=CompletedProcess([], 0)) as run:
|
||||||
t.cp_into(f.name, "/etc/git-gate/access-hook")
|
t.cp_into(f.name, "/etc/git-gate/access-hook")
|
||||||
remote_cmd = run.call_args.args[0][-1]
|
remote_cmd = run.call_args.args[0][-1]
|
||||||
self.assertIn("chmod 700", remote_cmd) # exec bit preserved over SSH
|
self.assertIn("chmod 700", remote_cmd) # exec bit preserved over SSH
|
||||||
|
|
||||||
def test_exec_raises_on_failure(self) -> None:
|
def test_exec_raises_on_failure(self) -> None:
|
||||||
t = SshGatewayTransport(Path("/k"), "10.0.0.1")
|
t = FirecrackerGatewayTransport(Path("/k"), "10.0.0.1")
|
||||||
with patch(f"{_GW}.subprocess.run",
|
with patch(f"{_GW_TRANSPORT}.subprocess.run",
|
||||||
return_value=CompletedProcess([], 1, stderr="nope")), \
|
return_value=CompletedProcess([], 1, stderr="nope")), \
|
||||||
self.assertRaises(GatewayProvisionError):
|
self.assertRaises(GatewayProvisionError):
|
||||||
t.exec(["mkdir", "-p", "/git-gate"])
|
t.exec(["mkdir", "-p", "/git-gate"])
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ from unittest.mock import Mock, patch
|
|||||||
|
|
||||||
from bot_bottle.backend.docker.gateway_provision import (
|
from bot_bottle.backend.docker.gateway_provision import (
|
||||||
GatewayProvisionError,
|
GatewayProvisionError,
|
||||||
DockerGatewayTransport,
|
|
||||||
deprovision_git_gate,
|
deprovision_git_gate,
|
||||||
provision_git_gate,
|
provision_git_gate,
|
||||||
)
|
)
|
||||||
|
from bot_bottle.backend.docker.gateway_transport import DockerGatewayTransport
|
||||||
from bot_bottle.git_gate import GitGatePlan, GitGateUpstream
|
from bot_bottle.git_gate import GitGatePlan, GitGateUpstream
|
||||||
|
|
||||||
_RUN = "bot_bottle.backend.docker.gateway_provision.run_docker"
|
_RUN = "bot_bottle.backend.docker.gateway_transport.run_docker"
|
||||||
|
|
||||||
|
|
||||||
def _proc(returncode: int = 0, stderr: str = "") -> Mock:
|
def _proc(returncode: int = 0, stderr: str = "") -> Mock:
|
||||||
|
|||||||
@@ -135,11 +135,11 @@ class TestMacosGatewayLifecycle(unittest.TestCase):
|
|||||||
self.gw.ca_cert_pem(timeout=0)
|
self.gw.ca_cert_pem(timeout=0)
|
||||||
|
|
||||||
def test_provisioning_transport_targets_the_gateway_container(self) -> None:
|
def test_provisioning_transport_targets_the_gateway_container(self) -> None:
|
||||||
from bot_bottle.backend.macos_container.gateway_provision import (
|
from bot_bottle.backend.macos_container.gateway_transport import (
|
||||||
AppleGatewayTransport,
|
MacosGatewayTransport,
|
||||||
)
|
)
|
||||||
transport = self.gw.provisioning_transport()
|
transport = self.gw.provisioning_transport()
|
||||||
assert isinstance(transport, AppleGatewayTransport)
|
assert isinstance(transport, MacosGatewayTransport)
|
||||||
self.assertEqual(self.gw.name, transport.gateway)
|
self.assertEqual(self.gw.name, transport.gateway)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ class TestDockerGateway(unittest.TestCase):
|
|||||||
self.sc.address()
|
self.sc.address()
|
||||||
|
|
||||||
def test_provisioning_transport_targets_the_gateway_container(self) -> None:
|
def test_provisioning_transport_targets_the_gateway_container(self) -> None:
|
||||||
from bot_bottle.backend.docker.gateway_provision import DockerGatewayTransport
|
from bot_bottle.backend.docker.gateway_transport import DockerGatewayTransport
|
||||||
|
|
||||||
transport = self.sc.provisioning_transport()
|
transport = self.sc.provisioning_transport()
|
||||||
assert isinstance(transport, DockerGatewayTransport)
|
assert isinstance(transport, DockerGatewayTransport)
|
||||||
|
|||||||
Reference in New Issue
Block a user