Skip to content

Commit 951c97b

Browse files
authored
Merge pull request #132 from manadart/4.0-handle-cluster-depart
fix: departing leader should not rewrite cluster addresses
2 parents f2cc29f + 95bce80 commit 951c97b

2 files changed

Lines changed: 86 additions & 3 deletions

File tree

src/charm.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ def _observe(self):
6969
self.framework.observe(self.on.install, self._on_install)
7070
self.framework.observe(self.on.collect_unit_status, self._on_collect_status)
7171
self.framework.observe(self.on.config_changed, self._on_config_changed)
72+
self.framework.observe(
73+
self.on.leader_elected, self._on_dbcluster_leader_elected)
7274
self.framework.observe(self.on.leader_elected, self._on_metrics_reconcile)
7375
self.framework.observe(self.on.upgrade_charm, self._on_metrics_reconcile)
7476
self.framework.observe(
@@ -296,8 +298,16 @@ def _on_dbcluster_relation_changed(self, event):
296298
self._update_bind_addresses(relation)
297299

298300
def _on_dbcluster_relation_departed(self, event):
299-
relation = event.relation
300-
self._update_bind_addresses(relation)
301+
# A departing unit receives this event once for each remaining peer.
302+
# It must not publish an aggregate from its shrinking view of the
303+
# relation.
304+
if event.departing_unit == self.unit:
305+
return
306+
self._update_bind_addresses(event.relation)
307+
308+
def _on_dbcluster_leader_elected(self, _event):
309+
for relation in self.model.relations["dbcluster"]:
310+
self._update_bind_addresses(relation)
301311

302312
def _on_tracing_relation_changed(self, event):
303313
if not self.tracing_requirer.is_ready(event.relation):

tests/test_charm.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from charm import JujuControllerCharm, AgentConfException
2121
from ops.model import BlockedStatus, ActiveStatus
2222
from ops.testing import Harness
23-
from unittest.mock import mock_open, patch
23+
from unittest.mock import Mock, mock_open, patch
2424
from unixsocket import ConnectionError as SocketConnectionError
2525

2626
agent_conf = '''
@@ -521,6 +521,79 @@ def test_dbcluster_relation_departed(
521521
harness.evaluate_status()
522522
self.assertIsInstance(harness.charm.unit.status, ActiveStatus)
523523

524+
@patch("builtins.open", new_callable=mock_open, read_data=agent_conf)
525+
@patch("configchangesocket.ConfigChangeSocketClient.get_controller_agent_id")
526+
@patch("ops.model.Model.get_binding")
527+
@patch("configchangesocket.ConfigChangeSocketClient.reload_config")
528+
def test_dbcluster_relation_departed_ignores_departing_self(
529+
self, mock_reload_config, mock_get_binding, mock_get_agent_id, *__):
530+
harness = self.harness
531+
mock_get_binding.return_value = mockBinding(['192.168.1.17'])
532+
mock_get_agent_id.return_value = '0'
533+
534+
harness.set_leader()
535+
relation_id = harness.add_relation('dbcluster', harness.charm.app.name)
536+
harness.add_relation_unit(relation_id, 'juju-controller/1')
537+
harness.update_relation_data(
538+
relation_id, 'juju-controller/1', {
539+
'db-bind-address': '192.168.1.100',
540+
'agent-id': '9',
541+
})
542+
543+
app_data = harness.get_relation_data(relation_id, 'juju-controller')
544+
expected = {'0': '192.168.1.17', '9': '192.168.1.100'}
545+
self.assertEqual(json.loads(app_data['db-bind-addresses']), expected)
546+
547+
mock_reload_config.reset_mock()
548+
event = Mock(
549+
relation=harness.model.get_relation('dbcluster', relation_id),
550+
departing_unit=harness.charm.unit,
551+
)
552+
harness.charm._on_dbcluster_relation_departed(event)
553+
554+
app_data = harness.get_relation_data(relation_id, 'juju-controller')
555+
self.assertEqual(json.loads(app_data['db-bind-addresses']), expected)
556+
mock_reload_config.assert_not_called()
557+
558+
@patch("builtins.open", new_callable=mock_open, read_data=agent_conf)
559+
@patch("configchangesocket.ConfigChangeSocketClient.get_controller_agent_id")
560+
@patch("ops.model.Model.get_binding")
561+
@patch("configchangesocket.ConfigChangeSocketClient.reload_config")
562+
def test_dbcluster_leader_elected_reconciles_bind_addresses(
563+
self, mock_reload_config, mock_get_binding, mock_get_agent_id, *__):
564+
harness = self.harness
565+
mock_get_binding.return_value = mockBinding(['192.168.1.17'])
566+
mock_get_agent_id.return_value = '1'
567+
568+
relation_id = harness.add_relation('dbcluster', harness.charm.app.name)
569+
harness.add_relation_unit(relation_id, 'juju-controller/2')
570+
harness.update_relation_data(
571+
relation_id, 'juju-controller/2', {
572+
'db-bind-address': '192.168.1.100',
573+
'agent-id': '2',
574+
})
575+
stale = {
576+
'0': '192.168.1.16',
577+
'1': '192.168.1.17',
578+
'2': '192.168.1.100',
579+
}
580+
harness.update_relation_data(
581+
relation_id,
582+
harness.charm.app.name,
583+
{'db-bind-addresses': json.dumps(stale)},
584+
)
585+
586+
app_data = harness.get_relation_data(relation_id, 'juju-controller')
587+
self.assertEqual(json.loads(app_data['db-bind-addresses']), stale)
588+
589+
mock_reload_config.reset_mock()
590+
harness.set_leader()
591+
592+
app_data = harness.get_relation_data(relation_id, 'juju-controller')
593+
expected = {'1': '192.168.1.17', '2': '192.168.1.100'}
594+
self.assertEqual(json.loads(app_data['db-bind-addresses']), expected)
595+
mock_reload_config.assert_called_once()
596+
524597

525598
class mockNetwork:
526599
def __init__(self, addresses):

0 commit comments

Comments
 (0)