Refactor backend and add registry build workflow
Build and publish Docker image / build-and-publish (push) Successful in 2m45s
Build and publish Docker image / build-and-publish (push) Successful in 2m45s
This commit is contained in:
commit
c177d7981d
43 files changed
+18124
No files matched your search
@@ -0,0 +1,244 @@
|
||||
"""Background schedulers, cleanup loops, and development fixture setup."""
|
||||
|
||||
|
||||
from currency_service import fetch_exchange_rates
|
||||
from database_service import database_connection, dict_from_row
|
||||
from domain_runtime import domain_manager
|
||||
from domain_service import cleanup_orphaned_ingressroutes
|
||||
from kubernetes_service import k8s_manager
|
||||
import os
|
||||
from runtime_config import (
|
||||
CACHE_DURATION,
|
||||
CHUNK_UPLOAD_DIR,
|
||||
chunked_uploads,
|
||||
logger,
|
||||
)
|
||||
import schedule
|
||||
from server_demo import cleanup_expired_demo_servers
|
||||
from server_provisioning import sync_server_ports_from_k8s, sync_server_status_from_k8s
|
||||
import threading
|
||||
import time
|
||||
from user_service import create_user, get_user_by_username
|
||||
|
||||
__all__ = ['cleanup_orphaned_uploads', 'initialize_sample_data', 'start_demo_cleanup_scheduler', 'start_exchange_rate_updater', 'start_ingress_cleanup_thread', 'start_status_sync_thread', 'start_upload_cleanup_scheduler', 'sync_all_servers_status']
|
||||
|
||||
|
||||
|
||||
def cleanup_orphaned_uploads():
|
||||
"""Clean up orphaned chunk upload files and sessions older than 1 hour"""
|
||||
try:
|
||||
current_time = time.time()
|
||||
max_age = 3600 # 1 hour
|
||||
|
||||
# Clean up old upload sessions
|
||||
uploads_to_remove = []
|
||||
for upload_id, upload_info in chunked_uploads.items():
|
||||
start_time = upload_info.get('start_time', current_time)
|
||||
if current_time - start_time > max_age:
|
||||
uploads_to_remove.append(upload_id)
|
||||
|
||||
for upload_id in uploads_to_remove:
|
||||
upload_info = chunked_uploads[upload_id]
|
||||
temp_file = upload_info.get('temp_file')
|
||||
if temp_file and os.path.exists(temp_file):
|
||||
try:
|
||||
os.remove(temp_file)
|
||||
logger.info(f"Cleaned up orphaned upload temp file: {temp_file}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to remove orphaned temp file {temp_file}: {e}")
|
||||
del chunked_uploads[upload_id]
|
||||
logger.info(f"Cleaned up orphaned upload session: {upload_id}")
|
||||
|
||||
# Clean up any remaining temp files older than 1 hour
|
||||
try:
|
||||
for filename in os.listdir(CHUNK_UPLOAD_DIR):
|
||||
filepath = os.path.join(CHUNK_UPLOAD_DIR, filename)
|
||||
if os.path.isfile(filepath):
|
||||
file_age = current_time - os.path.getmtime(filepath)
|
||||
if file_age > max_age:
|
||||
try:
|
||||
os.remove(filepath)
|
||||
logger.info(f"Cleaned up old temp file: {filepath}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to remove old temp file {filepath}: {e}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Error cleaning up temp files directory: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in cleanup_orphaned_uploads: {e}")
|
||||
|
||||
def start_upload_cleanup_scheduler():
|
||||
"""Start scheduler to clean up orphaned uploads"""
|
||||
def run_scheduler():
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
time.sleep(60) # Check every minute
|
||||
|
||||
# Run cleanup every 30 minutes
|
||||
schedule.every(30).minutes.do(cleanup_orphaned_uploads)
|
||||
thread = threading.Thread(target=run_scheduler, daemon=True)
|
||||
thread.start()
|
||||
logger.info("🧹 Upload cleanup scheduler started")
|
||||
|
||||
def start_demo_cleanup_scheduler():
|
||||
"""Start scheduler to clean up expired demo servers"""
|
||||
def run_scheduler():
|
||||
while True:
|
||||
schedule.run_pending()
|
||||
time.sleep(60) # Check every minute
|
||||
|
||||
# Run cleanup every minute to ensure immediate deletion of expired demo servers
|
||||
schedule.every(1).minutes.do(cleanup_expired_demo_servers)
|
||||
thread = threading.Thread(target=run_scheduler, daemon=True)
|
||||
thread.start()
|
||||
logger.info("🧹 Demo cleanup scheduler started (runs every minute)")
|
||||
|
||||
def start_exchange_rate_updater():
|
||||
"""Background thread to update exchange rates periodically"""
|
||||
def update_loop():
|
||||
# Initial fetch
|
||||
fetch_exchange_rates()
|
||||
|
||||
while True:
|
||||
try:
|
||||
time.sleep(CACHE_DURATION) # Wait 1 hour
|
||||
fetch_exchange_rates()
|
||||
except Exception as e:
|
||||
logger.error(f"Error in exchange rate update loop: {e}")
|
||||
time.sleep(60) # Wait 1 minute before retrying
|
||||
|
||||
thread = threading.Thread(target=update_loop, daemon=True)
|
||||
thread.start()
|
||||
logger.info("✓ Started exchange rate updater thread")
|
||||
|
||||
def sync_all_servers_status():
|
||||
"""Sync status for all servers"""
|
||||
if not k8s_manager.connected:
|
||||
return
|
||||
|
||||
try:
|
||||
with database_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT id, owner_id FROM servers WHERE status IN ('Creating', 'Starting', 'Stopping', 'Restarting')")
|
||||
servers = [dict_from_row(row) for row in cursor.fetchall()]
|
||||
|
||||
for server in servers:
|
||||
try:
|
||||
sync_server_status_from_k8s(server['id'], server['owner_id'])
|
||||
sync_server_ports_from_k8s(server['id'], server['owner_id'])
|
||||
except Exception as e:
|
||||
logger.error(f"Error syncing server {server['id']}: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error syncing all servers: {e}")
|
||||
|
||||
def start_status_sync_thread():
|
||||
"""Start background status sync"""
|
||||
def sync_loop():
|
||||
while True:
|
||||
try:
|
||||
sync_all_servers_status()
|
||||
time.sleep(30)
|
||||
except Exception as e:
|
||||
logger.error(f"Error in sync loop: {e}")
|
||||
time.sleep(60)
|
||||
|
||||
thread = threading.Thread(target=sync_loop, daemon=True)
|
||||
thread.start()
|
||||
logger.info("Started background status sync")
|
||||
|
||||
def initialize_sample_data():
|
||||
"""Create development fixtures only when explicitly enabled."""
|
||||
if os.environ.get('ENABLE_SAMPLE_DATA', '').casefold() != 'true':
|
||||
logger.info("Skipping sample data; set ENABLE_SAMPLE_DATA=true to opt in")
|
||||
return
|
||||
|
||||
sample_password = os.environ.get('SAMPLE_USER_PASSWORD')
|
||||
if not sample_password:
|
||||
logger.warning("Skipping sample user creation; SAMPLE_USER_PASSWORD is not configured")
|
||||
return
|
||||
|
||||
logger.info("Initializing sample data...")
|
||||
|
||||
k8s_manager.test_connection()
|
||||
|
||||
existing_user = get_user_by_username('testuser')
|
||||
if not existing_user:
|
||||
sample_user = create_user(
|
||||
username='testuser',
|
||||
email='test@example.com',
|
||||
password=sample_password,
|
||||
credits=100
|
||||
)
|
||||
if sample_user:
|
||||
logger.info(f"✓ Sample user created: {sample_user['username']}")
|
||||
else:
|
||||
logger.info(f"✓ Sample user exists: {existing_user['username']}")
|
||||
|
||||
# CREATE USER DOMAIN IN DNS IF IT DOESN'T EXIST
|
||||
if domain_manager and existing_user:
|
||||
try:
|
||||
logger.info(f"Checking/Creating domain for user: u-{existing_user['username']}.{domain_manager.base_domain}")
|
||||
|
||||
# Check if user domain exists
|
||||
user_domain = f"u-{existing_user['username']}.{domain_manager.base_domain}"
|
||||
domain_exists = domain_manager._check_domain_exists(user_domain)
|
||||
|
||||
if domain_exists is None:
|
||||
logger.error(f"Error checking domain {user_domain}")
|
||||
elif not domain_exists:
|
||||
logger.info(f"User domain doesn't exist, creating: {user_domain}")
|
||||
success = domain_manager._create_domain(user_domain)
|
||||
if success:
|
||||
logger.info(f"✓ Created user domain: {user_domain}")
|
||||
|
||||
# SETUP DNSSEC DELEGATION for new domain
|
||||
logger.info("Setting up DNSSEC delegation...")
|
||||
dnssec_result = domain_manager.setup_dnssec_delegation(existing_user['username'])
|
||||
if dnssec_result.get('success'):
|
||||
logger.info(f"✓ DNSSEC delegation configured")
|
||||
else:
|
||||
logger.warning(f"⚠ DNSSEC setup issues: {dnssec_result.get('message')}")
|
||||
|
||||
# Also create a default subdomain for testing
|
||||
test_result = domain_manager.create_subdomain(existing_user['username'], 'test')
|
||||
if test_result.get('success'):
|
||||
logger.info(f"✓ Created test subdomain for user")
|
||||
else:
|
||||
logger.error(f"✗ Failed to create test subdomain: {test_result.get('message')}")
|
||||
else:
|
||||
logger.error(f"✗ Failed to create user domain: {user_domain}")
|
||||
else:
|
||||
logger.info(f"✓ User domain already exists: {user_domain}")
|
||||
|
||||
# ENSURE DNSSEC IS PROPERLY CONFIGURED for existing domain
|
||||
logger.info("Ensuring DNSSEC is properly configured...")
|
||||
dnssec_result = domain_manager.setup_dnssec_delegation(existing_user['username'])
|
||||
if dnssec_result.get('success'):
|
||||
logger.info(f"✓ DNSSEC properly configured")
|
||||
else:
|
||||
logger.warning(f"⚠ DNSSEC configuration issues: {dnssec_result.get('message')}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating user domain: {e}")
|
||||
|
||||
if k8s_manager.connected:
|
||||
try:
|
||||
k8s_manager.create_namespace('user-testuser')
|
||||
logger.info("✓ Sample namespace created")
|
||||
except Exception as e:
|
||||
logger.error(f"⚠ Could not create namespace: {e}")
|
||||
|
||||
def start_ingress_cleanup_thread():
|
||||
"""Start background thread to clean up orphaned IngressRoutes"""
|
||||
def cleanup_loop():
|
||||
while True:
|
||||
try:
|
||||
cleanup_orphaned_ingressroutes()
|
||||
time.sleep(300) # Run every 5 minutes
|
||||
except Exception as e:
|
||||
logger.error(f"Error in ingress cleanup loop: {e}")
|
||||
time.sleep(60)
|
||||
|
||||
thread = threading.Thread(target=cleanup_loop, daemon=True)
|
||||
thread.start()
|
||||
logger.info("Started background IngressRoute cleanup")
|
||||
Reference in new issue
Block a user