[postgres] Fix database deletion lifecycle management (#2247)

## What this PR does

Previously, removing a database from `values.databases` had no effect —
the database and its associated roles (`<db>_admin`, `<db>_readonly`)
persisted in PostgreSQL indefinitely. This made it impossible to
declaratively manage database lifecycle via Helm values.

Added two cleanup stages to the init script, mirroring the existing user
deletion logic:
- **Delete databases** that have the `database managed by helm` comment
but are no longer listed in `values.databases` — active connections are
terminated before dropping
- **Delete orphaned roles** (`<db>_admin`, `<db>_readonly`) with proper
membership revocation (`REVOKE ... FROM`) before `REASSIGN OWNED` /
`DROP OWNED` / `DROP ROLE`

This also fixes the reported issue where creating a database, removing
it, and creating it again would silently retain stale data from the
first instance.

### Release note

```release-note
[postgres] Databases removed from `values.databases` are now properly dropped along with their associated roles. Previously removed databases and roles would persist indefinitely.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Enhanced database initialization to automatically remove databases and
roles that were removed from your Helm configuration, avoiding orphaned
resources.
* Now force-terminates active sessions, reassigns owned objects, and
cleans up role memberships to ensure reliable removals.
* Improves consistency of database state during upgrades and
configuration changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Kirill Ilin 2026-03-27 17:49:15 +05:00 committed by GitHub
commit 4f22f075e0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -66,6 +66,38 @@ stringData:
EOT
done
echo "== delete databases"
MANAGED_DBS=$(psql -v ON_ERROR_STOP=1 -t -A -c "SELECT datname FROM pg_database d JOIN pg_shdescription s ON d.oid = s.objoid WHERE s.description = 'database managed by helm'")
DEFINED_DBS="{{ join " " (keys .Values.databases) }}"
DELETE_DBS=$(for db in $MANAGED_DBS; do case " $DEFINED_DBS " in *" $db "*) :;; *) echo $db;; esac; done)
echo "databases to delete: $DELETE_DBS"
for db in $DELETE_DBS; do
psql -v ON_ERROR_STOP=1 --echo-all -c "DROP DATABASE IF EXISTS \"$db\" WITH (FORCE);"
done
echo "== delete orphaned managed roles"
MANAGED_ROLES=$(psql -v ON_ERROR_STOP=1 -t -A -c "SELECT rolname FROM pg_roles r JOIN pg_shdescription s ON r.oid = s.objoid WHERE s.description = 'role managed by helm'")
DEFINED_ROLES="{{ range $database, $d := .Values.databases }}{{ $database }}_admin {{ $database }}_readonly {{ end }}"
DELETE_ROLES=$(for role in $MANAGED_ROLES; do case " $DEFINED_ROLES " in *" $role "*) :;; *) echo $role;; esac; done)
echo "roles to delete: $DELETE_ROLES"
for role in $DELETE_ROLES; do
psql -v ON_ERROR_STOP=1 --echo-all <<EOT
DO \$\$
DECLARE
member record;
BEGIN
FOR member IN SELECT m.rolname FROM pg_auth_members am JOIN pg_roles m ON am.member = m.oid JOIN pg_roles r ON am.roleid = r.oid WHERE r.rolname = '$role' LOOP
EXECUTE format('REVOKE %I FROM %I', '$role', member.rolname);
END LOOP;
END\$\$;
REASSIGN OWNED BY "$role" TO postgres;
DROP OWNED BY "$role";
DROP ROLE IF EXISTS "$role";
EOT
done
echo "== create databases and roles"
{{- if .Values.databases }}
psql -v ON_ERROR_STOP=1 --echo-all <<\EOT