Improve the demo feature of the project
This commit is contained in:
138
shared/scripts/run_migrations.py
Executable file
138
shared/scripts/run_migrations.py
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Enhanced Migration Runner
|
||||
|
||||
Handles automatic table creation and Alembic migrations for Kubernetes deployments.
|
||||
Supports both first-time deployments and incremental migrations.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
import argparse
|
||||
import structlog
|
||||
from pathlib import Path
|
||||
|
||||
# Add the project root to the Python path
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from shared.database.base import DatabaseManager
|
||||
from shared.database.init_manager import initialize_service_database
|
||||
|
||||
# Configure logging
|
||||
structlog.configure(
|
||||
processors=[
|
||||
structlog.stdlib.filter_by_level,
|
||||
structlog.stdlib.add_logger_name,
|
||||
structlog.stdlib.add_log_level,
|
||||
structlog.stdlib.PositionalArgumentsFormatter(),
|
||||
structlog.processors.TimeStamper(fmt="iso"),
|
||||
structlog.processors.StackInfoRenderer(),
|
||||
structlog.processors.format_exc_info,
|
||||
structlog.processors.JSONRenderer()
|
||||
],
|
||||
context_class=dict,
|
||||
logger_factory=structlog.stdlib.LoggerFactory(),
|
||||
wrapper_class=structlog.stdlib.BoundLogger,
|
||||
cache_logger_on_first_use=True,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
async def run_service_migration(service_name: str, force_recreate: bool = False) -> bool:
|
||||
"""
|
||||
Run migrations for a specific service.
|
||||
|
||||
This script is for MIGRATION JOBS ONLY.
|
||||
Services themselves never run migrations - they only verify DB is ready.
|
||||
|
||||
Args:
|
||||
service_name: Name of the service (e.g., 'auth', 'inventory')
|
||||
force_recreate: Whether to force recreate tables (development mode)
|
||||
|
||||
Returns:
|
||||
True if successful, False otherwise
|
||||
"""
|
||||
logger.info("Migration job starting", service=service_name, force_recreate=force_recreate)
|
||||
|
||||
try:
|
||||
# Get database URL from environment (try both constructed and direct approaches)
|
||||
db_url_key = f"{service_name.upper().replace('-', '_')}_DATABASE_URL"
|
||||
database_url = os.getenv(db_url_key) or os.getenv("DATABASE_URL")
|
||||
|
||||
# If no direct URL, construct from components
|
||||
if not database_url:
|
||||
host = os.getenv("POSTGRES_HOST")
|
||||
port = os.getenv("POSTGRES_PORT")
|
||||
db_name = os.getenv("POSTGRES_DB")
|
||||
user = os.getenv("POSTGRES_USER")
|
||||
password = os.getenv("POSTGRES_PASSWORD")
|
||||
|
||||
if all([host, port, db_name, user, password]):
|
||||
database_url = f"postgresql+asyncpg://{user}:{password}@{host}:{port}/{db_name}"
|
||||
logger.info("Constructed database URL from components", host=host, port=port, db=db_name)
|
||||
else:
|
||||
logger.error("Database connection details not found",
|
||||
db_url_key=db_url_key,
|
||||
host=bool(host),
|
||||
port=bool(port),
|
||||
db=bool(db_name),
|
||||
user=bool(user),
|
||||
password=bool(password))
|
||||
return False
|
||||
|
||||
# Create database manager
|
||||
db_manager = DatabaseManager(database_url=database_url)
|
||||
|
||||
# Run migrations (verify_only=False means actually run migrations)
|
||||
result = await initialize_service_database(
|
||||
database_manager=db_manager,
|
||||
service_name=service_name,
|
||||
verify_only=False, # Migration jobs RUN migrations
|
||||
force_recreate=force_recreate
|
||||
)
|
||||
|
||||
logger.info("Migration job completed successfully", service=service_name, result=result)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Migration job failed", service=service_name, error=str(e))
|
||||
return False
|
||||
|
||||
finally:
|
||||
# Cleanup database connections
|
||||
try:
|
||||
await db_manager.close_connections()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main migration runner"""
|
||||
parser = argparse.ArgumentParser(description="Enhanced Migration Runner")
|
||||
parser.add_argument("service", help="Service name (e.g., auth, inventory)")
|
||||
parser.add_argument("--force-recreate", action="store_true",
|
||||
help="Force recreate tables (development mode)")
|
||||
parser.add_argument("--verbose", "-v", action="store_true", help="Verbose logging")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
logger.info("Starting migration runner", service=args.service,
|
||||
force_recreate=args.force_recreate)
|
||||
|
||||
# Run the migration
|
||||
success = await run_service_migration(args.service, args.force_recreate)
|
||||
|
||||
if success:
|
||||
logger.info("Migration runner completed successfully")
|
||||
sys.exit(0)
|
||||
else:
|
||||
logger.error("Migration runner failed")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user