refactor(docker): drop legacy per-sidecar container_name functions
Same line of cleanup as the supervise rename: the per-sidecar container names (`claude-bottle-pipelock-<slug>`, `claude-bottle-egress-<slug>`, `claude-bottle-git-gate-<slug>`) were docker-network aliases pointing at the bundle, kept so legacy URLs would keep resolving. Replaces them with short hostnames (`pipelock`, `egress`, `git-gate`) matching the existing `EGRESS_HOSTNAME` pattern, and inlines the bundle-loopback URL (`http://127.0.0.1:8888`) for the in-bundle egress→pipelock hop — matching what smolmachines already does. Drops the three `*_container_name` functions, `pipelock_proxy_url`, and `git_gate_host`. Their callers move to the new constants: - `PIPELOCK_HOSTNAME = "pipelock"` (claude_bottle/pipelock.py) - `GIT_GATE_HOSTNAME = "git-gate"` (claude_bottle/git_gate.py) - `BUNDLE_LOCAL_PIPELOCK_URL` (backend/docker/pipelock.py) The agent's HTTP_PROXY now reads `http://pipelock:8888` (vs the old `http://claude-bottle-pipelock-<slug>:8888`); the gitconfig insteadOf rewrites become `git://git-gate/<repo>.git`. The prepare- time orphan probe is collapsed onto the bundle container name (`claude-bottle-sidecars-<slug>`) instead of the four legacy per-sidecar names that no backend creates anymore. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -49,8 +49,9 @@ from ...egress import (
|
|||||||
EGRESS_HOSTNAME,
|
EGRESS_HOSTNAME,
|
||||||
EGRESS_ROUTES_IN_CONTAINER,
|
EGRESS_ROUTES_IN_CONTAINER,
|
||||||
)
|
)
|
||||||
|
from ...git_gate import GIT_GATE_HOSTNAME, git_gate_aggregate_extra_hosts
|
||||||
from ...log import die, warn
|
from ...log import die, warn
|
||||||
from ...git_gate import git_gate_aggregate_extra_hosts
|
from ...pipelock import PIPELOCK_HOSTNAME
|
||||||
from ...supervise import (
|
from ...supervise import (
|
||||||
CURRENT_CONFIG_DIR_IN_AGENT,
|
CURRENT_CONFIG_DIR_IN_AGENT,
|
||||||
QUEUE_DIR_IN_CONTAINER,
|
QUEUE_DIR_IN_CONTAINER,
|
||||||
@@ -62,20 +63,17 @@ from .bottle_plan import DockerBottlePlan
|
|||||||
from .egress import (
|
from .egress import (
|
||||||
EGRESS_CA_IN_CONTAINER,
|
EGRESS_CA_IN_CONTAINER,
|
||||||
EGRESS_PIPELOCK_CA_IN_CONTAINER,
|
EGRESS_PIPELOCK_CA_IN_CONTAINER,
|
||||||
egress_container_name,
|
|
||||||
)
|
)
|
||||||
from .git_gate import (
|
from .git_gate import (
|
||||||
GIT_GATE_ACCESS_HOOK_IN_CONTAINER,
|
GIT_GATE_ACCESS_HOOK_IN_CONTAINER,
|
||||||
GIT_GATE_CREDS_DIR_IN_CONTAINER,
|
GIT_GATE_CREDS_DIR_IN_CONTAINER,
|
||||||
GIT_GATE_ENTRYPOINT_IN_CONTAINER,
|
GIT_GATE_ENTRYPOINT_IN_CONTAINER,
|
||||||
GIT_GATE_HOOK_IN_CONTAINER,
|
GIT_GATE_HOOK_IN_CONTAINER,
|
||||||
git_gate_container_name,
|
|
||||||
)
|
)
|
||||||
from .pipelock import (
|
from .pipelock import (
|
||||||
PIPELOCK_CA_CERT_IN_CONTAINER,
|
PIPELOCK_CA_CERT_IN_CONTAINER,
|
||||||
PIPELOCK_CA_KEY_IN_CONTAINER,
|
PIPELOCK_CA_KEY_IN_CONTAINER,
|
||||||
PIPELOCK_PORT,
|
PIPELOCK_PORT,
|
||||||
pipelock_container_name,
|
|
||||||
)
|
)
|
||||||
from .provision.ca import AGENT_CA_BUNDLE, AGENT_CA_PATH
|
from .provision.ca import AGENT_CA_BUNDLE, AGENT_CA_PATH
|
||||||
from .sidecar_bundle import (
|
from .sidecar_bundle import (
|
||||||
@@ -232,17 +230,15 @@ def _sidecar_bundle_service(plan: DockerBottlePlan) -> dict[str, Any]:
|
|||||||
"read_only": False,
|
"read_only": False,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Internal-network aliases: every shortname + long-form legacy
|
# Internal-network aliases: the agent reaches each daemon through
|
||||||
# name routes to the bundle so the agent's HTTPS_PROXY URL
|
# its short name (pipelock / egress / git-gate / supervise) which
|
||||||
# (which references either `pipelock` or `egress`) keeps
|
# the bundle answers as if it were the daemon itself.
|
||||||
# resolving without an agent-side change.
|
|
||||||
internal_aliases = [
|
internal_aliases = [
|
||||||
pipelock_container_name(plan.slug),
|
PIPELOCK_HOSTNAME,
|
||||||
EGRESS_HOSTNAME,
|
EGRESS_HOSTNAME,
|
||||||
egress_container_name(plan.slug),
|
|
||||||
]
|
]
|
||||||
if gp.upstreams:
|
if gp.upstreams:
|
||||||
internal_aliases.append(git_gate_container_name(plan.slug))
|
internal_aliases.append(GIT_GATE_HOSTNAME)
|
||||||
if sp is not None:
|
if sp is not None:
|
||||||
internal_aliases.append(SUPERVISE_HOSTNAME)
|
internal_aliases.append(SUPERVISE_HOSTNAME)
|
||||||
|
|
||||||
@@ -328,7 +324,7 @@ def _agent_proxy_url(plan: DockerBottlePlan) -> str:
|
|||||||
if plan.egress_plan.routes:
|
if plan.egress_plan.routes:
|
||||||
from .egress import EGRESS_PORT
|
from .egress import EGRESS_PORT
|
||||||
return f"http://{EGRESS_HOSTNAME}:{EGRESS_PORT}"
|
return f"http://{EGRESS_HOSTNAME}:{EGRESS_PORT}"
|
||||||
return f"http://{pipelock_container_name(plan.slug)}:{PIPELOCK_PORT}"
|
return f"http://{PIPELOCK_HOSTNAME}:{PIPELOCK_PORT}"
|
||||||
|
|
||||||
|
|
||||||
def _agent_no_proxy(plan: DockerBottlePlan) -> str:
|
def _agent_no_proxy(plan: DockerBottlePlan) -> str:
|
||||||
|
|||||||
@@ -32,14 +32,6 @@ EGRESS_PIPELOCK_CA_IN_CONTAINER = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def egress_container_name(slug: str) -> str:
|
|
||||||
"""The legacy per-sidecar container name. Kept as a function so
|
|
||||||
the renderer can register it as a docker-network alias on the
|
|
||||||
bundle — any code still referring to `claude-bottle-egress-<slug>`
|
|
||||||
resolves to the bundle's IP."""
|
|
||||||
return f"claude-bottle-egress-{slug}"
|
|
||||||
|
|
||||||
|
|
||||||
def egress_tls_init(stage_dir: Path) -> tuple[Path, Path]:
|
def egress_tls_init(stage_dir: Path) -> tuple[Path, Path]:
|
||||||
"""Mint the per-bottle egress MITM CA via host `openssl req`.
|
"""Mint the per-bottle egress MITM CA via host `openssl req`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"""Docker-side git-gate helpers: in-container paths the renderer's
|
"""Docker-side git-gate constants: in-container paths the renderer's
|
||||||
bind-mounts target, port pin, and container naming. The
|
bind-mounts target + the listening port. The prepare-time entrypoint
|
||||||
prepare-time entrypoint/hook render lives on the platform-neutral
|
/ hook render lives on the platform-neutral `GitGate` ABC — backends
|
||||||
`GitGate` ABC — backends instantiate it directly. The git-gate
|
instantiate it directly. The git-gate daemon's container lifecycle
|
||||||
daemon's container lifecycle is owned by the sidecar bundle
|
is owned by the sidecar bundle (PRD 0024)."""
|
||||||
(PRD 0024)."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -15,18 +14,3 @@ GIT_GATE_CREDS_DIR_IN_CONTAINER = "/git-gate/creds"
|
|||||||
|
|
||||||
# git daemon's default listening port.
|
# git daemon's default listening port.
|
||||||
GIT_GATE_PORT = 9418
|
GIT_GATE_PORT = 9418
|
||||||
|
|
||||||
|
|
||||||
def git_gate_container_name(slug: str) -> str:
|
|
||||||
"""The legacy per-sidecar container name. Kept as a function so
|
|
||||||
the renderer can register it as a docker-network alias on the
|
|
||||||
bundle — any code still dialing `claude-bottle-git-gate-<slug>`
|
|
||||||
resolves to the bundle's IP."""
|
|
||||||
return f"claude-bottle-git-gate-{slug}"
|
|
||||||
|
|
||||||
|
|
||||||
def git_gate_host(slug: str) -> str:
|
|
||||||
"""The hostname the agent's git client connects to. Resolves via
|
|
||||||
the bundle's network alias to the bundle container, where the
|
|
||||||
git-gate daemon listens on GIT_GATE_PORT."""
|
|
||||||
return git_gate_container_name(slug)
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ from .compose import (
|
|||||||
)
|
)
|
||||||
from .egress import egress_tls_init
|
from .egress import egress_tls_init
|
||||||
from .pipelock import (
|
from .pipelock import (
|
||||||
pipelock_proxy_url,
|
BUNDLE_LOCAL_PIPELOCK_URL,
|
||||||
pipelock_tls_init,
|
pipelock_tls_init,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ def launch(
|
|||||||
mitmproxy_ca_host_path=egress_ca_host,
|
mitmproxy_ca_host_path=egress_ca_host,
|
||||||
mitmproxy_ca_cert_only_host_path=egress_ca_cert_only,
|
mitmproxy_ca_cert_only_host_path=egress_ca_cert_only,
|
||||||
pipelock_ca_host_path=ca_cert_host,
|
pipelock_ca_host_path=ca_cert_host,
|
||||||
pipelock_proxy_url=pipelock_proxy_url(plan.slug),
|
pipelock_proxy_url=BUNDLE_LOCAL_PIPELOCK_URL,
|
||||||
)
|
)
|
||||||
supervise_plan = plan.supervise_plan
|
supervise_plan = plan.supervise_plan
|
||||||
if supervise_plan is not None:
|
if supervise_plan is not None:
|
||||||
|
|||||||
@@ -35,12 +35,11 @@ PIPELOCK_IMAGE = os.environ.get(
|
|||||||
PIPELOCK_PORT = os.environ.get("CLAUDE_BOTTLE_PIPELOCK_PORT", "8888")
|
PIPELOCK_PORT = os.environ.get("CLAUDE_BOTTLE_PIPELOCK_PORT", "8888")
|
||||||
|
|
||||||
|
|
||||||
def pipelock_container_name(slug: str) -> str:
|
# The URL egress dials for its upstream HTTPS_PROXY. egress and
|
||||||
return f"claude-bottle-pipelock-{slug}"
|
# pipelock share the same container's network namespace inside the
|
||||||
|
# sidecar bundle, so loopback reaches pipelock directly — no docker
|
||||||
|
# DNS aliases involved.
|
||||||
def pipelock_proxy_url(slug: str) -> str:
|
BUNDLE_LOCAL_PIPELOCK_URL = f"http://127.0.0.1:{PIPELOCK_PORT}"
|
||||||
return f"http://{pipelock_container_name(slug)}:{PIPELOCK_PORT}"
|
|
||||||
|
|
||||||
|
|
||||||
def pipelock_tls_init(stage_dir: Path) -> tuple[Path, Path]:
|
def pipelock_tls_init(stage_dir: Path) -> tuple[Path, Path]:
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ from ...supervise import Supervise
|
|||||||
from .. import BottleSpec
|
from .. import BottleSpec
|
||||||
from . import util as docker_mod
|
from . import util as docker_mod
|
||||||
from .bottle_plan import DockerBottlePlan
|
from .bottle_plan import DockerBottlePlan
|
||||||
from .egress import egress_container_name
|
|
||||||
from .git_gate import git_gate_container_name
|
|
||||||
from .bottle_state import (
|
from .bottle_state import (
|
||||||
BottleMetadata,
|
BottleMetadata,
|
||||||
agent_state_dir,
|
agent_state_dir,
|
||||||
@@ -39,7 +37,7 @@ from .bottle_state import (
|
|||||||
supervise_state_dir,
|
supervise_state_dir,
|
||||||
write_metadata,
|
write_metadata,
|
||||||
)
|
)
|
||||||
from .pipelock import pipelock_container_name
|
from .sidecar_bundle import sidecar_bundle_container_name
|
||||||
|
|
||||||
|
|
||||||
def resolve_plan(
|
def resolve_plan(
|
||||||
@@ -126,28 +124,18 @@ def resolve_plan(
|
|||||||
f"clean up old containers with 'docker rm -f <name>'"
|
f"clean up old containers with 'docker rm -f <name>'"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Probe sidecar container names for orphans from a previous run.
|
# Probe the sidecar-bundle container name for an orphan from a
|
||||||
# Sidecar names are deterministic from the slug; an orphan would
|
# previous run. Otherwise a stale bundle surfaces as a
|
||||||
# surface as a docker-create conflict deep inside launch() with no
|
# docker-create conflict deep inside launch() with no actionable
|
||||||
# actionable hint. Fail fast here with a cleanup pointer instead.
|
# hint; failing fast here points at the cleanup command.
|
||||||
# Only probe sidecars this launch will actually try to create:
|
bundle_name = sidecar_bundle_container_name(slug)
|
||||||
# pipelock always; git-gate when bottle.git is non-empty;
|
if docker_mod.container_exists(bundle_name):
|
||||||
# egress when bottle.egress.routes is non-empty.
|
die(
|
||||||
sidecar_probes: list[tuple[str, str]] = [
|
f"sidecar bundle container '{bundle_name}' already exists. "
|
||||||
("pipelock", pipelock_container_name(slug)),
|
f"This is an orphan from a previous run; clean it up with "
|
||||||
]
|
f"'./cli.py cleanup' (or 'docker rm -f {bundle_name}') and "
|
||||||
if bottle.git:
|
f"retry."
|
||||||
sidecar_probes.append(("git-gate", git_gate_container_name(slug)))
|
)
|
||||||
if bottle.egress.routes:
|
|
||||||
sidecar_probes.append(("egress", egress_container_name(slug)))
|
|
||||||
for label, sidecar_name in sidecar_probes:
|
|
||||||
if docker_mod.container_exists(sidecar_name):
|
|
||||||
die(
|
|
||||||
f"{label} sidecar container '{sidecar_name}' already exists. "
|
|
||||||
f"This is an orphan from a previous run; clean it up with "
|
|
||||||
f"'./cli.py cleanup' (or 'docker rm -f {sidecar_name}') and "
|
|
||||||
f"retry."
|
|
||||||
)
|
|
||||||
|
|
||||||
# PRD 0018 chunk 2: prepare-time scratch files live under
|
# PRD 0018 chunk 2: prepare-time scratch files live under
|
||||||
# ~/.claude-bottle/state/<slug>/<service>/ so chunk 3's compose
|
# ~/.claude-bottle/state/<slug>/<service>/ so chunk 3's compose
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ....git_gate import GIT_GATE_HOSTNAME
|
||||||
from ....log import info
|
from ....log import info
|
||||||
from ....manifest import GitEntry
|
from ....manifest import GitEntry
|
||||||
from .. import util as docker_mod
|
from .. import util as docker_mod
|
||||||
from ..bottle_plan import DockerBottlePlan
|
from ..bottle_plan import DockerBottlePlan
|
||||||
from ..git_gate import git_gate_host
|
|
||||||
|
|
||||||
|
|
||||||
def provision_git(plan: DockerBottlePlan, target: str) -> None:
|
def provision_git(plan: DockerBottlePlan, target: str) -> None:
|
||||||
@@ -56,7 +56,7 @@ def _provision_cwd_git(plan: DockerBottlePlan, target: str) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def render_git_gate_gitconfig(slug: str, entries: tuple[GitEntry, ...]) -> str:
|
def render_git_gate_gitconfig(entries: tuple[GitEntry, ...]) -> str:
|
||||||
"""Render the ~/.gitconfig content for git-gate `insteadOf`
|
"""Render the ~/.gitconfig content for git-gate `insteadOf`
|
||||||
rewrites. Pure host-side, no docker; exposed for tests.
|
rewrites. Pure host-side, no docker; exposed for tests.
|
||||||
|
|
||||||
@@ -64,7 +64,6 @@ def render_git_gate_gitconfig(slug: str, entries: tuple[GitEntry, ...]) -> str:
|
|||||||
cleanly without conditional formatting at the call site."""
|
cleanly without conditional formatting at the call site."""
|
||||||
if not entries:
|
if not entries:
|
||||||
return ""
|
return ""
|
||||||
gate = git_gate_host(slug)
|
|
||||||
out = [
|
out = [
|
||||||
"# claude-bottle git-gate (PRD 0008): every git operation against\n",
|
"# claude-bottle git-gate (PRD 0008): every git operation against\n",
|
||||||
"# a declared upstream routes through the gate, which mirrors\n",
|
"# a declared upstream routes through the gate, which mirrors\n",
|
||||||
@@ -72,7 +71,7 @@ def render_git_gate_gitconfig(slug: str, entries: tuple[GitEntry, ...]) -> str:
|
|||||||
"# fetch-from-upstream-before-every-upload-pack via access-hook).\n",
|
"# fetch-from-upstream-before-every-upload-pack via access-hook).\n",
|
||||||
]
|
]
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
out.append(f'[url "git://{gate}/{entry.Name}.git"]\n')
|
out.append(f'[url "git://{GIT_GATE_HOSTNAME}/{entry.Name}.git"]\n')
|
||||||
out.append(f"\tinsteadOf = {entry.Upstream}\n")
|
out.append(f"\tinsteadOf = {entry.Upstream}\n")
|
||||||
return "".join(out)
|
return "".join(out)
|
||||||
|
|
||||||
@@ -87,7 +86,7 @@ def _provision_git_gate_config(plan: DockerBottlePlan, target: str) -> None:
|
|||||||
container_home = os.environ.get("CLAUDE_BOTTLE_CONTAINER_HOME", "/home/node")
|
container_home = os.environ.get("CLAUDE_BOTTLE_CONTAINER_HOME", "/home/node")
|
||||||
container_gitconfig = f"{container_home}/.gitconfig"
|
container_gitconfig = f"{container_home}/.gitconfig"
|
||||||
|
|
||||||
content = render_git_gate_gitconfig(plan.slug, bottle.git)
|
content = render_git_gate_gitconfig(bottle.git)
|
||||||
config_file = plan.stage_dir / "agent_gitconfig"
|
config_file = plan.stage_dir / "agent_gitconfig"
|
||||||
config_file.write_text(content)
|
config_file.write_text(content)
|
||||||
config_file.chmod(0o600)
|
config_file.chmod(0o600)
|
||||||
|
|||||||
@@ -42,19 +42,13 @@ from ..docker.git_gate import (
|
|||||||
GIT_GATE_ENTRYPOINT_IN_CONTAINER,
|
GIT_GATE_ENTRYPOINT_IN_CONTAINER,
|
||||||
GIT_GATE_HOOK_IN_CONTAINER,
|
GIT_GATE_HOOK_IN_CONTAINER,
|
||||||
)
|
)
|
||||||
from ..docker.pipelock import pipelock_tls_init
|
from ..docker.pipelock import BUNDLE_LOCAL_PIPELOCK_URL, pipelock_tls_init
|
||||||
from . import sidecar_bundle as _bundle
|
from . import sidecar_bundle as _bundle
|
||||||
from . import smolvm as _smolvm
|
from . import smolvm as _smolvm
|
||||||
from .bottle import SmolmachinesBottle
|
from .bottle import SmolmachinesBottle
|
||||||
from .bottle_plan import SmolmachinesBottlePlan
|
from .bottle_plan import SmolmachinesBottlePlan
|
||||||
|
|
||||||
|
|
||||||
# Pipelock's upstream when egress is in the bundle: localhost on
|
|
||||||
# the bundle's own loopback. No docker DNS aliases involved —
|
|
||||||
# pipelock + egress share the same container's network namespace.
|
|
||||||
_BUNDLE_LOCAL_PIPELOCK_URL = "http://127.0.0.1:8888"
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def launch(
|
def launch(
|
||||||
plan: SmolmachinesBottlePlan,
|
plan: SmolmachinesBottlePlan,
|
||||||
@@ -95,7 +89,7 @@ def launch(
|
|||||||
# On smolmachines, egress's upstream is pipelock
|
# On smolmachines, egress's upstream is pipelock
|
||||||
# on the bundle's localhost — they're in the same
|
# on the bundle's localhost — they're in the same
|
||||||
# container's network namespace.
|
# container's network namespace.
|
||||||
pipelock_proxy_url=_BUNDLE_LOCAL_PIPELOCK_URL,
|
pipelock_proxy_url=BUNDLE_LOCAL_PIPELOCK_URL,
|
||||||
)
|
)
|
||||||
plan = dataclasses.replace(
|
plan = dataclasses.replace(
|
||||||
plan, proxy_plan=proxy_plan, egress_plan=egress_plan,
|
plan, proxy_plan=proxy_plan, egress_plan=egress_plan,
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ from .log import die
|
|||||||
from .manifest import Bottle
|
from .manifest import Bottle
|
||||||
|
|
||||||
|
|
||||||
|
# Short network alias for git-gate inside the sidecar bundle. The
|
||||||
|
# agent's `.gitconfig` insteadOf rewrites resolve through this name.
|
||||||
|
GIT_GATE_HOSTNAME = "git-gate"
|
||||||
|
|
||||||
|
|
||||||
def _empty_str_map() -> dict[str, str]:
|
def _empty_str_map() -> dict[str, str]:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,12 @@ PIPELOCK_CA_CERT_IN_CONTAINER = "/etc/pipelock-ca.pem"
|
|||||||
PIPELOCK_CA_KEY_IN_CONTAINER = "/etc/pipelock-ca-key.pem"
|
PIPELOCK_CA_KEY_IN_CONTAINER = "/etc/pipelock-ca-key.pem"
|
||||||
|
|
||||||
|
|
||||||
|
# Short network alias for pipelock inside the sidecar bundle. The
|
||||||
|
# agent's HTTP_PROXY (when no egress is declared) and any in-bundle
|
||||||
|
# consumer's URL both reference this name.
|
||||||
|
PIPELOCK_HOSTNAME = "pipelock"
|
||||||
|
|
||||||
|
|
||||||
# --- Allowlist resolution --------------------------------------------------
|
# --- Allowlist resolution --------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -329,10 +335,10 @@ class PipelockProxy:
|
|||||||
|
|
||||||
`slug` is the agent-derived identifier (lowercased,
|
`slug` is the agent-derived identifier (lowercased,
|
||||||
hyphen-normalized) used as the suffix in every per-agent
|
hyphen-normalized) used as the suffix in every per-agent
|
||||||
resource name — the agent container, the pipelock container
|
resource name — the agent container, the sidecar bundle
|
||||||
(`claude-bottle-pipelock-<slug>`), the internal/egress
|
container, the internal/egress networks. It's stored on the
|
||||||
networks. It's stored on the returned plan so the backend's
|
returned plan so the backend's launch step can derive those
|
||||||
launch step can derive the sidecar's container name.
|
names.
|
||||||
|
|
||||||
The CA paths the YAML references are the module-level
|
The CA paths the YAML references are the module-level
|
||||||
in-container constants. The host-side counterparts are
|
in-container constants. The host-side counterparts are
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ class TestSandboxEscape(unittest.TestCase):
|
|||||||
("aws", "TEST_SECRET_AWS"),
|
("aws", "TEST_SECRET_AWS"),
|
||||||
("generic", "TEST_SECRET_GENERIC"),
|
("generic", "TEST_SECRET_GENERIC"),
|
||||||
]
|
]
|
||||||
gate_host = f"claude-bottle-git-gate-{self._identity}"
|
gate_host = "git-gate"
|
||||||
|
|
||||||
for name, var in shapes:
|
for name, var in shapes:
|
||||||
with self.subTest(secret=name):
|
with self.subTest(secret=name):
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ def _egress_plan(routes: tuple[EgressRoute, ...] = ()) -> EgressPlan:
|
|||||||
mitmproxy_ca_host_path=STATE / "egress-ca" / "mitmproxy-ca.pem",
|
mitmproxy_ca_host_path=STATE / "egress-ca" / "mitmproxy-ca.pem",
|
||||||
mitmproxy_ca_cert_only_host_path=STATE / "egress-ca" / "ca.pem",
|
mitmproxy_ca_cert_only_host_path=STATE / "egress-ca" / "ca.pem",
|
||||||
pipelock_ca_host_path=STATE / "pipelock-ca" / "ca.pem",
|
pipelock_ca_host_path=STATE / "pipelock-ca" / "ca.pem",
|
||||||
pipelock_proxy_url=f"http://claude-bottle-pipelock-{SLUG}:8888",
|
pipelock_proxy_url="http://127.0.0.1:8888",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@ class TestAgentAlwaysPresent(unittest.TestCase):
|
|||||||
proxy_lines = [e for e in env if e.startswith("HTTPS_PROXY=")]
|
proxy_lines = [e for e in env if e.startswith("HTTPS_PROXY=")]
|
||||||
self.assertEqual(1, len(proxy_lines))
|
self.assertEqual(1, len(proxy_lines))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
f"HTTPS_PROXY=http://claude-bottle-pipelock-{SLUG}:8888",
|
"HTTPS_PROXY=http://pipelock:8888",
|
||||||
proxy_lines[0],
|
proxy_lines[0],
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -304,12 +304,11 @@ class TestSidecarBundleShape(unittest.TestCase):
|
|||||||
|
|
||||||
def test_internal_aliases_cover_pipelock_and_egress_shortnames(self):
|
def test_internal_aliases_cover_pipelock_and_egress_shortnames(self):
|
||||||
# The agent's HTTPS_PROXY url references either `egress` or
|
# The agent's HTTPS_PROXY url references either `egress` or
|
||||||
# `pipelock` (long form). Both must resolve to the bundle.
|
# `pipelock`. Both must resolve to the bundle.
|
||||||
sc = self._render()["services"]["sidecars"]
|
sc = self._render()["services"]["sidecars"]
|
||||||
aliases = set(sc["networks"]["internal"]["aliases"])
|
aliases = set(sc["networks"]["internal"]["aliases"])
|
||||||
self.assertIn("egress", aliases)
|
self.assertIn("egress", aliases)
|
||||||
self.assertIn(f"claude-bottle-pipelock-{SLUG}", aliases)
|
self.assertIn("pipelock", aliases)
|
||||||
self.assertIn(f"claude-bottle-egress-{SLUG}", aliases)
|
|
||||||
|
|
||||||
def test_internal_aliases_omit_inactive_sidecars(self):
|
def test_internal_aliases_omit_inactive_sidecars(self):
|
||||||
# With no git-gate / supervise, those names are NOT aliased
|
# With no git-gate / supervise, those names are NOT aliased
|
||||||
@@ -317,13 +316,13 @@ class TestSidecarBundleShape(unittest.TestCase):
|
|||||||
# listening inside the bundle.
|
# listening inside the bundle.
|
||||||
sc = self._render()["services"]["sidecars"]
|
sc = self._render()["services"]["sidecars"]
|
||||||
aliases = set(sc["networks"]["internal"]["aliases"])
|
aliases = set(sc["networks"]["internal"]["aliases"])
|
||||||
self.assertNotIn(f"claude-bottle-git-gate-{SLUG}", aliases)
|
self.assertNotIn("git-gate", aliases)
|
||||||
self.assertNotIn("supervise", aliases)
|
self.assertNotIn("supervise", aliases)
|
||||||
|
|
||||||
def test_internal_aliases_include_active_sidecars(self):
|
def test_internal_aliases_include_active_sidecars(self):
|
||||||
sc = self._render(with_git=True, supervise=True)["services"]["sidecars"]
|
sc = self._render(with_git=True, supervise=True)["services"]["sidecars"]
|
||||||
aliases = set(sc["networks"]["internal"]["aliases"])
|
aliases = set(sc["networks"]["internal"]["aliases"])
|
||||||
self.assertIn(f"claude-bottle-git-gate-{SLUG}", aliases)
|
self.assertIn("git-gate", aliases)
|
||||||
self.assertIn("supervise", aliases)
|
self.assertIn("supervise", aliases)
|
||||||
|
|
||||||
def test_daemons_csv_lists_only_active(self):
|
def test_daemons_csv_lists_only_active(self):
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ from tests.fixtures import fixture_minimal, fixture_with_git
|
|||||||
class TestGitGateGitconfigRender(unittest.TestCase):
|
class TestGitGateGitconfigRender(unittest.TestCase):
|
||||||
def test_empty_entries_renders_nothing(self):
|
def test_empty_entries_renders_nothing(self):
|
||||||
bottle = fixture_minimal().bottles["dev"]
|
bottle = fixture_minimal().bottles["dev"]
|
||||||
self.assertEqual("", render_git_gate_gitconfig("demo", bottle.git))
|
self.assertEqual("", render_git_gate_gitconfig(bottle.git))
|
||||||
|
|
||||||
def test_one_block_per_entry(self):
|
def test_one_block_per_entry(self):
|
||||||
bottle = fixture_with_git().bottles["dev"]
|
bottle = fixture_with_git().bottles["dev"]
|
||||||
out = render_git_gate_gitconfig("demo", bottle.git)
|
out = render_git_gate_gitconfig(bottle.git)
|
||||||
# Both entries map to a [url ...] block keyed on the gate's
|
# Both entries map to a [url ...] block keyed on the gate's
|
||||||
# container hostname (claude-bottle-git-gate-<slug>).
|
# short network alias (`git-gate`) inside the sidecar bundle.
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
'[url "git://claude-bottle-git-gate-demo/claude-bottle.git"]',
|
'[url "git://git-gate/claude-bottle.git"]',
|
||||||
out,
|
out,
|
||||||
)
|
)
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
@@ -25,7 +25,7 @@ class TestGitGateGitconfigRender(unittest.TestCase):
|
|||||||
"ssh://git@gitea.dideric.is:30009/didericis/claude-bottle.git",
|
"ssh://git@gitea.dideric.is:30009/didericis/claude-bottle.git",
|
||||||
out,
|
out,
|
||||||
)
|
)
|
||||||
self.assertIn('[url "git://claude-bottle-git-gate-demo/foo.git"]', out)
|
self.assertIn('[url "git://git-gate/foo.git"]', out)
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"\tinsteadOf = ssh://git@github.com/didericis/foo.git",
|
"\tinsteadOf = ssh://git@github.com/didericis/foo.git",
|
||||||
out,
|
out,
|
||||||
@@ -37,7 +37,7 @@ class TestGitGateGitconfigRender(unittest.TestCase):
|
|||||||
# gate push and leave fetch on the original URL — exactly the
|
# gate push and leave fetch on the original URL — exactly the
|
||||||
# v1 design we've moved past.
|
# v1 design we've moved past.
|
||||||
bottle = fixture_with_git().bottles["dev"]
|
bottle = fixture_with_git().bottles["dev"]
|
||||||
out = render_git_gate_gitconfig("demo", bottle.git)
|
out = render_git_gate_gitconfig(bottle.git)
|
||||||
self.assertIn("\tinsteadOf", out)
|
self.assertIn("\tinsteadOf", out)
|
||||||
self.assertNotIn("pushInsteadOf", out)
|
self.assertNotIn("pushInsteadOf", out)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user