6aea953734
- Add SpawnService with container lifecycle (spawn/stop/status) - Generate Docker Compose services from tool manifests - Integrate Traefik label generation with subdomain routing - Mount workspace, config, and SSH key volumes - Add container status polling and health checks - Enhance tool instance API with spawn/stop/start/status endpoints - Add Traefik forwardAuth middleware for auth proxy - Update code-server manifest with runtime configuration
198 lines
6.8 KiB
Python
198 lines
6.8 KiB
Python
|
|
from app.services.traefik import TraefikLabelGenerator
|
|
|
|
|
|
class TestTraefikLabelGenerator:
|
|
def test_generate_subdomain(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
subdomain = gen.generate_subdomain(
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
)
|
|
assert subdomain == "code-server-my-project-alice.hq.example.com"
|
|
|
|
def test_generate_subdomain_with_different_domain(self):
|
|
gen = TraefikLabelGenerator(domain="tools.localhost")
|
|
subdomain = gen.generate_subdomain(
|
|
tool_key="opencode",
|
|
project_slug="test",
|
|
user_slug="bob",
|
|
)
|
|
assert subdomain == "opencode-test-bob.tools.localhost"
|
|
|
|
def test_generate_labels_basic(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
)
|
|
|
|
assert labels["traefik.enable"] == "true"
|
|
assert "Host(`code-server-my-project-alice.hq.example.com`)" in labels[
|
|
"traefik.http.routers.tool-abc12345.rule"
|
|
]
|
|
assert labels["traefik.http.routers.tool-abc12345.entrypoints"] == "websecure"
|
|
assert labels["traefik.http.routers.tool-abc12345.service"] == "tool-abc12345"
|
|
|
|
def test_generate_labels_tls(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com", entrypoint="websecure")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
)
|
|
|
|
assert labels["traefik.http.routers.tool-abc12345.tls"] == "true"
|
|
assert (
|
|
labels["traefik.http.routers.tool-abc12345.tls.certresolver"]
|
|
== "letsencrypt"
|
|
)
|
|
|
|
def test_generate_labels_no_tls_for_http(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com", entrypoint="web")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
)
|
|
|
|
assert "traefik.http.routers.tool-abc12345.tls" not in labels
|
|
assert "traefik.http.routers.tool-abc12345.tls.certresolver" not in labels
|
|
|
|
def test_generate_labels_service_config(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
)
|
|
|
|
assert (
|
|
labels["traefik.http.services.tool-abc12345.loadbalancer.server.port"]
|
|
== "8443"
|
|
)
|
|
assert (
|
|
labels["traefik.http.services.tool-abc12345.loadbalancer.server.scheme"]
|
|
== "http"
|
|
)
|
|
|
|
def test_generate_labels_security_headers(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
)
|
|
|
|
middleware_name = "tool-abc12345-sec"
|
|
assert (
|
|
labels[f"traefik.http.middlewares.{middleware_name}.headers.stsSeconds"]
|
|
== "31536000"
|
|
)
|
|
assert (
|
|
labels[
|
|
f"traefik.http.middlewares.{middleware_name}.headers.stsIncludeSubdomains"
|
|
]
|
|
== "true"
|
|
)
|
|
assert (
|
|
labels[
|
|
f"traefik.http.middlewares.{middleware_name}.headers.forceStsHeader"
|
|
]
|
|
== "true"
|
|
)
|
|
assert (
|
|
labels[
|
|
f"traefik.http.middlewares.{middleware_name}.headers.contentTypeNosniff"
|
|
]
|
|
== "true"
|
|
)
|
|
assert (
|
|
labels[
|
|
f"traefik.http.middlewares.{middleware_name}.headers.browserXssFilter"
|
|
]
|
|
== "true"
|
|
)
|
|
assert (
|
|
labels[
|
|
f"traefik.http.middlewares.{middleware_name}.headers.customFrameOptionsValue"
|
|
]
|
|
== "SAMEORIGIN"
|
|
)
|
|
|
|
def test_generate_labels_middleware_attached(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
)
|
|
|
|
assert labels["traefik.http.routers.tool-abc12345.middlewares"] == "tool-abc12345-sec"
|
|
|
|
def test_generate_labels_network(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
network_name="custom-network",
|
|
)
|
|
|
|
assert labels["traefik.docker.network"] == "custom-network"
|
|
|
|
def test_generate_labels_default_network(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
tool_key="code-server",
|
|
project_slug="my-project",
|
|
user_slug="alice",
|
|
container_port=8443,
|
|
)
|
|
|
|
assert labels["traefik.docker.network"] == "tools"
|
|
|
|
def test_generate_removal_labels(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_removal_labels(
|
|
instance_id="abc12345-6789-1234-5678-123456789abc",
|
|
)
|
|
|
|
assert labels["traefik.enable"] == "false"
|
|
assert labels["traefik.http.routers.tool-abc12345.rule"] == ""
|
|
|
|
def test_generate_labels_with_opencode(self):
|
|
gen = TraefikLabelGenerator(domain="hq.example.com")
|
|
labels = gen.generate_labels(
|
|
instance_id="xyz78901-2345-6789-0123-456789012345",
|
|
tool_key="opencode",
|
|
project_slug="demo",
|
|
user_slug="charlie",
|
|
container_port=3000,
|
|
)
|
|
|
|
assert "Host(`opencode-demo-charlie.hq.example.com`)" in labels[
|
|
"traefik.http.routers.tool-xyz78901.rule"
|
|
]
|
|
assert (
|
|
labels["traefik.http.services.tool-xyz78901.loadbalancer.server.port"]
|
|
== "3000"
|
|
)
|