Skip to content

Commit 81076fc

Browse files
Fix database_provides.py abstraction
1 parent d5cf053 commit 81076fc

File tree

7 files changed

+91
-165
lines changed

7 files changed

+91
-165
lines changed

kubernetes/src/abstract_charm.py

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -122,42 +122,23 @@ def _container(self) -> container.Container:
122122
def _logrotate(self) -> logrotate.LogRotate:
123123
"""logrotate"""
124124

125-
@property
126125
@abc.abstractmethod
127-
def _read_write_endpoints(self) -> str:
126+
def _read_write_endpoints(self, *, event) -> str:
128127
"""MySQL Router read-write endpoint"""
129128

130-
@property
131129
@abc.abstractmethod
132-
def _read_only_endpoints(self) -> str:
130+
def _read_only_endpoints(self, *, event) -> str:
133131
"""MySQL Router read-only endpoint"""
134132

135-
@property
136-
@abc.abstractmethod
137-
def _exposed_read_write_endpoints(self) -> typing.Optional[str]:
138-
"""The exposed read-write endpoint.
139-
140-
Only defined in vm charm.
141-
"""
142-
143-
@property
144-
@abc.abstractmethod
145-
def _exposed_read_only_endpoints(self) -> typing.Optional[str]:
146-
"""The exposed read-only endpoint.
147-
148-
Only defined in vm charm.
149-
"""
150-
151133
@abc.abstractmethod
152134
def is_externally_accessible(self, *, event) -> typing.Optional[bool]:
153135
"""Whether endpoints should be externally accessible.
154136
155137
Only defined in vm charm to return True/False. In k8s charm, returns None.
156138
"""
157139

158-
@property
159140
@abc.abstractmethod
160-
def _status(self) -> ops.StatusBase:
141+
def _status(self, *, event) -> typing.Optional[ops.StatusBase]:
161142
"""Status of the charm."""
162143

