Compare commits
2 Commits
aaf7e23603
...
d5a19ca9c3
| Author | SHA1 | Date | |
|---|---|---|---|
| d5a19ca9c3 | |||
| f4b08b93eb |
@ -38,9 +38,9 @@ spec:
|
|||||||
export SMTP_PORT="587"
|
export SMTP_PORT="587"
|
||||||
export SMTP_STARTTLS="true"
|
export SMTP_STARTTLS="true"
|
||||||
export SMTP_USE_TLS="false"
|
export SMTP_USE_TLS="false"
|
||||||
export SMTP_USERNAME="test@bstein.dev"
|
export SMTP_USERNAME="no-reply-portal@bstein.dev"
|
||||||
export SMTP_PASSWORD="{{ .Data.data.password }}"
|
export SMTP_PASSWORD="{{ .Data.data.password }}"
|
||||||
export SMTP_FROM="test@bstein.dev"
|
export SMTP_FROM="no-reply-portal@bstein.dev"
|
||||||
{{ end }}
|
{{ end }}
|
||||||
spec:
|
spec:
|
||||||
automountServiceAccountToken: true
|
automountServiceAccountToken: true
|
||||||
|
|||||||
@ -65,6 +65,23 @@ def _get_json(url: str, headers: dict[str, str] | None = None, timeout_s: int =
|
|||||||
raise SystemExit(f"HTTP {exc.code} from {url}: {raw}")
|
raise SystemExit(f"HTTP {exc.code} from {url}: {raw}")
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_portal_ready(base_url: str, timeout_s: int = 60) -> None:
|
||||||
|
health_url = f"{base_url.rstrip('/')}/api/healthz"
|
||||||
|
deadline_at = time.monotonic() + timeout_s
|
||||||
|
last_error = None
|
||||||
|
while time.monotonic() < deadline_at:
|
||||||
|
try:
|
||||||
|
req = urllib.request.Request(health_url, method="GET")
|
||||||
|
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||||
|
if resp.status == 200:
|
||||||
|
return
|
||||||
|
except Exception as exc:
|
||||||
|
last_error = str(exc)
|
||||||
|
time.sleep(2)
|
||||||
|
suffix = f" (last_error={last_error})" if last_error else ""
|
||||||
|
raise SystemExit(f"portal health check timed out{suffix}")
|
||||||
|
|
||||||
|
|
||||||
def _request_json(
|
def _request_json(
|
||||||
method: str,
|
method: str,
|
||||||
url: str,
|
url: str,
|
||||||
@ -235,6 +252,7 @@ def _imap_wait_for_verify_token(
|
|||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
portal_base = _env("PORTAL_BASE_URL").rstrip("/")
|
portal_base = _env("PORTAL_BASE_URL").rstrip("/")
|
||||||
|
portal_ready_timeout = int(os.environ.get("E2E_PORTAL_READY_TIMEOUT_SECONDS", "60"))
|
||||||
|
|
||||||
keycloak_base = _env("KEYCLOAK_ADMIN_URL").rstrip("/")
|
keycloak_base = _env("KEYCLOAK_ADMIN_URL").rstrip("/")
|
||||||
realm = _env("KEYCLOAK_REALM", "atlas")
|
realm = _env("KEYCLOAK_REALM", "atlas")
|
||||||
@ -274,6 +292,8 @@ def main() -> int:
|
|||||||
if not mailu_password:
|
if not mailu_password:
|
||||||
raise SystemExit(f"Keycloak user {imap_keycloak_username!r} missing mailu_app_password attribute")
|
raise SystemExit(f"Keycloak user {imap_keycloak_username!r} missing mailu_app_password attribute")
|
||||||
|
|
||||||
|
_wait_for_portal_ready(portal_base, timeout_s=portal_ready_timeout)
|
||||||
|
|
||||||
username_prefix = os.environ.get("E2E_USERNAME_PREFIX", "e2e-user")
|
username_prefix = os.environ.get("E2E_USERNAME_PREFIX", "e2e-user")
|
||||||
now = int(time.time())
|
now = int(time.time())
|
||||||
username = f"{username_prefix}-{now}"
|
username = f"{username_prefix}-{now}"
|
||||||
@ -336,6 +356,8 @@ def main() -> int:
|
|||||||
except SystemExit as exc:
|
except SystemExit as exc:
|
||||||
raise SystemExit(f"failed to exchange token for portal approval as {portal_admin_username!r}: {exc}")
|
raise SystemExit(f"failed to exchange token for portal approval as {portal_admin_username!r}: {exc}")
|
||||||
|
|
||||||
|
_wait_for_portal_ready(portal_base, timeout_s=portal_ready_timeout)
|
||||||
|
|
||||||
approve_url = f"{portal_base}/api/admin/access/requests/{urllib.parse.quote(username, safe='')}/approve"
|
approve_url = f"{portal_base}/api/admin/access/requests/{urllib.parse.quote(username, safe='')}/approve"
|
||||||
approve_timeout_s = int(os.environ.get("E2E_APPROVE_TIMEOUT_SECONDS", "180"))
|
approve_timeout_s = int(os.environ.get("E2E_APPROVE_TIMEOUT_SECONDS", "180"))
|
||||||
approve_attempts = int(os.environ.get("E2E_APPROVE_ATTEMPTS", "3"))
|
approve_attempts = int(os.environ.get("E2E_APPROVE_ATTEMPTS", "3"))
|
||||||
@ -348,6 +370,10 @@ def main() -> int:
|
|||||||
break
|
break
|
||||||
except (http.client.RemoteDisconnected, TimeoutError, urllib.error.URLError) as exc:
|
except (http.client.RemoteDisconnected, TimeoutError, urllib.error.URLError) as exc:
|
||||||
approve_error = str(exc)
|
approve_error = str(exc)
|
||||||
|
try:
|
||||||
|
_wait_for_portal_ready(portal_base, timeout_s=min(30, portal_ready_timeout))
|
||||||
|
except SystemExit:
|
||||||
|
pass
|
||||||
if attempt == approve_attempts:
|
if attempt == approve_attempts:
|
||||||
break
|
break
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|||||||
@ -32,6 +32,9 @@ spec:
|
|||||||
vault.hashicorp.com/agent-inject-secret-mailu-sync-credentials__client-secret: "kv/data/atlas/mailu/mailu-sync-credentials"
|
vault.hashicorp.com/agent-inject-secret-mailu-sync-credentials__client-secret: "kv/data/atlas/mailu/mailu-sync-credentials"
|
||||||
vault.hashicorp.com/agent-inject-template-mailu-sync-credentials__client-secret: |
|
vault.hashicorp.com/agent-inject-template-mailu-sync-credentials__client-secret: |
|
||||||
{{- with secret "kv/data/atlas/mailu/mailu-sync-credentials" -}}{{ index .Data.data "client-secret" }}{{- end -}}
|
{{- with secret "kv/data/atlas/mailu/mailu-sync-credentials" -}}{{ index .Data.data "client-secret" }}{{- end -}}
|
||||||
|
vault.hashicorp.com/agent-inject-secret-mailu-initial-account-secret__password: "kv/data/atlas/mailu/mailu-initial-account-secret"
|
||||||
|
vault.hashicorp.com/agent-inject-template-mailu-initial-account-secret__password: |
|
||||||
|
{{- with secret "kv/data/atlas/mailu/mailu-initial-account-secret" -}}{{ .Data.data.password }}{{- end -}}
|
||||||
spec:
|
spec:
|
||||||
restartPolicy: OnFailure
|
restartPolicy: OnFailure
|
||||||
serviceAccountName: mailu-vault-sync
|
serviceAccountName: mailu-vault-sync
|
||||||
@ -55,6 +58,8 @@ spec:
|
|||||||
value: bstein.dev
|
value: bstein.dev
|
||||||
- name: MAILU_DEFAULT_QUOTA
|
- name: MAILU_DEFAULT_QUOTA
|
||||||
value: "20000000000"
|
value: "20000000000"
|
||||||
|
- name: MAILU_SYSTEM_USERS
|
||||||
|
value: no-reply-portal@bstein.dev
|
||||||
- name: MAILU_DB_HOST
|
- name: MAILU_DB_HOST
|
||||||
value: postgres-service.postgres.svc.cluster.local
|
value: postgres-service.postgres.svc.cluster.local
|
||||||
- name: MAILU_DB_PORT
|
- name: MAILU_DB_PORT
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
apiVersion: batch/v1
|
apiVersion: batch/v1
|
||||||
kind: Job
|
kind: Job
|
||||||
metadata:
|
metadata:
|
||||||
name: mailu-sync-7
|
name: mailu-sync-8
|
||||||
namespace: mailu-mailserver
|
namespace: mailu-mailserver
|
||||||
spec:
|
spec:
|
||||||
template:
|
template:
|
||||||
@ -26,6 +26,9 @@ spec:
|
|||||||
vault.hashicorp.com/agent-inject-secret-mailu-sync-credentials__client-secret: "kv/data/atlas/mailu/mailu-sync-credentials"
|
vault.hashicorp.com/agent-inject-secret-mailu-sync-credentials__client-secret: "kv/data/atlas/mailu/mailu-sync-credentials"
|
||||||
vault.hashicorp.com/agent-inject-template-mailu-sync-credentials__client-secret: |
|
vault.hashicorp.com/agent-inject-template-mailu-sync-credentials__client-secret: |
|
||||||
{{- with secret "kv/data/atlas/mailu/mailu-sync-credentials" -}}{{ index .Data.data "client-secret" }}{{- end -}}
|
{{- with secret "kv/data/atlas/mailu/mailu-sync-credentials" -}}{{ index .Data.data "client-secret" }}{{- end -}}
|
||||||
|
vault.hashicorp.com/agent-inject-secret-mailu-initial-account-secret__password: "kv/data/atlas/mailu/mailu-initial-account-secret"
|
||||||
|
vault.hashicorp.com/agent-inject-template-mailu-initial-account-secret__password: |
|
||||||
|
{{- with secret "kv/data/atlas/mailu/mailu-initial-account-secret" -}}{{ .Data.data.password }}{{- end -}}
|
||||||
spec:
|
spec:
|
||||||
restartPolicy: OnFailure
|
restartPolicy: OnFailure
|
||||||
affinity:
|
affinity:
|
||||||
@ -63,6 +66,8 @@ spec:
|
|||||||
value: bstein.dev
|
value: bstein.dev
|
||||||
- name: MAILU_DEFAULT_QUOTA
|
- name: MAILU_DEFAULT_QUOTA
|
||||||
value: "20000000000"
|
value: "20000000000"
|
||||||
|
- name: MAILU_SYSTEM_USERS
|
||||||
|
value: no-reply-portal@bstein.dev
|
||||||
- name: MAILU_DB_HOST
|
- name: MAILU_DB_HOST
|
||||||
value: postgres-service.postgres.svc.cluster.local
|
value: postgres-service.postgres.svc.cluster.local
|
||||||
- name: MAILU_DB_PORT
|
- name: MAILU_DB_PORT
|
||||||
|
|||||||
@ -46,6 +46,9 @@ spec:
|
|||||||
vault.hashicorp.com/agent-inject-secret-mailu-sync-credentials__client-secret: "kv/data/atlas/mailu/mailu-sync-credentials"
|
vault.hashicorp.com/agent-inject-secret-mailu-sync-credentials__client-secret: "kv/data/atlas/mailu/mailu-sync-credentials"
|
||||||
vault.hashicorp.com/agent-inject-template-mailu-sync-credentials__client-secret: |
|
vault.hashicorp.com/agent-inject-template-mailu-sync-credentials__client-secret: |
|
||||||
{{- with secret "kv/data/atlas/mailu/mailu-sync-credentials" -}}{{ index .Data.data "client-secret" }}{{- end -}}
|
{{- with secret "kv/data/atlas/mailu/mailu-sync-credentials" -}}{{ index .Data.data "client-secret" }}{{- end -}}
|
||||||
|
vault.hashicorp.com/agent-inject-secret-mailu-initial-account-secret__password: "kv/data/atlas/mailu/mailu-initial-account-secret"
|
||||||
|
vault.hashicorp.com/agent-inject-template-mailu-initial-account-secret__password: |
|
||||||
|
{{- with secret "kv/data/atlas/mailu/mailu-initial-account-secret" -}}{{ .Data.data.password }}{{- end -}}
|
||||||
spec:
|
spec:
|
||||||
restartPolicy: Always
|
restartPolicy: Always
|
||||||
serviceAccountName: mailu-vault-sync
|
serviceAccountName: mailu-vault-sync
|
||||||
@ -69,6 +72,8 @@ spec:
|
|||||||
value: bstein.dev
|
value: bstein.dev
|
||||||
- name: MAILU_DEFAULT_QUOTA
|
- name: MAILU_DEFAULT_QUOTA
|
||||||
value: "20000000000"
|
value: "20000000000"
|
||||||
|
- name: MAILU_SYSTEM_USERS
|
||||||
|
value: no-reply-portal@bstein.dev
|
||||||
- name: MAILU_DB_HOST
|
- name: MAILU_DB_HOST
|
||||||
value: postgres-service.postgres.svc.cluster.local
|
value: postgres-service.postgres.svc.cluster.local
|
||||||
- name: MAILU_DB_PORT
|
- name: MAILU_DB_PORT
|
||||||
|
|||||||
@ -27,6 +27,12 @@ MAILU_DOMAIN = os.environ["MAILU_DOMAIN"]
|
|||||||
MAILU_DEFAULT_QUOTA = int(os.environ.get("MAILU_DEFAULT_QUOTA", "20000000000"))
|
MAILU_DEFAULT_QUOTA = int(os.environ.get("MAILU_DEFAULT_QUOTA", "20000000000"))
|
||||||
MAILU_ENABLED_ATTR = os.environ.get("MAILU_ENABLED_ATTR", "mailu_enabled")
|
MAILU_ENABLED_ATTR = os.environ.get("MAILU_ENABLED_ATTR", "mailu_enabled")
|
||||||
MAILU_EMAIL_ATTR = "mailu_email"
|
MAILU_EMAIL_ATTR = "mailu_email"
|
||||||
|
MAILU_SYSTEM_USERS = [
|
||||||
|
item.strip()
|
||||||
|
for item in os.environ.get("MAILU_SYSTEM_USERS", "").split(",")
|
||||||
|
if item.strip()
|
||||||
|
]
|
||||||
|
MAILU_SYSTEM_PASSWORD = os.environ.get("MAILU_SYSTEM_PASSWORD", "").strip()
|
||||||
|
|
||||||
DB_CONFIG = {
|
DB_CONFIG = {
|
||||||
"host": os.environ["MAILU_DB_HOST"],
|
"host": os.environ["MAILU_DB_HOST"],
|
||||||
@ -213,10 +219,26 @@ def ensure_mailu_user(cursor, email, password, display_name):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_system_mailboxes(cursor):
|
||||||
|
if not MAILU_SYSTEM_USERS:
|
||||||
|
return
|
||||||
|
if not MAILU_SYSTEM_PASSWORD:
|
||||||
|
log("MAILU_SYSTEM_USERS set but MAILU_SYSTEM_PASSWORD is missing; skipping system mailboxes")
|
||||||
|
return
|
||||||
|
|
||||||
|
for email in MAILU_SYSTEM_USERS:
|
||||||
|
localpart = email.split("@", 1)[0] if "@" in email else email
|
||||||
|
try:
|
||||||
|
ensure_mailu_user(cursor, email, MAILU_SYSTEM_PASSWORD, localpart)
|
||||||
|
log(f"Ensured system mailbox for {email}")
|
||||||
|
except Exception as exc:
|
||||||
|
log(f"Failed to ensure system mailbox {email}: {exc}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
token = retry_request("Keycloak token", get_kc_token)
|
token = retry_request("Keycloak token", get_kc_token)
|
||||||
users = retry_request("Keycloak user list", lambda: kc_get_users(token))
|
users = retry_request("Keycloak user list", lambda: kc_get_users(token))
|
||||||
if not users:
|
if not users and not MAILU_SYSTEM_USERS:
|
||||||
log("No users found; exiting.")
|
log("No users found; exiting.")
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -257,6 +279,8 @@ def main():
|
|||||||
ensure_mailu_user(cursor, mailu_email, app_pw, display_name)
|
ensure_mailu_user(cursor, mailu_email, app_pw, display_name)
|
||||||
log(f"Synced mailbox for {mailu_email}")
|
log(f"Synced mailbox for {mailu_email}")
|
||||||
|
|
||||||
|
ensure_system_mailboxes(cursor)
|
||||||
|
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|||||||
@ -12,3 +12,4 @@ export MAILU_DB_USER="$(read_secret mailu-db-secret__username)"
|
|||||||
export MAILU_DB_PASSWORD="$(read_secret mailu-db-secret__password)"
|
export MAILU_DB_PASSWORD="$(read_secret mailu-db-secret__password)"
|
||||||
export KEYCLOAK_CLIENT_ID="$(read_secret mailu-sync-credentials__client-id)"
|
export KEYCLOAK_CLIENT_ID="$(read_secret mailu-sync-credentials__client-id)"
|
||||||
export KEYCLOAK_CLIENT_SECRET="$(read_secret mailu-sync-credentials__client-secret)"
|
export KEYCLOAK_CLIENT_SECRET="$(read_secret mailu-sync-credentials__client-secret)"
|
||||||
|
export MAILU_SYSTEM_PASSWORD="$(read_secret mailu-initial-account-secret__password)"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user