feat(docker): Services & Failures widget lists what is wrong, then packs the healthy (#5252)
A verdict line (services healthy · other containers running · failed in 24h), then "Needs a look": troubled services with why (crash-looping, N of M running), since when and the last error, and troubled containers (unhealthy, restarting, exited with an error, out of memory). Then "Failed in the last 24h" by name, with how often, when and where. Then the healthy rest one line each (name · hosts · replicas) in as many columns as the widget is wide, collapsing after 40 to "+N more healthy". Per-container CPU is gone from this widget: Busiest Containers owns it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+22
-24
@@ -214,7 +214,6 @@ async def widget():
|
||||
{c.host_id for c in all_containers}
|
||||
| {n.host_id for n in nodes} | {s.host_id for s in services},
|
||||
)
|
||||
capacity = await docker_svc.host_capacity(db, {c.host_id for c in all_containers})
|
||||
|
||||
# Swarm tasks collapse into per-service rows (each replica tagged with its
|
||||
# host); non-swarm containers stay grouped by host.
|
||||
@@ -227,42 +226,41 @@ async def widget():
|
||||
|
||||
standalone = [c for c in all_containers if not c.service_name]
|
||||
running = [c for c in standalone if c.status == "running"]
|
||||
display = standalone if show_stopped else running
|
||||
|
||||
def trouble(c) -> int:
|
||||
if (c.status or "") == "restarting" or c.health == "unhealthy":
|
||||
return 0
|
||||
if c.status != "running" and docker_svc.exit_is_failure(c.exit_code, c.oom_killed):
|
||||
return 1
|
||||
return 2 if c.status == "running" else 3
|
||||
def troubled(c) -> bool:
|
||||
return ((c.status or "") == "restarting" or c.health == "unhealthy"
|
||||
or (c.status != "running" and docker_svc.exit_is_failure(c.exit_code, c.oom_killed)))
|
||||
|
||||
# Group for display so multi-host fleets read clearly; single-host stays flat.
|
||||
groups: dict[str, dict] = {}
|
||||
for c in sorted(display, key=lambda c: (trouble(c), c.name)):
|
||||
g = groups.setdefault(c.host_id, {
|
||||
"host_id": c.host_id,
|
||||
"host_name": host_name.get(c.host_id, c.host_id),
|
||||
"containers": [],
|
||||
})
|
||||
g["containers"].append({"c": c, "usage": docker_svc.usage(
|
||||
c, *capacity.get(c.host_id, (None, None))),
|
||||
"exit_failed": docker_svc.exit_is_failure(c.exit_code, c.oom_killed)})
|
||||
host_groups = sorted(groups.values(), key=lambda g: g["host_name"].lower())
|
||||
# The widget-shape order (UI & Design ruling): what is wrong first, then
|
||||
# the healthy rest one line each, collapsing past HEALTHY_SHOWN.
|
||||
bad_containers = sorted((c for c in standalone if troubled(c)), key=lambda c: c.name)
|
||||
healthy = ([{"kind": "service", "s": s} for s in swarm_services if s["state"] == "healthy"]
|
||||
+ [{"kind": "container", "c": c} for c in sorted(running, key=lambda c: c.name)
|
||||
if not troubled(c)]
|
||||
+ ([{"kind": "stopped", "c": c} for c in sorted(standalone, key=lambda c: c.name)
|
||||
if c.status != "running" and not troubled(c)] if show_stopped else []))
|
||||
|
||||
return await render_template(
|
||||
"docker/widget.html",
|
||||
host_groups=host_groups,
|
||||
multi_host=len(host_groups) > 1,
|
||||
swarm_services=swarm_services,
|
||||
bad_services=[s for s in swarm_services if s["state"] != "healthy"],
|
||||
bad_containers=bad_containers,
|
||||
healthy=healthy[:HEALTHY_SHOWN], healthy_more=max(0, len(healthy) - HEALTHY_SHOWN),
|
||||
services_total=len(swarm_services),
|
||||
services_healthy=sum(s["state"] == "healthy" for s in swarm_services),
|
||||
running_count=len(running),
|
||||
failures=failures,
|
||||
host_name=host_name,
|
||||
total_count=len(all_containers) + len(swarm_services),
|
||||
show_stopped=show_stopped,
|
||||
widget_id=widget_id,
|
||||
)
|
||||
|
||||
|
||||
# Healthy services and containers listed on the dashboard widget before the
|
||||
# rest collapse to "+N healthy"; the Docker page lists them all.
|
||||
HEALTHY_SHOWN = 40
|
||||
FAILURES_SHOWN = 5
|
||||
|
||||
|
||||
CPU_AVERAGE_MINUTES = 5
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user