Prepare railiance01 delivery: dynamic leases, migrate Job, operator runbook
VaultDynamicSecret pulls database/creds/* so a rotating lease is not frozen into KV. Runtime sets AUDIT_CORE_AUTO_MIGRATE=0; schema is a Job with the migration lease. Image base is digest-pinned. Namespace and NetworkPolicies are on the cluster; Deployment waits for the attended OpenBao ESO token.
This commit is contained in:
parent
bbf86b8373
commit
3a7d63e18f
18 changed files with 826 additions and 33 deletions
|
|
@ -69,6 +69,29 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
cleanup_parser.add_argument("--retention-days", type=int, default=None)
|
||||
cleanup_parser.set_defaults(func=cleanup)
|
||||
|
||||
schema_parser = sub.add_parser(
|
||||
"migrate",
|
||||
help="Apply pending PostgreSQL schema migrations and exit.",
|
||||
)
|
||||
schema_parser.add_argument(
|
||||
"--to-postgres", dest="destination", default=None,
|
||||
help="DSN; defaults to AUDIT_CORE_DATABASE_URL or a mounted credential dir.",
|
||||
)
|
||||
schema_parser.add_argument("--schema", default="audit_core")
|
||||
schema_parser.set_defaults(func=migrate_schema)
|
||||
|
||||
replay_parser = sub.add_parser(
|
||||
"replay",
|
||||
help="Re-submit a stored event through accept(); must reconcile as duplicate.",
|
||||
)
|
||||
replay_parser.add_argument("--event-id", required=True)
|
||||
replay_parser.add_argument(
|
||||
"--to-postgres", dest="destination", default=None,
|
||||
help="DSN; defaults to AUDIT_CORE_DATABASE_URL or a mounted credential dir.",
|
||||
)
|
||||
replay_parser.add_argument("--schema", default="audit_core")
|
||||
replay_parser.set_defaults(func=replay_event)
|
||||
|
||||
migrate_parser = sub.add_parser(
|
||||
"migrate-store", help="Move audit records from a SQLite store into PostgreSQL."
|
||||
)
|
||||
|
|
@ -87,6 +110,54 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
return parser
|
||||
|
||||
|
||||
def _postgres_backend(dsn: str | None, schema: str, *, migrate: bool):
|
||||
import os
|
||||
|
||||
from audit_core.postgres_backend import PostgresAuditBackend
|
||||
|
||||
return PostgresAuditBackend(
|
||||
dsn if dsn is not None else os.environ.get("AUDIT_CORE_DATABASE_URL") or "",
|
||||
schema=schema,
|
||||
credential_dir=os.environ.get("AUDIT_CORE_CREDENTIAL_DIR"),
|
||||
migrate=migrate,
|
||||
)
|
||||
|
||||
|
||||
def migrate_schema(args: argparse.Namespace) -> int:
|
||||
"""Apply pending schema migrations using the current connection.
|
||||
|
||||
Production runs this as a Job with the migration lease. The runtime
|
||||
Deployment sets AUDIT_CORE_AUTO_MIGRATE=0 so the app role never needs
|
||||
CREATE TABLE.
|
||||
"""
|
||||
backend = _postgres_backend(args.destination, args.schema, migrate=False)
|
||||
try:
|
||||
applied = backend.migrate()
|
||||
finally:
|
||||
backend.close()
|
||||
print(json.dumps({"ok": True, "schema": args.schema, "applied": applied}, sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
def replay_event(args: argparse.Namespace) -> int:
|
||||
"""Reconcile a stored event. A first-acceptance result is a custody defect."""
|
||||
backend = _postgres_backend(args.destination, args.schema, migrate=False)
|
||||
try:
|
||||
result = backend.replay(args.event_id)
|
||||
except KeyError:
|
||||
print(json.dumps({"ok": False, "error": "not_found", "event_id": args.event_id}))
|
||||
return 1
|
||||
finally:
|
||||
backend.close()
|
||||
print(json.dumps({
|
||||
"ok": True,
|
||||
"event_id": args.event_id,
|
||||
"duplicate": result.duplicate,
|
||||
"reference": result.reference,
|
||||
}, sort_keys=True))
|
||||
return 0 if result.duplicate else 2
|
||||
|
||||
|
||||
def migrate_store(args: argparse.Namespace) -> int:
|
||||
import os
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue