The agent sent Docker's working set (usage less inactive_file), which still counts recently used file cache. A file-heavy container with no limit read 191 GB while its processes held 2.2 GB, and the host's own figure, which already leaves cache out, disagreed with it. Agent 1.23.0 sends mem_used_bytes / mem_used_pct (anon + shmem, so Postgres's shared buffers count; v1: rss + shmem) and mem_cache_bytes apart. The new names keep the two meanings apart in history and in learned normals: docker_013 renames the columns, clears the old values and adds mem_cache_bytes. The Docker page, swarm by-node sums, Busiest Containers, container detail, normals and the MCP tools all read the new figure. File cache is shown only on the container's page and in get_container. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
64 lines
2.4 KiB
Python
64 lines
2.4 KiB
Python
"""Integration: Busiest Containers bars average each container's samples per
|
|
time bucket in one query (#5214), read from the one metric store (#5752). Two
|
|
samples share a bucket and must merge (lesson #5262). Requires
|
|
STEWARD_DATABASE_URL."""
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import importlib
|
|
import os
|
|
import uuid
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
import pytest
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
|
|
@pytest.fixture
|
|
def app():
|
|
if not os.environ.get("STEWARD_DATABASE_URL"):
|
|
pytest.skip("needs Postgres")
|
|
from steward.app import create_app
|
|
return create_app(testing=False)
|
|
|
|
|
|
def _docker(sub):
|
|
"""The app's loaded docker plugin module: importing plugins.docker.models a
|
|
second time raises "Table already defined" (see test_mcp_plugin_tools)."""
|
|
from steward.core.plugin_manager import get_loaded_plugin_modules
|
|
loaded = get_loaded_plugin_modules().get("docker")
|
|
base = loaded.__name__ if loaded is not None else "plugins.docker"
|
|
return importlib.import_module(f"{base}.{sub}")
|
|
|
|
|
|
def test_recent_usage_averages_each_containers_bucket(app):
|
|
from steward.models.hosts import Host
|
|
from steward.models.metrics import PluginMetric
|
|
service = _docker("service")
|
|
bar_slots, recent_usage = service.bar_slots, service.recent_usage
|
|
|
|
tag = uuid.uuid4().hex[:8]
|
|
hid = str(uuid.uuid4())
|
|
slots, width = bar_slots(datetime.now(timezone.utc), 6)
|
|
at = slots[-2] # a whole bucket, in the past
|
|
|
|
def m(name, secs, cpu, mem):
|
|
when = at + timedelta(seconds=secs)
|
|
return [PluginMetric(source_module="docker", resource_name=f"dock-{tag}/{name}",
|
|
metric_name=metric, value=value, recorded_at=when)
|
|
for metric, value in (("cpu_pct", cpu), ("mem_used_bytes", mem))]
|
|
|
|
async def go():
|
|
async with app.db_sessionmaker() as s:
|
|
async with s.begin():
|
|
s.add(Host(id=hid, name=f"dock-{tag}", address="10.9.9.4"))
|
|
async with s.begin():
|
|
s.add_all(m(f"a-{tag}", 10, 100.0, 1000) + m(f"a-{tag}", 70, 300.0, 3000)
|
|
+ m(f"b-{tag}", 10, 50.0, 500))
|
|
async with s.begin():
|
|
return await recent_usage(s, {(hid, f"a-{tag}")}, slots[0], width)
|
|
|
|
got = asyncio.run(go())
|
|
assert got == {(hid, f"a-{tag}"): {at: (200.0, 2000.0)}}
|