Skip to content

Commit

Permalink
[DPE-1956] Add a backup user
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitry-ratushnyy committed Jun 27, 2023
1 parent 49ae5e8 commit 02a3ffc
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 2 deletions.
41 changes: 39 additions & 2 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@
)
from charms.mongodb.v0.mongodb_provider import MongoDBProvider
from charms.mongodb.v0.mongodb_tls import MongoDBTLS
from charms.mongodb.v0.users import CHARM_USERS, MongoDBUser, MonitorUser, OperatorUser
from charms.mongodb.v0.users import (
CHARM_USERS,
BackupUser,
MongoDBUser,
MonitorUser,
OperatorUser,
)
from charms.prometheus_k8s.v0.prometheus_scrape import MetricsEndpointProvider
from ops.charm import (
ActionEvent,
Expand Down Expand Up @@ -134,6 +140,12 @@ def monitor_config(self) -> MongoDBConfiguration:
MonitorUser, [self.get_hostname_for_unit(self.unit)]
)

@property
def backup_config(self) -> MongoDBConfiguration:
"""Generates a MongoDBConfiguration object for backup."""
self._check_or_set_user_password(BackupUser)
return self._get_mongodb_config_for_user(BackupUser, BackupUser.get_hosts())

@property
def _monitor_layer(self) -> Layer:
"""Returns a Pebble configuration layer for mongodb_exporter."""
Expand Down Expand Up @@ -261,7 +273,7 @@ def _on_start(self, event) -> None:
event.defer()
return

if not container.exists(Config.SOCKET_PATH):
if not self._socket_exists(container):
logger.debug("The mongod socket is not ready yet.")
event.defer()
return
Expand Down Expand Up @@ -475,6 +487,27 @@ def _init_monitor_user(self):

self._connect_mongodb_exporter()

@retry(
stop=stop_after_attempt(3),
wait=wait_fixed(5),
reraise=True,
before=before_log(logger, logging.DEBUG),
)
def _init_backup_user(self):
"""Creates the backup user on the MongoDB database."""
if self._is_user_created(BackupUser):
return

with MongoDBConnection(self.mongodb_config) as mongo:
# first we must create the necessary roles for the PBM tool
logger.debug("creating the backup user roles...")
mongo.create_role(
role_name=BackupUser.get_mongodb_role(), privileges=BackupUser.get_privileges()
)
logger.debug("creating the backup user...")
mongo.create_user(self.backup_config)
self._set_user_created(BackupUser)

# END: user management

# BEGIN: helper functions
Expand Down Expand Up @@ -571,6 +604,7 @@ def _initialise_replica_set(self, event: StartEvent) -> None:
direct_mongo.init_replset()
logger.info("User initialization")
self._init_operator_user()
self._init_backup_user()
self._init_monitor_user()
logger.info("Reconcile relations")
self.client_relations.oversee_users(None, event)
Expand Down Expand Up @@ -745,6 +779,9 @@ def _connect_mongodb_exporter(self) -> None:
# Restart changed services and start startup-enabled services.
container.replan()

def _socket_exists(self, container) -> bool:
return container.exists(Config.SOCKET_PATH)

# END: helper functions

# BEGIN: static methods
Expand Down
42 changes: 42 additions & 0 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,3 +678,45 @@ def test__connect_mongodb_exporter_success(
)
expected_uri = uri_template.format(password="mongo123")
self.assertEqual(expected_uri, new_uri)

@patch("charm.MongoDBCharm._init_operator_user")
@patch("charm.MongoDBCharm._init_monitor_user")
@patch("charm.MongoDBCharm._connect_mongodb_exporter")
@patch("charm.MongoDBCharm._socket_exists")
@patch("charm.MongoDBCharm._pull_licenses")
@patch("ops.framework.EventBase.defer")
@patch("charm.MongoDBCharm._set_data_dir_permissions")
@patch("charm.MongoDBConnection")
def test__backup_user_created(
self,
connection,
fix_data_dir,
defer,
pull_licenses,
_socket_exists,
_connect_mongodb_exporter,
_init_operator_user,
_init_monitor_user,
):
"""Tests what backup user was created."""
container = self.harness.model.unit.get_container("mongod")
self.harness.set_can_connect(container, True)
self.harness.charm.on.start.emit()
password = self.harness.charm.app_peer_data["backup-password"]
self.assertIsNotNone(password) # verify the password is set

@patch("charm.MongoDBConnection")
def test_set_password_provided(self, connection):
"""Tests that a given password is set as the new mongodb password for backup user."""
container = self.harness.model.unit.get_container("mongod")
self.harness.set_leader(True)
self.harness.set_can_connect(container, True)
self.harness.charm.on.start.emit()
action_event = mock.Mock()
action_event.params = {"password": "canonical123", "username": "backup"}
self.harness.charm._on_set_password(action_event)
new_password = self.harness.charm.app_peer_data["backup-password"]

# verify app data is updated and results are reported to user
self.assertEqual("canonical123", new_password)
action_event.set_results.assert_called_with({"password": "canonical123"})

0 comments on commit 02a3ffc

Please sign in to comment.