Skip to content

Commit 85bb77e

Browse files
committed
feat: show storage usage and restart traffic
- Report mount-aware state and database-root capacity with role assignments - Separate role allocation from PostgreSQL and Redis-compatible live usage - Restart only Traefik and wait for container health after confirmation
1 parent 9912137 commit 85bb77e

26 files changed

Lines changed: 901 additions & 56 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
schema: spec-driven
2+
created: 2026-08-21
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## Context
2+
3+
`status._host()` currently measures only free space for `Paths.state`, even though configuration now
4+
contains an ordered `Host.data_roots` catalog and every role selects one root. The guided Host choice is
5+
a read-only screen, while database roles already have lifecycle actions. `database.info()` knows the role
6+
data path and delegates live facts to concrete engine modules, but it does not measure directory usage or
7+
engine-native data totals.
8+
9+
## Goals / Non-Goals
10+
11+
**Goals:**
12+
13+
- Identify every managed storage path and the filesystem that currently backs it.
14+
- Keep frequent root-menu status checks bounded while providing richer on-demand database details.
15+
- Report physical role storage separately from engine-native logical or in-memory data.
16+
- Provide one deliberate guided action that actually restarts Traefik and verifies its health.
17+
18+
**Non-Goals:**
19+
20+
- No expected-device configuration, mount creation, mount repair, or startup ordering.
21+
- No per-table, per-key, or per-logical-database breakdown.
22+
- No direct host restart command or change to database lifecycle behavior.
23+
- No storage quotas, cleanup, forecasting, or background metric collection.
24+
25+
## Decisions
26+
27+
### Read the current Linux mount table and retain separate storage concepts
28+
29+
Host assessment reads `/proc/self/mountinfo`, decodes its escaped fields, and selects the longest mount
30+
point containing each managed path. It reports mount point, source, filesystem type, and
31+
`shutil.disk_usage()` capacity without exposing mount options. The canonical state path remains
32+
`host.storage`; an additive `host.database_storage` list contains one item per configured root plus exact
33+
role assignments. Existing structured status fields remain present, so the status version does not
34+
change. Host health includes every configured storage location, and failures remain partial and
35+
path-specific.
36+
37+
Using only the configured path was rejected because it cannot reveal that an intended custom mount is
38+
currently backed by `/`. Calling `findmnt` was rejected because the kernel mount table provides the
39+
required facts without adding another host tool dependency.
40+
41+
### Measure role files only in database Details
42+
43+
Database Details recursively sums allocated blocks under the selected role's data directory without
44+
following symlinks. It combines that value with the same backing-filesystem facts used by host status.
45+
The root menu does not scan role trees, because a large PostgreSQL data directory could make every guided
46+
refresh slow.
47+
48+
Filesystem allocation and engine data are labeled separately. PostgreSQL reports the sum of
49+
`pg_database_size` for connectable non-template databases and their count. Redis and Dragonfly report
50+
dataset memory and total keys from `INFO memory` and `INFO keyspace`. A stopped role still reports path,
51+
directory allocation, and filesystem capacity while explicitly marking live data unavailable.
52+
53+
### Restart only the installed Traefik service
54+
55+
The Host submenu presents Restart traffic, warns that active database connections may briefly drop, and
56+
requires confirmation. The host operation takes the exclusive host lock, requires the installed Traefik
57+
Compose file, invokes `docker compose restart traefik`, and polls the owned container's health within the
58+
existing health timeout. It does not render files, run initialization, renew certificates, or touch
59+
database Compose projects.
60+
61+
A Compose `up` was rejected because an unchanged service may not restart. Full initialization was
62+
rejected because it performs unrelated DNS, certificate, repository, and systemd convergence.
63+
64+
## Risks / Trade-offs
65+
66+
- [Two configured paths use the same filesystem] -> Show both exact paths and assignments; repeated
67+
capacity values truthfully describe their shared backing filesystem.
68+
- [A configured mount is absent] -> Show the actual fallback mount and source; evdb does not infer the
69+
intended device or repair it.
70+
- [A role contains many files] -> Restrict recursive allocation measurement to an explicit Details view.
71+
- [Live engine metrics fail] -> Preserve filesystem facts and report live data unavailable with a bounded
72+
error instead of hiding the rest of Details.
73+
- [Traefik restart interrupts clients] -> Require confirmation, restart only Traefik, and wait for health
74+
before reporting completion.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## Why
2+
3+
Host details currently report only free space under `/var/lib/evdb`, hiding configured database roots and
4+
the filesystem that actually backs each path. Operators also cannot restart the dedicated Traefik traffic
5+
proxy from the guided Host section or see how much disk and live data one database role uses.
6+
7+
## What Changes
8+
9+
- Show state storage and every configured database root with its exact path, backing mount and source,
10+
filesystem type, used/total/free capacity, and assigned database roles.
11+
- Show each database role's allocated directory size and backing filesystem in Details without scanning
12+
every database during routine host status refreshes.
13+
- Show live PostgreSQL logical size and database count, or Redis/Dragonfly dataset memory and key count;
14+
retain filesystem facts and identify live data as unavailable when a role is stopped.
15+
- Add a confirmed Restart traffic action under Host that performs a real Traefik Compose restart, warns
16+
about a brief connection interruption, and waits for Traefik health without restarting databases or
17+
rerunning initialization.
18+
19+
## Capabilities
20+
21+
### New Capabilities
22+
23+
None.
24+
25+
### Modified Capabilities
26+
27+
- `jobs`: Expand host status from canonical state storage to mount-aware state and database-root storage.
28+
- `operator-cli`: Add the guided Host traffic action and storage presentation behavior.
29+
- `http`: Expand detailed database information with physical and engine-native data usage.
30+
- `deploy`: Define the dedicated proxy restart operation and its health requirement.
31+
32+
## Impact
33+
34+
The change affects host and database status collection, terminal rendering, Traefik Compose lifecycle
35+
helpers, PostgreSQL and Redis-compatible engine information, and focused unit tests. Structured status
36+
gains additive credential-free storage fields; no runtime dependency, direct CLI command, database
37+
restart, configuration schema, or backup format changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Dedicated traffic proxy restart
4+
The guided Host interface SHALL provide a confirmed Restart traffic operation that takes the exclusive
5+
host operation lock, restarts only the installed Traefik Compose service, and waits for the owned Traefik
6+
container to become healthy within the host health timeout. It SHALL preserve source, generated files,
7+
database services, database data, credentials, and certificate state and SHALL NOT rerun initialization.
8+
9+
#### Scenario: Traffic restart succeeds
10+
- **WHEN** the operator confirms Restart traffic and Traefik becomes healthy
11+
- **THEN** evdb reports completion without restarting any database Compose project
12+
13+
#### Scenario: Traffic restart remains unhealthy
14+
- **WHEN** Traefik does not become healthy before the timeout
15+
- **THEN** evdb reports the failed host operation and does not claim that traffic restart completed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: Detailed database view
4+
`evdb database info PROJECT/ROLE` SHALL display configured role and concrete engine, live image version
5+
and health, data and Compose paths, latest backup summary, native connection fields, and HTTP fields when
6+
enabled. Details SHALL show the data directory's allocated bytes and backing filesystem capacity, mount,
7+
source, and type. A running PostgreSQL role SHALL show total logical database bytes and connectable
8+
database count; a running Redis or Dragonfly role SHALL show dataset memory bytes and total key count.
9+
The command SHALL deliberately retrieve credentials from private host files and print them only to the
10+
terminal.
11+
12+
#### Scenario: Running database is shown
13+
- **WHEN** the selected role is running
14+
- **THEN** info distinguishes allocated directory storage from engine-native data size and count
15+
16+
#### Scenario: Stopped database is shown
17+
- **WHEN** the selected role is configured but stopped
18+
- **THEN** info still shows settings, storage and connection details, clearly identifies stopped state, and marks live data unavailable
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## MODIFIED Requirements
2+
3+
### Requirement: Host status
4+
Status SHALL include host identity, running evdb version, Traefik and network health, native listeners,
5+
one backup timer state, repository availability, source validation, and storage assessment. Storage SHALL
6+
cover `/var/lib/evdb` and every configured database root with exact path, backing mount point and source,
7+
filesystem type, used/total/free capacity, and assigned project/roles. It SHALL NOT assess machine-state
8+
compatibility, tool-version match, generated contract hashes, transaction directories, restore state, or
9+
maintenance timers.
10+
11+
#### Scenario: Disk space is below policy
12+
- **WHEN** free space for state storage or a configured database root is below the configured minimum
13+
- **THEN** host status identifies the affected path and backing filesystem and marks the host unhealthy
14+
15+
#### Scenario: Several database roots are configured
16+
- **WHEN** roles select database roots backed by different mounted filesystems
17+
- **THEN** host details show every root, its actual mount facts and capacity, and the roles assigned to it
18+
19+
#### Scenario: Backup timer is inactive
20+
- **WHEN** the one packaged backup timer is not loaded, enabled, and active
21+
- **THEN** host details report the inactive timer without listing per-database unit names
22+
23+
### Requirement: Structured status output
24+
`evdb status --json` SHALL emit one credential-free JSON object containing integer `version`, boolean
25+
`healthy`, a host object, a databases object keyed by exact project/role, and an errors array. Host fields
26+
SHALL cover identity, tool version, infrastructure, state storage, configured database-root storage,
27+
repository, and the one timer. Database fields SHALL cover project, role, engine, running, health,
28+
configured image, latest backup, and bounded error. Output SHALL omit machine state, contract comparison,
29+
operation history, restore, backup tests, and maintenance units.
30+
31+
#### Scenario: Automation requests JSON
32+
- **WHEN** status runs with `--json`
33+
- **THEN** stdout contains exactly one parseable document with additive mount-aware storage facts and no terminal presentation or credentials
34+
35+
#### Scenario: Human contract changes incompatibly
36+
- **WHEN** a future release removes or changes a required structured field
37+
- **THEN** the top-level status version changes
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Guided host actions
4+
Selecting Host from the guided root SHALL show mount-aware state and database-root storage, routing
5+
infrastructure, repository, timer, and current host errors followed by Restart traffic and Back actions.
6+
Restart traffic SHALL warn that active database connections may briefly drop and require confirmation
7+
before changing Traefik runtime state.
8+
9+
#### Scenario: Operator reviews custom storage
10+
- **WHEN** the host configures several database roots
11+
- **THEN** the Host view shows every exact path, backing mount and source, capacity, and assigned roles
12+
13+
#### Scenario: Operator declines traffic restart
14+
- **WHEN** the operator selects Restart traffic and declines confirmation
15+
- **THEN** Traefik and every database remain unchanged
16+
17+
#### Scenario: Operator confirms traffic restart
18+
- **WHEN** the operator confirms Restart traffic
19+
- **THEN** the Host view reports completion only after the dedicated Traefik container becomes healthy
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## 1. Host Storage And Traffic
2+
3+
- [x] 1.1 Report mount-aware state and configured database-root capacity with exact role assignments.
4+
- [x] 1.2 Add the confirmed guided Traefik restart action and verify container health after a real Compose restart.
5+
6+
## 2. Database Storage
7+
8+
- [x] 2.1 Add on-demand role directory allocation and backing-filesystem facts to Database Details.
9+
- [x] 2.2 Report PostgreSQL logical bytes and database count and Redis-compatible dataset memory and key count.
10+
11+
## 3. Verification
12+
13+
- [x] 3.1 Cover storage, stopped/live engine details, restart confirmation and health, partial failures, and structured output with focused tests.

