4e185fab6b
Remove 35+ unused imports across 20+ files (W0611). Wrap 19 lines to fit under 100 character limit (C0301). Add type casts and annotations in egress_addon_core.py to resolve pyright errors caused by JSON parsing of untyped objects. Key changes: - Remove unused imports (abstractmethod, mock utilities, etc) - Split long lines at logical breaks (method calls, error messages) - Add typing.cast() for proper type inference in JSON parsing - Explicit type annotations for dict/list accesses Results: - Pylint rating: 8.73/10 - egress_addon_core.py: 0 pyright errors (was 15) - All W0611 and C0301 issues fixed Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
116 lines
4.2 KiB
Python
116 lines
4.2 KiB
Python
"""Integration: PRD 0023 chunk 2c — bundle bringup on a per-bottle
|
|
docker bridge with the pinned IP.
|
|
|
|
End-to-end against the real docker daemon. Brings up just the
|
|
sidecar bundle on its own bridge, confirms the container lands at
|
|
the pinned IP, then tears down. Skipped under act_runner (docker
|
|
socket mount topology breaks bridge visibility) and when the
|
|
bundle image isn't available.
|
|
|
|
Full launch flow (smolvm + bundle + provisioning + the
|
|
localhost-reach / egress-port-bypass probes) lives in chunk 2d."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import time
|
|
import unittest
|
|
|
|
from bot_bottle.backend.smolmachines.sidecar_bundle import (
|
|
BundleLaunchSpec,
|
|
bundle_container_name,
|
|
bundle_network_name,
|
|
create_bundle_network,
|
|
remove_bundle_network,
|
|
start_bundle,
|
|
stop_bundle,
|
|
)
|
|
from tests._docker import skip_unless_docker
|
|
|
|
|
|
@skip_unless_docker()
|
|
@unittest.skipIf(
|
|
os.environ.get("GITEA_ACTIONS") == "true",
|
|
"skipped under act_runner: docker socket mount topology breaks "
|
|
"in-process visibility of networks created on the host daemon",
|
|
)
|
|
class TestBundleBringup(unittest.TestCase):
|
|
def setUp(self):
|
|
self.slug = f"cb-test-bundle-{os.getpid()}-{int(time.time())}"
|
|
self.network = bundle_network_name(self.slug)
|
|
self.container = bundle_container_name(self.slug)
|
|
|
|
def tearDown(self):
|
|
stop_bundle(self.slug)
|
|
remove_bundle_network(self.network)
|
|
|
|
def _bundle_image_built(self) -> bool:
|
|
"""The bundle image (`bot-bottle-sidecars:latest`) is
|
|
built lazily by the docker backend's compose. If a
|
|
smolmachines-only operator hasn't run the docker backend
|
|
first, the image won't exist locally. Skip rather than
|
|
fail."""
|
|
r = subprocess.run(
|
|
["docker", "image", "inspect", "bot-bottle-sidecars:latest"],
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
check=False,
|
|
)
|
|
return r.returncode == 0
|
|
|
|
def test_create_network_then_start_bundle_pins_ip(self):
|
|
if not self._bundle_image_built():
|
|
self.skipTest(
|
|
"bot-bottle-sidecars:latest not built; run a docker "
|
|
"bottle first or `docker build -f Dockerfile.sidecars .`"
|
|
)
|
|
|
|
# Pick a subnet unlikely to collide on the host. Last
|
|
# octet of the slug hash isn't deterministic across runs;
|
|
# we hardcode a high octet (.211) that the docker default
|
|
# bridges almost never use.
|
|
subnet = "192.168.211.0/24"
|
|
gateway = "192.168.211.1"
|
|
bundle_ip = "192.168.211.2"
|
|
|
|
create_bundle_network(self.network, subnet, gateway)
|
|
|
|
spec = BundleLaunchSpec(
|
|
slug=self.slug,
|
|
network_name=self.network,
|
|
subnet=subnet,
|
|
gateway=gateway,
|
|
bundle_ip=bundle_ip,
|
|
# Only run the pipelock daemon for this smoke — it's
|
|
# the lightest of the four and doesn't need bind
|
|
# mounts beyond what we'd skip without
|
|
# BOT_BOTTLE_SIDECAR_DAEMONS. (The init
|
|
# supervisor will exit if pipelock fails to find its
|
|
# yaml — that's expected here; we just need the
|
|
# container to land on the network at the right IP.)
|
|
daemons_csv="", # empty → init exits "no daemons selected"
|
|
)
|
|
start_bundle(spec)
|
|
|
|
# Inspect the container's IP on the per-bottle network.
|
|
r = subprocess.run(
|
|
["docker", "inspect",
|
|
"--format",
|
|
"{{(index .NetworkSettings.Networks \"" + self.network + "\").IPAddress}}",
|
|
self.container],
|
|
capture_output=True, text=True, check=False,
|
|
)
|
|
# Container may have exited (no daemons selected → exit 0).
|
|
# The inspect still works on exited containers as long as
|
|
# `--rm` hasn't fired yet, which is a race. Even if it has,
|
|
# the launch succeeded — the container existed, on the
|
|
# right network, at the right IP. We don't fail here on
|
|
# missing inspect.
|
|
if r.returncode == 0 and r.stdout.strip():
|
|
self.assertEqual(bundle_ip, r.stdout.strip(),
|
|
f"bundle landed at wrong IP: {r.stdout!r}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|