163144
@property
@@ -240,8 +221,8 @@ def _determine_app_status(self, *, event) -> ops.StatusBase:
240221
if self.refresh.app_status_higher_priority:
241222
return self.refresh.app_status_higher_priority
242223
statuses = []
243-
if self._status:
244-
statuses.append(self._status)
224+
if status := self._status(event=event):
225+
statuses.append(status)
245226
for endpoint in (self._database_requires, self._database_provides):
246227
if status := endpoint.get_status(event):
247228
statuses.append(status)
@@ -297,7 +278,7 @@ def _reconcile_ports(self, *, event) -> None:
297278
"""
298279

299280
@abc.abstractmethod
300-
def _update_endpoints(self) -> None:
281+
def _update_endpoints(self, *, event) -> None:
301282
"""Update the endpoints in the provider relation if necessary."""
302283

303284
# =======================
@@ -349,13 +330,11 @@ def reconcile(self, event=None) -> None: # noqa: C901
349330
self._reconcile_service()
350331
self._database_provides.reconcile_users(
351332
event=event,
352-
router_read_write_endpoints=self._read_write_endpoints,
353-
router_read_only_endpoints=self._read_only_endpoints,
354-
exposed_read_write_endpoints=self._exposed_read_write_endpoints,
355-
exposed_read_only_endpoints=self._exposed_read_only_endpoints,
333+
router_read_write_endpoints=self._read_write_endpoints(event=event),
334+
router_read_only_endpoints=self._read_only_endpoints(event=event),
356335
shell=workload_.shell,
357336
)
358-
self._update_endpoints()
337+
self._update_endpoints(event=event)
359338

360339
if workload_.container_ready:
361340
workload_.reconcile(

kubernetes/src/charm.py

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ def _container(self) -> rock.Rock:
134134
def _logrotate(self) -> logrotate.LogRotate:
135135
return kubernetes_logrotate.LogRotate(container_=self._container)
136136

137-
@property
138-
def _status(self) -> ops.StatusBase:
137+
def _status(self, *, event) -> typing.Optional[ops.StatusBase]:
139138
if self.config.get("expose-external", "false") not in [
140139
"false",
141140
"nodeport",
@@ -146,7 +145,7 @@ def _status(self) -> ops.StatusBase:
146145
self._peer_data.get_value(
147146
relations.secrets.APP_SCOPE, self._K8S_SERVICE_INITIALIZED_KEY
148147
)
149-
and not self._check_service_connectivity()
148+
and not self._check_service_connectivity(event=event)
150149
):
151150
if self._peer_data.get_value(
152151
relations.secrets.APP_SCOPE, self._K8S_SERVICE_CREATING_KEY
@@ -262,7 +261,7 @@ def _reconcile_service(self) -> None:
262261

263262
logger.info(f"Request to create desired service {desired_service_type=} dispatched")
264263

265-
def _check_service_connectivity(self) -> bool:
264+
def _check_service_connectivity(self, *, event) -> bool:
266265
"""Check if the service is available (connectable with a socket)."""
267266
if not self._get_service() or not isinstance(
268267
self.get_workload(event=None), workload.RunningWorkload
@@ -271,12 +270,12 @@ def _check_service_connectivity(self) -> bool:
271270
return False
272271

273272
for endpoints in (
274-
self._read_write_endpoints,
275-
self._read_only_endpoints,
273+
self._read_write_endpoints(event=event),
274+
self._read_only_endpoints(event=event),
276275
):
277276
if endpoints == "":
278277
logger.debug(
279-
f"Empty endpoints {self._read_write_endpoints=} {self._read_only_endpoints=}"
278+
f"Empty endpoints {self._read_write_endpoints(event=event)=} {self._read_only_endpoints(event=event)=}"
280279
)
281280
return False
282281

@@ -302,11 +301,11 @@ def _check_service_connectivity(self) -> bool:
302301
def _reconcile_ports(self, *, event) -> None:
303302
"""Needed for VM, so no-op"""
304303

305-
def _update_endpoints(self) -> None:
306-
if self._check_service_connectivity():
304+
def _update_endpoints(self, *, event) -> None:
305+
if self._check_service_connectivity(event=event):
307306
self._database_provides.update_endpoints(
308-
router_read_write_endpoints=self._read_write_endpoints,
309-
router_read_only_endpoints=self._read_only_endpoints,
307+
router_read_write_endpoints=self._read_write_endpoints(event=event),
308+
router_read_only_endpoints=self._read_only_endpoints(event=event),
310309
)
311310

312311
def wait_until_mysql_router_ready(self, *, event=None) -> None:
@@ -406,24 +405,12 @@ def _get_hosts_ports(self, port_type: str) -> str: # noqa: C901
406405

407406
return ""
408407

409-
@property
410-
def _read_write_endpoints(self) -> str:
408+
def _read_write_endpoints(self, *, event) -> str:
411409
return self._get_hosts_ports("rw")
412410

413-
@property
414-
def _read_only_endpoints(self) -> str:
411+
def _read_only_endpoints(self, *, event) -> str:
415412
return self._get_hosts_ports("ro")
416413

417-
@property
418-
def _exposed_read_write_endpoints(self) -> typing.Optional[str]:
419-
"""Only applies to VM charm, so no-op."""
420-
pass
421-
422-
@property
423-
def _exposed_read_only_endpoints(self) -> typing.Optional[str]:
424-
"""Only applies to VM charm, so no-op."""
425-
pass
426-
427414
def get_all_k8s_node_hostnames_and_ips(
428415
self,
429416
) -> typing.Tuple[typing.List[str], typing.List[str]]:

kubernetes/src/relations/database_provides.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ def __init__(
4848
# Application charm databag
4949
self._databag = remote_databag.RemoteDatabag(interface=interface, relation=relation)
5050

51+
# Whether endpoints should be externally accessible
52+
# (e.g. when related to `data-integrator` charm)
53+
# Implements DA073 - Add Expose Flag to the Database Interface
54+
# https://docs.google.com/document/d/1Y7OZWwMdvF8eEMuVKrqEfuFV3JOjpqLHL7_GPqJpRHU
55+
# Ignored on Kubernetes charm in favor of expose-external config option
56+
self.external_connectivity = self._databag.get("external-node-connectivity") == "true"
57+
5158
def __eq__(self, other) -> bool:
5259
if not isinstance(other, _Relation):
5360
return False
@@ -70,9 +77,8 @@ def __init__(
7077
) -> None:
7178
super().__init__(relation=relation, interface=interface)
7279
self._interface = interface
73-
if event and isinstance(event, ops.RelationBrokenEvent) and event.relation.id == self._id:
80+
if isinstance(event, ops.RelationBrokenEvent) and event.relation.id == self._id:
7481
raise _RelationBreaking
75-
7682
self._database: str = self._databag["database"]
7783
if self._databag.get("extra-user-roles"):
7884
raise _UnsupportedExtraUserRole(
@@ -151,7 +157,7 @@ def update_endpoints(
151157
router_read_write_endpoints: str,
152158
router_read_only_endpoints: str,
153159
) -> None:
154-
"""Update the endpoins in the databag."""
160+
"""Update the endpoints in the databag."""
155161
logger.debug(
156162
f"Updating endpoints {self._id} {router_read_write_endpoints=} {router_read_only_endpoints=}"
157163
)
@@ -198,6 +204,27 @@ def _shared_users(self) -> typing.List[_RelationWithSharedUser]:
198204
pass
199205
return shared_users
200206

207+
def external_connectivity(self, event) -> bool:
208+
"""Whether any of the relations are marked as external.
209+
210+
Only used on machines charm
211+
"""
212+
requested_users = []
213+
for relation in self._interface.relations:
214+
try:
215+
requested_users.append(
216+
_RelationThatRequestedUser(
217+
relation=relation, interface=self._interface, event=event
218+
)
219+
)
220+
except (
221+
_RelationBreaking,
222+
remote_databag.IncompleteDatabag,
223+
_UnsupportedExtraUserRole,
224+
):
225+
pass
226+
return any(relation.external_connectivity for relation in requested_users)
227+
201228
def update_endpoints(
202229
self,
203230
*,
@@ -218,7 +245,6 @@ def reconcile_users(
218245
router_read_write_endpoints: str,
219246
router_read_only_endpoints: str,
220247
shell: mysql_shell.Shell,
221-
**_,
222248
) -> None:
223249
"""Create requested users and delete inactive users.
224250

