Skip to content

Commit a3221f8

Browse files
[DPE-5306] Data Interafces v40 (#615)
* Data Interafces v40 * Adjustments on unittests * Fix on charm code not to publish before relation is fully established * Don't set uri if no database or password * Unit test --------- Co-authored-by: Dragomir Penev <[email protected]>
1 parent 7ef0f2a commit a3221f8

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

lib/charms/data_platform_libs/v0/data_interfaces.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def _on_topic_requested(self, event: TopicRequestedEvent):
331331

332332
# Increment this PATCH version before using `charmcraft publish-lib` or reset
333333
# to 0 if you are raising the major API version
334-
LIBPATCH = 39
334+
LIBPATCH = 40
335335

336336
PYDEPS = ["ops>=2.0.0"]
337337

@@ -391,6 +391,10 @@ class IllegalOperationError(DataInterfacesError):
391391
"""To be used when an operation is not allowed to be performed."""
392392

393393

394+
class PrematureDataAccessError(DataInterfacesError):
395+
"""To be raised when the Relation Data may be accessed (written) before protocol init complete."""
396+
397+
394398
##############################################################################
395399
# Global helpers / utilities
396400
##############################################################################
@@ -1453,6 +1457,8 @@ def _on_relation_changed_event(self, event: RelationChangedEvent) -> None:
14531457
class ProviderData(Data):
14541458
"""Base provides-side of the data products relation."""
14551459

1460+
DATABASE_FIELD = "database"
1461+
14561462
def __init__(
14571463
self,
14581464
model: Model,
@@ -1618,6 +1624,15 @@ def _fetch_my_specific_relation_data(
16181624
def _update_relation_data(self, relation: Relation, data: Dict[str, str]) -> None:
16191625
"""Set values for fields not caring whether it's a secret or not."""
16201626
req_secret_fields = []
1627+
1628+
keys = set(data.keys())
1629+
if self.fetch_relation_field(relation.id, self.DATABASE_FIELD) is None and (
1630+
keys - {"endpoints", "read-only-endpoints", "replset"}
1631+
):
1632+
raise PrematureDataAccessError(
1633+
"Premature access to relation data, update is forbidden before the connection is initialized."
1634+
)
1635+
16211636
if relation.app:
16221637
req_secret_fields = get_encoded_list(relation, relation.app, REQ_SECRET_FIELDS)
16231638

@@ -3290,6 +3305,8 @@ class KafkaRequiresEvents(CharmEvents):
32903305
class KafkaProviderData(ProviderData):
32913306
"""Provider-side of the Kafka relation."""
32923307

3308+
DATABASE_FIELD = "topic"
3309+
32933310
def __init__(self, model: Model, relation_name: str) -> None:
32943311
super().__init__(model, relation_name)
32953312

@@ -3539,6 +3556,8 @@ class OpenSearchRequiresEvents(CharmEvents):
35393556
class OpenSearchProvidesData(ProviderData):
35403557
"""Provider-side of the OpenSearch relation."""
35413558

3559+
DATABASE_FIELD = "index"
3560+
35423561
def __init__(self, model: Model, relation_name: str) -> None:
35433562
super().__init__(model, relation_name)
35443563

src/relations/postgresql_provider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@ def update_endpoints(self, event: DatabaseRequestedEvent = None) -> None:
194194
user = f"relation-{relation_id}"
195195
database = rel_data[relation_id].get("database")
196196
password = secret_data.get(relation_id, {}).get("password")
197+
if not database or not password:
198+
continue
197199

198200
# Set the read/write endpoint.
199201
self.database_provides.set_endpoints(

tests/unit/test_postgresql_provider.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,18 +281,35 @@ def test_update_endpoints_without_event(harness):
281281
"relations.postgresql_provider.DatabaseProvides.fetch_my_relation_data"
282282
) as _fetch_my_relation_data,
283283
):
284-
_fetch_my_relation_data.return_value.get().get.return_value = "test_password"
285-
286284
# Mock the members_ips list to simulate different scenarios
287285
# (with and without a replica).
288-
_members_ips.side_effect = [{"1.1.1.1", "2.2.2.2"}, {"1.1.1.1"}]
286+
_members_ips.side_effect = [{"1.1.1.1", "2.2.2.2"}, {"1.1.1.1", "2.2.2.2"}, {"1.1.1.1"}]
289287
rel_id = harness.model.get_relation(RELATION_NAME).id
290288

289+
# Don't set data if no password
290+
_fetch_my_relation_data.return_value.get().get.return_value = None
291+
292+
harness.charm.postgresql_client_relation.update_endpoints()
293+
assert harness.get_relation_data(rel_id, harness.charm.app.name) == {}
294+
295+
_fetch_my_relation_data.reset_mock()
296+
_fetch_my_relation_data.return_value.get().get.return_value = "test_password"
297+
291298
# Add two different relations.
292299
rel_id = harness.add_relation(RELATION_NAME, "application")
293300
another_rel_id = harness.add_relation(RELATION_NAME, "application")
294301

302+
relation_ids = [rel.id for rel in harness.charm.model.relations[RELATION_NAME]]
303+
other_rel_ids = set(relation_ids) - set({rel_id, another_rel_id})
304+
295305
with harness.hooks_disabled():
306+
for relation_id in other_rel_ids:
307+
harness.update_relation_data(
308+
relation_id,
309+
"application",
310+
{"database": "some_db", "extra-user-roles": ""},
311+
)
312+
296313
harness.update_relation_data(
297314
rel_id,
298315
"application",

0 commit comments

Comments
 (0)