refactor(ariadne): split service pod scripts

This commit is contained in:
codex 2026-04-21 01:14:39 -03:00
parent f0baa619dc
commit 2477ca3899
5 changed files with 415 additions and 403 deletions

View File

@ -2,10 +2,8 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any
import textwrap
import httpx import httpx
from typing import Any
from ..k8s.exec import ExecError, PodExecutor from ..k8s.exec import ExecError, PodExecutor
from ..k8s.pods import PodSelectionError from ..k8s.pods import PodSelectionError
@ -13,6 +11,8 @@ from ..settings import settings
from ..utils.logging import get_logger from ..utils.logging import get_logger
from ..utils.passwords import random_password from ..utils.passwords import random_password
from .keycloak_admin import keycloak_admin 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 from .mailu import mailu
@ -27,230 +27,6 @@ FIREFLY_PASSWORD_ROTATED_ATTR = "firefly_password_rotated_at"
logger = get_logger(__name__) logger = get_logger(__name__)
_FIREFLY_SYNC_SCRIPT = textwrap.dedent(
"""
<?php
declare(strict_types=1);
use FireflyIII\\Console\\Commands\\Correction\\CreatesGroupMemberships;
use FireflyIII\\Models\\Role;
use FireflyIII\\Repositories\\User\\UserRepositoryInterface;
use FireflyIII\\Support\\Facades\\FireflyConfig;
use FireflyIII\\User;
use Illuminate\\Contracts\\Console\\Kernel as ConsoleKernel;
function log_line(string $message): void
{
fwrite(STDOUT, $message . PHP_EOL);
}
function error_line(string $message): void
{
fwrite(STDERR, $message . PHP_EOL);
}
function find_app_root(): string
{
$candidates = [];
$env_root = getenv('FIREFLY_APP_DIR') ?: '';
if ($env_root !== '') {
$candidates[] = $env_root;
}
$candidates[] = '/var/www/html';
$candidates[] = '/var/www/firefly-iii';
$candidates[] = '/app';
foreach ($candidates as $candidate) {
if (!is_dir($candidate)) {
continue;
}
if (file_exists($candidate . '/vendor/autoload.php')) {
return $candidate;
}
}
return '';
}
$email = trim((string) getenv('FIREFLY_USER_EMAIL'));
$password = (string) getenv('FIREFLY_USER_PASSWORD');
if ($email === '' || $password === '') {
error_line('missing FIREFLY_USER_EMAIL or FIREFLY_USER_PASSWORD');
exit(1);
}
$root = find_app_root();
if ($root === '') {
error_line('firefly app root not found');
exit(1);
}
$autoload = $root . '/vendor/autoload.php';
$app_bootstrap = $root . '/bootstrap/app.php';
if (!file_exists($autoload) || !file_exists($app_bootstrap)) {
error_line('firefly bootstrap files missing');
exit(1);
}
require $autoload;
$app = require $app_bootstrap;
$kernel = $app->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(
"""
<?php
declare(strict_types=1);
use FireflyIII\\Support\\Facades\\FireflyConfig;
use FireflyIII\\User;
use Illuminate\\Contracts\\Console\\Kernel as ConsoleKernel;
use Illuminate\\Support\\Facades\\Hash;
function log_line(string $message): void
{
fwrite(STDOUT, $message . PHP_EOL);
}
function error_line(string $message): void
{
fwrite(STDERR, $message . PHP_EOL);
}
function find_app_root(): string
{
$candidates = [];
$env_root = getenv('FIREFLY_APP_DIR') ?: '';
if ($env_root !== '') {
$candidates[] = $env_root;
}
$candidates[] = '/var/www/html';
$candidates[] = '/var/www/firefly-iii';
$candidates[] = '/app';
foreach ($candidates as $candidate) {
if (!is_dir($candidate)) {
continue;
}
if (file_exists($candidate . '/vendor/autoload.php')) {
return $candidate;
}
}
return '';
}
$email = trim((string) getenv('FIREFLY_USER_EMAIL'));
$username = trim((string) getenv('FIREFLY_USER_USERNAME'));
$password = (string) getenv('FIREFLY_USER_PASSWORD');
if (($email === '' && $username === '') || $password === '') {
error_line('missing FIREFLY_USER_EMAIL or FIREFLY_USER_USERNAME or FIREFLY_USER_PASSWORD');
exit(2);
}
$root = find_app_root();
if ($root === '') {
error_line('firefly app root not found');
exit(2);
}
$autoload = $root . '/vendor/autoload.php';
$app_bootstrap = $root . '/bootstrap/app.php';
if (!file_exists($autoload) || !file_exists($app_bootstrap)) {
error_line('firefly bootstrap files missing');
exit(2);
}
require $autoload;
$app = require $app_bootstrap;
$kernel = $app->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: def _firefly_exec_command() -> str:
return f"php <<'PHP'\n{_FIREFLY_SYNC_SCRIPT}\nPHP" return f"php <<'PHP'\n{_FIREFLY_SYNC_SCRIPT}\nPHP"

View File

@ -0,0 +1,230 @@
"""Embedded scripts executed inside the firefly application pod."""
from __future__ import annotations
import textwrap
FIREFLY_SYNC_SCRIPT = textwrap.dedent(
"""
<?php
declare(strict_types=1);
use FireflyIII\\Console\\Commands\\Correction\\CreatesGroupMemberships;
use FireflyIII\\Models\\Role;
use FireflyIII\\Repositories\\User\\UserRepositoryInterface;
use FireflyIII\\Support\\Facades\\FireflyConfig;
use FireflyIII\\User;
use Illuminate\\Contracts\\Console\\Kernel as ConsoleKernel;
function log_line(string $message): void
{
fwrite(STDOUT, $message . PHP_EOL);
}
function error_line(string $message): void
{
fwrite(STDERR, $message . PHP_EOL);
}
function find_app_root(): string
{
$candidates = [];
$env_root = getenv('FIREFLY_APP_DIR') ?: '';
if ($env_root !== '') {
$candidates[] = $env_root;
}
$candidates[] = '/var/www/html';
$candidates[] = '/var/www/firefly-iii';
$candidates[] = '/app';
foreach ($candidates as $candidate) {
if (!is_dir($candidate)) {
continue;
}
if (file_exists($candidate . '/vendor/autoload.php')) {
return $candidate;
}
}
return '';
}
$email = trim((string) getenv('FIREFLY_USER_EMAIL'));
$password = (string) getenv('FIREFLY_USER_PASSWORD');
if ($email === '' || $password === '') {
error_line('missing FIREFLY_USER_EMAIL or FIREFLY_USER_PASSWORD');
exit(1);
}
$root = find_app_root();
if ($root === '') {
error_line('firefly app root not found');
exit(1);
}
$autoload = $root . '/vendor/autoload.php';
$app_bootstrap = $root . '/bootstrap/app.php';
if (!file_exists($autoload) || !file_exists($app_bootstrap)) {
error_line('firefly bootstrap files missing');
exit(1);
}
require $autoload;
$app = require $app_bootstrap;
$kernel = $app->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(
"""
<?php
declare(strict_types=1);
use FireflyIII\\Support\\Facades\\FireflyConfig;
use FireflyIII\\User;
use Illuminate\\Contracts\\Console\\Kernel as ConsoleKernel;
use Illuminate\\Support\\Facades\\Hash;
function log_line(string $message): void
{
fwrite(STDOUT, $message . PHP_EOL);
}
function error_line(string $message): void
{
fwrite(STDERR, $message . PHP_EOL);
}
function find_app_root(): string
{
$candidates = [];
$env_root = getenv('FIREFLY_APP_DIR') ?: '';
if ($env_root !== '') {
$candidates[] = $env_root;
}
$candidates[] = '/var/www/html';
$candidates[] = '/var/www/firefly-iii';
$candidates[] = '/app';
foreach ($candidates as $candidate) {
if (!is_dir($candidate)) {
continue;
}
if (file_exists($candidate . '/vendor/autoload.php')) {
return $candidate;
}
}
return '';
}
$email = trim((string) getenv('FIREFLY_USER_EMAIL'));
$username = trim((string) getenv('FIREFLY_USER_USERNAME'));
$password = (string) getenv('FIREFLY_USER_PASSWORD');
if (($email === '' && $username === '') || $password === '') {
error_line('missing FIREFLY_USER_EMAIL or FIREFLY_USER_USERNAME or FIREFLY_USER_PASSWORD');
exit(2);
}
$root = find_app_root();
if ($root === '') {
error_line('firefly app root not found');
exit(2);
}
$autoload = $root . '/vendor/autoload.php';
$app_bootstrap = $root . '/bootstrap/app.php';
if (!file_exists($autoload) || !file_exists($app_bootstrap)) {
error_line('firefly bootstrap files missing');
exit(2);
}
require $autoload;
$app = require $app_bootstrap;
$kernel = $app->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()

View File

@ -3,7 +3,6 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any from typing import Any
import textwrap
from ..k8s.exec import ExecError, PodExecutor from ..k8s.exec import ExecError, PodExecutor
from ..k8s.pods import PodSelectionError from ..k8s.pods import PodSelectionError
@ -12,6 +11,8 @@ from ..utils.logging import get_logger
from ..utils.passwords import random_password from ..utils.passwords import random_password
from .keycloak_admin import keycloak_admin from .keycloak_admin import keycloak_admin
from .mailu import mailu 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 EXIT_PASSWORD_MATCH = 0
@ -23,179 +24,6 @@ WGER_PASSWORD_ROTATED_ATTR = "wger_password_rotated_at"
logger = get_logger(__name__) 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: def _wger_exec_command() -> str:
bootstrap = ". /vault/secrets/wger-env >/dev/null 2>&1 || true" bootstrap = ". /vault/secrets/wger-env >/dev/null 2>&1 || true"

View File

@ -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()

View File

@ -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/services/comms.py split planned; comms adapters still consolidated
ariadne/manager/provisioning.py split planned; provisioning flow modules pending extraction ariadne/manager/provisioning.py split planned; provisioning flow modules pending extraction
ariadne/services/nextcloud.py split planned; provider methods pending partition 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/settings.py split planned; settings schema + helpers pending split
ariadne/services/jenkins_workspace_cleanup.py split planned; job orchestration pending extraction 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_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_services.py test module split planned; broad service contract coverage retained meanwhile
tests/test_app.py test module split planned; API coverage retained meanwhile tests/test_app.py test module split planned; API coverage retained meanwhile

1 # path reason
4 ariadne/services/comms.py split planned; comms adapters still consolidated
5 ariadne/manager/provisioning.py split planned; provisioning flow modules pending extraction
6 ariadne/services/nextcloud.py split planned; provider methods pending partition
ariadne/services/firefly.py split planned; provider methods pending partition
7 ariadne/settings.py split planned; settings schema + helpers pending split
8 ariadne/services/jenkins_workspace_cleanup.py split planned; job orchestration pending extraction
ariadne/services/wger.py split planned; provider methods pending partition
9 tests/test_provisioning.py test module split planned; broad provisioning coverage retained meanwhile
10 tests/test_services.py test module split planned; broad service contract coverage retained meanwhile
11 tests/test_app.py test module split planned; API coverage retained meanwhile