Skip to content

Commit a3734ec

Browse files
authored
[DPE-5591] refactor: Rework status handling (#141)
1 parent aaedc28 commit a3734ec

18 files changed

+2189
-1781
lines changed

poetry.lock

+258-264
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ package-mode = false
3131

3232
[tool.poetry.dependencies]
3333
python = "^3.10"
34-
ops = ">=2.4.1"
34+
ops = ">=2.17.0"
3535
kazoo = ">=2.8.0"
3636
tenacity = ">=8.0.1"
3737
pure-sasl = ">=0.6.2"
@@ -85,7 +85,7 @@ pytest = ">=7.2"
8585
coverage = { extras = ["toml"], version = ">7.0" }
8686
pytest-mock = "^3.11.1"
8787
packaging = "^23.1"
88-
ops-scenario = "^7.0.0"
88+
ops = { version = ">=2.17.0", extras = ["testing"] }
8989

9090
[tool.poetry.group.security]
9191
optional = true

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ attrs==24.2.0 ; python_version >= "3.10" and python_version < "4.0"
33
certifi==2024.8.30 ; python_version >= "3.10" and python_version < "4.0"
44
cffi==1.17.1 ; python_version >= "3.10" and python_version < "4.0" and platform_python_implementation != "PyPy"
55
charset-normalizer==3.4.0 ; python_version >= "3.10" and python_version < "4.0"
6-
cryptography==43.0.1 ; python_version >= "3.10" and python_version < "4.0"
6+
cryptography==43.0.3 ; python_version >= "3.10" and python_version < "4.0"
77
exceptiongroup==1.2.2 ; python_version >= "3.10" and python_version < "3.11"
88
h11==0.14.0 ; python_version >= "3.10" and python_version < "4.0"
99
httpcore==1.0.6 ; python_version >= "3.10" and python_version < "4.0"

src/charm.py

+6-17
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from charms.prometheus_k8s.v0.prometheus_scrape import MetricsEndpointProvider
1313
from charms.rolling_ops.v0.rollingops import RollingOpsManager
1414
from ops import (
15-
ActiveStatus,
1615
CollectStatusEvent,
1716
EventBase,
1817
StatusBase,
@@ -51,6 +50,7 @@ def __init__(self, *args):
5150
super().__init__(*args)
5251
self.name = CHARM_KEY
5352
self.substrate: Substrates = SUBSTRATE
53+
self.pending_inactive_statuses: list[Status] = []
5454

5555
# Common attrs init
5656
self.state = ClusterState(self, substrate=self.substrate)
@@ -62,6 +62,7 @@ def __init__(self, *args):
6262
self.restart = RollingOpsManager(self, relation="restart", callback=self._restart_broker)
6363

6464
self.framework.observe(getattr(self.on, "config_changed"), self._on_roles_changed)
65+
self.framework.observe(self.on.collect_unit_status, self._on_collect_status)
6566
self.framework.observe(self.on.collect_app_status, self._on_collect_status)
6667

6768
# peer-cluster events are shared between all roles, so necessary to init here to avoid instantiating multiple times
@@ -128,24 +129,12 @@ def _set_status(self, key: Status) -> None:
128129
log_level: DebugLevel = key.value.log_level
129130

130131
getattr(logger, log_level.lower())(status.message)
131-
self.unit.status = status
132+
# self.unit.status = status
133+
self.pending_inactive_statuses.append(key)
132134

133135
def _on_collect_status(self, event: CollectStatusEvent):
134-
ready_to_start = self.state.ready_to_start.value.status
135-
event.add_status(ready_to_start)
136-
137-
if not isinstance(ready_to_start, ActiveStatus):
138-
return
139-
140-
if not self.state.runs_broker:
141-
# early return, the next checks only concern the broker
142-
return
143-
144-
if not self.broker.workload.active():
145-
event.add_status(Status.BROKER_NOT_RUNNING.value.status)
146-
147-
if not self.state.zookeeper.broker_active():
148-
event.add_status(Status.ZK_NOT_CONNECTED.value.status)
136+
for status in self.pending_inactive_statuses + [Status.ACTIVE]:
137+
event.add_status(status.value.status)
149138

150139

151140
if __name__ == "__main__":

src/events/balancer.py

+19-16
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from ops import (
1313
ActionEvent,
14-
ActiveStatus,
1514
EventBase,
1615
InstallEvent,
1716
Object,
@@ -61,6 +60,9 @@ def __init__(self, charm) -> None:
6160
config=self.charm.config,
6261
)
6362

63+
# Before fast exit to avoid silently ignoring the action
64+
self.framework.observe(getattr(self.charm.on, "rebalance_action"), self.rebalance)
65+
6466
# Fast exit after workload instantiation, but before any event observer
6567
if BALANCER.value not in self.charm.config.roles or not self.charm.unit.is_leader():
6668
return
@@ -82,8 +84,6 @@ def __init__(self, charm) -> None:
8284
self.framework.observe(self.charm.on.update_status, self._on_config_changed)
8385
self.framework.observe(self.charm.on.config_changed, self._on_config_changed)
8486

85-
self.framework.observe(getattr(self.charm.on, "rebalance_action"), self.rebalance)
86-
8787
def _on_install(self, event: InstallEvent) -> None:
8888
"""Handler for `install` event."""
8989
if not self.workload.container_can_connect:
@@ -101,8 +101,9 @@ def _on_install(self, event: InstallEvent) -> None:
101101

102102
def _on_start(self, event: StartEvent | PebbleReadyEvent) -> None:
103103
"""Handler for `start` or `pebble-ready` events."""
104-
self.charm._set_status(self.charm.state.ready_to_start)
105-
if not isinstance(self.charm.unit.status, ActiveStatus):
104+
current_status = self.charm.state.ready_to_start
105+
if current_status is not Status.ACTIVE:
106+
self.charm._set_status(current_status)
106107
event.defer()
107108
return
108109

@@ -206,33 +207,36 @@ def rebalance(self, event: ActionEvent) -> None:
206207
available_brokers = [int(broker.split("/")[1]) for broker in brokers]
207208

208209
failure_conditions = [
209-
(not self.charm.unit.is_leader(), "Action must be ran on the application leader"),
210210
(
211-
not self.balancer_manager.cruise_control.monitoring,
211+
lambda: not self.charm.unit.is_leader(),
212+
"Action must be ran on the application leader",
213+
),
214+
(
215+
lambda: not self.balancer_manager.cruise_control.monitoring,
212216
"CruiseControl balancer service is not yet ready",
213217
),
214218
(
215-
self.balancer_manager.cruise_control.executing,
219+
lambda: self.balancer_manager.cruise_control.executing,
216220
"CruiseControl balancer service is currently executing a task, please try again later",
217221
),
218222
(
219-
not self.balancer_manager.cruise_control.ready,
223+
lambda: not self.balancer_manager.cruise_control.ready,
220224
"CruiseControl balancer service has not yet collected enough data to provide a partition reallocation proposal",
221225
),
222226
(
223-
event.params["mode"] in (MODE_ADD, MODE_REMOVE)
227+
lambda: event.params["mode"] in (MODE_ADD, MODE_REMOVE)
224228
and event.params.get("brokerid", None) is None,
225229
"'add' and 'remove' rebalance action require passing the 'brokerid' parameter",
226230
),
227231
(
228-
event.params["mode"] in (MODE_ADD, MODE_REMOVE)
232+
lambda: event.params["mode"] in (MODE_ADD, MODE_REMOVE)
229233
and event.params.get("brokerid") not in available_brokers,
230234
"invalid brokerid",
231235
),
232236
]
233237

234238
for check, msg in failure_conditions:
235-
if check:
239+
if check():
236240
logging.error(msg)
237241
event.set_results({"error": msg})
238242
event.fail(msg)
@@ -260,8 +264,6 @@ def rebalance(self, event: ActionEvent) -> None:
260264

261265
event.set_results(sanitised_response)
262266

263-
self.charm._set_status(Status.ACTIVE)
264-
265267
@property
266268
def healthy(self) -> bool:
267269
"""Checks and updates various charm lifecycle states.
@@ -273,8 +275,9 @@ def healthy(self) -> bool:
273275
if not self.charm.state.runs_balancer:
274276
return True
275277

276-
self.charm._set_status(self.charm.state.ready_to_start)
277-
if not isinstance(self.charm.unit.status, ActiveStatus):
278+
current_status = self.charm.state.ready_to_start
279+
if current_status is not Status.ACTIVE:
280+
self.charm._set_status(current_status)
278281
return False
279282

280283
if not self.workload.active() and self.charm.unit.is_leader():

src/events/broker.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from charms.operator_libs_linux.v1.snap import SnapError
1313
from ops import (
14-
ActiveStatus,
1514
EventBase,
1615
InstallEvent,
1716
Object,
@@ -166,8 +165,9 @@ def _on_start(self, event: StartEvent | PebbleReadyEvent) -> None:
166165

167166
self.update_external_services()
168167

169-
self.charm._set_status(self.charm.state.ready_to_start)
170-
if not isinstance(self.charm.unit.status, ActiveStatus):
168+
current_status = self.charm.state.ready_to_start
169+
if current_status is not Status.ACTIVE:
170+
self.charm._set_status(current_status)
171171
event.defer()
172172
return
173173

@@ -197,7 +197,7 @@ def _on_start(self, event: StartEvent | PebbleReadyEvent) -> None:
197197
self.charm.on.update_status.emit()
198198

199199
# only log once on successful 'on-start' run
200-
if isinstance(self.charm.unit.status, ActiveStatus):
200+
if not self.charm.pending_inactive_statuses:
201201
logger.info(f'Broker {self.charm.unit.name.split("/")[1]} connected')
202202

203203
def _on_config_changed(self, event: EventBase) -> None:
@@ -312,8 +312,6 @@ def _on_update_status(self, _: UpdateStatusEvent) -> None:
312312
self.charm._set_status(Status.BROKER_NOT_RUNNING)
313313
return
314314

315-
self.charm._set_status(Status.ACTIVE)
316-
317315
def _on_secret_changed(self, event: SecretChangedEvent) -> None:
318316
"""Handler for `secret_changed` events."""
319317
if not event.secret.label or not self.charm.state.cluster.relation:
@@ -387,8 +385,9 @@ def healthy(self) -> bool:
387385
Returns:
388386
True if service is alive and active. Otherwise False
389387
"""
390-
self.charm._set_status(self.charm.state.ready_to_start)
391-
if not isinstance(self.charm.unit.status, ActiveStatus):
388+
current_status = self.charm.state.ready_to_start
389+
if current_status is not Status.ACTIVE:
390+
self.charm._set_status(current_status)
392391
return False
393392

394393
if not self.workload.active():

src/events/tls.py

-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
RelationJoinedEvent,
2727
)
2828
from ops.framework import Object
29-
from ops.model import ActiveStatus
3029

3130
from literals import TLS_RELATION, TRUSTED_CA_RELATION, TRUSTED_CERTIFICATE_RELATION, Status
3231

@@ -138,7 +137,6 @@ def _trusted_relation_created(self, event: EventBase) -> None:
138137

139138
# Create a "mtls" flag so a new listener (CLIENT_SSL) is created
140139
self.charm.state.cluster.update({"mtls": "enabled"})
141-
self.charm.app.status = ActiveStatus()
142140

143141
def _trusted_relation_joined(self, event: RelationJoinedEvent) -> None:
144142
"""Generate a CSR so the tls-certificates operator works as expected."""

src/events/zookeeper.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,13 @@ def _on_zookeeper_broken(self, _: RelationEvent) -> None:
121121
# Kafka keeps a meta.properties in every log.dir with a unique ClusterID
122122
# this ID is provided by ZK, and removing it on relation-broken allows
123123
# re-joining to another ZK cluster.
124-
for storage in self.charm.model.storages["data"]:
125-
self.charm.workload.exec(["rm", f"{storage.location}/meta.properties"])
124+
self.charm.workload.exec(
125+
[
126+
"bash",
127+
"-c",
128+
f"""find {self.charm.workload.paths.data_path} -type f -name meta.properties -delete || true""",
129+
]
130+
)
126131

127132
if not self.charm.unit.is_leader():
128133
return

0 commit comments

Comments
 (0)