|
5 | 5 | """Charmed Kubernetes Operator for the PostgreSQL database.""" |
6 | 6 | import json |
7 | 7 | import logging |
8 | | -from typing import List |
| 8 | +from typing import Dict, List, Optional |
9 | 9 |
|
10 | 10 | from charms.postgresql_k8s.v0.postgresql import PostgreSQL |
11 | 11 | from lightkube import ApiError, Client, codecs |
|
30 | 30 | from requests import ConnectionError |
31 | 31 | from tenacity import RetryError |
32 | 32 |
|
33 | | -from constants import PEER, USER |
| 33 | +from constants import PEER, REPLICATION_PASSWORD_KEY, USER, USER_PASSWORD_KEY |
34 | 34 | from patroni import NotReadyError, Patroni |
35 | 35 | from relations.db import DbProvides |
36 | 36 | from relations.postgresql_provider import PostgreSQLProvider |
@@ -69,6 +69,48 @@ def __init__(self, *args): |
69 | 69 | self.legacy_db_relation = DbProvides(self, admin=False) |
70 | 70 | self.legacy_db_admin_relation = DbProvides(self, admin=True) |
71 | 71 |
|
| 72 | + @property |
| 73 | + def app_peer_data(self) -> Dict: |
| 74 | + """Application peer relation data object.""" |
| 75 | + relation = self.model.get_relation(PEER) |
| 76 | + if relation is None: |
| 77 | + return {} |
| 78 | + |
| 79 | + return relation.data[self.app] |
| 80 | + |
| 81 | + @property |
| 82 | + def unit_peer_data(self) -> Dict: |
| 83 | + """Unit peer relation data object.""" |
| 84 | + relation = self.model.get_relation(PEER) |
| 85 | + if relation is None: |
| 86 | + return {} |
| 87 | + |
| 88 | + return relation.data[self.unit] |
| 89 | + |
| 90 | + def _get_secret(self, scope: str, key: str) -> Optional[str]: |
| 91 | + """Get secret from the secret storage.""" |
| 92 | + if scope == "unit": |
| 93 | + return self.unit_peer_data.get(key, None) |
| 94 | + elif scope == "app": |
| 95 | + return self.app_peer_data.get(key, None) |
| 96 | + else: |
| 97 | + raise RuntimeError("Unknown secret scope.") |
| 98 | + |
| 99 | + def _set_secret(self, scope: str, key: str, value: Optional[str]) -> None: |
| 100 | + """Get secret from the secret storage.""" |
| 101 | + if scope == "unit": |
| 102 | + if not value: |
| 103 | + del self.unit_peer_data[key] |
| 104 | + return |
| 105 | + self.unit_peer_data.update({key: value}) |
| 106 | + elif scope == "app": |
| 107 | + if not value: |
| 108 | + del self.app_peer_data[key] |
| 109 | + return |
| 110 | + self.app_peer_data.update({key: value}) |
| 111 | + else: |
| 112 | + raise RuntimeError("Unknown secret scope.") |
| 113 | + |
72 | 114 | @property |
73 | 115 | def postgresql(self) -> PostgreSQL: |
74 | 116 | """Returns an instance of the object used to interact with the database.""" |
@@ -249,15 +291,11 @@ def _get_hostname_from_unit(self, member: str) -> str: |
249 | 291 |
|
250 | 292 | def _on_leader_elected(self, event: LeaderElectedEvent) -> None: |
251 | 293 | """Handle the leader-elected event.""" |
252 | | - data = self._peers.data[self.app] |
253 | | - operator_password = data.get("operator-password", None) |
254 | | - replication_password = data.get("replication-password", None) |
255 | | - |
256 | | - if operator_password is None: |
257 | | - self._peers.data[self.app]["operator-password"] = new_password() |
| 294 | + if self._get_secret("app", USER_PASSWORD_KEY) is None: |
| 295 | + self._set_secret("app", USER_PASSWORD_KEY, new_password()) |
258 | 296 |
|
259 | | - if replication_password is None: |
260 | | - self._peers.data[self.app]["replication-password"] = new_password() |
| 297 | + if self._get_secret("app", REPLICATION_PASSWORD_KEY) is None: |
| 298 | + self._set_secret("app", REPLICATION_PASSWORD_KEY, new_password()) |
261 | 299 |
|
262 | 300 | # Create resources and add labels needed for replication. |
263 | 301 | self._create_resources() |
@@ -389,7 +427,7 @@ def _create_resources(self) -> None: |
389 | 427 |
|
390 | 428 | def _on_get_operator_password(self, event: ActionEvent) -> None: |
391 | 429 | """Returns the password for the operator user as an action response.""" |
392 | | - event.set_results({"operator-password": self._get_operator_password()}) |
| 430 | + event.set_results({USER_PASSWORD_KEY: self._get_operator_password()}) |
393 | 431 |
|
394 | 432 | def _on_get_primary(self, event: ActionEvent) -> None: |
395 | 433 | """Get primary instance.""" |
@@ -498,14 +536,12 @@ def _peers(self) -> Relation: |
498 | 536 |
|
499 | 537 | def _get_operator_password(self) -> str: |
500 | 538 | """Get operator user password.""" |
501 | | - data = self._peers.data[self.app] |
502 | | - return data.get("operator-password", None) |
| 539 | + return self._get_secret("app", USER_PASSWORD_KEY) |
503 | 540 |
|
504 | 541 | @property |
505 | 542 | def _replication_password(self) -> str: |
506 | 543 | """Get replication user password.""" |
507 | | - data = self._peers.data[self.app] |
508 | | - return data.get("replication-password", None) |
| 544 | + return self._get_secret("app", REPLICATION_PASSWORD_KEY) |
509 | 545 |
|
510 | 546 | def _unit_name_to_pod_name(self, unit_name: str) -> str: |
511 | 547 | """Converts unit name to pod name. |
|
0 commit comments