Skip to content

Commit 2ec999d

Browse files
feat(api): sync dokku with database's service table
1 parent 4e4395c commit 2ec999d

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

src/api/app.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@
1010

1111
from src.api.middlewares import SessionUserMiddleware
1212
from src.api.router import get_router
13-
from src.api.services import AppService
13+
from src.api.services import AppService, DatabaseService
1414
from src.config import Config
1515

1616
APP_ROOT = Path(__file__).parent
1717

1818
scheduler = AsyncIOScheduler()
1919

2020

21-
async def sync_database_with_dokku():
22-
await AppService.sync_database()
21+
async def sync_dokku_with_api_database():
22+
await DatabaseService.sync_dokku_with_api_database()
23+
await AppService.sync_dokku_with_api_database()
2324

2425

2526
def get_app() -> FastAPI:
@@ -51,7 +52,7 @@ def get_app() -> FastAPI:
5152
async def startup():
5253
scheduler.start()
5354
scheduler.add_job(
54-
sync_database_with_dokku,
55+
sync_dokku_with_api_database,
5556
trigger=IntervalTrigger(hours=1),
5657
id="sync_db_w_dokku_task",
5758
replace_existing=True,

src/api/services/apps.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -575,13 +575,14 @@ async def download_file(
575575
return False, f"Failed to parse xxd output: {error}"
576576

577577
@staticmethod
578-
async def sync_database() -> None:
578+
async def sync_dokku_with_api_database() -> None:
579579
success, message = await run_command("apps:list")
580580

581581
if not success:
582-
raise SystemError("Could not recover apps list to sync database")
582+
logging.warning("Could not recover apps list to sync with database")
583+
return
583584

584-
logging.warning("[sync_app_database]::Syncing database...")
585+
logging.warning("[sync_dokku_w_app_database]::Syncing Dokku...")
585586

586587
apps = parse_apps_list(message)
587588
apps = {name: True for name in apps if get_user_id_from_app(name) is not None}
@@ -593,8 +594,8 @@ async def sync_database() -> None:
593594

594595
for app_name in apps:
595596
logging.warning(
596-
f"[sync_app_database]:{app_name}::Destroying unused application..."
597+
f"[sync_dokku_w_app_database]:{app_name}::Destroying unused application..."
597598
)
598599
await run_command(f"--force apps:destroy {app_name}")
599600

600-
logging.warning("[sync_app_database]::Sync complete.")
601+
logging.warning("[sync_dokku_w_app_database]::Sync complete.")

src/api/services/databases.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import asyncio
2+
import logging
23
import re
34
from abc import ABC
4-
from typing import Any, Dict, Tuple
5+
from typing import Any, Dict, Optional, Tuple
56

67
from fastapi import HTTPException
78

8-
from src.api.models import App, Service, create_resource, delete_resource
9+
from src.api.models import App, Service, create_resource, delete_resource, get_resources
910
from src.api.schemas import UserSchema
1011
from src.api.tools.name import ResourceName
1112
from src.api.tools.ssh import run_command
@@ -29,6 +30,26 @@ def parse_service_info(plugin_name: str, info_str: str) -> Dict:
2930
return result
3031

3132

33+
def parse_service_list(text: str):
34+
"""
35+
Extract the list of services from a formatted string.
36+
"""
37+
lines = text.strip().split("\n")
38+
apps = [
39+
line.strip() for line in lines if line.strip() and not line.startswith("=====>")
40+
]
41+
return apps
42+
43+
44+
def get_user_id_from_service(name) -> Optional[int]:
45+
id = name.split("_", maxsplit=1)[0]
46+
47+
try:
48+
return int(id)
49+
except ValueError:
50+
return None
51+
52+
3253
def extract_database_uri(text):
3354
pattern = re.compile(
3455
r"\b(?:[a-z]+)://(?:[^:@\s]+):(?:[^:@\s]+)@(?:[^:@\s]+):\d+/\S+\b",
@@ -358,3 +379,37 @@ async def restart_database(
358379
return False, None
359380

360381
return True, message
382+
383+
@staticmethod
384+
async def sync_dokku_with_api_database() -> None:
385+
available_databases = (await DatabaseService.list_available_databases())[1]
386+
services = {}
387+
388+
for plugin_name in available_databases:
389+
success, message = await run_command(f"{plugin_name}:list")
390+
391+
if not success:
392+
logging.warning(
393+
f"Could not recover {plugin_name} services list to sync with database"
394+
)
395+
continue
396+
397+
for name in parse_service_list(message):
398+
if get_user_id_from_service(name):
399+
services[f"{plugin_name}:{name}"] = True
400+
401+
logging.warning("[sync_dokku_w_service_database]::Syncing Dokku...")
402+
403+
db_services = await get_resources(Service, offset=0, limit=None)
404+
405+
for service in db_services:
406+
services.pop(service["name"], None)
407+
408+
for service_name in services:
409+
plugin_name, service_name = service_name.split(":", maxsplit=1)
410+
logging.warning(
411+
f"[sync_dokku_w_service_database]:{plugin_name}:{service_name}::Destroying unused service..."
412+
)
413+
await run_command(f"--force {plugin_name}:destroy {service_name}")
414+
415+
logging.warning("[sync_dokku_w_service_database]::Sync complete.")

0 commit comments

Comments
 (0)