Files
computer-playbook/utils/tests/swarm/force_shared_db.py
kevinveenbirkenbachandClaude Opus 5 27bed25eb3 style: drop the Exception-prefixed rationale comments
b384f4825 put two of them above churned_past_grace and has_fatal_task, and
e41508f48 put one in the force_shared_db module docstring. The prefix is not
a licence to keep explanatory prose; the comment rule wants it deleted.

The docstring paragraph keeps its content and loses only the prefix: it
records that Magento hard-fails setup:install against the central MariaDB
major, which is the reason the manager-pinned carve-out exists at all.

No executable line changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 02:59:21 +02:00

142 lines
5.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Force ``shared: true`` on every svc-db-* provider service in the swarm-test
inventory host_vars.
Swarm-test only (called by the swarm matrix after provision). In swarm an
embedded per-app database (``shared: false``) resolves to a per-app mariadb
whose volume is NFS-backed with no placement pin — InnoDB on NFS plus a
free-floating task is unsupported (locking, split-brain). Embedded DBs are a
compose-only pattern. Flipping every consumer's DB service to ``shared: true``
routes it to the central svc-db-* provider (as the baseline variant does), so
no per-app DB is scheduled in swarm.
A manager-pinned role keeps its volumes node-local (``swarm_nfs_backed``) and
its DB sidecar carries the matching placement constraint, so a literal
``shared: false`` there is honoured instead of overridden — Magento hard-fails
``setup:install`` on the central MariaDB major.
Inputs (env): ``INV_DIR`` — inventory dir holding ``host_vars/*.yml``
(default ``/tmp/inv``).
"""
from __future__ import annotations
import os
from pathlib import Path
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap
from utils import PROJECT_ROOT
from utils.cache.yaml import load_yaml_any
from utils.roles.mapping import ROLE_FILE_META_SERVICES
from utils.roles.meta_lookup import get_role_placement
_ROLES_DIR = PROJECT_ROOT / "roles"
def db_provider_service_keys(roles_dir: Path) -> set[str]:
"""Top-level provider service keys declared by every svc-db-* role."""
keys: set[str] = set()
for role_dir in sorted(roles_dir.glob("svc-db-*")):
services_file = role_dir / ROLE_FILE_META_SERVICES
if not services_file.exists():
continue
services = load_yaml_any(services_file)
if not isinstance(services, dict):
continue
for key, entry in services.items():
if isinstance(entry, dict) and any(
flag in entry for flag in ("enabled", "shared", "provides")
):
keys.add(str(key))
return keys
def pinned_local_db(app_id: str, svc_name: str) -> bool:
"""True when the role owns this DB on purpose and swarm can host it.
A literal ``shared: false`` (not a Jinja expression) is the role author
pinning an engine the central provider cannot serve. Honouring it is only
safe on a manager-pinned role, where the volume stays node-local and the
sidecar carries the matching placement constraint.
Args:
app_id: consumer role name, e.g. ``web-app-magento``.
svc_name: DB service key under the role's ``meta/services.yml``.
"""
services_file = _ROLES_DIR / app_id / ROLE_FILE_META_SERVICES
if not services_file.exists():
return False
services = load_yaml_any(services_file)
if not isinstance(services, dict):
return False
entry = services.get(svc_name)
if not isinstance(entry, dict) or entry.get("shared") is not False:
return False
placement = get_role_placement(_ROLES_DIR / app_id, role_name=app_id)
return str(placement or "").strip() == "manager"
def force_shared_true(host_vars_file: Path, db_keys: set[str]) -> bool:
"""Set ``shared: true`` on any DB-provider service already present under
``applications.<app>.services.<key>`` in host_vars_file. Only existing
entries are touched. Returns True if the file changed."""
if not host_vars_file.exists():
return False
yaml_rt = YAML(typ="rt")
yaml_rt.preserve_quotes = True
with host_vars_file.open("r", encoding="utf-8") as f:
doc = yaml_rt.load(f)
if not isinstance(doc, CommentedMap):
return False
applications = doc.get("applications")
if not isinstance(applications, CommentedMap):
return False
changed = False
for app_id, app_data in applications.items():
if not isinstance(app_data, CommentedMap):
continue
svc_map = app_data.get("services")
if not isinstance(svc_map, CommentedMap):
continue
for svc_name, svc in svc_map.items():
if (
svc_name in db_keys
and isinstance(svc, CommentedMap)
and svc.get("enabled") is not False
and svc.get("shared") is not True
and not pinned_local_db(str(app_id), str(svc_name))
):
svc["shared"] = True
changed = True
print(
f"[INFO] swarm force-shared: "
f"{app_id}.services.{svc_name} → shared=true"
)
if changed:
with host_vars_file.open("w", encoding="utf-8") as f:
yaml_rt.dump(doc, f)
return changed
def main() -> int:
inv_dir = Path(os.environ.get("INV_DIR", "/tmp/inv")) # noqa: S108 - ephemeral swarm-test inventory path, overridable via INV_DIR
host_vars_dir = inv_dir / "host_vars"
if not host_vars_dir.is_dir():
return 0
db_keys = db_provider_service_keys(_ROLES_DIR)
if not db_keys:
return 0
for host_vars_file in sorted(host_vars_dir.glob("*.yml")):
force_shared_true(host_vars_file, db_keys)
return 0
if __name__ == "__main__":
raise SystemExit(main())