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)
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:
@@ -13,7 +13,42 @@ async def test_ansible_comes_before_the_docker_list(app, monkeypatch):
|
||||
session["user_role"] = "operator"
|
||||
page = await render_template(
|
||||
"hosts/detail.html", host=host, monitors=[], uptime=None, alerts=[],
|
||||
monitor_types=[], linked_target=None, linkable_targets=[], ansible_sources=[])
|
||||
linked_target=None, linkable_targets=[], ansible_sources=[])
|
||||
docker = page.index('hx-get="/plugins/docker/host/h1"')
|
||||
assert page.index(">Ansible</h3>") < docker
|
||||
assert page.index('hx-get="/plugins/host_agent/panel/h1"') < docker
|
||||
# Monitors are added on their own form, filed under this host (#5277);
|
||||
# each row's own page has the labelled Edit/Pause/Delete.
|
||||
assert 'href="/monitors/new?host_id=h1"' in page
|
||||
assert "row-actions" not in page and 'action="/monitors/add"' not in page
|
||||
|
||||
|
||||
async def test_adding_a_monitor_from_a_host_page_says_whose_it_is(app):
|
||||
host = SimpleNamespace(id="h1", name="box", address="10.0.0.5")
|
||||
async with app.test_request_context("/monitors/new?host_id=h1"):
|
||||
session["user_role"] = "operator"
|
||||
page = await render_template(
|
||||
"monitors/form.html", monitor=None, host=host, mtype="icmp", config={}, types=[],
|
||||
values={"name": "", "target": "10.0.0.5", "host_id": "h1", "check_interval_seconds": 0})
|
||||
assert '<a href="/hosts/h1">box</a>' in page # crumb and hint name the host
|
||||
assert 'name="host_id" value="h1"' in page and 'value="10.0.0.5"' in page
|
||||
assert 'href="/hosts/h1" class="btn btn-sm btn-ghost">Cancel' in page
|
||||
|
||||
|
||||
async def test_the_agent_card_keeps_its_actions_under_manage(app):
|
||||
"""#5278: "Remove" said nothing about what it removed."""
|
||||
from datetime import datetime, timezone
|
||||
from tests._templates import render_plugin_template
|
||||
host = SimpleNamespace(id="h1", name="box", address="10.0.0.5")
|
||||
reg = SimpleNamespace(agent_version="1.8.0", last_seen_at=datetime.now(timezone.utc))
|
||||
target = SimpleNamespace(id="t1", name="box")
|
||||
async with app.test_request_context("/plugins/host_agent/panel/h1"):
|
||||
session["user_role"] = "admin"
|
||||
page = await render_plugin_template(
|
||||
app, "host_agent", "panel.html", host=host, reg=reg, reporting=True, stale=False,
|
||||
target=target, ansible_available=True, managed_key_set=True)
|
||||
manage = page.index(">Manage agent</summary>")
|
||||
assert page.index("Full metrics →") < manage
|
||||
for action in (">Update agent</button>", ">Rotate token</button>", ">Unregister agent</button>"):
|
||||
assert page.index(action) > manage
|
||||
assert ">Remove</button>" not in page
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user