Skip to content

Commit 1b0bb19

Browse files
Address PR feedback + fix broken upgrades
1 parent 40beb97 commit 1b0bb19

15 files changed

+213
-112
lines changed

poetry.lock

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

pyproject.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ authors = []
1010

1111
[tool.poetry.dependencies]
1212
python = "^3.8.1" # ^3.8.1 required by flake8
13+
# there is a breaking change in ops 2.10.0: https://github.com/canonical/operator/pull/1091#issuecomment-1888644075
1314
ops = "<2.10.0"
1415
tenacity = "^8.2.3"
1516
poetry-core = "^1.7.0"
@@ -18,6 +19,7 @@ requests = "^2.31.0"
1819

1920
[tool.poetry.group.charm-libs.dependencies]
2021
# data_platform_libs/v0/data_interfaces.py
22+
# there is a breaking change in ops 2.10.0: https://github.com/canonical/operator/pull/1091#issuecomment-1888644075
2123
ops = "<2.10.0"
2224
# tls_certificates_interface/v2/tls_certificates.py
2325
# tls_certificates lib v2 uses a feature only available in cryptography >=42.0.5
@@ -53,7 +55,7 @@ pytest = "^7.4.0"
5355
pytest-xdist = "^3.3.1"
5456
pytest-cov = "^4.1.0"
5557
ops-scenario = "^5.4.1"
56-
ops = "<2.10.0"
58+
ops = ">=2.0.0"
5759
pytest-mock = "^3.11.1"
5860

