Skip to content

Commit 5d89fb6

Browse files
Port bug fixes and changes noticed while testing COS integration in k8s charm (#116)
## Issue There were a number of issues encountered while testing the COS integration in the k8s charm. The fixes for these issues are still missing from this repo ## Solution Port fixes
1 parent fd35e6c commit 5d89fb6

File tree

10 files changed

+192
-90
lines changed

10 files changed

+192
-90
lines changed

poetry.lock

Lines changed: 21 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ ops = "^2.6.0"
1414
tenacity = "^8.2.3"
1515
poetry-core = "^1.7.0"
1616
jinja2 = "^3.1.2"
17+
requests = "^2.31.0"
1718

1819
[tool.poetry.group.charm-libs.dependencies]
1920
# data_platform_libs/v0/data_interfaces.py

src/abstract_charm.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def __init__(self, *args) -> None:
3939
self._authenticated_workload_type = workload.AuthenticatedWorkload
4040
self._database_requires = relations.database_requires.RelationEndpoint(self)
4141
self._database_provides = relations.database_provides.RelationEndpoint(self)
42+
self._cos_relation = relations.cos.COSRelation(self, self._container)
4243
self.framework.observe(self.on.update_status, self.reconcile)
4344
self.framework.observe(
4445
self.on[upgrade.PEER_RELATION_ENDPOINT_NAME].relation_changed, self.reconcile
@@ -83,11 +84,6 @@ def _upgrade(self) -> typing.Optional[upgrade.Upgrade]:
8384
def _logrotate(self) -> logrotate.LogRotate:
8485
"""logrotate"""
8586

86-
@property
87-
@abc.abstractmethod
88-
def _cos(self) -> relations.cos.COSRelation:
89-
"""COS"""
90-
9187
@property
9288
@abc.abstractmethod
9389
def _read_write_endpoint(self) -> str:
@@ -105,23 +101,30 @@ def _tls_certificate_saved(self) -> bool:
105101
return False
106102

107103
@property
108-
def _tls_key(self) -> str:
104+
def _tls_key(self) -> typing.Optional[str]:
109105
"""Custom TLS key"""
110106
# TODO VM TLS: Update property after implementing TLS on machine_charm
111107
return None
112108

113109
@property
114-
def _tls_certificate(self) -> str:
110+
def _tls_certificate(self) -> typing.Optional[str]:
115111
"""Custom TLS certificate"""
116112
# TODO VM TLS: Update property after implementing TLS on machine_charm
117113
return None
118114

115+
@property
116+
def _tls_certificate_authority(self) -> typing.Optional[str]:
117+
# TODO VM TLS: Update property after implementing TLS on machine charm
118+
return None
119+
119120
def _cos_exporter_config(self, event) -> typing.Optional[relations.cos.ExporterConfig]:
120121
"""Returns the exporter config for MySQLRouter exporter if cos relation exists"""
121-
cos_relation_exists = self._cos.relation_exists and not self._cos.is_relation_breaking(
122-
event
122+
cos_relation_exists = (
123+
self._cos_relation.relation_exists
124+
and not self._cos_relation.is_relation_breaking(event)
123125
)
124-
return self._cos.exporter_user_config if cos_relation_exists else None
126+
if cos_relation_exists:
127+
return self._cos_relation.exporter_user_config
125128

126129
def get_workload(self, *, event):
127130
"""MySQL Router workload"""
@@ -130,11 +133,11 @@ def get_workload(self, *, event):
130133
container_=self._container,
131134
logrotate_=self._logrotate,
132135
connection_info=connection_info,
133-
cos=self._cos,
136+
cos=self._cos_relation,
134137
charm_=self,
135138
)
136139
return self._workload_type(
137-
container_=self._container, logrotate_=self._logrotate, cos=self._cos
140+
container_=self._container, logrotate_=self._logrotate, cos=self._cos_relation
138141
)
139142

140143
@staticmethod
@@ -259,8 +262,8 @@ def reconcile(self, event=None) -> None: # noqa: C901
259262
f"{isinstance(workload_, workload.AuthenticatedWorkload)=}, "
260263
f"{workload_.container_ready=}, "
261264
f"{self._database_requires.is_relation_breaking(event)=}, "
262-
f"{self._upgrade.in_progress=}"
263-
f"{self._cos.is_relation_breaking(event)=}"
265+
f"{self._upgrade.in_progress=}, "
266+
f"{self._cos_relation.is_relation_breaking(event)=}"
264267
)
265268

266269
try:
@@ -289,6 +292,7 @@ def reconcile(self, event=None) -> None: # noqa: C901
289292
exporter_config=self._cos_exporter_config(event),
290293
key=self._tls_key,
291294
certificate=self._tls_certificate,
295+
certificate_authority=self._tls_certificate_authority,
292296
)
293297
# Empty waiting status means we're waiting for database requires relation before
294298
# starting workload

