diff --git a/lib/charms/mongodb/v0/helpers.py b/lib/charms/mongodb/v0/helpers.py index 1dc877da7..ba33c09d9 100644 --- a/lib/charms/mongodb/v0/helpers.py +++ b/lib/charms/mongodb/v0/helpers.py @@ -1,9 +1,9 @@ """Simple functions, which can be used in both K8s and VM charms.""" # Copyright 2023 Canonical Ltd. # See LICENSE file for licensing details. +import json import logging import os -import re import secrets import string import subprocess @@ -27,7 +27,7 @@ # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 7 +LIBPATCH = 8 # path to store mongodb ketFile @@ -223,43 +223,26 @@ def process_pbm_error(error_string: Optional[_StrOrBytes]) -> str: def current_pbm_op(pbm_status: str) -> str: """Parses pbm status for the operation that pbm is running.""" - pbm_status_lines = pbm_status.splitlines() - for i in range(0, len(pbm_status_lines)): - line = pbm_status_lines[i] - - # operation is two lines after the line "Currently running:" - if line == "Currently running:": - return pbm_status_lines[i + 2] - - return "" + pbm_status = json.loads(pbm_status) + return pbm_status["running"] if "running" in pbm_status else "" def process_pbm_status(pbm_status: str) -> StatusBase: """Parses current pbm operation and returns unit status.""" - if type(pbm_status) == bytes: - pbm_status = pbm_status.decode("utf-8") - - # pbm is running resync operation - if "Resync" in current_pbm_op(pbm_status): - return WaitingStatus("waiting to sync s3 configurations.") - + current_op = current_pbm_op(pbm_status) # no operations are currently running with pbm - if "(none)" in current_pbm_op(pbm_status): + if current_op == {}: return ActiveStatus("") - # Example of backup id: 2023-08-21T13:08:22Z - backup_match = re.search( - r'Snapshot backup "(?P\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)"', pbm_status - ) - if backup_match: - backup_id = backup_match.group("backup_id") + if current_op["type"] == "backup": + backup_id = current_op["name"] return MaintenanceStatus(f"backup started/running, backup id:'{backup_id}'") - restore_match = re.search( - r'Snapshot restore "(?P\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)"', pbm_status - ) - if restore_match: - backup_id = restore_match.group("backup_id") + if current_op["type"] == "restore": + backup_id = current_op["name"] return MaintenanceStatus(f"restore started/running, backup id:'{backup_id}'") + if current_op["type"] == "resync": + return WaitingStatus("waiting to sync s3 configurations.") + return ActiveStatus() diff --git a/lib/charms/mongodb/v0/mongodb_backups.py b/lib/charms/mongodb/v0/mongodb_backups.py index 0b5e85989..6f705c4ba 100644 --- a/lib/charms/mongodb/v0/mongodb_backups.py +++ b/lib/charms/mongodb/v0/mongodb_backups.py @@ -23,13 +23,7 @@ ) from charms.operator_libs_linux.v1 import snap from ops.framework import Object -from ops.model import ( - BlockedStatus, - MaintenanceStatus, - ModelError, - StatusBase, - WaitingStatus, -) +from ops.model import BlockedStatus, MaintenanceStatus, StatusBase, WaitingStatus from ops.pebble import ExecError from tenacity import ( Retrying, @@ -48,7 +42,7 @@ # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 1 +LIBPATCH = 2 logger = logging.getLogger(__name__) @@ -110,11 +104,10 @@ def _restore_retry_stop_condition(retry_state) -> bool: class MongoDBBackups(Object): """Manages MongoDB backups.""" - def __init__(self, charm, substrate): + def __init__(self, charm): """Manager of MongoDB client relations.""" super().__init__(charm, "client-relations") self.charm = charm - self.substrate = substrate # s3 relation handles the config options for s3 backups self.s3_client = S3Requirer(self.charm, S3_RELATION) @@ -136,10 +129,7 @@ def _on_s3_credential_changed(self, event: CredentialsChangedEvent): ) return - try: - # TODO VM charm should implement this method - self.charm.get_backup_service() - except ModelError: + if not self.charm.has_backup_service(): self._defer_action_with_info_log( event, action, "Set PBM configurations, pbm-agent service not found." ) @@ -335,14 +325,14 @@ def _configure_pbm_options(self, event) -> None: def _set_config_options(self): """Applying given configurations with pbm.""" - # TODO VM charm should implement this method - self.charm.set_pbm_config_file() + # clearing out configurations options before resetting them leads to a quicker reysnc + # process + self.charm.clear_pbm_config_file() # the pbm tool can only set one configuration at a time. for pbm_key, pbm_value in self._get_pbm_configs().items(): try: config_cmd = ["config", "--set", f"{pbm_key}={pbm_value}"] - # TODO VM charm should implement this method self.charm.run_pbm_command(config_cmd) except (subprocess.CalledProcessError, ExecError): logger.error( @@ -364,7 +354,6 @@ def _get_pbm_configs(self) -> Dict: def _resync_config_options(self): """Attempts to sync pbm config options and sets status in case of failure.""" - # TODO VM charm should implement this method self.charm.start_backup_service() # pbm has a flakely resync and it is necessary to wait for no actions to be running before @@ -382,7 +371,6 @@ def _resync_config_options(self): # if a resync is running restart the service if isinstance(pbm_status, (WaitingStatus)): - # TODO VM charm should implement this method self.charm.restart_backup_service() raise PBMBusyError @@ -420,7 +408,6 @@ def _wait_pbm_status(self) -> None: ): with attempt: try: - # TODO VM charm should implement this method pbm_status = self.charm.run_pbm_command(PBM_STATUS_CMD) if "Resync" in current_pbm_op(pbm_status): @@ -435,10 +422,7 @@ def _wait_pbm_status(self) -> None: def _get_pbm_status(self) -> StatusBase: """Retrieve pbm status.""" - try: - # TODO VM charm should implement this method - self.charm.get_backup_service() - except ModelError: + if not self.charm.has_backup_service(): return WaitingStatus("waiting for pbm to start") try: @@ -534,7 +518,10 @@ def _try_to_restore(self, backup_id: str) -> None: with attempt: try: remapping_args = self._remap_replicaset(backup_id) - self.charm.run_pbm_restore_command(backup_id, remapping_args) + restore_cmd = ["restore", backup_id] + if remapping_args: + restore_cmd = restore_cmd + remapping_args.split(" ") + self.charm.run_pbm_command(restore_cmd) except (subprocess.CalledProcessError, ExecError) as e: if type(e) == subprocess.CalledProcessError: error_message = e.output.decode("utf-8") diff --git a/src/charm.py b/src/charm.py index 26e8310b8..7a322da31 100755 --- a/src/charm.py +++ b/src/charm.py @@ -48,6 +48,7 @@ BlockedStatus, Container, MaintenanceStatus, + ModelError, Relation, RelationDataContent, SecretNotFoundError, @@ -110,7 +111,7 @@ def __init__(self, *args): self.client_relations = MongoDBProvider(self) self.tls = MongoDBTLS(self, Config.Relations.PEERS, Config.SUBSTRATE) - self.backups = MongoDBBackups(self, substrate=Config.SUBSTRATE) + self.backups = MongoDBBackups(self) self.metrics_endpoint = MetricsEndpointProvider( self, refresh_event=self.on.start, jobs=Config.Monitoring.JOBS @@ -1109,10 +1110,15 @@ def _connect_pbm_agent(self) -> None: container.add_layer(Config.Backup.SERVICE_NAME, self._backup_layer, combine=True) container.replan() - def get_backup_service(self) -> ServiceInfo: + def has_backup_service(self) -> ServiceInfo: """Returns the backup service.""" container = self.unit.get_container(Config.CONTAINER_NAME) - return container.get_service(Config.Backup.SERVICE_NAME) + try: + container.get_service(Config.Backup.SERVICE_NAME) + except ModelError: + return False + + return True def is_unit_in_replica_set(self) -> bool: """Check if the unit is in the replica set.""" @@ -1134,15 +1140,8 @@ def run_pbm_command(self, cmd: List[str]) -> str: stdout, _ = process.wait_output() return stdout - def run_pbm_restore_command(self, backup_id: str, remapping_args: str) -> str: - """Executes a restore command in the workload container.""" - restore_cmd = ["restore", backup_id] - if remapping_args: - restore_cmd = restore_cmd + remapping_args.split(" ") - return self.run_pbm_command(restore_cmd) - - def set_pbm_config_file(self) -> None: - """Sets the pbm config file.""" + def clear_pbm_config_file(self) -> None: + """Overwrites existing config file with an empty file.""" container = self.unit.get_container(Config.CONTAINER_NAME) container.push( Config.Backup.PBM_CONFIG_FILE_PATH, diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 77df3db13..895b066cf 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -746,18 +746,18 @@ def test_connect_to_mongo_exporter_on_set_password(self, connect_exporter, conne connect_exporter.assert_called() @patch("charm.MongoDBBackups._get_pbm_status") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBConnection") @patch("charm.MongoDBCharm._connect_mongodb_exporter") def test_event_set_password_secrets( - self, connect_exporter, connection, get_backup_service, get_pbm_status + self, connect_exporter, connection, has_backup_service, get_pbm_status ): """Test _connect_mongodb_exporter is called when the password is set for 'montior' user. Furthermore: in Juju 3.x we want to use secrets """ pw = "bla" - get_backup_service.return_value = "pbm" + has_backup_service.return_value = True get_pbm_status.return_value = ActiveStatus() self.harness.set_leader(True) @@ -778,17 +778,17 @@ def test_event_set_password_secrets( assert args_pw["password"] == pw @patch("charm.MongoDBBackups._get_pbm_status") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBConnection") @patch("charm.MongoDBCharm._connect_mongodb_exporter") def test_event_auto_reset_password_secrets_when_no_pw_value_shipped( - self, connect_exporter, connection, get_backup_service, get_pbm_status + self, connect_exporter, connection, has_backup_service, get_pbm_status ): """Test _connect_mongodb_exporter is called when the password is set for 'montior' user. Furthermore: in Juju 3.x we want to use secrets """ - get_backup_service.return_value = "pbm" + has_backup_service.return_value = True get_pbm_status.return_value = ActiveStatus() self._setup_secrets() self.harness.set_leader(True) @@ -927,14 +927,14 @@ def test_set_password_provided(self, connection): self.assertEqual("canonical123", new_password) @patch_network_get(private_address="1.1.1.1") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBBackups._get_pbm_status") - def test_set_backup_password_pbm_busy(self, pbm_status, get_backup_service): + def test_set_backup_password_pbm_busy(self, pbm_status, has_backup_service): """Tests changes to passwords fail when pbm is restoring/backing up.""" self.harness.set_leader(True) original_password = "pass123" action_event = mock.Mock() - get_backup_service.return_value = "pbm" + has_backup_service.return_value = True for username in ["backup", "monitor", "operator"]: self.harness.charm.app_peer_data[f"{username}-password"] = original_password diff --git a/tests/unit/test_mongodb_backups.py b/tests/unit/test_mongodb_backups.py index d099dd193..fbddcb632 100644 --- a/tests/unit/test_mongodb_backups.py +++ b/tests/unit/test_mongodb_backups.py @@ -40,7 +40,7 @@ def setUp(self): self.charm = self.harness.charm self.addCleanup(self.harness.cleanup) - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_get_pbm_status_snap_not_present(self, pbm_command, service): """Tests that when the snap is not present pbm is in blocked state.""" @@ -49,27 +49,29 @@ def test_get_pbm_status_snap_not_present(self, pbm_command, service): pbm_command.side_effect = ModelError("service pbm-agent not found") self.assertTrue(isinstance(self.harness.charm.backups._get_pbm_status(), BlockedStatus)) - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_get_pbm_status_resync(self, pbm_command, service): """Tests that when pbm is resyncing that pbm is in waiting state.""" container = self.harness.model.unit.get_container("mongod") self.harness.set_can_connect(container, True) service.return_value = "pbm" - pbm_command.return_value = "Currently running:\n====\nResync op" + pbm_command.return_value = ( + '{"running":{"type":"resync","opID":"64f5cc22a73b330c3880e3b2"}}' + ) self.assertTrue(isinstance(self.harness.charm.backups._get_pbm_status(), WaitingStatus)) - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_get_pbm_status_running(self, pbm_command, service): """Tests that when pbm not running an op that pbm is in active state.""" container = self.harness.model.unit.get_container("mongod") self.harness.set_can_connect(container, True) service.return_value = "pbm" - pbm_command.return_value = b"Currently running:\n====\n(none)" + pbm_command.return_value = '{"running":{}}' self.assertTrue(isinstance(self.harness.charm.backups._get_pbm_status(), ActiveStatus)) - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_get_pbm_status_incorrect_cred(self, pbm_command, service): """Tests that when pbm has incorrect credentials that pbm is in blocked state.""" @@ -81,7 +83,7 @@ def test_get_pbm_status_incorrect_cred(self, pbm_command, service): ) self.assertTrue(isinstance(self.harness.charm.backups._get_pbm_status(), BlockedStatus)) - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_get_pbm_status_incorrect_conf(self, pbm_command, service): """Tests that when pbm has incorrect configs that pbm is in blocked state.""" @@ -96,7 +98,7 @@ def test_get_pbm_status_incorrect_conf(self, pbm_command, service): @patch("charms.mongodb.v0.mongodb_backups.wait_fixed") @patch("charms.mongodb.v0.mongodb_backups.stop_after_attempt") @patch("ops.model.Container.start") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_verify_resync_config_error(self, pbm_command, service, start, retry_wait, retry_stop): """Tests that when pbm cannot perform the resync command it raises an error.""" @@ -116,7 +118,7 @@ def test_verify_resync_config_error(self, pbm_command, service, start, retry_wai @patch("charms.mongodb.v0.mongodb_backups.wait_fixed") @patch("charms.mongodb.v0.mongodb_backups.stop_after_attempt") @patch("ops.model.Container.start") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_verify_resync_cred_error(self, pbm_command, service, start, retry_wait, retry_stop): """Tests that when pbm cannot resync due to creds that it raises an error.""" @@ -135,7 +137,7 @@ def test_verify_resync_cred_error(self, pbm_command, service, start, retry_wait, @patch("ops.model.Container.restart") @patch("ops.model.Container.start") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") @patch("charm.MongoDBBackups._get_pbm_status") def test_verify_resync_syncing(self, pbm_status, run_pbm_command, service, start, restart): @@ -144,21 +146,23 @@ def test_verify_resync_syncing(self, pbm_status, run_pbm_command, service, start self.harness.set_can_connect(container, True) service.return_value = "pbm" - pbm_status.return_value = "Currently running:\n====\nResync op" - run_pbm_command.return_value = "Currently running:\n====\nResync op" + pbm_status.return_value = MaintenanceStatus() + run_pbm_command.return_value = ( + '{"running":{"type":"resync","opID":"64f5cc22a73b330c3880e3b2"}}' + ) # disable retry self.harness.charm.backups._wait_pbm_status.retry.retry = tenacity.retry_if_not_result( lambda x: True ) - with self.assertRaises(ResyncError): + with self.assertRaises(PBMBusyError): self.harness.charm.backups._resync_config_options() @patch("ops.model.Container.start") @patch("charms.mongodb.v0.mongodb_backups.wait_fixed") @patch("charms.mongodb.v0.mongodb_backups.stop_after_attempt") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBBackups._get_pbm_status") def test_resync_config_options_failure( self, pbm_status, service, retry_stop, retry_wait, start @@ -177,7 +181,7 @@ def test_resync_config_options_failure( @patch("charms.mongodb.v0.mongodb_backups.stop_after_attempt") @patch("ops.model.Container.restart") @patch("ops.model.Container.start") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBBackups._get_pbm_status") def test_resync_config_restart( self, pbm_status, service, start, restart, retry_stop, retry_wait @@ -244,7 +248,7 @@ def test_s3_credentials_no_db(self, defer): defer.assert_called() @patch_network_get(private_address="1.1.1.1") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBBackups._set_config_options") def test_s3_credentials_set_pbm_failure(self, _set_config_options, service): """Test charm goes into blocked state when setting pbm configs fail.""" @@ -273,7 +277,7 @@ def test_s3_credentials_set_pbm_failure(self, _set_config_options, service): @patch("charm.MongoDBBackups._set_config_options") @patch("charm.MongoDBBackups._resync_config_options") @patch("ops.framework.EventBase.defer") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBBackups._get_pbm_status") def test_s3_credentials_config_error( self, pbm_status, service, defer, resync, _set_config_options @@ -303,7 +307,7 @@ def test_s3_credentials_config_error( @patch("charm.MongoDBBackups._set_config_options") @patch("charm.MongoDBBackups._resync_config_options") @patch("ops.framework.EventBase.defer") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBBackups._get_pbm_status") def test_s3_credentials_syncing(self, pbm_status, service, defer, resync, _set_config_options): """Test charm defers when more time is needed to sync pbm credentials.""" @@ -332,7 +336,7 @@ def test_s3_credentials_syncing(self, pbm_status, service, defer, resync, _set_c @patch("charm.MongoDBBackups._set_config_options") @patch("charm.MongoDBBackups._resync_config_options") @patch("ops.framework.EventBase.defer") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBBackups._get_pbm_status") def test_s3_credentials_pbm_busy( self, pbm_status, service, defer, resync, _set_config_options @@ -364,7 +368,7 @@ def test_s3_credentials_pbm_busy( @patch("charm.MongoDBBackups._set_config_options") @patch("charm.MongoDBBackups._resync_config_options") @patch("ops.framework.EventBase.defer") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_s3_credentials_pbm_error( self, pbm_command, service, defer, resync, _set_config_options @@ -396,7 +400,7 @@ def test_s3_credentials_pbm_error( defer.assert_not_called() self.assertTrue(isinstance(self.harness.charm.unit.status, BlockedStatus)) - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") @patch("charm.MongoDBBackups._get_pbm_status") def test_backup_failed(self, pbm_status, pbm_command, service): @@ -426,7 +430,7 @@ def test_backup_list_without_rel(self): self.harness.charm.backups._on_list_backups_action(action_event) action_event.fail.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_backup_list_syncing(self, pbm_command, service): """Verifies backup list is deferred if more time is needed to resync.""" @@ -437,14 +441,16 @@ def test_backup_list_syncing(self, pbm_command, service): action_event = mock.Mock() action_event.params = {} - pbm_command.return_value = "Currently running:\n====\nResync op" + pbm_command.return_value = ( + '{"running":{"type":"resync","opID":"64f5cc22a73b330c3880e3b2"}}' + ) self.harness.add_relation(RELATION_NAME, "s3-integrator") self.harness.charm.backups._on_list_backups_action(action_event) action_event.defer.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_backup_list_wrong_cred(self, pbm_command, service): """Verifies backup list fails with wrong credentials.""" @@ -462,7 +468,7 @@ def test_backup_list_wrong_cred(self, pbm_command, service): self.harness.charm.backups._on_list_backups_action(action_event) action_event.fail.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") @patch("charm.MongoDBBackups._get_pbm_status") def test_backup_list_failed(self, pbm_status, pbm_command, service): @@ -544,21 +550,23 @@ def test_restore_without_rel(self): self.harness.charm.backups._on_restore_action(action_event) action_event.fail.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_restore_syncing(self, pbm_command, service): """Verifies restore is deferred if more time is needed to resync.""" action_event = mock.Mock() action_event.params = {"backup-id": "back-me-up"} service.return_value = "pbm" - pbm_command.return_value = "Currently running:\n====\nResync op" + pbm_command.return_value = ( + '{"running":{"type":"resync","opID":"64f5cc22a73b330c3880e3b2"}}' + ) self.harness.add_relation(RELATION_NAME, "s3-integrator") self.harness.charm.backups._on_restore_action(action_event) action_event.defer.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_restore_running_backup(self, pbm_command, service): """Verifies restore is fails if another backup is already running.""" @@ -573,7 +581,7 @@ def test_restore_running_backup(self, pbm_command, service): action_event.fail.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") @patch("charm.MongoDBBackups._get_pbm_status") def test_restore_wrong_cred(self, pbm_status, pbm_command, service): @@ -592,7 +600,7 @@ def test_restore_wrong_cred(self, pbm_status, pbm_command, service): self.harness.charm.backups._on_restore_action(action_event) action_event.fail.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") @patch("charm.MongoDBBackups._get_pbm_status") def test_restore_failed(self, pbm_status, pbm_command, service): @@ -657,41 +665,41 @@ def test_remap_replicaset_remap_necessary(self, run_pbm_command): remap = self.harness.charm.backups._remap_replicaset("2002-02-14T13:59:14Z") self.assertEqual(remap, "--replset-remapping current-app-name=old-cluster-name") - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_get_pbm_status_backup(self, run_pbm_command, service): """Tests that when pbm running a backup that pbm is in maintenance state.""" service.return_value = "pbm" - run_pbm_command.return_value = ( - 'Currently running:\n====\nSnapshot backup "2023-08-21T13:08:22Z"' - ) + run_pbm_command.return_value = '{"running":{"type":"backup","name":"2023-09-04T12:15:58Z","startTS":1693829759,"status":"oplog backup","opID":"64f5ca7e777e294530289465"}}' self.assertTrue( isinstance(self.harness.charm.backups._get_pbm_status(), MaintenanceStatus) ) def test_current_pbm_op(self): """Test if _current_pbm_op can identify the operation pbm is running.""" - action = current_pbm_op("nothing\nCurrently running:\n====\nexpected action") - self.assertEqual(action, "expected action") + action = current_pbm_op('{"running":{"type":"my-action"}}') + self.assertEqual(action, {"type": "my-action"}) - no_action = current_pbm_op("pbm not started") - self.assertEqual(no_action, "") + no_action = current_pbm_op('{"running":{}}') + self.assertEqual(no_action, {}) - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_backup_syncing(self, run_pbm_command, service): """Verifies backup is deferred if more time is needed to resync.""" action_event = mock.Mock() action_event.params = {} service.return_value = "pbm" - run_pbm_command.return_value = "Currently running:\n====\nResync op" + run_pbm_command.return_value = ( + '{"running":{"type":"resync","opID":"64f5cc22a73b330c3880e3b2"}}' + ) self.harness.add_relation(RELATION_NAME, "s3-integrator") self.harness.charm.backups._on_create_backup_action(action_event) action_event.defer.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_backup_running_backup(self, run_pbm_command, service): """Verifies backup is fails if another backup is already running.""" @@ -706,7 +714,7 @@ def test_backup_running_backup(self, run_pbm_command, service): self.harness.charm.backups._on_create_backup_action(action_event) action_event.fail.assert_called() - @patch("charm.MongoDBCharm.get_backup_service") + @patch("charm.MongoDBCharm.has_backup_service") @patch("charm.MongoDBCharm.run_pbm_command") def test_backup_wrong_cred(self, run_pbm_command, service): """Verifies backup is fails if the credentials are incorrect."""