Skip to content

Commit d0af047

Browse files
Standardize abstract_charm.py file with the K8s charm (#182)
## Issue The `abstract_charm.py` has diverged with the K8s charm following in support of HACluster charm integration + `expose-external` config in the K8s charm. ## Solution Standardize the files Counterpart PR in K8s charm: canonical/mysql-router-k8s-operator#328
1 parent d49eda8 commit d0af047

File tree

10 files changed

+141
-85
lines changed

10 files changed

+141
-85
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666

6767
build:
6868
name: Build charm
69-
uses: canonical/data-platform-workflows/.github/workflows/[email protected].0
69+
uses: canonical/data-platform-workflows/.github/workflows/[email protected].1
7070
with:
7171
# Use of cache blocked by https://github.com/canonical/charmcraft/issues/1456
7272
# Details: https://github.com/canonical/charmcraftcache/issues/3

.github/workflows/release.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ jobs:
1515

1616
build:
1717
name: Build charm
18-
uses: canonical/data-platform-workflows/.github/workflows/[email protected].0
18+
uses: canonical/data-platform-workflows/.github/workflows/[email protected].1
1919

2020
release:
2121
name: Release charm
2222
needs:
2323
- ci-tests
2424
- build
25-
uses: canonical/data-platform-workflows/.github/workflows/[email protected].0
25+
uses: canonical/data-platform-workflows/.github/workflows/[email protected].1
2626
with:
2727
channel: dpe/edge
2828
artifact-prefix: ${{ needs.build.outputs.artifact-prefix }}

.github/workflows/sync_docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010
jobs:
1111
sync-docs:
1212
name: Sync docs from Discourse
13-
uses: canonical/data-platform-workflows/.github/workflows/[email protected].0
13+
uses: canonical/data-platform-workflows/.github/workflows/[email protected].1
1414
with:
1515
reviewers: a-velasco
1616
permissions:

lib/charms/data_platform_libs/v0/data_interfaces.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def _on_topic_requested(self, event: TopicRequestedEvent):
331331

332332
# Increment this PATCH version before using `charmcraft publish-lib` or reset
333333
# to 0 if you are raising the major API version
334-
LIBPATCH = 39
334+
LIBPATCH = 40
335335

336336
PYDEPS = ["ops>=2.0.0"]
337337

@@ -391,6 +391,10 @@ class IllegalOperationError(DataInterfacesError):
391391
"""To be used when an operation is not allowed to be performed."""
392392

393393

394+
class PrematureDataAccessError(DataInterfacesError):
395+
"""To be raised when the Relation Data may be accessed (written) before protocol init complete."""
396+
397+
394398
##############################################################################
395399
# Global helpers / utilities
396400
##############################################################################
@@ -1453,6 +1457,8 @@ def _on_relation_changed_event(self, event: RelationChangedEvent) -> None:
14531457
class ProviderData(Data):
14541458
"""Base provides-side of the data products relation."""
14551459

1460+
RESOURCE_FIELD = "database"
1461+
14561462
def __init__(
14571463
self,
14581464
model: Model,
@@ -1618,6 +1624,15 @@ def _fetch_my_specific_relation_data(
16181624
def _update_relation_data(self, relation: Relation, data: Dict[str, str]) -> None:
16191625
"""Set values for fields not caring whether it's a secret or not."""
16201626
req_secret_fields = []
1627+
1628+
keys = set(data.keys())
1629+
if self.fetch_relation_field(relation.id, self.RESOURCE_FIELD) is None and (
1630+
keys - {"endpoints", "read-only-endpoints", "replset"}
1631+
):
1632+
raise PrematureDataAccessError(
1633+
"Premature access to relation data, update is forbidden before the connection is initialized."
1634+
)
1635+
16211636
if relation.app:
16221637
req_secret_fields = get_encoded_list(relation, relation.app, REQ_SECRET_FIELDS)
16231638

@@ -3290,6 +3305,8 @@ class KafkaRequiresEvents(CharmEvents):
32903305
class KafkaProviderData(ProviderData):
32913306
"""Provider-side of the Kafka relation."""
32923307

3308+
RESOURCE_FIELD = "topic"
3309+
32933310
def __init__(self, model: Model, relation_name: str) -> None:
32943311
super().__init__(model, relation_name)
32953312

@@ -3539,6 +3556,8 @@ class OpenSearchRequiresEvents(CharmEvents):
35393556
class OpenSearchProvidesData(ProviderData):
35403557
"""Provider-side of the OpenSearch relation."""
35413558

3559+
RESOURCE_FIELD = "index"
3560+
35423561
def __init__(self, model: Model, relation_name: str) -> None:
35433562
super().__init__(model, relation_name)
35443563

lib/charms/tempo_coordinator_k8s/v0/charm_tracing.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ def my_tracing_endpoint(self) -> Optional[str]:
6969
- every event as a span (including custom events)
7070
- every charm method call (except dunders) as a span
7171
72+
We recommend that you scale up your tracing provider and relate it to an ingress so that your tracing requests
73+
go through the ingress and get load balanced across all units. Otherwise, if the provider's leader goes down, your tracing goes down.
74+
7275
7376
## TLS support
7477
If your charm integrates with a TLS provider which is also trusted by the tracing provider (the Tempo charm),
@@ -269,7 +272,7 @@ def _remove_stale_otel_sdk_packages():
269272
# Increment this PATCH version before using `charmcraft publish-lib` or reset
270273
# to 0 if you are raising the major API version
271274

272-
LIBPATCH = 2
275+
LIBPATCH = 3
273276

274277
PYDEPS = ["opentelemetry-exporter-otlp-proto-http==1.21.0"]
275278

lib/charms/tempo_coordinator_k8s/v0/tracing.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def __init__(self, *args):
3434
`TracingEndpointRequirer.request_protocols(*protocol:str, relation:Optional[Relation])` method.
3535
Using this method also allows you to use per-relation protocols.
3636
37-
Units of provider charms obtain the tempo endpoint to which they will push their traces by calling
37+
Units of requirer charms obtain the tempo endpoint to which they will push their traces by calling
3838
`TracingEndpointRequirer.get_endpoint(protocol: str)`, where `protocol` is, for example:
3939
- `otlp_grpc`
4040
- `otlp_http`
@@ -44,7 +44,10 @@ def __init__(self, *args):
4444
If the `protocol` is not in the list of protocols that the charm requested at endpoint set-up time,
4545
the library will raise an error.
4646
47-
## Requirer Library Usage
47+
We recommend that you scale up your tracing provider and relate it to an ingress so that your tracing requests
48+
go through the ingress and get load balanced across all units. Otherwise, if the provider's leader goes down, your tracing goes down.
49+
50+
## Provider Library Usage
4851
4952
The `TracingEndpointProvider` object may be used by charms to manage relations with their
5053
trace sources. For this purposes a Tempo-like charm needs to do two things
@@ -107,7 +110,7 @@ def __init__(self, *args):
107110

108111
# Increment this PATCH version before using `charmcraft publish-lib` or reset
109112
# to 0 if you are raising the major API version
110-
LIBPATCH = 2
113+
LIBPATCH = 3
111114

112115
PYDEPS = ["pydantic"]
113116

src/abstract_charm.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,23 +100,29 @@ def _logrotate(self) -> logrotate.LogRotate:
100100

101101
@property
102102
@abc.abstractmethod
103-
def _read_write_endpoint(self) -> str:
103+
def _read_write_endpoints(self) -> str:
104104
"""MySQL Router read-write endpoint"""
105105

106106
@property
107107
@abc.abstractmethod
108-
def _read_only_endpoint(self) -> str:
108+
def _read_only_endpoints(self) -> str:
109109
"""MySQL Router read-only endpoint"""
110110

111111
@property
112112
@abc.abstractmethod
113-
def _exposed_read_write_endpoint(self) -> str:
114-
"""The exposed read-write endpoint"""
113+
def _exposed_read_write_endpoints(self) -> typing.Optional[str]:
114+
"""The exposed read-write endpoint.
115+
116+
Only defined in vm charm.
117+
"""
115118

116119
@property
117120
@abc.abstractmethod
118-
def _exposed_read_only_endpoint(self) -> str:
119-
"""The exposed read-only endpoint"""
121+
def _exposed_read_only_endpoints(self) -> typing.Optional[str]:
122+
"""The exposed read-only endpoint.
123+
124+
Only defined in vm charm.
125+
"""
120126

121127
@abc.abstractmethod
122128
def is_externally_accessible(self, *, event) -> typing.Optional[bool]:
@@ -125,6 +131,11 @@ def is_externally_accessible(self, *, event) -> typing.Optional[bool]:
125131
Only defined in vm charm to return True/False. In k8s charm, returns None.
126132
"""
127133

134+
@property
135+
@abc.abstractmethod
136+
def _status(self) -> ops.StatusBase:
137+
"""Status of the charm."""
138+
128139
@property
129140
def _tls_certificate_saved(self) -> bool:
130141
"""Whether a TLS certificate is available to use"""
@@ -202,6 +213,8 @@ def _determine_app_status(self, *, event) -> ops.StatusBase:
202213
# (Relations should not be modified during upgrade.)
203214
return upgrade_status
204215
statuses = []
216+
if self._status:
217+
statuses.append(self._status)
205218
for endpoint in (self._database_requires, self._database_provides):
206219
if status := endpoint.get_status(event):
207220
statuses.append(status)
@@ -236,8 +249,8 @@ def wait_until_mysql_router_ready(self, *, event) -> None:
236249
"""
237250

238251
@abc.abstractmethod
239-
def _reconcile_node_port(self, *, event) -> None:
240-
"""Reconcile node port.
252+
def _reconcile_service(self) -> None:
253+
"""Reconcile service.
241254
242255
Only applies to Kubernetes charm
243256
"""
@@ -249,6 +262,10 @@ def _reconcile_ports(self, *, event) -> None:
249262
Only applies to Machine charm
250263
"""
251264

265+
@abc.abstractmethod
266+
def _update_endpoints(self) -> None:
267+
"""Update the endpoints in the provider relation if necessary."""
268+
252269
# =======================
253270
# Handlers
254271
# =======================
@@ -332,23 +349,17 @@ def reconcile(self, event=None) -> None: # noqa: C901
332349
and isinstance(workload_, workload.AuthenticatedWorkload)
333350
and workload_.container_ready
334351
):
335-
self._reconcile_node_port(event=event)
352+
self._reconcile_service()
336353
self._database_provides.reconcile_users(
337354
event=event,
338-
router_read_write_endpoint=self._read_write_endpoint,
339-
router_read_only_endpoint=self._read_only_endpoint,
340-
exposed_read_write_endpoint=self._exposed_read_write_endpoint,
341-
exposed_read_only_endpoint=self._exposed_read_only_endpoint,
355+
router_read_write_endpoints=self._read_write_endpoints,
356+
router_read_only_endpoints=self._read_only_endpoints,
357+
exposed_read_write_endpoints=self._exposed_read_write_endpoints,
358+
exposed_read_only_endpoints=self._exposed_read_only_endpoints,
342359
shell=workload_.shell,
343360
)
344-
# _ha_cluster only assigned a value in machine charms
345-
if self._ha_cluster:
346-
self._database_provides.update_endpoints(
347-
router_read_write_endpoint=self._read_write_endpoint,
348-
router_read_only_endpoint=self._read_only_endpoint,
349-
exposed_read_write_endpoint=self._exposed_read_write_endpoint,
350-
exposed_read_only_endpoint=self._exposed_read_only_endpoint,
351-
)
361+
self._update_endpoints()
362+
352363
if workload_.container_ready:
353364
workload_.reconcile(
354365
event=event,

src/machine_charm.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ def _upgrade(self) -> typing.Optional[machine_upgrade.Upgrade]:
7979
except upgrade.PeerRelationNotReady:
8080
pass
8181

82+
@property
83+
def _status(self) -> ops.StatusBase:
84+
pass
85+
8286
@property
8387
def _logrotate(self) -> machine_logrotate.LogRotate:
8488
return machine_logrotate.LogRotate(container_=self._container)
@@ -95,25 +99,25 @@ def host_address(self) -> str:
9599
return str(self.model.get_binding("juju-info").network.bind_address)
96100

97101
@property
98-
def _read_write_endpoint(self) -> str:
102+
def _read_write_endpoints(self) -> str:
99103
return f'file://{self._container.path("/run/mysqlrouter/mysql.sock")}'
100104

101105
@property
102-
def _read_only_endpoint(self) -> str:
106+
def _read_only_endpoints(self) -> str:
103107
return f'file://{self._container.path("/run/mysqlrouter/mysqlro.sock")}'
104108

105109
@property
106-
def _exposed_read_write_endpoint(self) -> str:
110+
def _exposed_read_write_endpoints(self) -> typing.Optional[str]:
107111
return f"{self.host_address}:{self._READ_WRITE_PORT}"
108112

109113
@property
110-
def _exposed_read_only_endpoint(self) -> str:
114+
def _exposed_read_only_endpoints(self) -> typing.Optional[str]:
111115
return f"{self.host_address}:{self._READ_ONLY_PORT}"
112116

113117
def is_externally_accessible(self, *, event) -> typing.Optional[bool]:
114118
return self._database_provides.external_connectivity(event)
115119

116-
def _reconcile_node_port(self, *, event) -> None:
120+
def _reconcile_service(self) -> None:
117121
"""Only applies to Kubernetes charm, so no-op."""
118122
pass
119123

@@ -124,6 +128,18 @@ def _reconcile_ports(self, *, event) -> None:
124128
ports = []
125129
self.unit.set_ports(*ports)
126130

131+
def _update_endpoints(self) -> None:
132+
self._database_provides.update_endpoints(
133+
router_read_write_endpoints=self._read_write_endpoints,
134+
router_read_only_endpoints=self._read_only_endpoints,
135+
exposed_read_write_endpoints=self._exposed_read_write_endpoints,
136+
exposed_read_only_endpoints=self._exposed_read_only_endpoints,
137+
)
138+
139+
def _wait_until_service_reconciled(self) -> None:
140+
"""Only applies to Kubernetes charm, so no-op."""
141+
pass
142+
127143
def wait_until_mysql_router_ready(self, *, event) -> None:
128144
logger.debug("Waiting until MySQL Router is ready")
129145
self.unit.status = ops.MaintenanceStatus("MySQL Router starting")

src/relations/database_providers_wrapper.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,27 @@ def external_connectivity(self, event) -> bool:
4545
def update_endpoints(
4646
self,
4747
*,
48-
router_read_write_endpoint: str,
49-
router_read_only_endpoint: str,
50-
exposed_read_write_endpoint: str,
51-
exposed_read_only_endpoint: str,
48+
router_read_write_endpoints: str,
49+
router_read_only_endpoints: str,
50+
exposed_read_write_endpoints: str,
51+
exposed_read_only_endpoints: str,
5252
) -> None:
5353
"""Update the endpoints in the provides relationship databags."""
5454
self._database_provides.update_endpoints(
55-
router_read_write_endpoint=router_read_write_endpoint,
56-
router_read_only_endpoint=router_read_only_endpoint,
57-
exposed_read_write_endpoint=exposed_read_write_endpoint,
58-
exposed_read_only_endpoint=exposed_read_only_endpoint,
55+
router_read_write_endpoints=router_read_write_endpoints,
56+
router_read_only_endpoints=router_read_only_endpoints,
57+
exposed_read_write_endpoints=exposed_read_write_endpoints,
58+
exposed_read_only_endpoints=exposed_read_only_endpoints,
5959
)
6060

6161
def reconcile_users(
6262
self,
6363
*,
6464
event,
65-
router_read_write_endpoint: str,
66-
router_read_only_endpoint: str,
67-
exposed_read_write_endpoint: str,
68-
exposed_read_only_endpoint: str,
65+
router_read_write_endpoints: str,
66+
router_read_only_endpoints: str,
67+
exposed_read_write_endpoints: str,
68+
exposed_read_only_endpoints: str,
6969
shell: mysql_shell.Shell,
7070
) -> None:
7171
"""Create requested users and delete inactive users.
@@ -76,10 +76,10 @@ def reconcile_users(
7676
"""
7777
self._database_provides.reconcile_users(
7878
event=event,
79-
router_read_write_endpoint=router_read_write_endpoint,
80-
router_read_only_endpoint=router_read_only_endpoint,
81-
exposed_read_write_endpoint=exposed_read_write_endpoint,
82-
exposed_read_only_endpoint=exposed_read_only_endpoint,
79+
router_read_write_endpoints=router_read_write_endpoints,
80+
router_read_only_endpoints=router_read_only_endpoints,
81+
exposed_read_write_endpoints=exposed_read_write_endpoints,
82+
exposed_read_only_endpoints=exposed_read_only_endpoints,
8383
shell=shell,
8484
)
8585
self._deprecated_shared_db.reconcile_users(event=event, shell=shell)

0 commit comments

Comments
 (0)