Skip to content

Commit 22d4f17

Browse files
committed
feat(config): add peer-state config/user hash + tls unit accessors
Scaffolds the config-subsystem port into the library: a later change persists the applied config/user hashes and the last-rendered TLS flag to peer relation data so the update_config manager can detect drift without re-deriving it from live workload state. Add the plain-databag accessors now so that change only wires them up. Signed-off-by: Marcelo Henrique Neppel <marcelo.neppel@canonical.com>
1 parent 7742b99 commit 22d4f17

2 files changed

Lines changed: 166 additions & 0 deletions

File tree

single_kernel_postgresql/core/peer_relation.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,48 @@ def is_connectivity_enabled(self) -> bool:
225225
return True
226226
return self.relation.data[self.unit].get("connectivity", "on") == "on"
227227

228+
@property
229+
def config_hash(self) -> str | None:
230+
"""Get the last-applied PostgreSQL config hash from the peer relation data."""
231+
if not self.relation:
232+
return None
233+
return self.relation.data[self.unit].get("config_hash")
234+
235+
@config_hash.setter
236+
def config_hash(self, value: str) -> None:
237+
"""Set the last-applied PostgreSQL config hash in the peer relation data."""
238+
if not self.relation:
239+
return
240+
self.relation.data[self.unit]["config_hash"] = value
241+
242+
@property
243+
def user_hash(self) -> str | None:
244+
"""Get the last-applied users hash from the peer relation data."""
245+
if not self.relation:
246+
return None
247+
return self.relation.data[self.unit].get("user_hash")
248+
249+
@user_hash.setter
250+
def user_hash(self, value: str) -> None:
251+
"""Set the last-applied users hash in the peer relation data."""
252+
if not self.relation:
253+
return
254+
self.relation.data[self.unit]["user_hash"] = value
255+
256+
@property
257+
def tls(self) -> bool:
258+
"""Get the last-rendered TLS flag from the peer relation data."""
259+
if not self.relation:
260+
return False
261+
return self.relation.data[self.unit].get("tls") == "enabled"
262+
263+
@tls.setter
264+
def tls(self, value: bool) -> None:
265+
"""Set the last-rendered TLS flag in the peer relation data."""
266+
if not self.relation:
267+
return
268+
self.relation.data[self.unit]["tls"] = "enabled" if value else ""
269+
228270
@cached_property
229271
def data(self) -> MutableMapping[str, str]:
230272
"""Escape hatch method to access the peer data directly."""
@@ -418,6 +460,20 @@ def is_ldap_enabled(self) -> bool:
418460
"""Return whether this unit has LDAP enabled."""
419461
return self.is_ldap_charm_related and self.is_cluster_initialised
420462

463+
@property
464+
def user_hash(self) -> str | None:
465+
"""Get the last-applied users hash from the peer relation data."""
466+
if not self.relation:
467+
return None
468+
return self.relation.data[self.app].get("user_hash")
469+
470+
@user_hash.setter
471+
def user_hash(self, value: str) -> None:
472+
"""Set the last-applied users hash in the peer relation data."""
473+
if not self.relation:
474+
return
475+
self.relation.data[self.app]["user_hash"] = value
476+
421477
def get_secret(self, key: str) -> str | None:
422478
"""Get the secret value for 'key' from the peer relation data."""
423479
if not self.relation:

tests/unit/test_peer_relation.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2026 Canonical Ltd.
4+
# See LICENSE file for licensing details.
5+
6+
"""Tests for the config/user hash + tls plain-databag accessors on peer state.
7+
8+
These accessors scaffold the config-subsystem port: PostgreSQLPeer.config_hash
9+
and .user_hash persist the last-applied config/user hashes, PostgreSQLPeer.tls
10+
persists the last-rendered TLS flag, and PostgreSQLApplication.user_hash
11+
persists the app-wide user hash. All are plain (non-secret) databag values,
12+
matching the charm's unit_peer_data / app_peer_data storage.
13+
"""
14+
15+
16+
def _get_unit_db(harness, key):
17+
rel_id = harness.model.get_relation("database-peers").id
18+
return harness.get_relation_data(rel_id, harness.charm.unit.name).get(key)
19+
20+
21+
def _get_app_db(harness, key):
22+
rel_id = harness.model.get_relation("database-peers").id
23+
return harness.get_relation_data(rel_id, harness.charm.app.name).get(key)
24+
25+
26+
# -- PostgreSQLPeer.config_hash ----------------------------------------------
27+
28+
29+
def test_config_hash_unset_is_none(harness):
30+
assert harness.charm.state.peer.config_hash is None
31+
32+
33+
def test_config_hash_roundtrip(harness):
34+
peer = harness.charm.state.peer
35+
peer.config_hash = "abc123"
36+
assert peer.config_hash == "abc123"
37+
38+
39+
def test_config_hash_writes_exact_databag_key(harness):
40+
harness.charm.state.peer.config_hash = "abc123"
41+
assert _get_unit_db(harness, "config_hash") == "abc123"
42+
43+
44+
# -- PostgreSQLPeer.user_hash -------------------------------------------------
45+
46+
47+
def test_peer_user_hash_unset_is_none(harness):
48+
assert harness.charm.state.peer.user_hash is None
49+
50+
51+
def test_peer_user_hash_roundtrip(harness):
52+
peer = harness.charm.state.peer
53+
peer.user_hash = "def456"
54+
assert peer.user_hash == "def456"
55+
56+
57+
def test_peer_user_hash_writes_exact_databag_key(harness):
58+
harness.charm.state.peer.user_hash = "def456"
59+
assert _get_unit_db(harness, "user_hash") == "def456"
60+
61+
62+
# -- PostgreSQLPeer.tls --------------------------------------------------------
63+
64+
65+
def test_tls_unset_is_false(harness):
66+
assert harness.charm.state.peer.tls is False
67+
68+
69+
def test_tls_set_true_roundtrip(harness):
70+
peer = harness.charm.state.peer
71+
peer.tls = True
72+
assert peer.tls is True
73+
74+
75+
def test_tls_set_true_writes_enabled(harness):
76+
harness.charm.state.peer.tls = True
77+
assert _get_unit_db(harness, "tls") == "enabled"
78+
79+
80+
def test_tls_set_false_roundtrip(harness):
81+
peer = harness.charm.state.peer
82+
peer.tls = True
83+
peer.tls = False
84+
assert peer.tls is False
85+
86+
87+
def test_tls_set_false_clears_enabled_key(harness):
88+
"""tls=False writes "", which ops relation data drops on write — observed as key absence."""
89+
peer = harness.charm.state.peer
90+
peer.tls = True
91+
peer.tls = False
92+
assert _get_unit_db(harness, "tls") is None
93+
94+
95+
# -- PostgreSQLApplication.user_hash ------------------------------------------
96+
97+
98+
def test_app_user_hash_unset_is_none(harness):
99+
assert harness.charm.state.application.user_hash is None
100+
101+
102+
def test_app_user_hash_roundtrip(harness):
103+
application = harness.charm.state.application
104+
application.user_hash = "ghi789"
105+
assert application.user_hash == "ghi789"
106+
107+
108+
def test_app_user_hash_writes_exact_databag_key(harness):
109+
harness.charm.state.application.user_hash = "ghi789"
110+
assert _get_app_db(harness, "user_hash") == "ghi789"

0 commit comments

Comments
 (0)