From 2477ca38992b62960dcea2423215ac00bd3bf6b6 Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 21 Apr 2026 01:14:39 -0300 Subject: [PATCH] refactor(ariadne): split service pod scripts --- ariadne/services/firefly.py | 230 +--------------------------- ariadne/services/firefly_scripts.py | 230 ++++++++++++++++++++++++++++ ariadne/services/wger.py | 176 +-------------------- ariadne/services/wger_scripts.py | 180 ++++++++++++++++++++++ ci/loc_hygiene_waivers.tsv | 2 - 5 files changed, 415 insertions(+), 403 deletions(-) create mode 100644 ariadne/services/firefly_scripts.py create mode 100644 ariadne/services/wger_scripts.py diff --git a/ariadne/services/firefly.py b/ariadne/services/firefly.py index fecee78..118ec27 100644 --- a/ariadne/services/firefly.py +++ b/ariadne/services/firefly.py @@ -2,10 +2,8 @@ from __future__ import annotations from dataclasses import dataclass from datetime import datetime, timezone -from typing import Any -import textwrap - import httpx +from typing import Any from ..k8s.exec import ExecError, PodExecutor from ..k8s.pods import PodSelectionError @@ -13,6 +11,8 @@ from ..settings import settings from ..utils.logging import get_logger from ..utils.passwords import random_password from .keycloak_admin import keycloak_admin +from .firefly_scripts import FIREFLY_PASSWORD_CHECK_SCRIPT as _FIREFLY_PASSWORD_CHECK_SCRIPT +from .firefly_scripts import FIREFLY_SYNC_SCRIPT as _FIREFLY_SYNC_SCRIPT from .mailu import mailu @@ -27,230 +27,6 @@ FIREFLY_PASSWORD_ROTATED_ATTR = "firefly_password_rotated_at" logger = get_logger(__name__) -_FIREFLY_SYNC_SCRIPT = textwrap.dedent( - """ - make(ConsoleKernel::class); - $kernel->bootstrap(); - - try { - FireflyConfig::set('single_user_mode', true); - } catch (Throwable $exc) { - error_line('failed to enforce single_user_mode: ' . $exc->getMessage()); - } - - $repository = $app->make(UserRepositoryInterface::class); - - $existing_user = User::where('email', $email)->first(); - $first_user = User::count() == 0; - - if (!$existing_user) { - $existing_user = User::create( - [ - 'email' => $email, - 'password' => bcrypt($password), - 'blocked' => false, - 'blocked_code' => null, - ] - ); - - if ($first_user) { - $role = Role::where('name', 'owner')->first(); - if ($role) { - $existing_user->roles()->attach($role); - } - } - - log_line(sprintf('created firefly user %s', $email)); - } else { - log_line(sprintf('updating firefly user %s', $email)); - } - - $existing_user->blocked = false; - $existing_user->blocked_code = null; - $existing_user->save(); - - $repository->changePassword($existing_user, $password); - CreatesGroupMemberships::createGroupMembership($existing_user); - - log_line('firefly user sync complete'); - """ -).strip() - -_FIREFLY_PASSWORD_CHECK_SCRIPT = textwrap.dedent( - """ - make(ConsoleKernel::class); - $kernel->bootstrap(); - - try { - FireflyConfig::set('single_user_mode', true); - } catch (Throwable $exc) { - error_line('failed to enforce single_user_mode: ' . $exc->getMessage()); - } - - if ($email !== '') { - $query = User::where('email', $email); - } else { - $query = User::where('username', $username); - } - - if ($email !== '' && $username !== '') { - $query = $query->orWhere('username', $username); - } - - $existing_user = $query->first(); - if (!$existing_user) { - error_line('firefly user missing'); - exit(3); - } - - if (Hash::check($password, $existing_user->password)) { - log_line('password match'); - exit(0); - } - - log_line('password mismatch'); - exit(1); - """ -).strip() - - def _firefly_exec_command() -> str: return f"php <<'PHP'\n{_FIREFLY_SYNC_SCRIPT}\nPHP" diff --git a/ariadne/services/firefly_scripts.py b/ariadne/services/firefly_scripts.py new file mode 100644 index 0000000..d197157 --- /dev/null +++ b/ariadne/services/firefly_scripts.py @@ -0,0 +1,230 @@ +"""Embedded scripts executed inside the firefly application pod.""" + +from __future__ import annotations + +import textwrap + +FIREFLY_SYNC_SCRIPT = textwrap.dedent( + """ + make(ConsoleKernel::class); + $kernel->bootstrap(); + + try { + FireflyConfig::set('single_user_mode', true); + } catch (Throwable $exc) { + error_line('failed to enforce single_user_mode: ' . $exc->getMessage()); + } + + $repository = $app->make(UserRepositoryInterface::class); + + $existing_user = User::where('email', $email)->first(); + $first_user = User::count() == 0; + + if (!$existing_user) { + $existing_user = User::create( + [ + 'email' => $email, + 'password' => bcrypt($password), + 'blocked' => false, + 'blocked_code' => null, + ] + ); + + if ($first_user) { + $role = Role::where('name', 'owner')->first(); + if ($role) { + $existing_user->roles()->attach($role); + } + } + + log_line(sprintf('created firefly user %s', $email)); + } else { + log_line(sprintf('updating firefly user %s', $email)); + } + + $existing_user->blocked = false; + $existing_user->blocked_code = null; + $existing_user->save(); + + $repository->changePassword($existing_user, $password); + CreatesGroupMemberships::createGroupMembership($existing_user); + + log_line('firefly user sync complete'); + """ +).strip() + +FIREFLY_PASSWORD_CHECK_SCRIPT = textwrap.dedent( + """ + make(ConsoleKernel::class); + $kernel->bootstrap(); + + try { + FireflyConfig::set('single_user_mode', true); + } catch (Throwable $exc) { + error_line('failed to enforce single_user_mode: ' . $exc->getMessage()); + } + + if ($email !== '') { + $query = User::where('email', $email); + } else { + $query = User::where('username', $username); + } + + if ($email !== '' && $username !== '') { + $query = $query->orWhere('username', $username); + } + + $existing_user = $query->first(); + if (!$existing_user) { + error_line('firefly user missing'); + exit(3); + } + + if (Hash::check($password, $existing_user->password)) { + log_line('password match'); + exit(0); + } + + log_line('password mismatch'); + exit(1); + """ +).strip() + + diff --git a/ariadne/services/wger.py b/ariadne/services/wger.py index 30f50de..5e5b0d6 100644 --- a/ariadne/services/wger.py +++ b/ariadne/services/wger.py @@ -3,7 +3,6 @@ from __future__ import annotations from dataclasses import dataclass from datetime import datetime, timezone from typing import Any -import textwrap from ..k8s.exec import ExecError, PodExecutor from ..k8s.pods import PodSelectionError @@ -12,6 +11,8 @@ from ..utils.logging import get_logger from ..utils.passwords import random_password from .keycloak_admin import keycloak_admin from .mailu import mailu +from .wger_scripts import WGER_PASSWORD_CHECK_SCRIPT as _WGER_PASSWORD_CHECK_SCRIPT +from .wger_scripts import WGER_SYNC_SCRIPT as _WGER_SYNC_SCRIPT EXIT_PASSWORD_MATCH = 0 @@ -23,179 +24,6 @@ WGER_PASSWORD_ROTATED_ATTR = "wger_password_rotated_at" logger = get_logger(__name__) -_WGER_SYNC_SCRIPT = textwrap.dedent( - """ - from __future__ import annotations - - import os - import sys - - import django - - - def _env(name: str, default: str = "") -> str: - value = os.getenv(name, default) - return value.strip() if isinstance(value, str) else "" - - - def _setup_django() -> None: - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.main") - django.setup() - - - def _set_default_gym(user) -> None: - try: - from wger.gym.models import GymConfig - except Exception: - return - - try: - config = GymConfig.objects.first() - except Exception: - return - - if not config or not getattr(config, "default_gym", None): - return - - profile = getattr(user, "userprofile", None) - if not profile or getattr(profile, "gym", None): - return - - profile.gym = config.default_gym - profile.save() - - - def _ensure_profile(user) -> None: - profile = getattr(user, "userprofile", None) - if not profile: - return - if hasattr(profile, "email_verified") and not profile.email_verified: - profile.email_verified = True - if hasattr(profile, "is_temporary") and profile.is_temporary: - profile.is_temporary = False - profile.save() - - - def _ensure_admin(username: str, password: str, email: str) -> None: - from django.contrib.auth.models import User - - if not username or not password: - raise RuntimeError("admin username/password missing") - - user, created = User.objects.get_or_create(username=username) - if created: - user.is_active = True - if not user.is_staff: - user.is_staff = True - if email: - user.email = email - user.set_password(password) - user.save() - - _ensure_profile(user) - _set_default_gym(user) - print(f"ensured admin user {username}") - - - def _ensure_user(username: str, password: str, email: str) -> None: - from django.contrib.auth.models import User - - if not username or not password: - raise RuntimeError("username/password missing") - - user, created = User.objects.get_or_create(username=username) - if created: - user.is_active = True - if email and user.email != email: - user.email = email - user.set_password(password) - user.save() - - _ensure_profile(user) - _set_default_gym(user) - action = "created" if created else "updated" - print(f"{action} user {username}") - - - def main() -> int: - admin_user = _env("WGER_ADMIN_USERNAME") - admin_password = _env("WGER_ADMIN_PASSWORD") - admin_email = _env("WGER_ADMIN_EMAIL") - - username = _env("WGER_USERNAME") or _env("ONLY_USERNAME") - password = _env("WGER_PASSWORD") - email = _env("WGER_EMAIL") - - if not any([admin_user and admin_password, username and password]): - print("no admin or user payload provided; exiting") - return 0 - - _setup_django() - - if admin_user and admin_password: - _ensure_admin(admin_user, admin_password, admin_email) - - if username and password: - _ensure_user(username, password, email) - - return 0 - - - if __name__ == "__main__": - sys.exit(main()) - """ -).strip() - -_WGER_PASSWORD_CHECK_SCRIPT = textwrap.dedent( - """ - from __future__ import annotations - - import os - import sys - - import django - - - def _env(name: str, default: str = "") -> str: - value = os.getenv(name, default) - return value.strip() if isinstance(value, str) else "" - - - def _setup_django() -> None: - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.main") - django.setup() - - - def main() -> int: - username = _env("WGER_USERNAME") - password = _env("WGER_PASSWORD") - - if not username or not password: - print("missing username or password") - return 2 - - _setup_django() - - from django.contrib.auth.models import User - - user = User.objects.filter(username=username).first() - if not user: - print(f"user {username} missing") - return 3 - - if user.check_password(password): - print("password match") - return 0 - - print("password mismatch") - return 1 - - - if __name__ == "__main__": - sys.exit(main()) - """ -).strip() - def _wger_exec_command() -> str: bootstrap = ". /vault/secrets/wger-env >/dev/null 2>&1 || true" diff --git a/ariadne/services/wger_scripts.py b/ariadne/services/wger_scripts.py new file mode 100644 index 0000000..cee49bc --- /dev/null +++ b/ariadne/services/wger_scripts.py @@ -0,0 +1,180 @@ +"""Embedded scripts executed inside the wger application pod.""" + +from __future__ import annotations + +import textwrap + +WGER_SYNC_SCRIPT = textwrap.dedent( + """ + from __future__ import annotations + + import os + import sys + + import django + + + def _env(name: str, default: str = "") -> str: + value = os.getenv(name, default) + return value.strip() if isinstance(value, str) else "" + + + def _setup_django() -> None: + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.main") + django.setup() + + + def _set_default_gym(user) -> None: + try: + from wger.gym.models import GymConfig + except Exception: + return + + try: + config = GymConfig.objects.first() + except Exception: + return + + if not config or not getattr(config, "default_gym", None): + return + + profile = getattr(user, "userprofile", None) + if not profile or getattr(profile, "gym", None): + return + + profile.gym = config.default_gym + profile.save() + + + def _ensure_profile(user) -> None: + profile = getattr(user, "userprofile", None) + if not profile: + return + if hasattr(profile, "email_verified") and not profile.email_verified: + profile.email_verified = True + if hasattr(profile, "is_temporary") and profile.is_temporary: + profile.is_temporary = False + profile.save() + + + def _ensure_admin(username: str, password: str, email: str) -> None: + from django.contrib.auth.models import User + + if not username or not password: + raise RuntimeError("admin username/password missing") + + user, created = User.objects.get_or_create(username=username) + if created: + user.is_active = True + if not user.is_staff: + user.is_staff = True + if email: + user.email = email + user.set_password(password) + user.save() + + _ensure_profile(user) + _set_default_gym(user) + print(f"ensured admin user {username}") + + + def _ensure_user(username: str, password: str, email: str) -> None: + from django.contrib.auth.models import User + + if not username or not password: + raise RuntimeError("username/password missing") + + user, created = User.objects.get_or_create(username=username) + if created: + user.is_active = True + if email and user.email != email: + user.email = email + user.set_password(password) + user.save() + + _ensure_profile(user) + _set_default_gym(user) + action = "created" if created else "updated" + print(f"{action} user {username}") + + + def main() -> int: + admin_user = _env("WGER_ADMIN_USERNAME") + admin_password = _env("WGER_ADMIN_PASSWORD") + admin_email = _env("WGER_ADMIN_EMAIL") + + username = _env("WGER_USERNAME") or _env("ONLY_USERNAME") + password = _env("WGER_PASSWORD") + email = _env("WGER_EMAIL") + + if not any([admin_user and admin_password, username and password]): + print("no admin or user payload provided; exiting") + return 0 + + _setup_django() + + if admin_user and admin_password: + _ensure_admin(admin_user, admin_password, admin_email) + + if username and password: + _ensure_user(username, password, email) + + return 0 + + + if __name__ == "__main__": + sys.exit(main()) + """ +).strip() + +WGER_PASSWORD_CHECK_SCRIPT = textwrap.dedent( + """ + from __future__ import annotations + + import os + import sys + + import django + + + def _env(name: str, default: str = "") -> str: + value = os.getenv(name, default) + return value.strip() if isinstance(value, str) else "" + + + def _setup_django() -> None: + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.main") + django.setup() + + + def main() -> int: + username = _env("WGER_USERNAME") + password = _env("WGER_PASSWORD") + + if not username or not password: + print("missing username or password") + return 2 + + _setup_django() + + from django.contrib.auth.models import User + + user = User.objects.filter(username=username).first() + if not user: + print(f"user {username} missing") + return 3 + + if user.check_password(password): + print("password match") + return 0 + + print("password mismatch") + return 1 + + + if __name__ == "__main__": + sys.exit(main()) + """ +).strip() + + diff --git a/ci/loc_hygiene_waivers.tsv b/ci/loc_hygiene_waivers.tsv index edab0af..d73dd71 100644 --- a/ci/loc_hygiene_waivers.tsv +++ b/ci/loc_hygiene_waivers.tsv @@ -4,10 +4,8 @@ ariadne/app.py split planned; Flask app bootstrap/routes currently co-located ariadne/services/comms.py split planned; comms adapters still consolidated ariadne/manager/provisioning.py split planned; provisioning flow modules pending extraction ariadne/services/nextcloud.py split planned; provider methods pending partition -ariadne/services/firefly.py split planned; provider methods pending partition ariadne/settings.py split planned; settings schema + helpers pending split ariadne/services/jenkins_workspace_cleanup.py split planned; job orchestration pending extraction -ariadne/services/wger.py split planned; provider methods pending partition tests/test_provisioning.py test module split planned; broad provisioning coverage retained meanwhile tests/test_services.py test module split planned; broad service contract coverage retained meanwhile tests/test_app.py test module split planned; API coverage retained meanwhile