src/container.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,33 @@ def update_mysql_router_service(self, *, enabled: bool, tls: bool = None) -> Non
134134

135135
@abc.abstractmethod
136136
def update_mysql_router_exporter_service(
137-
self, *, enabled: bool, config: "relations.cos.ExporterConfig" = None
137+
self,
138+
*,
139+
enabled: bool,
140+
config: "relations.cos.ExporterConfig" = None,
141+
tls: bool = None,
142+
key_filename: str = None,
143+
certificate_filename: str = None,
144+
certificate_authority_filename: str = None,
138145
) -> None:
139146
"""Update and restart the MySQL Router exporter service.
140147
141148
Args:
142149
enabled: Whether MySQL Router exporter service is enabled
143150
config: The configuration for MySQL Router exporter
151+
tls: Whether custom TLS is enabled on the unit
152+
key_filename: The TLS key filename
153+
certificate_filename: The TLS certificate filename
154+
certificate_authority_filename: The TLS certificate authority filename
144155
"""
145156
if enabled and not config:
146157
raise ValueError("Missing MySQL Router exporter config")
147158

159+
if tls and not (certificate_authority_filename and certificate_filename and key_filename):
160+
raise ValueError(
161+
"`key`, `certificate` and `certificate_authority` required when tls=True"
162+
)
163+
148164
@abc.abstractmethod
149165
def upgrade(self, unit: ops.Unit) -> None:
150166
"""Upgrade container version
@@ -203,6 +219,18 @@ def set_mysql_router_rest_api_password(
203219
"""Set REST API credentials using the mysqlrouter_password command."""
204220
self.create_router_rest_api_credentials_file()
205221

222+
if not password:
223+
users_credentials = self._run_command(
224+
[
225+
self._mysql_router_password_command,
226+
"list",
227+
str(self.rest_api_credentials_file),
228+
],
229+
timeout=30,
230+
)
231+
if user not in users_credentials:
232+
return
233+
206234
action = "set" if password else "delete"
207235
self._run_command(
208236
[

src/machine_charm.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import abstract_charm
1515
import machine_logrotate
1616
import machine_upgrade
17-
import relations.cos
1817
import relations.database_providers_wrapper
1918
import snap
2019
import socket_workload
@@ -33,8 +32,6 @@ def __init__(self, *args) -> None:
3332
self._database_provides = relations.database_providers_wrapper.RelationEndpoint(
3433
self, self._database_provides
3534
)
36-
self._cos_relation = relations.cos.COSRelation(self, self._container)
37-
3835
self._authenticated_workload_type = socket_workload.AuthenticatedSocketWorkload
3936
self.framework.observe(self.on.install, self._on_install)
4037
self.framework.observe(self.on.remove, self._on_remove)
@@ -63,10 +60,6 @@ def _upgrade(self) -> typing.Optional[machine_upgrade.Upgrade]:
6360
def _logrotate(self) -> machine_logrotate.LogRotate:
6461
return machine_logrotate.LogRotate(container_=self._container)
6562

66-
@property
67-
def _cos(self) -> relations.cos.COSRelation:
68-
return self._cos_relation
69-
7063
@property
7164
def _read_write_endpoint(self) -> str:
7265
return f'file://{self._container.path("/run/mysqlrouter/mysql.sock")}'

src/relations/cos.py

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ class COSRelation:
3434
"""Relation with the cos bundle."""
3535

3636
_EXPORTER_PORT = "49152"
37-
_HTTP_SERVER_PORT = "8443"
37+
HTTP_SERVER_PORT = "8443"
3838
_NAME = "cos-agent"
3939
_PEER_RELATION_NAME = "cos"
4040

41-
_MONITORING_USERNAME = "monitoring"
41+
MONITORING_USERNAME = "monitoring"
4242
_MONITORING_PASSWORD_KEY = "monitoring-password"
4343

4444
def __init__(self, charm_: "abstract_charm.MySQLRouterCharm", container_: container.Container):
@@ -52,7 +52,7 @@ def __init__(self, charm_: "abstract_charm.MySQLRouterCharm", container_: contai
5252
],
5353
log_slots=[f"{_SNAP_NAME}:logs"],
5454
)
55-
self.charm = charm_
55+
self._charm = charm_
5656
self._container = container_
5757

5858
charm_.framework.observe(
@@ -74,17 +74,17 @@ def __init__(self, charm_: "abstract_charm.MySQLRouterCharm", container_: contai
7474
def exporter_user_config(self) -> ExporterConfig:
7575
"""Returns user config needed for the router exporter service."""
7676
return ExporterConfig(
77-
url=f"https://127.0.0.1:{self._HTTP_SERVER_PORT}",
78-
username=self._MONITORING_USERNAME,
79-
password=self._get_monitoring_password(),
77+
url=f"https://127.0.0.1:{self.HTTP_SERVER_PORT}",
78+
username=self.MONITORING_USERNAME,
79+
password=self.get_monitoring_password(),
8080
)
8181

8282
@property
8383
def relation_exists(self) -> bool:
8484
"""Whether relation with cos exists."""
85-
return len(self.charm.model.relations.get(self._NAME, [])) == 1
85+
return len(self._charm.model.relations.get(self._NAME, [])) == 1
8686

87-
def _get_monitoring_password(self) -> str:
87+
def get_monitoring_password(self) -> str:
8888
"""Gets the monitoring password from unit peer data, or generate and cache it."""
8989
monitoring_password = self._secrets.get_secret(
9090
relations.secrets.UNIT_SCOPE, self._MONITORING_PASSWORD_KEY
@@ -109,24 +109,5 @@ def is_relation_breaking(self, event) -> bool:
109109

110110
return (
111111
isinstance(event, ops.RelationBrokenEvent)
112-
and event.relation.id == self.charm.model.relations[self._NAME][0].id
112+
and event.relation.id == self._charm.model.relations[self._NAME][0].id
113113
)
114-
115-
def setup_monitoring_user(self) -> None:
116-
"""Set up a router REST API use for mysqlrouter exporter."""
117-
logger.debug("Setting up router REST API user for mysqlrouter exporter")
118-
self._container.set_mysql_router_rest_api_password(
119-
user=self._MONITORING_USERNAME,
120-
password=self._get_monitoring_password(),
121-
)
122-
logger.debug("Set up router REST API user for mysqlrouter exporter")
123-
124-
def cleanup_monitoring_user(self) -> None:
125-
"""Clean up router REST API user for mysqlrouter exporter."""
126-
logger.debug("Cleaning router REST API user for mysqlrouter exporter")
127-
self._container.set_mysql_router_rest_api_password(
128-
user=self._MONITORING_USERNAME,
129-
password=None,
130-
)
131-
self._reset_monitoring_password()
132-
logger.debug("Cleaned router REST API user for mysqlrouter exporter")

0 commit comments

Comments
 (0)