Files
FabledSteward/tests/integration/test_docker_recent_usage.py
T
bvandeusenandClaude Opus 5.5 5a0e150789
CI / lint (push) Successful in 3s
CI / integration (push) Failing after 1m18s
CI / unit (push) Successful in 1m24s
CI / publish (push) Skipped
refactor(docker): container CPU and memory live in the one metric store (#5752)
#5752 "Container CPU and memory history lives in the one metric store;
docker_metrics and docker_metrics_hourly are retired". Container cpu_pct and
mem_pct already went to plugin_metrics for alerting; now mem_usage_bytes does
too, and every snapshot in an ingest (backlog included) is recorded at its
own time. The container history chart, Busiest Containers, a service's
history and the MCP container tools read plugin_metrics under resource
"<host>/<container>", rolled up and pruned by the shared metric retention.

docker_012_drop_metrics drops both tables. Their history is not copied
(lesson #5221 "A data migration's cost is set by the live table"); the
store already holds 7 days of container CPU and memory %. Docker retention
now prunes only lifecycle events, and Settings -> Thresholds has one
"Metric retention" card for every source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:39:56 -04:00

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_usage_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)}}