fix: exact container name matching in get_container_id/get_container_name

docker ps --filter name= uses substring matching, so searching for
code-server-headquarter-abc123 also matches tunnel-code-server-headquarter-abc123.
This caused start_instance to store the tunnel container's ID instead of the
tool container's ID, breaking tunnel connectivity and all container operations.

Switched both helpers to docker inspect, which does exact name matching.

Quality gates: ruff clean
This commit is contained in:
2026-05-30 15:01:40 +02:00
parent fdf78353ad
commit 2c2c4f3683
2 changed files with 25 additions and 24 deletions
+14 -2
View File
@@ -2554,11 +2554,23 @@ async def recreate_tunnel_endpoint(
# Also probe from inside the API container directly to the target
probe = subprocess.run(
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "5", target_url],
[
"curl",
"-s",
"-o",
"/dev/null",
"-w",
"%{http_code}",
"--max-time",
"5",
target_url,
],
capture_output=True,
text=True,
)
logger.info("Direct probe from API to %s: HTTP %s", target_url, probe.stdout.strip())
logger.info(
"Direct probe from API to %s: HTTP %s", target_url, probe.stdout.strip()
)
instance.tunnel_id = tunnel_info["container_name"]
instance.public_url = tunnel_info["url"]
+11 -22
View File
@@ -179,54 +179,43 @@ def execute_compose_command(
def get_container_id(instance_name: str) -> str | None:
"""Get the container ID for a compose service.
Searches all containers including stopped/exited ones.
Uses exact name matching via docker inspect to avoid substring collisions
with tunnel containers (e.g. tunnel-code-server-... matching code-server-...).
Args:
instance_name: The service name in compose
instance_name: The exact container name (case-insensitive for Docker).
Returns:
Container ID or None if not found
Container ID or None if not found.
"""
# Docker container names are lowercase internally; normalize to ensure match
result = subprocess.run(
["docker", "ps", "-a", "-q", "--filter", f"name={instance_name.lower()}"],
["docker", "inspect", "-f", "{{.Id}}", instance_name.lower()],
capture_output=True,
text=True,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip().split("\n")[0]
return result.stdout.strip()
return None
def get_container_name(instance_name: str) -> str | None:
"""Get the full container name for a compose service.
Searches all containers including stopped/exited ones.
Uses exact name matching via docker inspect to avoid substring collisions.
Args:
instance_name: The service name in compose
instance_name: The exact container name (case-insensitive for Docker).
Returns:
Container name or None if not found
Container name or None if not found.
"""
# Docker container names are lowercase internally; normalize to ensure match
result = subprocess.run(
[
"docker",
"ps",
"-a",
"--format",
"{{.Names}}",
"--filter",
f"name={instance_name.lower()}",
],
["docker", "inspect", "-f", "{{.Name}}", instance_name.lower()],
capture_output=True,
text=True,
)
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip().split("\n")[0]
return result.stdout.strip().lstrip("/")
return None