feat(config): promote /etc/decnet/decnet.ini to real config with domain sections
The config file `decnet init` dropped at /etc/decnet/config.ini was a stub with a single [decnet] header saying 'reserved for future structured settings.' Admins who wanted to tune DECNET_API_HOST, DECNET_DB_URL, DECNET_BATCH_SIZE, etc. had to hunt env.py for the exact variable name and drop it in .env.local. Changes: - decnet/config_ini.py — adds a _DOMAIN_MAP translation table covering [api], [web], [database], [bus], [swarm], [logging], [ingester], [tracing]. Loads regardless of mode; unknown keys inside a known section log a WARNING (operator typos shouldn't be silent). Explicit key map (not auto kebab-to-snake) so [web] admin-user lands in DECNET_ADMIN_USER without silently renaming the env-var contract consumers import from decnet.env. - decnet/cli/init.py — renames the placeholder target config.ini → decnet.ini (unifies with the name already used by load_ini_config and the enroll bundle's _render_decnet_ini). Placeholder body now shows every domain section as a commented example so admins learn the shape by reading. Deinit removes both decnet.ini and the legacy config.ini so upgrading hosts leave no orphan file. Precedence is unchanged: real env > INI > built-in default in env.py. os.environ.setdefault means systemd EnvironmentFile= and one-off DECNET_FOO=bar decnet ... invocations always win. Secrets explicitly NOT moved to the INI: - DECNET_JWT_SECRET - DECNET_ADMIN_PASSWORD - DECNET_DB_PASSWORD They stay in .env.local / EnvironmentFile= — never in a group-readable INI, never in a diff, never on the dashboard. Dev/profiling flags (DECNET_DEVELOPER, DECNET_EMBED_*, DECNET_PROFILE_*) also stay env-only per maintainer direction — dev knobs shouldn't be one 'I'll flip this for tonight' away. Tests: +5 in test_config_ini.py (domain sections load regardless of mode, env beats INI for domain keys, unknown key warns, absent section is no-op, role section beats domain section via setdefault precedence). +1 in test_init.py (placeholder writes decnet.ini with every section header present as commented guidance). 31 tests pass across the two files (was 26).
This commit is contained in:
@@ -32,10 +32,64 @@ from .utils import console, log
|
||||
|
||||
|
||||
_CONFIG_PLACEHOLDER = """\
|
||||
# /etc/decnet/config.ini — DECNET master-host config.
|
||||
# Placeholder; reserved for future structured settings.
|
||||
# Today, most knobs live in /opt/decnet/.env.local as env vars.
|
||||
# /etc/decnet/decnet.ini — DECNET host config.
|
||||
#
|
||||
# Every key is OPTIONAL. Absent keys fall through to env-var defaults
|
||||
# defined in decnet/env.py. Real env vars always win over this file
|
||||
# (precedence: env > INI > default), so systemd EnvironmentFile= and
|
||||
# one-off `DECNET_FOO=bar decnet ...` invocations always take effect.
|
||||
#
|
||||
# Secrets (JWT, admin password, DB password) intentionally DO NOT
|
||||
# live here. Put them in /opt/decnet/.env.local or the systemd
|
||||
# EnvironmentFile= — never in a group-readable INI.
|
||||
|
||||
[decnet]
|
||||
# mode = master # or "agent"
|
||||
|
||||
# [api]
|
||||
# host = 127.0.0.1
|
||||
# port = 8000
|
||||
|
||||
# [web]
|
||||
# host = 127.0.0.1
|
||||
# port = 8080
|
||||
# admin-user = admin
|
||||
# cors-origins = http://localhost:8080 # comma-separated
|
||||
|
||||
# [database]
|
||||
# type = sqlite # or "mysql"
|
||||
# url = mysql+asyncmy://user@host:3306/decnet # if set, wins over host/port/name/user
|
||||
# host = localhost
|
||||
# port = 3306
|
||||
# name = decnet
|
||||
# user = decnet
|
||||
|
||||
# [bus]
|
||||
# enabled = true
|
||||
# type = unix # or "fake"
|
||||
# socket = /run/decnet/bus.sock
|
||||
# group = decnet
|
||||
|
||||
# [swarm]
|
||||
# master-host = 10.0.0.1
|
||||
# syslog-port = 6514
|
||||
# swarmctl-port = 8770
|
||||
|
||||
# [logging]
|
||||
# system-log = /var/log/decnet/decnet.system.log
|
||||
# ingest-log = /var/log/decnet/decnet.log
|
||||
# agent-log = /var/log/decnet/agent.log
|
||||
|
||||
# [ingester]
|
||||
# batch-size = 100
|
||||
# batch-max-wait-ms = 250
|
||||
|
||||
# [tracing]
|
||||
# enabled = false
|
||||
# otel-endpoint = http://localhost:4317
|
||||
|
||||
# [agent]
|
||||
# Managed by the enroll bundle — do NOT edit by hand on an agent host.
|
||||
"""
|
||||
|
||||
|
||||
@@ -487,7 +541,13 @@ def register(app: typer.Typer) -> None:
|
||||
lambda: (_run(["systemctl", "daemon-reload"], dry_run=dry_run), "ok")[1],
|
||||
)
|
||||
_step(
|
||||
f"remove {etc_decnet / 'config.ini'}",
|
||||
f"remove {etc_decnet / 'decnet.ini'}",
|
||||
lambda: _remove_file(etc_decnet / "decnet.ini", dry_run=dry_run),
|
||||
)
|
||||
# Legacy name from pre-domain-sections placeholder era.
|
||||
# Harmless if absent (the _remove_file step logs skip).
|
||||
_step(
|
||||
f"remove legacy {etc_decnet / 'config.ini'}",
|
||||
lambda: _remove_file(etc_decnet / "config.ini", dry_run=dry_run),
|
||||
)
|
||||
_step(
|
||||
@@ -572,8 +632,8 @@ def register(app: typer.Typer) -> None:
|
||||
_ensure_dir(p, mode=m, owner=o, group=g, dry_run=dry_run),
|
||||
)
|
||||
_step(
|
||||
f"write {etc_decnet / 'config.ini'}",
|
||||
lambda: _ensure_config(etc_decnet / "config.ini", group, dry_run=dry_run),
|
||||
f"write {etc_decnet / 'decnet.ini'}",
|
||||
lambda: _ensure_config(etc_decnet / "decnet.ini", group, dry_run=dry_run),
|
||||
)
|
||||
_step(
|
||||
"install systemd units",
|
||||
|
||||
Reference in New Issue
Block a user