machines/src/abstract_charm.py

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -122,42 +122,23 @@ def _container(self) -> container.Container:
122122
def _logrotate(self) -> logrotate.LogRotate:
123123
"""logrotate"""
124124

125-
@property
126125
@abc.abstractmethod
127-
def _read_write_endpoints(self) -> str:
126+
def _read_write_endpoints(self, *, event) -> str:
128127
"""MySQL Router read-write endpoint"""
129128

130-
@property
131129
@abc.abstractmethod
132-
def _read_only_endpoints(self) -> str:
130+
def _read_only_endpoints(self, *, event) -> str:
133131
"""MySQL Router read-only endpoint"""
134132

135-
@property
136-
@abc.abstractmethod
137-
def _exposed_read_write_endpoints(self) -> typing.Optional[str]:
138-
"""The exposed read-write endpoint.
139-
140-
Only defined in vm charm.
141-
"""
142-
143-
@property
144-
@abc.abstractmethod
145-
def _exposed_read_only_endpoints(self) -> typing.Optional[str]:
146-
"""The exposed read-only endpoint.
147-
148-
Only defined in vm charm.
149-
"""
150-
151133
@abc.abstractmethod
152134
def is_externally_accessible(self, *, event) -> typing.Optional[bool]:
153135
"""Whether endpoints should be externally accessible.
154136
155137
Only defined in vm charm to return True/False. In k8s charm, returns None.
156138
"""
157139

158-
@property
159140
@abc.abstractmethod
160-
def _status(self) -> ops.StatusBase:
141+
def _status(self, *, event) -> typing.Optional[ops.StatusBase]:
161142
"""Status of the charm."""
162143

