fire-planner: add stack, Vault DB role, dashboard, DB

New stacks/fire-planner/ mirrors payslip-ingest layout:
- ExternalSecret pulling RECOMPUTE_BEARER_TOKEN from Vault secret/fire-planner
- DB ExternalSecret templating DB_CONNECTION_STRING via static role pg-fire-planner
- FastAPI Deployment (serve), CronJob (recompute-all monthly on 2nd at 09:00 UTC,
  scheduled after wealthfolio-sync's 1st at 08:00), ClusterIP Service
- Grafana datasource ConfigMap "FirePlanner" — `database` inside jsonData
  (cc56ba29 fix; otherwise Grafana 11.2+ hits "you do not have default database")

Plus:
- vault/main.tf: pg-fire-planner static role (7d rotation), allowed_roles
- dbaas/modules/dbaas/main.tf: null_resource creates fire_planner DB+role
- monitoring/dashboards/fire-planner.json: 9-panel Finance-folder dashboard
  (NW timeseries, MC fan chart, success heatmap, lifetime tax bars,
  years-to-ruin table, optimal leave-UK stat, ending wealth stat,
  UK success-by-strategy bars, sequence-risk correlation table)
- monitoring/modules/monitoring/grafana.tf: register "fire-planner.json" in Finance folder

Apply order:
  1. vault stack — creates the static role
  2. dbaas stack — creates the database & role
  3. external-secrets stack picks up vault-database refs (no change needed)
  4. fire-planner stack — first apply with -target=kubernetes_manifest.db_external_secret
     before full apply, per the plan-time-data-source pattern
  5. monitoring stack — picks up the new dashboard ConfigMap

[ci skip]

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-04-25 17:27:19 +00:00
parent 484b4c7190
commit f0ce7b0363
6 changed files with 675 additions and 1 deletions

View file

@ -1236,6 +1236,34 @@ resource "null_resource" "pg_wealthfolio_sync_db" {
}
}
# Create fire_planner database for the FIRE retirement-planning service.
# Role password is managed by Vault Database Secrets Engine
# (static role `pg-fire-planner`, 7d rotation).
# fire_planner reads from payslip_ingest + wealthfolio_sync (read-only)
# and writes its own MC results into schema fire_planner.
resource "null_resource" "pg_fire_planner_db" {
depends_on = [null_resource.pg_cluster]
triggers = {
db_name = "fire_planner"
username = "fire_planner"
}
provisioner "local-exec" {
command = <<-EOT
PRIMARY=$(kubectl --kubeconfig ${var.kube_config_path} get cluster -n dbaas pg-cluster -o jsonpath='{.status.currentPrimary}')
kubectl --kubeconfig ${var.kube_config_path} exec -n dbaas $PRIMARY -c postgres -- \
bash -c '
psql -U postgres -tc "SELECT 1 FROM pg_catalog.pg_roles WHERE rolname = '"'"'fire_planner'"'"'" | grep -q 1 || \
psql -U postgres -c "CREATE ROLE fire_planner WITH LOGIN PASSWORD '"'"'changeme-vault-will-rotate'"'"'"
psql -U postgres -tc "SELECT 1 FROM pg_catalog.pg_database WHERE datname = '"'"'fire_planner'"'"'" | grep -q 1 || \
psql -U postgres -c "CREATE DATABASE fire_planner OWNER fire_planner"
psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE fire_planner TO fire_planner"
'
EOT
}
}
# Old PostgreSQL deployment kept commented for rollback reference
# resource "kubernetes_deployment" "postgres" {
# metadata {