openspec/specs/deploy/spec.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ evdb SHALL manage one dedicated Traefik Compose project that owns host ports 543
3232
- **WHEN** a PostgreSQL client connects through the native proxy and advertises the `postgresql` ALPN protocol
3333
- **THEN** Traefik completes TLS negotiation and routes the connection through the matching Postgres SNI router
3434

35+
### Requirement: Dedicated traffic proxy restart
36+
The guided Host interface SHALL provide a confirmed Restart traffic operation that takes the exclusive
37+
host operation lock, restarts only the installed Traefik Compose service, and waits for the owned Traefik
38+
container to become healthy within the host health timeout. It SHALL preserve source, generated files,
39+
database services, database data, credentials, and certificate state and SHALL NOT rerun initialization.
40+
41+
#### Scenario: Traffic restart succeeds
42+
- **WHEN** the operator confirms Restart traffic and Traefik becomes healthy
43+
- **THEN** evdb reports completion without restarting any database Compose project
44+
45+
#### Scenario: Traffic restart remains unhealthy
46+
- **WHEN** Traefik does not become healthy before the timeout
47+
- **THEN** evdb reports the failed host operation and does not claim that traffic restart completed
48+
3549
### Requirement: ACME DNS certificates
3650
Dedicated Traefik SHALL use a configured ACME DNS-01 resolver for native database certificates, a scoped DNS-provider credential in a private host file, and persistent `acme.json` with mode `0600`. It SHALL NOT require or bind ports 80 or 443.
3751

openspec/specs/http/spec.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,21 @@ Disposable integration tests SHALL run Redis and Dragonfly HTTP sidecars and pro
6464
- **THEN** each value appears only in its matching KV engine
6565

6666
### Requirement: Detailed database view
67-
`evdb database info PROJECT/ROLE` SHALL display configured role and concrete engine, live image version and health, data and Compose paths, latest backup summary, native connection fields, and HTTP fields when enabled. The command SHALL deliberately retrieve credentials from private host files and print them only to the terminal.
67+
`evdb database info PROJECT/ROLE` SHALL display configured role and concrete engine, live image version
68+
and health, data and Compose paths, latest backup summary, native connection fields, and HTTP fields when
69+
enabled. Details SHALL show the data directory's allocated bytes and backing filesystem capacity, mount,
70+
source, and type. A running PostgreSQL role SHALL show total logical database bytes and connectable
71+
database count; a running Redis or Dragonfly role SHALL show dataset memory bytes and total key count.
72+
The command SHALL deliberately retrieve credentials from private host files and print them only to the
73+
terminal.
74+
75+
#### Scenario: Running database is shown
76+
- **WHEN** the selected role is running
77+
- **THEN** info distinguishes allocated directory storage from engine-native data size and count
6878

6979
#### Scenario: Stopped database is shown
7080
- **WHEN** the selected role is configured but stopped
71-
- **THEN** info still shows settings, storage and connection details and clearly identifies stopped state
81+
- **THEN** info still shows settings, storage and connection details, clearly identifies stopped state, and marks live data unavailable
7282

7383
### Requirement: Complete Postgres connection details
7484
The Postgres information view SHALL show hostname, port, username, database name, TLS requirement, and a complete percent-encoded `postgresql://` URL containing the current host-owned password. The hostname SHALL be the project hostname `<project>.<host-id>.<base-domain>`.

0 commit comments

Comments
 (0)