163144
@property
@@ -240,8 +221,8 @@ def _determine_app_status(self, *, event) -> ops.StatusBase:
240221
if self.refresh.app_status_higher_priority:
241222
return self.refresh.app_status_higher_priority
242223
statuses = []
243-
if self._status:
244-
statuses.append(self._status)
224+
if status := self._status(event=event):
225+
statuses.append(status)
245226
for endpoint in (self._database_requires, self._database_provides):
246227
if status := endpoint.get_status(event):
247228
statuses.append(status)
@@ -297,7 +278,7 @@ def _reconcile_ports(self, *, event) -> None:
297278
"""
298279

299280
@abc.abstractmethod
300-
def _update_endpoints(self) -> None:
281+
def _update_endpoints(self, *, event) -> None:
301282
"""Update the endpoints in the provider relation if necessary."""
302283

303284
# =======================
@@ -349,13 +330,11 @@ def reconcile(self, event=None) -> None: # noqa: C901
349330
self._reconcile_service()
350331
self._database_provides.reconcile_users(
351332
event=event,
352-
router_read_write_endpoints=self._read_write_endpoints,
353-
router_read_only_endpoints=self._read_only_endpoints,
354-
exposed_read_write_endpoints=self._exposed_read_write_endpoints,
355-
exposed_read_only_endpoints=self._exposed_read_only_endpoints,
333+
router_read_write_endpoints=self._read_write_endpoints(event=event),
334+
router_read_only_endpoints=self._read_only_endpoints(event=event),
356335
shell=workload_.shell,
357336
)
358-
self._update_endpoints()
337+
self._update_endpoints(event=event)
359338

360339
if workload_.container_ready:
361340
workload_.reconcile(

machines/src/charm.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ def _subordinate_relation_endpoint_names(self) -> typing.Optional[typing.Iterabl
120120
def _container(self) -> snap.Snap:
121121
return snap.Snap(unit_name=self.unit.name)
122122

123-
@property
124-
def _status(self) -> ops.StatusBase:
123+
def _status(self, *, event) -> typing.Optional[ops.StatusBase]:
125124
pass
126125

127126
@property
@@ -140,21 +139,17 @@ def host_address(self) -> str:
140139

141140
return self.config["vip"]
142141

143-
@property
144-
def _read_write_endpoints(self) -> str:
145-
return f"file://{self._container.path('/run/mysqlrouter/mysql.sock')}"
146-
147-
@property
148-
def _read_only_endpoints(self) -> str:
149-
return f"file://{self._container.path('/run/mysqlrouter/mysqlro.sock')}"
150-
151-
@property
152-
def _exposed_read_write_endpoints(self) -> typing.Optional[str]:
153-
return f"{self.host_address}:{self._READ_WRITE_PORT}"
142+
def _read_write_endpoints(self, *, event) -> str:
143+
if self.is_externally_accessible(event=event):
144+
return f"{self.host_address}:{self._READ_WRITE_PORT}"
145+
else:
146+
return f"file://{self._container.path('/run/mysqlrouter/mysql.sock')}"
154147

155-
@property
156-
def _exposed_read_only_endpoints(self) -> typing.Optional[str]:
157-
return f"{self.host_address}:{self._READ_ONLY_PORT}"
148+
def _read_only_endpoints(self, *, event) -> str:
149+
if self.is_externally_accessible(event=event):
150+
return f"{self.host_address}:{self._READ_ONLY_PORT}"
151+
else:
152+
return f"file://{self._container.path('/run/mysqlrouter/mysqlro.sock')}"
158153

159154
def is_externally_accessible(self, *, event) -> typing.Optional[bool]:
160155
return self._database_provides.external_connectivity(event)
@@ -170,12 +165,10 @@ def _reconcile_ports(self, *, event) -> None:
170165
ports = []
171166
self.unit.set_ports(*ports)
172167

173-
def _update_endpoints(self) -> None:
168+
def _update_endpoints(self, *, event) -> None:
174169
self._database_provides.update_endpoints(
175-
router_read_write_endpoints=self._read_write_endpoints,
176-
router_read_only_endpoints=self._read_only_endpoints,
177-
exposed_read_write_endpoints=self._exposed_read_write_endpoints,
178-
exposed_read_only_endpoints=self._exposed_read_only_endpoints,
170+
router_read_write_endpoints=self._read_write_endpoints(event=event),
171+
router_read_only_endpoints=self._read_only_endpoints(event=event),
179172
)
180173

181174
def _wait_until_service_reconciled(self) -> None:

0 commit comments

Comments
 (0)