feat(docker): docker_logs table + docker_009 migration [M79 step 2]
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 40s
CI / integration (push) Successful in 2m20s
CI / publish (push) Successful in 7s

Dedicated host-scoped table for pushed container log lines (one row per line),
chained after docker_008. host_id FK CASCADE; twin indexes — (host_id,
container_name, ts) for the viewer, ts alone for the age-cutoff prune — mirroring
docker_events. Integration schema-shape test asserts columns + both indexes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CAGR73DUowdVFVvYzLXC5C
This commit is contained in:
2026-07-19 18:43:14 -04:00
parent c95194747d
commit 45565b2c01
3 changed files with 107 additions and 0 deletions
+22
View File
@@ -75,6 +75,28 @@ def test_events_and_swarm_tables_exist(app):
asyncio.run(_go())
@_NEEDS_DB
def test_docker_logs_table_shape(app):
"""docker_009 created docker_logs: host-scoped, with the twin indexes the
viewer (host, container, ts) and the age-cutoff prune (ts) rely on."""
from sqlalchemy import text
async def _go():
async with app.db_sessionmaker() as s:
cols = {r[0] for r in (await s.execute(text(
"SELECT column_name FROM information_schema.columns "
"WHERE table_name = 'docker_logs'"))).all()}
assert cols, "docker_logs missing entirely"
assert {"id", "host_id", "container_name", "ts", "stream", "line"} <= cols
idx = {r[0] for r in (await s.execute(text(
"SELECT indexname FROM pg_indexes "
"WHERE tablename = 'docker_logs'"))).all()}
assert "ix_docker_logs_host_container_time" in idx
assert "ix_docker_logs_ts" in idx
asyncio.run(_go())
def _persist_fn(app):
"""Resolve persist_host_docker via the registered capability if the docker
plugin is loaded, else import it directly (the import is safe only when the