mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 20:26:33 +00:00
fix(web-app-pgadmin): keep the converger that asks the application, drop the one that asked a marker file
The merge left the role with two ways to the same end. The wired shell path called setup.py update-user, so it could neither create a missing account nor clear a lockout, and it decided its own idempotency from a sha256 marker in the persistent volume, which lies after a restore: marker matching, database not, reported unchanged and did nothing. The python converger from 0709de6fa1 queries the application instead, creates what is missing, verifies the password, unlocks, resets the failed-login counter and ensures the Administrator role, and it carried the only tests either path has; it was wired to nothing after the merge resolved towards manager_ops.yml. That engine now serves the surviving wiring: renamed into the family spelling the sibling roles use, reading the env names the task already passes and emitting the markers the task already parses. tasks/administrator.yml had no caller anywhere in the tree and goes with it.
Trade-off worth knowing: the deleted path used the supported pgAdmin CLI while this one uses internal APIs, so a pgAdmin upgrade can move it. Neither path has ever executed in CI, the pgadmin job died at the health wait before reaching the realign, so the choice rests on capability and tests rather than on field evidence.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0d3656e0c5
commit
eb07583e80
5 files changed
+30
-84
No files matched your search
+24
-10
@@ -1,11 +1,14 @@
|
||||
"""Converge the pgAdmin administrator account on every deploy.
|
||||
|
||||
Args:
|
||||
PGADMIN_DEFAULT_EMAIL: login e-mail of the administrator (container env).
|
||||
PGADMIN_DEFAULT_PASSWORD: password the account must accept (container env).
|
||||
Usage:
|
||||
Piped into the ``/venv/bin/python3 -`` of the running pgAdmin container.
|
||||
|
||||
Prints ``PGADMIN_ADMINISTRATOR=changed`` when it created or repaired the
|
||||
account and ``PGADMIN_ADMINISTRATOR=ok`` when the account already matched.
|
||||
Args:
|
||||
PGADMIN_ADMIN_EMAIL: login e-mail of the administrator (container env).
|
||||
PGADMIN_ADMIN_PASSWORD: password the account must accept (container env).
|
||||
|
||||
Exit: 0 with ``[CHANGED]`` or ``[UNCHANGED]`` on stdout, 1 with ``[FAIL]``
|
||||
on stderr.
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -41,7 +44,8 @@ def converge(email: str, password: str) -> str:
|
||||
}
|
||||
)
|
||||
if not created:
|
||||
sys.exit(f"creating the administrator failed: {message}")
|
||||
print(f"[FAIL] creating the administrator failed: {message}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
return "changed"
|
||||
secret = normalise_password(password)
|
||||
drift = (
|
||||
@@ -66,12 +70,22 @@ def main() -> None:
|
||||
import config
|
||||
from pgadmin import create_app
|
||||
|
||||
email = os.environ.get("PGADMIN_ADMIN_EMAIL", "")
|
||||
password = os.environ.get("PGADMIN_ADMIN_PASSWORD", "")
|
||||
if not email or not password:
|
||||
print(
|
||||
"[FAIL] PGADMIN_ADMIN_EMAIL or PGADMIN_ADMIN_PASSWORD is empty.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
app = create_app(config.APP_NAME + "-cli")
|
||||
with app.test_request_context():
|
||||
state = converge(
|
||||
os.environ["PGADMIN_DEFAULT_EMAIL"], os.environ["PGADMIN_DEFAULT_PASSWORD"]
|
||||
)
|
||||
print(f"PGADMIN_ADMINISTRATOR={state}")
|
||||
state = converge(email, password)
|
||||
if state == "changed":
|
||||
print("[CHANGED] the pgAdmin administrator was realigned with the declared state")
|
||||
else:
|
||||
print("[UNCHANGED] the pgAdmin administrator already matches the declared state")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Usage:
|
||||
# Piped into the `sh -s` of the running pgAdmin container, no arguments.
|
||||
#
|
||||
# PGADMIN_ADMIN_EMAIL login of the internal account to realign
|
||||
# PGADMIN_ADMIN_PASSWORD password it must accept afterwards
|
||||
#
|
||||
# Exit: 0 with [CHANGED] or [UNCHANGED] on stdout, 1 with [FAIL] on stderr.
|
||||
set -eu
|
||||
|
||||
marker="/var/lib/pgadmin/.admin_password_sha256"
|
||||
|
||||
if [ -z "${PGADMIN_ADMIN_EMAIL:-}" ] || [ -z "${PGADMIN_ADMIN_PASSWORD:-}" ]; then
|
||||
echo "[FAIL] PGADMIN_ADMIN_EMAIL or PGADMIN_ADMIN_PASSWORD is empty." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declared_sha="$(printf '%s' "$PGADMIN_ADMIN_PASSWORD" | sha256sum | cut -d' ' -f1)"
|
||||
current_sha=""
|
||||
if [ -f "$marker" ]; then
|
||||
current_sha="$(cat "$marker")"
|
||||
fi
|
||||
|
||||
if [ "$current_sha" = "$declared_sha" ]; then
|
||||
echo "[UNCHANGED] the pgAdmin administrator already accepts the declared password"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! out="$(/venv/bin/python3 /pgadmin4/setup.py update-user \
|
||||
"$PGADMIN_ADMIN_EMAIL" --admin --password "$PGADMIN_ADMIN_PASSWORD" 2>&1)"; then
|
||||
echo "[FAIL] setup.py update-user exited non-zero: ${out}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$out" in
|
||||
*"User Details"*) ;;
|
||||
*)
|
||||
echo "[FAIL] setup.py update-user did not realign the account: ${out}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if ! printf '%s' "$declared_sha" >"$marker"; then
|
||||
echo "[FAIL] the password was realigned but the marker stayed unwritten." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[CHANGED] the pgAdmin administrator was realigned with the declared password"
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
- name: "⏳ Wait until pgAdmin is healthy before converging its administrator"
|
||||
ansible.builtin.shell:
|
||||
cmd: >-
|
||||
container inspect --type container --format '{% raw %}{{.State.Health.Status}}{% endraw %}' {{ PGADMIN_CONTAINER_ADDRESS }}
|
||||
register: pgadmin_health
|
||||
until: pgadmin_health.stdout | trim == "healthy"
|
||||
retries: 60
|
||||
delay: 5
|
||||
changed_when: false
|
||||
|
||||
- name: "🔑 Converge the pgAdmin administrator account"
|
||||
ansible.builtin.shell:
|
||||
cmd: >-
|
||||
container exec -i {{ PGADMIN_CONTAINER_ADDRESS }} /venv/bin/python3 -
|
||||
stdin: "{{ lookup('ansible.builtin.file', role_path ~ '/files/python/administrator.py') }}"
|
||||
register: pgadmin_administrator
|
||||
changed_when: "'PGADMIN_ADMINISTRATOR=changed' in pgadmin_administrator.stdout"
|
||||
failed_when: >-
|
||||
pgadmin_administrator.rc != 0
|
||||
or pgadmin_administrator.stdout_lines | select('match', '^PGADMIN_ADMINISTRATOR=(changed|ok)$') | list | length != 1
|
||||
timeout: "{{ lookup('timeout', 600) | int }}"
|
||||
@@ -15,8 +15,8 @@
|
||||
cmd: >-
|
||||
container exec -i
|
||||
-e PGADMIN_ADMIN_EMAIL -e PGADMIN_ADMIN_PASSWORD
|
||||
{{ PGADMIN_CONTAINER_ADDRESS }} sh -s
|
||||
stdin: "{{ lookup('file', 'shell/realign_admin_password.sh') }}"
|
||||
{{ PGADMIN_CONTAINER_ADDRESS }} /venv/bin/python3 -
|
||||
stdin: "{{ lookup('file', 'python/realign_admin_password.py') }}"
|
||||
environment:
|
||||
PGADMIN_ADMIN_EMAIL: "{{ PGADMIN_ADMIN_EMAIL }}"
|
||||
PGADMIN_ADMIN_PASSWORD: "{{ PGADMIN_ADMIN_PASSWORD }}"
|
||||
|
||||
+4
-2
@@ -7,13 +7,15 @@ from unittest.mock import patch
|
||||
|
||||
from . import PROJECT_ROOT
|
||||
|
||||
SCRIPT_PATH = PROJECT_ROOT / "roles/web-app-pgadmin/files/python/administrator.py"
|
||||
SCRIPT_PATH = (
|
||||
PROJECT_ROOT / "roles/web-app-pgadmin/files/python/realign_admin_password.py"
|
||||
)
|
||||
EMAIL = "administrator@example.org"
|
||||
PASSWORD = "p$ssw0rd-from-the-users-spot"
|
||||
|
||||
|
||||
def load_script() -> object:
|
||||
spec = importlib.util.spec_from_file_location("administrator", SCRIPT_PATH)
|
||||
spec = importlib.util.spec_from_file_location("realign_admin_password", SCRIPT_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
Reference in new issue
Block a user