feat(streaming): open a container's desktop by its name

This commit is contained in:
Zach Clendenen
2026-09-25 15:20:39 -05:00
parent 6e491beeda
commit f45cd3d3cc
4 changed files with 52 additions and 10 deletions
+2
View File
@@ -230,6 +230,8 @@ class ContainerSessionSchema(BaseModel):
class AdminContainerSchema(BaseModel):
container: str
name: str = ""
"""What URLs call this container: its own label when unique, else the key."""
label: str | None = None
host: str
platforms: list[str]
+5 -2
View File
@@ -69,6 +69,7 @@ from handler.streaming.config import (
ResolvedContainer,
configured_emulator,
container_for_session,
container_names,
containers_by_key,
containers_for_platform,
emulator_labels,
@@ -165,7 +166,7 @@ class LoadStateRequest(BaseModel):
class DesktopStreamingSessionRequest(BaseModel):
# The container to open, named by the key GET /streaming/containers
# The container to open, by the name or key GET /streaming/containers
# reports. Named rather than pooled: an admin configuring a container
# needs that one, not whichever is free.
container: Annotated[str, Field(min_length=1, max_length=300)]
@@ -1374,6 +1375,7 @@ async def list_containers(request: Request) -> AdminContainersResponse:
raise HTTPException(status_code=403, detail="Forbidden")
containers: list[dict[str, Any]] = []
names = container_names()
for container_key, entries in containers_by_key().items():
first = entries[0]
held = await get_session(container_key) if container_key else None
@@ -1386,6 +1388,7 @@ async def list_containers(request: Request) -> AdminContainersResponse:
containers.append(
{
"container": container_key,
"name": names.get(container_key, ""),
"label": first.container_label or first.label,
"host": first.host,
"platforms": [e.platform for e in entries],
@@ -1429,7 +1432,7 @@ async def claim_desktop_session(
if request.user.role != Role.ADMIN:
raise HTTPException(status_code=403, detail="Forbidden")
container, platform = access.container_by_key(req.container)
container, platform = access.container_by_name(req.container)
if not container.protocol.supports_desktop:
raise HTTPException(
status_code=400,
+8 -8
View File
@@ -11,6 +11,7 @@ from handler.streaming.config import (
containers_by_key,
containers_for_platform,
entry_for_platform,
key_for_name,
)
from handler.streaming.session_store import (
get_live_session,
@@ -306,17 +307,16 @@ async def require_claim(
return target
def container_by_key(container_key: str) -> tuple[ResolvedContainer, str]:
"""A configured container named by its key, plus the platform to file its
sessions under.
def container_by_name(name: str) -> tuple[ResolvedContainer, str]:
"""A configured container named by its name or key, plus the platform to
file its sessions under.
The session routes are platform-keyed, so a container serving several gets
the first, which `container_for_session` resolves back to this entry.
Raises 404 when the key names no container.
Raises 404 when the name matches no container.
"""
entries = containers_by_key().get(container_key) if container_key else None
key = key_for_name(name) if name else None
entries = containers_by_key().get(key) if key else None
if not entries:
raise HTTPException(
status_code=404, detail=f"No streaming container '{container_key}'"
)
raise HTTPException(status_code=404, detail=f"No streaming container '{name}'")
return entries[0], entries[0].platform
+37
View File
@@ -2284,6 +2284,43 @@ def test_containers_shows_what_is_running(client, access_token):
assert session["username"] == "test_admin"
def test_containers_reports_each_containers_name(client, access_token):
labelled = _webstation(label="WEBSTATION-DEV")
unlabelled = _webstation(
label=None,
host="http://192.168.1.11:3000",
broker_host="http://192.168.1.11:8000",
)
with _streaming(labelled, unlabelled):
rows = _containers(client, access_token).json()["containers"]
assert [row["name"] for row in rows] == ["WEBSTATION-DEV", _key_of(unlabelled)]
assert rows[0]["container"] == _key_of(labelled)
def test_desktop_opens_by_label(client, access_token):
container = _webstation(label="Emulation station")
with _streaming(container):
response, activate = _desktop(client, access_token, "emulation station")
assert response.status_code == 200
assert response.json()["container"] == _key_of(container)
activate.assert_called_once()
def test_desktop_still_opens_by_key(client, access_token):
container = _webstation(label="WEBSTATION-DEV")
with _streaming(container):
response, _ = _desktop(client, access_token, _key_of(container))
assert response.status_code == 200
assert response.json()["container"] == _key_of(container)
def test_desktop_refuses_an_unknown_name(client, access_token):
with _streaming(_webstation(label="WEBSTATION-DEV")):
response, activate = _desktop(client, access_token, "WEBSTATION-OTHER")
assert response.status_code == 404
activate.assert_not_called()
def test_containers_is_admin_only(client, viewer_access_token):
with _streaming(_webstation()):
assert _containers(client, viewer_access_token).status_code == 403