diff --git a/trove/guestagent/datastore/experimental/mongodb/manager.py b/trove/guestagent/datastore/experimental/mongodb/manager.py index 0a337f9945..f3a67eff9a 100644 --- a/trove/guestagent/datastore/experimental/mongodb/manager.py +++ b/trove/guestagent/datastore/experimental/mongodb/manager.py @@ -90,6 +90,11 @@ class Manager(periodic_task.PeriodicTasks): if backup_info: self._perform_restore(backup_info, context, mount_point, self.app) + if service.MongoDBAdmin().is_root_enabled(): + self.status.report_root('root') + elif root_password: + LOG.debug('Root password provided. Enabling root.') + service.MongoDBAdmin().enable_root(root_password) else: if cluster_config['instance_type'] == "query_router": self.app.reset_configuration({'config_contents': @@ -216,13 +221,11 @@ class Manager(periodic_task.PeriodicTasks): def enable_root(self, context): LOG.debug("Enabling root.") - raise exception.DatastoreOperationNotSupported( - operation='enable_root', datastore=MANAGER) + return service.MongoDBAdmin().enable_root() def is_root_enabled(self, context): LOG.debug("Checking if root is enabled.") - raise exception.DatastoreOperationNotSupported( - operation='is_root_enabled', datastore=MANAGER) + return service.MongoDBAdmin().is_root_enabled() def _perform_restore(self, backup_info, context, restore_location, app): LOG.info(_("Restoring database from backup %s.") % backup_info['id']) diff --git a/trove/guestagent/datastore/experimental/mongodb/service.py b/trove/guestagent/datastore/experimental/mongodb/service.py index fe0fb8fbd9..d17c99ef72 100644 --- a/trove/guestagent/datastore/experimental/mongodb/service.py +++ b/trove/guestagent/datastore/experimental/mongodb/service.py @@ -533,6 +533,23 @@ class MongoDBAdmin(object): return pagination.paginate_list(users, limit, marker, include_marker) + def enable_root(self, password=None): + """Create a user 'root' with role 'root'.""" + if not password: + LOG.debug('Generating root user password.') + password = utils.generate_random_password() + root_user = models.MongoDBUser(name='admin.root', password=password) + root_user.roles = 'root' + self.create_user(root_user) + return root_user.serialize() + + def is_root_enabled(self): + """Check if user 'admin.root' exists.""" + with MongoDBClient(self._admin_user()) as admin_client: + return bool(admin_client.admin.system.users.find_one( + {'roles.role': 'root'} + )) + def list_database_names(self): """Get the list of database names.""" with MongoDBClient(self._admin_user()) as admin_client: diff --git a/trove/tests/unittests/guestagent/test_mongodb_manager.py b/trove/tests/unittests/guestagent/test_mongodb_manager.py index b633ee9947..a52ca8e817 100644 --- a/trove/tests/unittests/guestagent/test_mongodb_manager.py +++ b/trove/tests/unittests/guestagent/test_mongodb_manager.py @@ -53,10 +53,11 @@ class GuestAgentMongoDBManagerTest(trove_testtools.TestCase): self.manager.update_status(self.context) status.update.assert_any_call() - def _prepare_method(self, databases=None, users=None, device_path=None, + def _prepare_method(self, packages=['packages'], databases=None, + memory_mb='2048', users=None, device_path=None, mount_point=None, backup_info=None, - cluster_config=None, overrides=None, memory_mb='2048', - packages=['packages']): + config_contents=None, root_password=None, + overrides=None, cluster_config=None,): """self.manager.app must be correctly mocked before calling.""" self.manager.status = mock.Mock() @@ -67,6 +68,8 @@ class GuestAgentMongoDBManagerTest(trove_testtools.TestCase): device_path=device_path, mount_point=mount_point, backup_info=backup_info, + config_contents=config_contents, + root_password=root_password, overrides=overrides, cluster_config=cluster_config) @@ -102,7 +105,8 @@ class GuestAgentMongoDBManagerTest(trove_testtools.TestCase): mock_secure.assert_called_with(None) @mock.patch.object(backup, 'restore') - def test_prepare_from_backup(self, mocked_restore): + @mock.patch.object(service.MongoDBAdmin, 'is_root_enabled') + def test_prepare_from_backup(self, mocked_root_check, mocked_restore): self.manager.app = mock.Mock() backup_info = {'id': 'backup_id_123abc', @@ -114,6 +118,15 @@ class GuestAgentMongoDBManagerTest(trove_testtools.TestCase): mocked_restore.assert_called_with(self.context, backup_info, '/var/lib/mongodb') + mocked_root_check.assert_any_call() + + @mock.patch.object(service.MongoDBAdmin, 'enable_root') + def test_provide_root_password(self, mocked_enable_root): + self.manager.app = mock.Mock() + + self._prepare_method(root_password='test_password') + + mocked_enable_root.assert_called_with('test_password') # This is used in the test_*_user tests below _serialized_user = {'_name': 'testdb.testuser', '_password': None, @@ -200,3 +213,22 @@ class GuestAgentMongoDBManagerTest(trove_testtools.TestCase): self.assertEqual(None, next_marker) self.assertEqual(sorted([user1, user2]), users) + + @mock.patch.object(service.MongoDBAdmin, 'create_user') + @mock.patch.object(utils, 'generate_random_password', + return_value='password') + def test_enable_root(self, mock_gen_rand_pwd, mock_create_user): + root_user = {'_name': 'admin.root', + '_username': 'root', + '_database': {'_name': 'admin', + '_character_set': None, + '_collate': None}, + '_password': 'password', + '_roles': ['root'], + '_databases': [], + '_host': None} + + result = self.manager.enable_root(self.context) + + self.assertTrue(mock_create_user.called) + self.assertEqual(root_user, result)