vaultwarden: add retry safeguards and db tuning
This commit is contained in:
parent
343d41ecc7
commit
8b8d2c4aa8
@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime, timezone
|
||||||
from typing import Any, Iterable
|
from typing import Any, Iterable
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
@ -16,6 +18,8 @@ from atlas_portal.vaultwarden import invite_user
|
|||||||
VAULTWARDEN_EMAIL_ATTR = "vaultwarden_email"
|
VAULTWARDEN_EMAIL_ATTR = "vaultwarden_email"
|
||||||
VAULTWARDEN_STATUS_ATTR = "vaultwarden_status"
|
VAULTWARDEN_STATUS_ATTR = "vaultwarden_status"
|
||||||
VAULTWARDEN_SYNCED_AT_ATTR = "vaultwarden_synced_at"
|
VAULTWARDEN_SYNCED_AT_ATTR = "vaultwarden_synced_at"
|
||||||
|
VAULTWARDEN_RETRY_COOLDOWN_SEC = int(os.getenv("VAULTWARDEN_RETRY_COOLDOWN_SEC", "1800"))
|
||||||
|
VAULTWARDEN_FAILURE_BAILOUT = int(os.getenv("VAULTWARDEN_FAILURE_BAILOUT", "2"))
|
||||||
|
|
||||||
|
|
||||||
def _iter_keycloak_users(page_size: int = 200) -> Iterable[dict[str, Any]]:
|
def _iter_keycloak_users(page_size: int = 200) -> Iterable[dict[str, Any]]:
|
||||||
@ -82,6 +86,21 @@ def _extract_attr(attrs: Any, key: str) -> str:
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_synced_at(value: str) -> float | None:
|
||||||
|
value = (value or "").strip()
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
for fmt in ("%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%dT%H:%M:%S%z"):
|
||||||
|
try:
|
||||||
|
parsed = datetime.strptime(value, fmt)
|
||||||
|
if parsed.tzinfo is None:
|
||||||
|
parsed = parsed.replace(tzinfo=timezone.utc)
|
||||||
|
return parsed.timestamp()
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _vaultwarden_email_for_user(user: dict[str, Any]) -> str:
|
def _vaultwarden_email_for_user(user: dict[str, Any]) -> str:
|
||||||
username = (user.get("username") if isinstance(user.get("username"), str) else "") or ""
|
username = (user.get("username") if isinstance(user.get("username"), str) else "") or ""
|
||||||
username = username.strip()
|
username = username.strip()
|
||||||
@ -129,6 +148,7 @@ def main() -> int:
|
|||||||
created = 0
|
created = 0
|
||||||
skipped = 0
|
skipped = 0
|
||||||
failures = 0
|
failures = 0
|
||||||
|
consecutive_failures = 0
|
||||||
|
|
||||||
for user in _iter_keycloak_users():
|
for user in _iter_keycloak_users():
|
||||||
username = (user.get("username") if isinstance(user.get("username"), str) else "") or ""
|
username = (user.get("username") if isinstance(user.get("username"), str) else "") or ""
|
||||||
@ -158,6 +178,11 @@ def main() -> int:
|
|||||||
|
|
||||||
current_status = _extract_attr(full_user.get("attributes"), VAULTWARDEN_STATUS_ATTR)
|
current_status = _extract_attr(full_user.get("attributes"), VAULTWARDEN_STATUS_ATTR)
|
||||||
current_synced_at = _extract_attr(full_user.get("attributes"), VAULTWARDEN_SYNCED_AT_ATTR)
|
current_synced_at = _extract_attr(full_user.get("attributes"), VAULTWARDEN_SYNCED_AT_ATTR)
|
||||||
|
current_synced_ts = _parse_synced_at(current_synced_at)
|
||||||
|
if current_status in {"rate_limited", "error"} and current_synced_ts:
|
||||||
|
if time.time() - current_synced_ts < VAULTWARDEN_RETRY_COOLDOWN_SEC:
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
email = _vaultwarden_email_for_user(full_user)
|
email = _vaultwarden_email_for_user(full_user)
|
||||||
if not email:
|
if not email:
|
||||||
print(f"skip {username}: missing email", file=sys.stderr)
|
print(f"skip {username}: missing email", file=sys.stderr)
|
||||||
@ -188,6 +213,7 @@ def main() -> int:
|
|||||||
result = invite_user(email)
|
result = invite_user(email)
|
||||||
if result.ok:
|
if result.ok:
|
||||||
created += 1
|
created += 1
|
||||||
|
consecutive_failures = 0
|
||||||
print(f"ok {username}: {result.status}")
|
print(f"ok {username}: {result.status}")
|
||||||
try:
|
try:
|
||||||
_set_user_attribute(username, VAULTWARDEN_STATUS_ATTR, result.status)
|
_set_user_attribute(username, VAULTWARDEN_STATUS_ATTR, result.status)
|
||||||
@ -196,12 +222,17 @@ def main() -> int:
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
failures += 1
|
failures += 1
|
||||||
|
if result.status in {"rate_limited", "error"}:
|
||||||
|
consecutive_failures += 1
|
||||||
print(f"err {username}: {result.status} {result.detail}", file=sys.stderr)
|
print(f"err {username}: {result.status} {result.detail}", file=sys.stderr)
|
||||||
try:
|
try:
|
||||||
_set_user_attribute(username, VAULTWARDEN_STATUS_ATTR, result.status)
|
_set_user_attribute(username, VAULTWARDEN_STATUS_ATTR, result.status)
|
||||||
_set_user_attribute(username, VAULTWARDEN_SYNCED_AT_ATTR, time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()))
|
_set_user_attribute(username, VAULTWARDEN_SYNCED_AT_ATTR, time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()))
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
if consecutive_failures >= VAULTWARDEN_FAILURE_BAILOUT:
|
||||||
|
print("vaultwarden: too many consecutive failures; aborting run", file=sys.stderr)
|
||||||
|
break
|
||||||
|
|
||||||
print(
|
print(
|
||||||
f"done processed={processed} created_or_present={created} skipped={skipped} failures={failures}",
|
f"done processed={processed} created_or_present={created} skipped={skipped} failures={failures}",
|
||||||
|
|||||||
@ -68,6 +68,12 @@ spec:
|
|||||||
value: bstein-dev-home-admin
|
value: bstein-dev-home-admin
|
||||||
- name: HTTP_CHECK_TIMEOUT_SEC
|
- name: HTTP_CHECK_TIMEOUT_SEC
|
||||||
value: "20"
|
value: "20"
|
||||||
|
- name: VAULTWARDEN_ADMIN_SESSION_TTL_SEC
|
||||||
|
value: "900"
|
||||||
|
- name: VAULTWARDEN_RETRY_COOLDOWN_SEC
|
||||||
|
value: "1800"
|
||||||
|
- name: VAULTWARDEN_FAILURE_BAILOUT
|
||||||
|
value: "2"
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: vaultwarden-cred-sync-script
|
- name: vaultwarden-cred-sync-script
|
||||||
mountPath: /scripts
|
mountPath: /scripts
|
||||||
|
|||||||
@ -50,6 +50,16 @@ spec:
|
|||||||
value: "true"
|
value: "true"
|
||||||
- name: DOMAIN
|
- name: DOMAIN
|
||||||
value: "https://vault.bstein.dev"
|
value: "https://vault.bstein.dev"
|
||||||
|
- name: DB_CONNECTION_RETRIES
|
||||||
|
value: "0"
|
||||||
|
- name: DATABASE_TIMEOUT
|
||||||
|
value: "60"
|
||||||
|
- name: DATABASE_MIN_CONNS
|
||||||
|
value: "2"
|
||||||
|
- name: DATABASE_MAX_CONNS
|
||||||
|
value: "20"
|
||||||
|
- name: DATABASE_IDLE_TIMEOUT
|
||||||
|
value: "600"
|
||||||
- name: SMTP_HOST
|
- name: SMTP_HOST
|
||||||
value: "mail.bstein.dev"
|
value: "mail.bstein.dev"
|
||||||
- name: SMTP_PORT
|
- name: SMTP_PORT
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user