117 lines
4.8 KiB
Python
117 lines
4.8 KiB
Python
"""Health-related CLI commands."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import typer
|
|
|
|
from app.cli.core.utils import print_json
|
|
from app.db.migrations import (
|
|
bootstrap_or_upgrade_to_head,
|
|
current_revision,
|
|
head_revision,
|
|
)
|
|
from app.repositories.factory import build_portfolio_db_config
|
|
from app.repositories.postgres import create_database_if_missing, database_exists
|
|
|
|
|
|
def initialize_database(*, settings, maintenance_db: str = "postgres") -> dict[str, object]:
|
|
config = build_portfolio_db_config(settings)
|
|
existed_before = database_exists(config, maintenance_db=maintenance_db)
|
|
created = False
|
|
if not existed_before:
|
|
created = create_database_if_missing(config, maintenance_db=maintenance_db)
|
|
|
|
bootstrap_or_upgrade_to_head(settings)
|
|
|
|
return {
|
|
"database": config.dbname,
|
|
"host": config.host,
|
|
"port": config.port,
|
|
"created": created,
|
|
"schema_initialized": True,
|
|
"migration_applied": "head",
|
|
}
|
|
|
|
|
|
def register_health_commands(app: typer.Typer) -> None:
|
|
@app.command("init-db")
|
|
def init_db(
|
|
ctx: typer.Context,
|
|
maintenance_db: str = typer.Option("postgres", help="Database to connect to for CREATE DATABASE."),
|
|
) -> None:
|
|
app_context = ctx.obj
|
|
try:
|
|
result = initialize_database(settings=app_context.settings, maintenance_db=maintenance_db)
|
|
except Exception as exc:
|
|
app_context.console.print(f"init-db failed: {exc}")
|
|
raise typer.Exit(code=1) from exc
|
|
else:
|
|
if app_context.output_format == "json":
|
|
print_json(app_context.console, result)
|
|
else:
|
|
app_context.console.print(f"database: {result['database']}")
|
|
app_context.console.print(f"host: {result['host']}")
|
|
app_context.console.print(f"port: {result['port']}")
|
|
app_context.console.print(f"created: {result['created']}")
|
|
app_context.console.print("schema_initialized: True")
|
|
app_context.console.print("migration_applied: head")
|
|
|
|
@app.command("migrate-db")
|
|
def migrate_db(ctx: typer.Context) -> None:
|
|
app_context = ctx.obj
|
|
try:
|
|
bootstrap_or_upgrade_to_head(app_context.settings)
|
|
result = {
|
|
"database": app_context.settings.active_db_name,
|
|
"host": app_context.settings.active_db_host,
|
|
"port": app_context.settings.active_db_port,
|
|
"migration_applied": "head",
|
|
}
|
|
except Exception as exc:
|
|
app_context.console.print(f"migrate-db failed: {exc}")
|
|
raise typer.Exit(code=1) from exc
|
|
else:
|
|
if app_context.output_format == "json":
|
|
print_json(app_context.console, result)
|
|
else:
|
|
app_context.console.print(f"database: {result['database']}")
|
|
app_context.console.print(f"host: {result['host']}")
|
|
app_context.console.print(f"port: {result['port']}")
|
|
app_context.console.print("migration_applied: head")
|
|
|
|
@app.command("health-check")
|
|
def health_check(ctx: typer.Context) -> None:
|
|
app_context = ctx.obj
|
|
repository = app_context.repository_factory(app_context.settings)
|
|
try:
|
|
health = {
|
|
"app_name": app_context.settings.app_name,
|
|
"environment": app_context.settings.environment,
|
|
"db_enabled": app_context.settings.db_enabled,
|
|
"active_db_host": app_context.settings.active_db_host,
|
|
"active_db_port": app_context.settings.active_db_port,
|
|
"repository": repository.__class__.__name__,
|
|
}
|
|
if hasattr(repository, "ping"):
|
|
repository.ping()
|
|
if app_context.settings.db_enabled:
|
|
database_revision = current_revision(app_context.settings)
|
|
expected_revision = head_revision(app_context.settings)
|
|
health["database_revision"] = database_revision
|
|
health["expected_revision"] = expected_revision
|
|
if database_revision != expected_revision:
|
|
raise RuntimeError(
|
|
f"database revision {database_revision!r} is not packaged head {expected_revision!r}"
|
|
)
|
|
except Exception as exc:
|
|
app_context.console.print(f"health-check failed: {exc}")
|
|
raise typer.Exit(code=1) from exc
|
|
else:
|
|
if app_context.output_format == "json":
|
|
print_json(app_context.console, health)
|
|
else:
|
|
for key, value in health.items():
|
|
app_context.console.print(f"{key}: {value}")
|
|
finally:
|
|
if callable(getattr(type(repository), "close", None)):
|
|
repository.close() |