5961
[tool.poetry.group.integration.dependencies]
@@ -65,7 +67,7 @@ pytest-github-secrets = {git = "https://github.com/canonical/data-platform-workf
6567
juju = "3.2.0.1"
6668
mysql-connector-python = "~8.0.33"
6769
tenacity = "^8.2.2"
68-
ops = "<2.10.0"
70+
ops = ">=2.0.0"
6971
pytest-mock = "^3.11.1"
7072

7173

src/abstract_charm.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,9 @@ def _exposed_read_write_endpoint(self) -> str:
109109
def _exposed_read_only_endpoint(self) -> str:
110110
"""The exposed read-only endpoint"""
111111

112-
@property
113112
@abc.abstractmethod
114-
def is_exposed(self) -> typing.Optional[bool]:
115-
"""Whether router is exposed externally"""
113+
def is_externally_accessible(self, event=None) -> typing.Optional[bool]:
114+
"""Whether router is externally accessible"""
116115

117116
@property
118117
def _tls_certificate_saved(self) -> bool:
@@ -265,7 +264,9 @@ def reconcile(self, event=None) -> None: # noqa: C901
265264
if self._upgrade.unit_state == "outdated":
266265
if self._upgrade.authorized:
267266
self._upgrade.upgrade_unit(
268-
workload_=workload_, tls=self._tls_certificate_saved
267+
workload_=workload_,
268+
tls=self._tls_certificate_saved,
269+
exporter_config=self._cos_exporter_config(event),
269270
)
270271
else:
271272
self.set_status(event=event)

src/machine_charm.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,15 @@ def _exposed_read_write_endpoint(self) -> str:
8383
def _exposed_read_only_endpoint(self) -> str:
8484
return f"{self.host_address}:{self._READ_ONLY_PORT}"
8585

86-
@property
87-
def is_exposed(self) -> typing.Optional[bool]:
88-
return self._database_provides.external_connectivity
86+
def is_externally_accessible(self, event=None) -> typing.Optional[bool]:
87+
return self._database_provides.external_connectivity(event)
8988

9089
def _reconcile_node_port(self, event) -> None:
9190
"""Only applies to Kubernetes charm, so no-op."""
9291
pass
9392

9493
def _reconcile_ports(self) -> None:
95-
if self.is_exposed:
94+
if self.is_externally_accessible():
9695
ports = [self._READ_WRITE_PORT, self._READ_ONLY_PORT]
9796
else:
9897
ports = []
@@ -108,7 +107,7 @@ def wait_until_mysql_router_ready(self) -> None:
108107
wait=tenacity.wait_fixed(5),
109108
):
110109
with attempt:
111-
if self.is_exposed:
110+
if self.is_externally_accessible():
112111
for port in (
113112
self._READ_WRITE_PORT,
114113
self._READ_ONLY_PORT,

src/machine_logrotate.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ def enable(self) -> None:
5151

5252
def disable(self) -> None:
5353
logger.debug("Removing cron job for log rotation of mysqlrouter")
54-
self._logrotate_config.unlink()
55-
self._cron_file.unlink()
54+
if self._logrotate_config.exists():
55+
self._logrotate_config.unlink()
56+
if self._cron_file.exists():
57+
self._cron_file.unlink()
5658
logger.debug("Removed cron job for log rotation of mysqlrouter")

src/machine_upgrade.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import upgrade
1818
import workload
1919

20+
if typing.TYPE_CHECKING:
21+
import relations.cos
22+
2023
logger = logging.getLogger(__name__)
2124

2225

@@ -135,9 +138,15 @@ def authorized(self) -> bool:
135138
return False
136139
return False
137140

138-
def upgrade_unit(self, *, workload_: workload.Workload, tls: bool) -> None:
141+
def upgrade_unit(
142+
self,
143+
*,
144+
workload_: workload.Workload,
145+
tls: bool,
146+
exporter_config: "relations.cos.ExporterConfig",
147+
) -> None:
139148
logger.debug(f"Upgrading {self.authorized=}")
140149
self.unit_state = "upgrading"
141-
workload_.upgrade(unit=self._unit, tls=tls)
150+
workload_.upgrade(unit=self._unit, tls=tls, exporter_config=exporter_config)
142151
self._unit_databag["snap_revision"] = snap.REVISION
143152
logger.debug(f"Saved {snap.REVISION=} in unit databag after upgrade")

src/machine_workload.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class AuthenticatedMachineWorkload(workload.AuthenticatedWorkload):
2020
# TODO python3.10 min version: Use `list` instead of `typing.List`
2121
def _get_bootstrap_command(self, password: str) -> typing.List[str]:
2222
command = super()._get_bootstrap_command(password)
23-
if self._charm.is_exposed:
23+
if self._charm.is_externally_accessible():
2424
command.extend(
2525
[
2626
"--conf-bind-address",
@@ -35,17 +35,18 @@ def _get_bootstrap_command(self, password: str) -> typing.List[str]:
3535
# set. Workaround for https://bugs.mysql.com/bug.php?id=107291
3636
"--conf-set-option",
3737
"DEFAULT.server_ssl_mode=PREFERRED",
38+
"--conf-skip-tcp",
3839
]
3940
)
4041
return command
4142

42-
def _update_configured_socket_file_locations_and_bind_address(self) -> None:
43+
def _update_configured_socket_file_locations(self) -> None:
4344
"""Update configured socket file locations from `/tmp` to `/run/mysqlrouter`.
4445
4546
Called after MySQL Router bootstrap & before MySQL Router service is enabled
4647
4748
Change configured location of socket files before socket files are created by MySQL Router
48-
service. Also remove bind_address and bind_port for all router services: rw, ro, x_rw, x_ro
49+
service.
4950
5051
Needed since `/tmp` inside a snap is not accessible to non-root users. The socket files
5152
must be accessible to applications related via database_provides endpoint.
@@ -59,14 +60,12 @@ def _update_configured_socket_file_locations_and_bind_address(self) -> None:
5960
section["socket"] = str(
6061
self._container.path("/run/mysqlrouter") / pathlib.PurePath(section["socket"]).name
6162
)
62-
del section["bind_address"]
63-
del section["bind_port"]
6463
with io.StringIO() as output:
6564
config.write(output)
6665
self._container.router_config_file.write_text(output.getvalue())
6766
logger.debug("Updated configured socket file locations")
6867

6968
def _bootstrap_router(self, *, tls: bool) -> None:
7069
super()._bootstrap_router(tls=tls)
71-
if not self._charm.is_exposed:
72-
self._update_configured_socket_file_locations_and_bind_address()
70+
if not self._charm.is_externally_accessible():
71+
self._update_configured_socket_file_locations()

src/relations/database_providers_wrapper.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,9 @@ def __init__(
3838
charm_
3939
)
4040

41-
@property
42-
def external_connectivity(self) -> bool:
43-
"""Whether the relation is exposed"""
44-
return self._database_provides.external_connectivity
41+
def external_connectivity(self, event) -> bool:
42+
"""Whether any of the relations are marked as external."""
43+
return self._database_provides.external_connectivity(event)
4544

4645
def reconcile_users(
4746
self,

src/relations/database_provides.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def __init__(
7474
# (e.g. when related to `data-integrator` charm)
7575
# Implements DA073 - Add Expose Flag to the Database Interface
7676
# https://docs.google.com/document/d/1Y7OZWwMdvF8eEMuVKrqEfuFV3JOjpqLHL7_GPqJpRHU
77-
self._external_connectivity = databag.get("external-node-connectivity") == "true"
77+
self.external_connectivity = databag.get("external-node-connectivity") == "true"
7878
if databag.get("extra-user-roles"):
7979
raise _UnsupportedExtraUserRole(
8080
app_name=relation.app.name, endpoint_name=relation.name
@@ -125,13 +125,11 @@ def create_database_and_user(
125125

126126
rw_endpoint = (
127127
exposed_read_write_endpoint
128-
if self._external_connectivity
128+
if self.external_connectivity
129129
else router_read_write_endpoint
130130
)
131131
ro_endpoint = (
132-
exposed_read_only_endpoint
133-
if self._external_connectivity
134-
else router_read_only_endpoint
132+
exposed_read_only_endpoint if self.external_connectivity else router_read_only_endpoint
135133
)
136134

137135
self._set_databag(
@@ -199,13 +197,23 @@ def _shared_users(self) -> typing.List[_RelationWithSharedUser]:
199197
pass
200198
return shared_users
201199

202-
@property
203-
def external_connectivity(self) -> bool:
204-
"""Whether the relation is exposed."""
205-
relation_data = self._interface.fetch_relation_data(fields=["external-node-connectivity"])
206-
return any(
207-
[data.get("external-node-connectivity") == "true" for data in relation_data.values()]
208-
)
200+
def external_connectivity(self, event) -> bool:
201+
"""Whether any of the relations are marked as external."""
202+
requested_users = []
203+
for relation in self._interface.relations:
204+
try:
205+
requested_users.append(
206+
_RelationThatRequestedUser(
207+
relation=relation, interface=self._interface, event=event
208+
)
209+
)
210+
except (
211+
_RelationBreaking,
212+
remote_databag.IncompleteDatabag,
213+
_UnsupportedExtraUserRole,
214+
):
215+
pass
216+
return any(relation.external_connectivity for relation in requested_users)
209217

210218
def reconcile_users(
211219
self,

src/relations/tls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def save_certificate(self, event: tls_certificates.CertificateAvailableEvent) ->
114114
def _generate_csr(self, key: bytes) -> bytes:
115115
"""Generate certificate signing request (CSR)."""
116116
sans_ip = ["127.0.0.1"] # needed for the HTTP server when related with COS
117-
if self._charm.is_exposed:
117+
if self._charm.is_externally_accessible():
118118
sans_ip.append(self._charm.host_address)
119119

120120
return tls_certificates.generate_csr(

0 commit comments

Comments
 (0)