feat(hosts): a host's page stays short — Docker folds past the busiest, monitors add on their own form, agent actions under Manage (#5276, #5277, #5278)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 59s
CI / unit (push) Successful in 1m17s
CI / publish (push) Successful in 1m3s

Docker panel (#5276): problems first, then the 8 busiest running
containers by CPU; the other running and the stopped fold behind one
"+N more running · M stopped" line. Containers that are not swarm tasks
read the way the Docker page reads them: CI jobs as "CI: workflow · job",
buildx builders as "BuildKit builder · name".

Monitors card (#5277): the edit/delete icon buttons and the inline add
form are gone. Each row links to its monitor's page; "Add monitor" opens
the add form filed under this host, starting from its address, with the
host in its breadcrumbs and Cancel back to it.

Agent card (#5278): Full metrics stays in view; Update, Rotate token,
Unregister agent (was "Remove") and Re-provision sit under "Manage agent".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 17:08:46 -04:00
co-authored by Claude Opus 5.5
parent 6788ce60f1
commit d4974ddf6c
10 changed files with 224 additions and 126 deletions
+44
View File
@@ -267,3 +267,47 @@ def test_bar_slots_end_at_the_bucket_now_falls_in():
assert width == 900 and len(slots) == 24
assert slots[-1] == datetime(2026, 10, 7, 12, 0, tzinfo=timezone.utc)
assert slots[0] == datetime(2026, 10, 7, 6, 15, tzinfo=timezone.utc)
def test_builders_and_ci_jobs_read_by_what_they_are_on_a_hosts_page():
assert svc.display_name(_c("buildx_buildkit_steward-builder0")) == "BuildKit builder · steward-builder"
ci = "FORGEJO-ACTIONS-TASK-8597-WORKFLOW-CI-JOB-unit"
assert svc.task_label(_c(ci)) == "CI: CI · unit" # not a swarm task: display_name
assert svc.task_label(_c("codex_db.1.37ma", service_name="codex_db")) == "codex_db · 1"
def _panel_row(name, cpu, status="running", problems=()):
c = _c(name, cpu_pct=cpu, status=status)
return {"c": c, "label": name, "problems": list(problems), "since": None,
"usage": svc.usage(c, 4, 8 << 30), "uptime": "2h" if status == "running" else None}
def _panel_rows():
return ([_panel_row(f"busy{n:02d}", float(n)) for n in range(1, 11)]
+ [_panel_row("old", None, status="exited"),
_panel_row("dead", None, status="exited", problems=["exited_nonzero"])])
def test_a_hosts_docker_panel_shows_problems_then_the_busiest_and_folds_the_rest():
"""#5276: 33 containers made the card run off the page."""
from plugins.docker.routes import HOST_PANEL_BUSIEST, host_panel_rows
shown, folded = host_panel_rows(_panel_rows())
assert [r["label"] for r in shown] == ["dead"] + [f"busy{n:02d}" for n in range(10, 2, -1)]
assert len(shown) == 1 + HOST_PANEL_BUSIEST
assert [r["label"] for r in folded] == ["busy02", "busy01", "old"]
async def test_the_folded_containers_sit_behind_one_line(app):
from plugins.docker.routes import host_panel_rows
shown, folded = host_panel_rows(_panel_rows())
async with app.test_request_context("/plugins/docker/host/h1"):
page = await render_plugin_template(
app, "docker", "docker/host_panel.html", rows=shown, folded=folded,
says=svc.PROBLEM_SAYS, severity=svc.PROBLEM_SEVERITY,
running=10, stopped=2, problems=1, replaced=0, busiest=8)
fold = page.index("<details")
assert "+2 more running · 1 stopped" in page
assert page.index(">busy10<") < fold < page.index(">busy01<")
assert page.index(">dead<") < page.index(">busy10<")
assert len(re.findall(r"<th[ >]", page)) == 6 # one header row per table
assert not hex_colours(page), hex_colours(page)