Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DPE-4345] Disable pgBackRest service initialisation #530

Merged
merged 15 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,10 +1091,18 @@ def _on_start(self, event: StartEvent) -> None:
# On replicas, only prepare for starting the instance later.
if not self.unit.is_leader():
self._start_replica(event)
self._restart_services_after_reboot()
return

# Bootstrap the cluster in the leader unit.
self._start_primary(event)
self._restart_services_after_reboot()

def _restart_services_after_reboot(self):
"""Restart the Patroni and pgBackRest after a reboot."""
if self._unit_ip in self.members_ips:
self._patroni.start_patroni()
self.backup.start_stop_pgbackrest_service()

def _setup_exporter(self) -> None:
"""Set up postgresql_exporter options."""
Expand Down
5 changes: 4 additions & 1 deletion src/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@
SNAP_PACKAGES = [
(
POSTGRESQL_SNAP_NAME,
{"revision": {"aarch64": "114", "x86_64": "115"}, "channel": "14/stable"},
{
"revision": {"aarch64": "121", "x86_64": "120"},
"channel": "14/stable",
},
)
]

Expand Down
2 changes: 1 addition & 1 deletion src/dependency.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"dependencies": {},
"name": "charmed-postgresql",
"upgrade_supported": "^14",
"version": "14.11"
"version": "14.12"
}
}
139 changes: 95 additions & 44 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright 2021 Canonical Ltd.
# See LICENSE file for licensing details.
import itertools
import json
import logging
import platform
import subprocess
Expand Down Expand Up @@ -558,6 +559,9 @@ def test_enable_disable_extensions(harness, caplog):
@patch_network_get(private_address="1.1.1.1")
def test_on_start(harness):
with (
patch(
"charm.PostgresqlOperatorCharm._restart_services_after_reboot"
) as _restart_services_after_reboot,
patch(
"charm.PostgresqlOperatorCharm._set_primary_status_message"
) as _set_primary_status_message,
Expand Down Expand Up @@ -617,33 +621,41 @@ def test_on_start(harness):
harness.charm.on.start.emit()
_bootstrap_cluster.assert_called_once()
_oversee_users.assert_not_called()
_restart_services_after_reboot.assert_called_once()
assert isinstance(harness.model.unit.status, BlockedStatus)
# Set an initial waiting status (like after the install hook was triggered).
harness.model.unit.status = WaitingStatus("fake message")

# Test the event of an error happening when trying to create the default postgres user.
_restart_services_after_reboot.reset_mock()
_member_started.return_value = True
harness.charm.on.start.emit()
_postgresql.create_user.assert_called_once()
_oversee_users.assert_not_called()
_restart_services_after_reboot.assert_called_once()
assert isinstance(harness.model.unit.status, BlockedStatus)

# Set an initial waiting status again (like after the install hook was triggered).
harness.model.unit.status = WaitingStatus("fake message")

# Then test the event of a correct cluster bootstrapping.
_restart_services_after_reboot.reset_mock()
harness.charm.on.start.emit()
assert _postgresql.create_user.call_count == 4 # Considering the previous failed call.
_oversee_users.assert_called_once()
_enable_disable_extensions.assert_called_once()
_set_primary_status_message.assert_called_once()
_restart_services_after_reboot.assert_called_once()


@patch_network_get(private_address="1.1.1.1")
def test_on_start_replica(harness):
with (
patch("charm.snap.SnapCache") as _snap_cache,
patch("charm.Patroni.get_postgresql_version") as _get_postgresql_version,
patch(
"charm.PostgresqlOperatorCharm._restart_services_after_reboot"
) as _restart_services_after_reboot,
patch("charm.Patroni.configure_patroni_on_unit") as _configure_patroni_on_unit,
patch(
"charm.Patroni.member_started",
Expand Down Expand Up @@ -674,25 +686,30 @@ def test_on_start_replica(harness):
harness.charm._peers.data[harness.charm.app].update({"cluster_initialised": ""})
harness.charm.on.start.emit()
_defer.assert_called_once()
_restart_services_after_reboot.assert_called_once()

# Set an initial waiting status again (like after a machine restart).
harness.model.unit.status = WaitingStatus("fake message")

# Mark the cluster as initialised and with the workload up and running.
_restart_services_after_reboot.reset_mock()
harness.charm._peers.data[harness.charm.app].update({"cluster_initialised": "True"})
_member_started.return_value = True
harness.charm.on.start.emit()
_configure_patroni_on_unit.assert_not_called()
_restart_services_after_reboot.assert_called_once()
assert isinstance(harness.model.unit.status, ActiveStatus)

# Set an initial waiting status (like after the install hook was triggered).
harness.model.unit.status = WaitingStatus("fake message")

# Check that the unit status doesn't change when the workload is not running.
# In that situation only Patroni is configured in the unit (but not started).
_restart_services_after_reboot.reset_mock()
_member_started.return_value = False
harness.charm.on.start.emit()
_configure_patroni_on_unit.assert_called_once()
_restart_services_after_reboot.assert_called_once()
assert isinstance(harness.model.unit.status, WaitingStatus)


Expand Down Expand Up @@ -2447,70 +2464,104 @@ def test_set_primary_status_message(harness, is_leader):
harness.charm._set_primary_status_message()
tc.assertIsInstance(harness.charm.unit.status, MaintenanceStatus)

@patch("charm.Patroni.update_patroni_restart_condition")
@patch("charm.Patroni.get_patroni_restart_condition")
@patch("charm.PostgresqlOperatorCharm._unit_ip")
def test_override_patroni_restart_condition(
self, _unit_ip, get_restart_condition, update_restart_condition

def test_override_patroni_restart_condition(harness):
with (
patch("charm.Patroni.update_patroni_restart_condition") as _update_restart_condition,
patch("charm.Patroni.get_patroni_restart_condition") as _get_restart_condition,
patch("charm.PostgresqlOperatorCharm._unit_ip") as _unit_ip,
):
get_restart_condition.return_value = "always"
_get_restart_condition.return_value = "always"

# Do override without repeat_cause
assert self.charm.override_patroni_restart_condition("no") is True
get_restart_condition.assert_called_once()
update_restart_condition.assert_called_once_with("no")
get_restart_condition.reset_mock()
update_restart_condition.reset_mock()
assert harness.charm.override_patroni_restart_condition("no", None) is True
_get_restart_condition.assert_called_once()
_update_restart_condition.assert_called_once_with("no")
_get_restart_condition.reset_mock()
_update_restart_condition.reset_mock()

get_restart_condition.return_value = "no"
_get_restart_condition.return_value = "no"

# Must not be overridden twice without repeat_cause
assert self.charm.override_patroni_restart_condition("on-failure") is False
get_restart_condition.assert_called_once()
update_restart_condition.assert_not_called()
get_restart_condition.reset_mock()
update_restart_condition.reset_mock()
assert harness.charm.override_patroni_restart_condition("on-failure", None) is False
_get_restart_condition.assert_called_once()
_update_restart_condition.assert_not_called()
_get_restart_condition.reset_mock()
_update_restart_condition.reset_mock()

# Reset override
self.charm.restore_patroni_restart_condition()
update_restart_condition.assert_called_once_with("always")
update_restart_condition.reset_mock()
harness.charm.restore_patroni_restart_condition()
_update_restart_condition.assert_called_once_with("always")
_update_restart_condition.reset_mock()

# Must not be reset twice
self.charm.restore_patroni_restart_condition()
update_restart_condition.assert_not_called()
update_restart_condition.reset_mock()
harness.charm.restore_patroni_restart_condition()
_update_restart_condition.assert_not_called()
_update_restart_condition.reset_mock()

get_restart_condition.return_value = "always"
_get_restart_condition.return_value = "always"

# Do override with repeat_cause
assert self.charm.override_patroni_restart_condition("no", "test_charm") is True
get_restart_condition.assert_called_once()
update_restart_condition.assert_called_once_with("no")
get_restart_condition.reset_mock()
update_restart_condition.reset_mock()
assert harness.charm.override_patroni_restart_condition("no", "test_charm") is True
_get_restart_condition.assert_called_once()
_update_restart_condition.assert_called_once_with("no")
_get_restart_condition.reset_mock()
_update_restart_condition.reset_mock()

get_restart_condition.return_value = "no"
_get_restart_condition.return_value = "no"

# Do re-override with repeat_cause
assert self.charm.override_patroni_restart_condition("on-success", "test_charm") is True
get_restart_condition.assert_called_once()
update_restart_condition.assert_called_once_with("on-success")
get_restart_condition.reset_mock()
update_restart_condition.reset_mock()
assert harness.charm.override_patroni_restart_condition("on-success", "test_charm") is True
_get_restart_condition.assert_called_once()
_update_restart_condition.assert_called_once_with("on-success")
_get_restart_condition.reset_mock()
_update_restart_condition.reset_mock()

get_restart_condition.return_value = "on-success"
_get_restart_condition.return_value = "on-success"

# Must not be re-overridden with different repeat_cause
assert (
self.charm.override_patroni_restart_condition("on-failure", "test_not_charm") is False
harness.charm.override_patroni_restart_condition("on-failure", "test_not_charm")
is False
)
get_restart_condition.assert_called_once()
update_restart_condition.assert_not_called()
get_restart_condition.reset_mock()
update_restart_condition.reset_mock()
_get_restart_condition.assert_called_once()
_update_restart_condition.assert_not_called()
_get_restart_condition.reset_mock()
_update_restart_condition.reset_mock()

# Reset override
self.charm.restore_patroni_restart_condition()
update_restart_condition.assert_called_once_with("always")
update_restart_condition.reset_mock()
harness.charm.restore_patroni_restart_condition()
_update_restart_condition.assert_called_once_with("always")
_update_restart_condition.reset_mock()


def test_restart_services_after_reboot(harness):
with (
patch(
"backups.PostgreSQLBackups.start_stop_pgbackrest_service"
) as _start_stop_pgbackrest_service,
patch("charm.Patroni.start_patroni") as _start_patroni,
patch(
"charm.PostgresqlOperatorCharm._unit_ip",
new_callable=PropertyMock(return_value="1.1.1.1"),
) as _unit_ip,
):
with harness.hooks_disabled():
harness.update_relation_data(
harness.model.get_relation(PEER).id,
harness.charm.app.name,
{"members_ips": json.dumps([])},
)
harness.charm._restart_services_after_reboot()
_start_patroni.assert_not_called()
_start_stop_pgbackrest_service.assert_not_called()

with harness.hooks_disabled():
harness.update_relation_data(
harness.model.get_relation(PEER).id,
harness.charm.app.name,
{"members_ips": json.dumps([_unit_ip])},
)
harness.charm._restart_services_after_reboot()
_start_patroni.assert_called_once()
_start_stop_pgbackrest_service.assert_called_once()
Loading