Files
FabledSteward/plugins/docker/migrations/versions/docker_003_container_enrichment.py
T
bvandeusen 82c3d2cf36
CI / lint (push) Successful in 2s
CI / unit (push) Successful in 44s
CI / integration (push) Successful in 2m20s
CI / publish (push) Successful in 1m10s
feat(docker): per-container enrichment — health, restarts, exit code, I/O, grouping
First slice of milestone 77 (Docker monitoring depth). Surfaces real per-container
stats beyond basic state, all read-only on the existing push model.

- agent (→1.4.0): collect_docker now inspects each container (health, restart
  count, exit code, OOM) and reads net + block I/O from the stats payload; pulls
  compose project + swarm service/task/node from container labels. Per-container
  inspect+stats calls run over a small bounded ThreadPool so the ~1s-per-stats
  blocking doesn't stretch the sample on a busy host.
- schema (docker_003): additive columns on docker_containers — health, exit_code,
  oom_killed, compose_project, service_name, task_id, node_id, and BigInteger
  net/blk byte counters.
- ingest: persists the enrichment + restart_count (.get keeps older agents working).
- ui: Docker page rows now show health badge, uptime ("up 3d 4h"), restart count,
  exit code (+OOM) for stopped containers, and compose/service grouping label.
- tests: agent helpers (grouping, inspect fields, net/IO sum) + collect_docker
  assembly incl. inspect; integration asserts enrichment round-trips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Jg27rgypiW2efULXJDtMC
2026-06-18 20:37:40 -04:00

42 lines
2.1 KiB
Python

"""Docker container enrichment: health, exit/restart, grouping, I/O counters
Adds the fields the agent (≥1.4.0) now reports per container beyond the basic
state: health status, exit code, OOM flag, compose/swarm grouping labels, and
cumulative network/block I/O counters. Additive columns — no data loss, so no
DROP+recreate needed here.
Revision ID: docker_003_container_enrichment
Revises: docker_002_host_scoped
Create Date: 2026-06-19
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "docker_003_container_enrichment"
down_revision: Union[str, None] = "docker_002_host_scoped"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.add_column("docker_containers", sa.Column("health", sa.String(16), nullable=True))
op.add_column("docker_containers", sa.Column("exit_code", sa.Integer, nullable=True))
op.add_column("docker_containers",
sa.Column("oom_killed", sa.Boolean, nullable=False, server_default=sa.false()))
op.add_column("docker_containers", sa.Column("compose_project", sa.String(255), nullable=True))
op.add_column("docker_containers", sa.Column("service_name", sa.String(255), nullable=True))
op.add_column("docker_containers", sa.Column("task_id", sa.String(64), nullable=True))
op.add_column("docker_containers", sa.Column("node_id", sa.String(64), nullable=True))
op.add_column("docker_containers", sa.Column("net_rx_bytes", sa.BigInteger, nullable=True))
op.add_column("docker_containers", sa.Column("net_tx_bytes", sa.BigInteger, nullable=True))
op.add_column("docker_containers", sa.Column("blk_read_bytes", sa.BigInteger, nullable=True))
op.add_column("docker_containers", sa.Column("blk_write_bytes", sa.BigInteger, nullable=True))
def downgrade() -> None:
for col in ("blk_write_bytes", "blk_read_bytes", "net_tx_bytes", "net_rx_bytes",
"node_id", "task_id", "service_name", "compose_project",
"oom_killed", "exit_code", "health"):
op.drop_column("docker_containers", col)