Skip to content

Commit 3e9b4d0

Browse files
authored
[MISC] Hold database created hook for pg_hba changes (#1001)
* Hold database created hook for pg_hba changes * Restore the old blocking hook * 16/edge lib changes * Set to expected patch version
1 parent d37a671 commit 3e9b4d0

File tree

3 files changed

+35
-25
lines changed

3 files changed

+35
-25
lines changed

lib/charms/postgresql_k8s/v0/postgresql.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
# Increment this PATCH version before using `charmcraft publish-lib` or reset
3737
# to 0 if you are raising the major API version
38-
LIBPATCH = 55
38+
LIBPATCH = 53
3939

4040
# Groups to distinguish HBA access
4141
ACCESS_GROUP_IDENTITY = "identity_access"
@@ -780,6 +780,7 @@ def list_valid_privileges_and_roles(self) -> Tuple[Set[str], Set[str]]:
780780
def set_up_database(self) -> None:
781781
"""Set up postgres database with the right permissions."""
782782
connection = None
783+
cursor = None
783784
try:
784785
with self._connect_to_database(
785786
database="template1"
@@ -879,6 +880,8 @@ def set_up_database(self) -> None:
879880
logger.error(f"Failed to set up databases: {e}")
880881
raise PostgreSQLDatabasesSetupError() from e
881882
finally:
883+
if cursor is not None:
884+
cursor.close()
882885
if connection is not None:
883886
connection.close()
884887

@@ -1078,3 +1081,21 @@ def validate_group_map(self, group_map: Optional[str]) -> bool:
10781081
return False
10791082

10801083
return True
1084+
1085+
def is_user_in_hba(self, username: str) -> bool:
1086+
"""Check if user was added in pg_hba."""
1087+
connection = None
1088+
try:
1089+
with self._connect_to_database() as connection, connection.cursor() as cursor:
1090+
cursor.execute(
1091+
SQL(
1092+
"SELECT COUNT(*) FROM pg_hba_file_rules WHERE {} = ANY(user_name);"
1093+
).format(Literal(username))
1094+
)
1095+
return cursor.fetchone()[0] > 0
1096+
except psycopg2.Error as e:
1097+
logger.debug(f"Failed to check pg_hba: {e}")
1098+
return False
1099+
finally:
1100+
if connection:
1101+
connection.close()

src/relations/postgresql_provider.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"""Postgres client relation hooks & helpers."""
55

66
import logging
7+
from datetime import datetime
78

89
from charms.data_platform_libs.v0.data_interfaces import (
910
DatabaseProvides,
@@ -21,6 +22,7 @@
2122
from ops.charm import CharmBase, RelationBrokenEvent, RelationChangedEvent, RelationDepartedEvent
2223
from ops.framework import Object
2324
from ops.model import ActiveStatus, BlockedStatus, Relation
25+
from tenacity import RetryError, Retrying, stop_after_attempt, wait_fixed
2426

2527
from constants import (
2628
DATABASE_PORT,
@@ -66,9 +68,6 @@ def __init__(self, charm: CharmBase, relation_name: str = "database") -> None:
6668
self.framework.observe(
6769
self.database_provides.on.database_requested, self._on_database_requested
6870
)
69-
self.framework.observe(
70-
charm.on[self.relation_name].relation_changed, self._on_relation_changed
71-
)
7271

7372
@staticmethod
7473
def _sanitize_extra_roles(extra_roles: str | None) -> list[str]:
@@ -163,26 +162,19 @@ def _on_database_requested(self, event: DatabaseRequestedEvent) -> None:
163162
if issubclass(type(e), PostgreSQLCreateUserError) and e.message is not None
164163
else f"Failed to initialize {self.relation_name} relation"
165164
)
166-
167-
def _on_relation_changed(self, event: RelationChangedEvent) -> None:
168-
# Check for some conditions before trying to access the PostgreSQL instance.
169-
if not self.charm.is_cluster_initialised:
170-
logger.debug(
171-
"Deferring on_relation_changed: Cluster must be initialized before configuration can be updated with relation users"
172-
)
173-
event.defer()
174165
return
175166

176-
if (
177-
not self.charm._patroni.member_started
178-
or f"relation_id_{event.relation.id}"
179-
not in self.charm.postgresql.list_users(current_host=True)
180-
):
181-
logger.debug("Deferring on_relation_changed: user was not created yet")
182-
event.defer()
183-
return
184-
185-
self.charm.update_config()
167+
# Try to wait for pg_hba trigger
168+
try:
169+
for attempt in Retrying(stop=stop_after_attempt(3), wait=wait_fixed(1)):
170+
with attempt:
171+
if not self.charm.postgresql.is_user_in_hba(user):
172+
raise Exception("pg_hba not ready")
173+
self.charm.unit_peer_data.update({
174+
"pg_hba_needs_update_timestamp": str(datetime.now())
175+
})
176+
except RetryError:
177+
logger.warning("database requested: Unable to check pg_hba rule update")
186178

187179
def _on_relation_departed(self, event: RelationDepartedEvent) -> None:
188180
"""Set a flag to avoid deleting database users when not wanted."""

tests/unit/test_postgresql_provider.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,6 @@ def request_database(_harness):
7676
def test_on_database_requested(harness):
7777
with (
7878
patch("charm.PostgresqlOperatorCharm.update_config"),
79-
patch(
80-
"relations.postgresql_provider.PostgreSQLProvider._on_relation_changed"
81-
) as _on_relation_changed,
8279
patch.object(PostgresqlOperatorCharm, "postgresql", Mock()) as postgresql_mock,
8380
patch.object(EventBase, "defer") as _defer,
8481
patch("charm.Patroni.member_started", new_callable=PropertyMock) as _member_started,

0 commit comments

Comments
 (0)