phase: 28_gateway_agent_proxy
**Phase 28 final pass — complete, 100 % green.** All five tasks were already `complete/`; this pass verified the criteria, re-ran the live half, and did the docs pass. - Gate `bash .agents/validate.sh` **rc=0**: `1650 passed, 15 skipped`, coverage **98.97 %** (floor 94), `ruff` clean, `pyright` **0 errors**, `node --check` clean, hygiene clean (`/tmp/validate-done.log`). - Live: `pytest -m live --live` **11 passed**; `./scripts/dev-server.sh status` nine sources `ok`; mTLS re-read of `gateway` → agent **1.2.0**, all three sections `available: true`, 6/6 frontends routed, 11 nginx servers + `$targetBackend`, **19** listeners, no core errors (`data/logs/28-finalpass-live.out`); installed agent file byte-identical to this tree (`b0e9ebf1b718…`). - Criteria: 1 ✅ pytest/coverage · 2 ✅ three sections over a captured tree, `false`/`null` + one `errors` line each elsewhere with `cpu`/`mem`/`disk_io`/`system` intact · 3 ✅ `AGENT_VERSION` 1.2.0 + SCHEMA names all three · 4 ✅ six fixtures verbatim, capture refuses a bad read, 116 socket address columns hold no globally routable endpoint · 5 ✅ gateway runs 1.2.0 and answers over mTLS (transcript `data/logs/28-task05-rollout.md`, `28-finalpass-live.out`) · 6 ✅ validate · 7 ✅ additive: HEAD's 1.1.0 and this 1.2.0 `collect()`ed over one `--root` are key-for-key equal plus the three new sections, `--no-proxy` equal exactly (`data/logs/verify-28-final.py` → 41 PASS, `28-final-verify.out`). - Docs fixed (README/AGENTS/Containerfile, all re-green): README's opening said "Four dashboards" above a five-row table and said the new sections are "stored in the snapshot" (they are in the **agent's** document — no `/api` payload, no `sample` row, nothing charts them until phase 29); `Containerfile`'s header still said "three dashboards"; AGENTS's coverage figure (98.94 → **98.97 %**), invariant 6 gained the captured-socket-table identifier rule, the agent bullet gained "phase 28 stops at the wire", and the fleet record names the stash-and-compare control plus the volatile listener count. Full record: `data/logs/NOTES-28-final.md`. - Deviations: none functional; no git index/history touched, phase files untouched (`PLAN.md` and `complete/` unchanged). `uv pip list --outdated` still lists 4 packages (fastapi 0.153→0.161 etc.) — advisory only, dependencies untouched by this phase, left for a deliberate pass. - Next pending phase: `.agents/phases/todo/29_gateway_dashboard_page/` (the page that consumes these three sections).
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
**Phase 28 final pass — complete, 100 % green.** All five tasks were already `complete/`; this pass verified the criteria, re-ran the live half, and did the docs pass.
|
||||
|
||||
- Gate `bash .agents/validate.sh` **rc=0**: `1650 passed, 15 skipped`, coverage **98.97 %** (floor 94), `ruff` clean, `pyright` **0 errors**, `node --check` clean, hygiene clean (`/tmp/validate-done.log`).
|
||||
- Live: `pytest -m live --live` **11 passed**; `./scripts/dev-server.sh status` nine sources `ok`; mTLS re-read of `gateway` → agent **1.2.0**, all three sections `available: true`, 6/6 frontends routed, 11 nginx servers + `$targetBackend`, **19** listeners, no core errors (`data/logs/28-finalpass-live.out`); installed agent file byte-identical to this tree (`b0e9ebf1b718…`).
|
||||
- Criteria: 1 ✅ pytest/coverage · 2 ✅ three sections over a captured tree, `false`/`null` + one `errors` line each elsewhere with `cpu`/`mem`/`disk_io`/`system` intact · 3 ✅ `AGENT_VERSION` 1.2.0 + SCHEMA names all three · 4 ✅ six fixtures verbatim, capture refuses a bad read, 116 socket address columns hold no globally routable endpoint · 5 ✅ gateway runs 1.2.0 and answers over mTLS (transcript `data/logs/28-task05-rollout.md`, `28-finalpass-live.out`) · 6 ✅ validate · 7 ✅ additive: HEAD's 1.1.0 and this 1.2.0 `collect()`ed over one `--root` are key-for-key equal plus the three new sections, `--no-proxy` equal exactly (`data/logs/verify-28-final.py` → 41 PASS, `28-final-verify.out`).
|
||||
- Docs fixed (README/AGENTS/Containerfile, all re-green): README's opening said "Four dashboards" above a five-row table and said the new sections are "stored in the snapshot" (they are in the **agent's** document — no `/api` payload, no `sample` row, nothing charts them until phase 29); `Containerfile`'s header still said "three dashboards"; AGENTS's coverage figure (98.94 → **98.97 %**), invariant 6 gained the captured-socket-table identifier rule, the agent bullet gained "phase 28 stops at the wire", and the fleet record names the stash-and-compare control plus the volatile listener count. Full record: `data/logs/NOTES-28-final.md`.
|
||||
- Deviations: none functional; no git index/history touched, phase files untouched (`PLAN.md` and `complete/` unchanged). `uv pip list --outdated` still lists 4 packages (fastapi 0.153→0.161 etc.) — advisory only, dependencies untouched by this phase, left for a deliberate pass.
|
||||
- Next pending phase: `.agents/phases/todo/29_gateway_dashboard_page/` (the page that consumes these three sections).
|
||||
@@ -0,0 +1 @@
|
||||
ok
|
||||
@@ -0,0 +1,71 @@
|
||||
== uv run pytest --cov (hermetic: no network, live tests skip) ==
|
||||
........................................................................ [ 4%]
|
||||
........................................................................ [ 8%]
|
||||
........................................................................ [ 12%]
|
||||
........................................................................ [ 17%]
|
||||
........................................................................ [ 21%]
|
||||
........................................................................ [ 25%]
|
||||
........................................................................ [ 30%]
|
||||
.......s................................................................ [ 34%]
|
||||
........................................................................ [ 38%]
|
||||
........................................................................ [ 43%]
|
||||
........................................................................ [ 47%]
|
||||
........................................................................ [ 51%]
|
||||
........................................................................ [ 56%]
|
||||
........................................................................ [ 60%]
|
||||
........................................................................ [ 64%]
|
||||
........................................................................ [ 69%]
|
||||
....................................sssssssssss......................... [ 73%]
|
||||
........................................................................ [ 77%]
|
||||
........................................................................ [ 82%]
|
||||
........................................................................ [ 86%]
|
||||
........................................................................ [ 90%]
|
||||
........................................................................ [ 95%]
|
||||
.......................sss.............................................. [ 99%]
|
||||
......... [100%]
|
||||
=============================== warnings summary ===============================
|
||||
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
|
||||
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
|
||||
from starlette.testclient import TestClient as TestClient # noqa
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
================================ tests coverage ================================
|
||||
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
|
||||
|
||||
Name Stmts Miss Cover Missing
|
||||
------------------------------------------------------------------
|
||||
src/dashboards/__init__.py 3 0 100%
|
||||
src/dashboards/__main__.py 14 1 93% 32
|
||||
src/dashboards/alerts.py 176 0 100%
|
||||
src/dashboards/api.py 440 0 100%
|
||||
src/dashboards/app.py 186 0 100%
|
||||
src/dashboards/auth.py 265 0 100%
|
||||
src/dashboards/certs.py 292 0 100%
|
||||
src/dashboards/collectors.py 206 4 98% 304, 308-309, 326
|
||||
src/dashboards/config.py 120 0 100%
|
||||
src/dashboards/debug.py 14 0 100%
|
||||
src/dashboards/poller.py 165 3 98% 192-194, 279
|
||||
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
|
||||
src/dashboards/rates.py 73 0 100%
|
||||
src/dashboards/severity.py 49 0 100%
|
||||
src/dashboards/sources/__init__.py 0 0 100%
|
||||
src/dashboards/sources/disks.py 134 0 100%
|
||||
src/dashboards/sources/host.py 209 0 100%
|
||||
src/dashboards/sources/llm.py 178 1 99% 285
|
||||
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
|
||||
src/dashboards/sources/nut_sim.py 134 0 100%
|
||||
src/dashboards/store.py 313 2 99% 553-554
|
||||
------------------------------------------------------------------
|
||||
TOTAL 3496 36 99%
|
||||
Required test coverage of 94% reached. Total coverage: 98.97%
|
||||
1650 passed, 15 skipped, 1 warning in 157.81s (0:02:37)
|
||||
== uv run ruff check src tests agent ==
|
||||
All checks passed!
|
||||
== uv run pyright ==
|
||||
0 errors, 0 warnings, 0 informations
|
||||
== node --check src/dashboards/web/static/*.js ==
|
||||
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
|
||||
coverage 7.16.2 7.17.0 wheel
|
||||
fastapi 0.153.0 0.161.1 wheel
|
||||
pycparser 3.11 3.12 wheel
|
||||
ruff 0.17.1 0.17.2 wheel
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
**Task 28/01 complete — green.**
|
||||
|
||||
- `agent/host_agent.py`: new `haproxy` snapshot section — `haproxy_config` argument (+`--haproxy-config`, `--no-proxy`), module-level `parse_haproxy_config()` / `_scan_haproxy_config()`, `Collector.haproxy()`, wired into `snapshot()` before `errors`. Shape matches the task's assumptions; `:::22`→`addr "::"`, port `22`; a portless target keeps `port: null`; `use_backend %[…]` contributes no name; `listen` becomes the pair haproxy defines; design-rules + SCHEMA docstring updated (`AGENT_VERSION` left at 1.1.0 — task 04 owns the bump).
|
||||
- Three-way `available` pinned: `true` / `false` (missing file, one `haproxy: … not found …` line) / `null` (mode-000, a directory, oversized, non-haproxy text) — each exactly **one** line, snapshot and `/health` intact. `--no-proxy` removes the key and the line, Collector-level and through `main()`.
|
||||
- `src/dashboards/collectors.py`: `ADDON_ERROR_PREFIXES = ("smart", "haproxy")` — defect found while implementing. The task mandates the absence line, but `degraded` (that list) is `alerts.py`'s input, so shipping it unfiltered would have put a red banner + overview WARN on every host with no haproxy after the upgrade. Filtered there, still verbatim in `agent.errors` for phase 29.
|
||||
- Tests: 19 new (17 `tests/test_agent.py`, 2 `tests/test_collectors.py`); three existing `errors == []` assertions now filter add-on lines via `core_errors()`.
|
||||
- `tests/fixtures/gateway_haproxy.cfg`: **read-only** ssh capture, body byte-identical to `gateway:/etc/haproxy/haproxy.cfg` (mode 0644 — so the agent user can read it; no credential in it). Parsed end-to-end: 6 frontends / 6 backends, all `default_backend`s resolve.
|
||||
- Docs: README agent section, AGENTS.md agent + `collectors.py` bullets, gateway's fleet row, pytest tally.
|
||||
- Gate: `bash .agents/validate.sh` **rc=0** — 1568 passed / 15 skipped, coverage 98.94 % (floor 94), ruff clean, pyright 0 errors, `node --check` clean. Only the pre-existing advisory (fastapi 0.153.0→0.159.1; no dependency touched).
|
||||
- Dev server restarted: 9 sources `ok`; `/api/system/snapshot` unchanged on the live fleet (agents are 1.1.0, so no `haproxy` key at all). Evidence: `data/logs/NOTES-28-haproxy.md`.
|
||||
- No live change made to any host; no git index/history touched; phase files unmodified. Cosmetic note for task 05/29: after a fleet upgrade, a non-haproxy host's collapsed "More detail" banner will print the absence line — same as a smart line today; alerts are unaffected.
|
||||
+1
@@ -0,0 +1 @@
|
||||
ok
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
== uv run pytest --cov (hermetic: no network, live tests skip) ==
|
||||
........................................................................ [ 4%]
|
||||
........................................................................ [ 9%]
|
||||
........................................................................ [ 13%]
|
||||
........................................................................ [ 18%]
|
||||
........................................................................ [ 22%]
|
||||
........................................................................ [ 27%]
|
||||
.........s.............................................................. [ 31%]
|
||||
........................................................................ [ 36%]
|
||||
........................................................................ [ 40%]
|
||||
........................................................................ [ 45%]
|
||||
........................................................................ [ 50%]
|
||||
........................................................................ [ 54%]
|
||||
........................................................................ [ 59%]
|
||||
........................................................................ [ 63%]
|
||||
........................................................................ [ 68%]
|
||||
..........................sssssssssss................................... [ 72%]
|
||||
........................................................................ [ 77%]
|
||||
........................................................................ [ 81%]
|
||||
........................................................................ [ 86%]
|
||||
........................................................................ [ 90%]
|
||||
........................................................................ [ 95%]
|
||||
.............sss....................................................... [100%]
|
||||
=============================== warnings summary ===============================
|
||||
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
|
||||
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
|
||||
from starlette.testclient import TestClient as TestClient # noqa
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
================================ tests coverage ================================
|
||||
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
|
||||
|
||||
Name Stmts Miss Cover Missing
|
||||
------------------------------------------------------------------
|
||||
src/dashboards/__init__.py 3 0 100%
|
||||
src/dashboards/__main__.py 14 1 93% 32
|
||||
src/dashboards/alerts.py 176 0 100%
|
||||
src/dashboards/api.py 440 0 100%
|
||||
src/dashboards/app.py 186 0 100%
|
||||
src/dashboards/auth.py 265 0 100%
|
||||
src/dashboards/certs.py 292 0 100%
|
||||
src/dashboards/collectors.py 206 5 98% 296-297, 301-302, 319
|
||||
src/dashboards/config.py 120 0 100%
|
||||
src/dashboards/debug.py 14 0 100%
|
||||
src/dashboards/poller.py 165 3 98% 192-194, 279
|
||||
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
|
||||
src/dashboards/rates.py 73 0 100%
|
||||
src/dashboards/severity.py 49 0 100%
|
||||
src/dashboards/sources/__init__.py 0 0 100%
|
||||
src/dashboards/sources/disks.py 134 0 100%
|
||||
src/dashboards/sources/host.py 209 0 100%
|
||||
src/dashboards/sources/llm.py 178 1 99% 285
|
||||
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
|
||||
src/dashboards/sources/nut_sim.py 134 0 100%
|
||||
src/dashboards/store.py 313 2 99% 553-554
|
||||
------------------------------------------------------------------
|
||||
TOTAL 3496 37 99%
|
||||
Required test coverage of 94% reached. Total coverage: 98.94%
|
||||
1568 passed, 15 skipped, 1 warning in 165.97s (0:02:45)
|
||||
== uv run ruff check src tests agent ==
|
||||
All checks passed!
|
||||
== uv run pyright ==
|
||||
0 errors, 0 warnings, 0 informations
|
||||
== node --check src/dashboards/web/static/*.js ==
|
||||
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
|
||||
fastapi 0.153.0 0.159.1 wheel
|
||||
@@ -0,0 +1,17 @@
|
||||
**Implemented (phase 28 task 02 — agent `nginx` section)**
|
||||
- `agent/host_agent.py`: additive `nginx` section — `Collector.nginx(errors)` + `nginx_include_files()` + pure `nginx_include_paths()` / `parse_nginx_config()`; brace-block scanner (`_nginx_tokens`/`_nginx_statements`/`_collect_nginx`) so quoted `;`/`{`, comments, nested `server`/`location`/`upstream`/`map` all parse; `--nginx-config` flag; shared `_config_path`/`read_config`/`_config_label` now used by both proxy sections; SCHEMA docstring + design-rules bullet updated.
|
||||
- Three-way `available` with exactly one `nginx:` line per arm ("not found" pinned); includes followed **one level**, bounded 8 files / 1 MB (excess dropped with one line naming the count, section stays `available: true`); `files` = main + merged docs in the config's own spelling; `proxy_pass $var` served with `variable` + `target: null` (never merged); `--no-proxy` now removes `haproxy` **and** `nginx`.
|
||||
- `collectors.ADDON_ERROR_PREFIXES` grew `nginx` in the same commit (AGENTS.md rule) + new pin that the agent-side and dashboard-side lists are equal (that pin was claimed but missing — defect fixed).
|
||||
- Verified the parser read-only against the **real** gateway tree (`cat` over ssh): 11 servers, both maps, 17 `$targetBackend` entries, the `127.0.0.1:8081` literal, stream `proxy_pass $targetBackend` unmerged. No live change made (install is task 05).
|
||||
|
||||
**Tests / lint** (`uv run …`)
|
||||
- `bash .agents/validate.sh` → **EXIT=0**; `uv run pytest --cov --cov-fail-under=94` → **1603 passed, 15 skipped, 98.94%** (was 1568; 34 new nginx cases, one 6-way parametrise over malformed files).
|
||||
- `uv run ruff check src tests agent` → All checks passed; `uv run pyright` → 0 errors.
|
||||
- `uv run pytest -m live --live` → 11 passed; `./scripts/dev-server.sh status` → 9 sources `ok`.
|
||||
- Agent-file coverage via `coverage run --branch --source=agent`: 94%, new code fully covered except the unreachable defensive `except` in `_config_label` (>90% criterion met).
|
||||
|
||||
**Docs**: `README.md` (nginx add-on paragraph), `AGENTS.md` (agent bullet for the `nginx` section, `ADDON_ERROR_PREFIXES` list, gateway fleet row, test tally). Transcript: `data/logs/NOTES-28-nginx.md` (gitignored).
|
||||
|
||||
**Deviations**: map entry keys carry `variable`/`key` with the `$` dropped on both sides of the proxy_pass↔map join (one canonical spelling instead of the task's mixed `$targetBackend`/`targetBackend`); map values that are not addresses report `target: null` rather than a host-shaped guess. `uv pip list --outdated` still reports a pre-existing `fastapi 0.153.0 → 0.159.1` (validate.sh prints it as an advisory only; deps untouched by this task).
|
||||
|
||||
**Next pending task**: `28_gateway_agent_proxy/03_socket_section.md`.
|
||||
+1
@@ -0,0 +1 @@
|
||||
ok
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
== uv run pytest --cov (hermetic: no network, live tests skip) ==
|
||||
........................................................................ [ 4%]
|
||||
........................................................................ [ 8%]
|
||||
........................................................................ [ 13%]
|
||||
........................................................................ [ 17%]
|
||||
........................................................................ [ 22%]
|
||||
........................................................................ [ 26%]
|
||||
............................................s........................... [ 31%]
|
||||
........................................................................ [ 35%]
|
||||
........................................................................ [ 40%]
|
||||
........................................................................ [ 44%]
|
||||
........................................................................ [ 48%]
|
||||
........................................................................ [ 53%]
|
||||
........................................................................ [ 57%]
|
||||
........................................................................ [ 62%]
|
||||
........................................................................ [ 66%]
|
||||
.............................................................sssssssssss [ 71%]
|
||||
........................................................................ [ 75%]
|
||||
........................................................................ [ 80%]
|
||||
........................................................................ [ 84%]
|
||||
........................................................................ [ 88%]
|
||||
........................................................................ [ 93%]
|
||||
................................................sss..................... [ 97%]
|
||||
.................................. [100%]
|
||||
=============================== warnings summary ===============================
|
||||
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
|
||||
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
|
||||
from starlette.testclient import TestClient as TestClient # noqa
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
================================ tests coverage ================================
|
||||
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
|
||||
|
||||
Name Stmts Miss Cover Missing
|
||||
------------------------------------------------------------------
|
||||
src/dashboards/__init__.py 3 0 100%
|
||||
src/dashboards/__main__.py 14 1 93% 32
|
||||
src/dashboards/alerts.py 176 0 100%
|
||||
src/dashboards/api.py 440 0 100%
|
||||
src/dashboards/app.py 186 0 100%
|
||||
src/dashboards/auth.py 265 0 100%
|
||||
src/dashboards/certs.py 292 0 100%
|
||||
src/dashboards/collectors.py 206 5 98% 302-303, 307-308, 325
|
||||
src/dashboards/config.py 120 0 100%
|
||||
src/dashboards/debug.py 14 0 100%
|
||||
src/dashboards/poller.py 165 3 98% 192-194, 279
|
||||
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
|
||||
src/dashboards/rates.py 73 0 100%
|
||||
src/dashboards/severity.py 49 0 100%
|
||||
src/dashboards/sources/__init__.py 0 0 100%
|
||||
src/dashboards/sources/disks.py 134 0 100%
|
||||
src/dashboards/sources/host.py 209 0 100%
|
||||
src/dashboards/sources/llm.py 178 1 99% 285
|
||||
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
|
||||
src/dashboards/sources/nut_sim.py 134 0 100%
|
||||
src/dashboards/store.py 313 2 99% 553-554
|
||||
------------------------------------------------------------------
|
||||
TOTAL 3496 37 99%
|
||||
Required test coverage of 94% reached. Total coverage: 98.94%
|
||||
1603 passed, 15 skipped, 1 warning in 162.84s (0:02:42)
|
||||
== uv run ruff check src tests agent ==
|
||||
All checks passed!
|
||||
== uv run pyright ==
|
||||
0 errors, 0 warnings, 0 informations
|
||||
== node --check src/dashboards/web/static/*.js ==
|
||||
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
|
||||
fastapi 0.153.0 0.159.1 wheel
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
Task 03 (`sockets` section) is complete.
|
||||
|
||||
**Implemented** — `agent/host_agent.py`:
|
||||
- `parse_proc_net_tcp(text, family)` keeps only `0A` LISTEN → `{family, addr, port}` and `01` ESTABLISHED → `{family, local, remote}`; every other state is skipped silently. `_proc_net_addr` unrolls `/proc`'s per-32-bit-word little-endian spelling through `socket.inet_ntop`, so wildcards stay literal `0.0.0.0` / `::` and a bad word yields `addr: null` (never `0`); a bad port is `null` too.
|
||||
- `Collector.read_proc_table()` bounds each table to `PROC_NET_MAX_LINES` (4096, `/proc`'s header among them) and reports the excess as **one** line naming the path; `Collector.sockets(errors)` reads v4/v6 independently — either answering gives `available: true`, a missing `tcp6` is silent (kernel without IPv6), a denied table always costs one line, neither answering gives `available: null` plus one combined line. There is deliberately **no `false` arm** (`false` beside empty lists would read "no listeners").
|
||||
- `document["sockets"]` is attached inside `Collector.snapshot()` only when `proxy_enabled`, wrapped in `_safe`, so an unexpected exception costs one `sockets:` line and nothing else — `/snapshot` and `/health` never fail. `--no-proxy` removes the key with no error line.
|
||||
- `src/dashboards/collectors.py`: `ADDON_ERROR_PREFIXES = ("smart", "haproxy", "nginx", "sockets")` so absence/errors do not turn `/system` red.
|
||||
|
||||
**Tests** — `tests/test_agent.py`: v4+v6 parsing incl. wildcard `0.0.0.0`/`::`, every non-`0A`/`01` state skipped, malformed endpoint words, header line ignored, `--root` fake-tree reads, additivity (only `ts`/`duration_ms`/`sockets`/`errors` move), both-missing → one line naming both paths, missing `tcp6` silent, denied table costs one line, truncation reported once, `--no-proxy` (payload, `--once`, `/schema`), `_safe` fallback, `inet_ntop` raising, and a real-`/proc` shape test. `tests/test_collectors.py`: `sockets:` lines excluded from `degraded`, prefix tuple pinned.
|
||||
|
||||
**Verification** — `bash .agents/validate.sh`: **1630 passed, 15 skipped**; coverage **98.94%** (floor 94% met); `ruff` clean; `pyright` 0 errors. Real read-only `--once` on this box: 14 listeners / 8 established, matching `ss -ltnH` exactly, no error lines. No live-infrastructure changes; no git index/history commands.
|
||||
|
||||
**Docs** — README agent section + AGENTS.md `sockets` rules and the add-on-prefix sentence.
|
||||
|
||||
**Next pending task, not started**: `04_version_and_capture.md`.
|
||||
+1
@@ -0,0 +1 @@
|
||||
ok
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
== uv run pytest --cov (hermetic: no network, live tests skip) ==
|
||||
........................................................................ [ 4%]
|
||||
........................................................................ [ 8%]
|
||||
........................................................................ [ 13%]
|
||||
........................................................................ [ 17%]
|
||||
........................................................................ [ 21%]
|
||||
........................................................................ [ 26%]
|
||||
.......................................................................s [ 30%]
|
||||
........................................................................ [ 35%]
|
||||
........................................................................ [ 39%]
|
||||
........................................................................ [ 43%]
|
||||
........................................................................ [ 48%]
|
||||
........................................................................ [ 52%]
|
||||
........................................................................ [ 56%]
|
||||
........................................................................ [ 61%]
|
||||
........................................................................ [ 65%]
|
||||
........................................................................ [ 70%]
|
||||
................sssssssssss............................................. [ 74%]
|
||||
........................................................................ [ 78%]
|
||||
........................................................................ [ 83%]
|
||||
........................................................................ [ 87%]
|
||||
........................................................................ [ 91%]
|
||||
........................................................................ [ 96%]
|
||||
...sss....................................................... [100%]
|
||||
=============================== warnings summary ===============================
|
||||
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
|
||||
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
|
||||
from starlette.testclient import TestClient as TestClient # noqa
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
================================ tests coverage ================================
|
||||
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
|
||||
|
||||
Name Stmts Miss Cover Missing
|
||||
------------------------------------------------------------------
|
||||
src/dashboards/__init__.py 3 0 100%
|
||||
src/dashboards/__main__.py 14 1 93% 32
|
||||
src/dashboards/alerts.py 176 0 100%
|
||||
src/dashboards/api.py 440 0 100%
|
||||
src/dashboards/app.py 186 0 100%
|
||||
src/dashboards/auth.py 265 0 100%
|
||||
src/dashboards/certs.py 292 0 100%
|
||||
src/dashboards/collectors.py 206 5 98% 303-304, 308-309, 326
|
||||
src/dashboards/config.py 120 0 100%
|
||||
src/dashboards/debug.py 14 0 100%
|
||||
src/dashboards/poller.py 165 3 98% 192-194, 279
|
||||
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
|
||||
src/dashboards/rates.py 73 0 100%
|
||||
src/dashboards/severity.py 49 0 100%
|
||||
src/dashboards/sources/__init__.py 0 0 100%
|
||||
src/dashboards/sources/disks.py 134 0 100%
|
||||
src/dashboards/sources/host.py 209 0 100%
|
||||
src/dashboards/sources/llm.py 178 1 99% 285
|
||||
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
|
||||
src/dashboards/sources/nut_sim.py 134 0 100%
|
||||
src/dashboards/store.py 313 2 99% 553-554
|
||||
------------------------------------------------------------------
|
||||
TOTAL 3496 37 99%
|
||||
Required test coverage of 94% reached. Total coverage: 98.94%
|
||||
1630 passed, 15 skipped, 1 warning in 449.51s (0:07:29)
|
||||
== uv run ruff check src tests agent ==
|
||||
All checks passed!
|
||||
== uv run pyright ==
|
||||
0 errors, 0 warnings, 0 informations
|
||||
== node --check src/dashboards/web/static/*.js ==
|
||||
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
|
||||
fastapi 0.153.0 0.159.1 wheel
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
**Task 04 complete — `AGENT_VERSION` 1.2.0, the SCHEMA, and the gateway captures.**
|
||||
|
||||
**Agent** — `agent/host_agent.py`: `AGENT_VERSION` `1.1.0` → `1.2.0`; the `GET /schema` block already names `haproxy`/`nginx`/`sockets` (each with its `available`), `--no-proxy`, `--haproxy-config`, `--nginx-config`. `tests/test_agent.py` pins the constant plus all of that; the phase-15 TLS pin keeps its schema assertions and no longer competes over the number.
|
||||
|
||||
**Capture** — new `scripts/capture-gateway-config.sh` (`GATEWAY_SSH`, default `gateway-ops`): one `ssh … cat` per document (`scp` would copy `/proc/net/tcp` as 0 bytes), no sudo, haproxy.cfg + `nginx.conf` **plus every document its own glob `include` names** + `/proc/net/tcp{,6}`, provenance header naming the remote path and script, and a refusal (`exit 1`, nothing written, temp removed) for a failed/empty read, an unnameable include dir or a missing glob. Ran it: 7 fixtures now in `tests/fixtures/` (haproxy, nginx main + `conf_d_http` + `stream_d_stream`, both socket tables).
|
||||
|
||||
**Deviation from the task's "scrubs nothing" assumption** — configs stay verbatim (domains, `proxy_pass` targets, key *paths*); the two socket tables now replace **only globally routable endpoints** with documentation space (RFC 5737/3849), idempotent and column-width-preserving, because a global IPv6 here is EUI-64 (MAC-derived) and an inbound visitor's address is not ours to publish — the smartctl serial rule (invariant 6). Documented in README + AGENTS.md and pinned in both test files.
|
||||
|
||||
**Tests** — `test_agent.py`: 7 tests over the captures (header/lineage, the merged include tree, SNI map + `proxy_pass $var` unmerged, every table row decodes, config↔socket join on 22/443/4443/8443/9120, no globally routable endpoint, one collector answering all three sections). `test_deploy_scripts.py`: 10 pins (alias dialed and nothing else, no `sudo`/IP in code, full tree recorded, domains verbatim, dead gateway + empty read leave fixtures alone, the two refusal messages, substitution behaviour + idempotence).
|
||||
|
||||
**Live probe (read-only)** — `data/logs/verify-28-task04.py` → `28-task04-probe.out`: gateway `active`, installed **1.1.0**, CA-only `/health` 200, `/snapshot` 403 `client certificate required`, mTLS snapshot has every pre-1.2.0 section with live numbers and **none** of the three new ones → **PASS: no behaviour change**.
|
||||
|
||||
**Gate** — `bash .agents/validate.sh`: **1650 passed, 15 skipped**, coverage **98.94%**, ruff clean, pyright 0 errors, `node --check` clean, hygiene clean (only the pre-existing `fastapi` advisory); `pytest -m live --live` with `.env.dev`: **11 passed**. Docs updated in README + AGENTS.md (capture script, the three capture rules, the fleet read). Nothing staged or committed; new script/fixtures are untracked for task 05.
|
||||
+1
@@ -0,0 +1 @@
|
||||
ok
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
== uv run pytest --cov (hermetic: no network, live tests skip) ==
|
||||
........................................................................ [ 4%]
|
||||
........................................................................ [ 8%]
|
||||
........................................................................ [ 12%]
|
||||
........................................................................ [ 17%]
|
||||
........................................................................ [ 21%]
|
||||
........................................................................ [ 25%]
|
||||
........................................................................ [ 30%]
|
||||
.......s................................................................ [ 34%]
|
||||
........................................................................ [ 38%]
|
||||
........................................................................ [ 43%]
|
||||
........................................................................ [ 47%]
|
||||
........................................................................ [ 51%]
|
||||
........................................................................ [ 56%]
|
||||
........................................................................ [ 60%]
|
||||
........................................................................ [ 64%]
|
||||
........................................................................ [ 69%]
|
||||
....................................sssssssssss......................... [ 73%]
|
||||
........................................................................ [ 77%]
|
||||
........................................................................ [ 82%]
|
||||
........................................................................ [ 86%]
|
||||
........................................................................ [ 90%]
|
||||
........................................................................ [ 95%]
|
||||
.......................sss.............................................. [ 99%]
|
||||
......... [100%]
|
||||
=============================== warnings summary ===============================
|
||||
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
|
||||
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
|
||||
from starlette.testclient import TestClient as TestClient # noqa
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
================================ tests coverage ================================
|
||||
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
|
||||
|
||||
Name Stmts Miss Cover Missing
|
||||
------------------------------------------------------------------
|
||||
src/dashboards/__init__.py 3 0 100%
|
||||
src/dashboards/__main__.py 14 1 93% 32
|
||||
src/dashboards/alerts.py 176 0 100%
|
||||
src/dashboards/api.py 440 0 100%
|
||||
src/dashboards/app.py 186 0 100%
|
||||
src/dashboards/auth.py 265 0 100%
|
||||
src/dashboards/certs.py 292 0 100%
|
||||
src/dashboards/collectors.py 206 5 98% 303-304, 308-309, 326
|
||||
src/dashboards/config.py 120 0 100%
|
||||
src/dashboards/debug.py 14 0 100%
|
||||
src/dashboards/poller.py 165 3 98% 192-194, 279
|
||||
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
|
||||
src/dashboards/rates.py 73 0 100%
|
||||
src/dashboards/severity.py 49 0 100%
|
||||
src/dashboards/sources/__init__.py 0 0 100%
|
||||
src/dashboards/sources/disks.py 134 0 100%
|
||||
src/dashboards/sources/host.py 209 0 100%
|
||||
src/dashboards/sources/llm.py 178 1 99% 285
|
||||
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
|
||||
src/dashboards/sources/nut_sim.py 134 0 100%
|
||||
src/dashboards/store.py 313 2 99% 553-554
|
||||
------------------------------------------------------------------
|
||||
TOTAL 3496 37 99%
|
||||
Required test coverage of 94% reached. Total coverage: 98.94%
|
||||
1650 passed, 15 skipped, 1 warning in 457.28s (0:07:37)
|
||||
== uv run ruff check src tests agent ==
|
||||
All checks passed!
|
||||
== uv run pyright ==
|
||||
0 errors, 0 warnings, 0 informations
|
||||
== node --check src/dashboards/web/static/*.js ==
|
||||
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
|
||||
fastapi 0.153.0 0.159.1 wheel
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
**Task 05 complete — gateway host now runs agent 1.2.0, verified over mTLS.**
|
||||
|
||||
- Re-staged `/home/ops/agent-install/` on `gateway-ops` (gap-10 rule): `host_agent.py` 1.2.0 (`b0e9ebf1…`, md5 `9e062e4f…`) + installer (`ffbcd91f…`), old files snapshotted as `*-20261022-backup` (names the sudo grant doesn't name).
|
||||
- Installed via the granted path with the unit's own args (`--bind 10.1.1.27 --port 9120`); **re-run guard printed nothing** — `systemctl cat` byte-identical before/after (`26bd812…`); bundle + token reused, not rewritten.
|
||||
- `sudo systemctl restart dashboards-agent` → `active`, pid 20708, `https … client cert required, rev 2`.
|
||||
- Verified: CA-only `--status` answers `{"ok":true,…,"version":"1.2.0"}` and still 403s `/snapshot`; mTLS probe (`data/logs/verify-28-task05.py` → `28-task05-verify.out`, exit 0) asserts version 1.2.0, all three new keys, `haproxy.available=true` (6 routed frontends/6 backends), `nginx.available=true` (11 servers, `$targetBackend` map, 19 entries), `sockets.available=true` (17 listens incl. **443**), and every pre-1.2.0 section intact with live numbers, no core error.
|
||||
- Dashboard side untouched: `./scripts/dev-server.sh status` → **nine sources `ok`**; `/api/system/snapshot` gateway row `agent.version 1.2.0`, no `degraded`; `/api/disks/snapshot` still 2 hosts.
|
||||
- Transcript: `data/logs/28-task05-rollout.md` (+ `28-task05-raw.log`) — hashes, ports and counts only, no config body, no address, no credential.
|
||||
|
||||
**Gates:** `bash .agents/validate.sh` → **exit 0**: `uv run pytest --cov --cov-fail-under=94` **1650 passed / 15 skipped**, coverage **98.94 %**; `ruff check src tests agent` clean; `pyright` clean; `node --check` clean; hygiene clean. Re-run after doc edits `uv run pytest -q` → exit 0; doc pins `tests/test_auth_docs.py tests/test_deploy_scripts.py` → **141 passed**; `uv run pytest -m live --live` → **11 passed** (read-only).
|
||||
|
||||
**Docs:** `AGENTS.md` fleet record (gateway on 1.2.0, other two still 1.1.0 and why that costs nothing; the rollout procedure, byte-identical unit, verification pointers) + gap 11's end-state line; `README.md` new "Upgrading an already-registered host is one file, not a rollout" paragraph (bundle-on-disk vs `--cert-bundle` flag, silent re-run guard, restart, both probes).
|
||||
|
||||
**Deviations:** no code change (as the task states); `uv pip list --outdated` shows pre-existing `fastapi 0.153.0 → 0.159.1` drift left alone — this task moved no dependencies and a bump belongs in its own change.
|
||||
|
||||
**Next pending:** phase 28 has no further tasks; `29_gateway_dashboard_page/01_…` is next in `todo/`.
|
||||
+1
@@ -0,0 +1 @@
|
||||
ok
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
== uv run pytest --cov (hermetic: no network, live tests skip) ==
|
||||
........................................................................ [ 4%]
|
||||
........................................................................ [ 8%]
|
||||
........................................................................ [ 12%]
|
||||
........................................................................ [ 17%]
|
||||
........................................................................ [ 21%]
|
||||
........................................................................ [ 25%]
|
||||
........................................................................ [ 30%]
|
||||
.......s................................................................ [ 34%]
|
||||
........................................................................ [ 38%]
|
||||
........................................................................ [ 43%]
|
||||
........................................................................ [ 47%]
|
||||
........................................................................ [ 51%]
|
||||
........................................................................ [ 56%]
|
||||
........................................................................ [ 60%]
|
||||
........................................................................ [ 64%]
|
||||
........................................................................ [ 69%]
|
||||
....................................sssssssssss......................... [ 73%]
|
||||
........................................................................ [ 77%]
|
||||
........................................................................ [ 82%]
|
||||
........................................................................ [ 86%]
|
||||
........................................................................ [ 90%]
|
||||
........................................................................ [ 95%]
|
||||
.......................sss.............................................. [ 99%]
|
||||
......... [100%]
|
||||
=============================== warnings summary ===============================
|
||||
.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1
|
||||
/var/home/ducoterra/Projects/Personal/dashboards/.venv/lib64/python3.14/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
|
||||
from starlette.testclient import TestClient as TestClient # noqa
|
||||
|
||||
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
|
||||
================================ tests coverage ================================
|
||||
_______________ coverage: platform linux, python 3.14.4-final-0 ________________
|
||||
|
||||
Name Stmts Miss Cover Missing
|
||||
------------------------------------------------------------------
|
||||
src/dashboards/__init__.py 3 0 100%
|
||||
src/dashboards/__main__.py 14 1 93% 32
|
||||
src/dashboards/alerts.py 176 0 100%
|
||||
src/dashboards/api.py 440 0 100%
|
||||
src/dashboards/app.py 186 0 100%
|
||||
src/dashboards/auth.py 265 0 100%
|
||||
src/dashboards/certs.py 292 0 100%
|
||||
src/dashboards/collectors.py 206 5 98% 303-304, 308-309, 326
|
||||
src/dashboards/config.py 120 0 100%
|
||||
src/dashboards/debug.py 14 0 100%
|
||||
src/dashboards/poller.py 165 3 98% 192-194, 279
|
||||
src/dashboards/prom.py 112 5 96% 126, 163, 177, 180, 183
|
||||
src/dashboards/rates.py 73 0 100%
|
||||
src/dashboards/severity.py 49 0 100%
|
||||
src/dashboards/sources/__init__.py 0 0 100%
|
||||
src/dashboards/sources/disks.py 134 0 100%
|
||||
src/dashboards/sources/host.py 209 0 100%
|
||||
src/dashboards/sources/llm.py 178 1 99% 285
|
||||
src/dashboards/sources/nut.py 413 20 95% 153, 172, 186, 198, 204, 217, 225, 241, 319-320, 322, 579, 591, 626, 657, 661, 718-719, 724, 736
|
||||
src/dashboards/sources/nut_sim.py 134 0 100%
|
||||
src/dashboards/store.py 313 2 99% 553-554
|
||||
------------------------------------------------------------------
|
||||
TOTAL 3496 37 99%
|
||||
Required test coverage of 94% reached. Total coverage: 98.94%
|
||||
1650 passed, 15 skipped, 1 warning in 455.76s (0:07:35)
|
||||
== uv run ruff check src tests agent ==
|
||||
All checks passed!
|
||||
== uv run pyright ==
|
||||
0 errors, 0 warnings, 0 informations
|
||||
== node --check src/dashboards/web/static/*.js ==
|
||||
advisory: dependencies are out of date (AGENTS.md rule 8 — update on purpose):
|
||||
fastapi 0.153.0 0.159.1 wheel
|
||||
Reference in New Issue
Block a user