SEP-1800: Support a global DATABASE settings block that per-service *__DATABASE__* names override - #1375
SEP-1800: Support a global DATABASE settings block that per-service *__DATABASE__* names override#1375peter-o-addo wants to merge 8 commits into
DATABASE settings block that per-service *__DATABASE__* names override#1375Conversation
DATABASE settings block that per-service *__DATABASE__* names override
1 — Per-service env beats global, regardless of orderSet both 2 — One global secret file supplies all three servicesMounted only 3 — Per-service secret file overrides globalMounted both 4 — Foreign prefix is invisible to another serviceSet only 5 — Celery beat URI picks up the global mounted passwordMounted 6 — Side-car profile defines DATABASE once; all services resolve the same connectionParsed |
There was a problem hiding this comment.
Pull request overview
Adds deterministic per-service precedence over global database settings and supports one shared side-car database password mount.
Changes:
- Separates global and prefixed settings before merging.
- Shares side-car database configuration through YAML aliases.
- Adds precedence/profile tests and operator documentation.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
app/core/config.py |
Implements prefixed-key precedence. |
sidecar/settings.yaml |
Defines one shared database block. |
README.md |
Documents global settings resolution. |
sidecar/README.md |
Updates side-car mount guidance. |
changelog.d/SEP-1800.config.md |
Adds the operator-facing change note. |
tests/app/core/test_config.py |
Tests source precedence and global passwords. |
tests/sidecar/test_embedded_settings.py |
Tests the shared embedded profile. |
tests/sidecar/test_settings_env.py |
Clarifies shell-export behavior. |
Suppressed comments (1)
app/core/config.py:317
- Update
settings_customise_sources’s docstring to describe the new collision rule: prefixed keys override unprefixed keys within each environment, dotenv, or secret-file source, while the existing source ordering still wins across sources. Without that distinction, callers cannot infer the actual precedence contract from this shared API.
unprefixed: dict[str, Any] = {}
prefixed: dict[str, Any] = {}
for key, value in env_source.env_vars.items():
stripped = prefix_pattern.sub(r"\1", key)
(unprefixed if stripped == key else prefixed)[stripped] = value
env_source.env_vars = {**unprefixed, **prefixed}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| An unprefixed global name such as `DATABASE__PASSWORD` resolves for every prefixed | ||
| settings class that reads the same destination — one mounted file reaches SEP, | ||
| Inventory, and Tasks when all three share one database. A per-service spelling such | ||
| as `SEP__DATABASE__PASSWORD` overrides the global one for that service only; when | ||
| both are present, the more specific name wins regardless of ordering. A name spelled | ||
| with another class's prefix — `INVENTORY__DATABASE__PASSWORD` read by | ||
| `SEPSettings`, say — stays invisible to that class. |
| @@ -0,0 +1 @@ | |||
| A global unprefixed settings name such as DATABASE__PASSWORD now resolves for every prefixed service; a per-service {SEP,INVENTORY,TASKS}__* spelling overrides it when both are set. The PMM side-car mount recipe is one DATABASE__PASSWORD file instead of three identical per-service password files. | |||
| prefix_pattern = re.compile( | ||
| f"^{'__'.join(cls.SETTINGS_PREFIXES).lower()}__([a-zA-Z0-9_-]+)$" | ||
| ) | ||
| for env_source in [env_settings, dotenv_settings, secret_settings]: | ||
| env_vars = {} | ||
| unprefixed: dict[str, Any] = {} | ||
| prefixed: dict[str, Any] = {} | ||
| for key, value in env_source.env_vars.items(): | ||
| env_vars[ | ||
| re.sub(f"^{env_prefix}__([a-zA-Z0-9_-]+)$", r"\1", key) | ||
| ] = value | ||
| env_source.env_vars = env_vars | ||
| stripped = prefix_pattern.sub(r"\1", key) | ||
| (unprefixed if stripped == key else prefixed)[stripped] = value | ||
| env_source.env_vars = {**unprefixed, **prefixed} |
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||
Summary
Give per-service settings names deterministic priority over global unprefixed ones, and document one shared DATABASE mount for the PMM side-car.
{SEP,INVENTORY,TASKS}__*beats the global spelling within each sourceDATABASE__PASSWORD, per-service override precedence, and the updated side-car mount recipeTested
Checklist
make test)make run-pre-commit)make makemigrations)changelog.d/if the change is user-facing (make changelog-add), or confirmed N/A (internal-only change, or a same-release-cycle fix for an unreleased sibling ticket)