trove/trove/tests/unittests/taskmanager/test_manager.py

322 lines
15 KiB
Python

# Copyright 2014 eBay Software Foundation
# Copyright [2015] Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest.mock import MagicMock, Mock, patch, PropertyMock
from proboscis.asserts import assert_equal
from trove.backup.models import Backup
from trove.common.exception import TroveError, ReplicationSlaveAttachError
from trove.common import server_group as srv_grp
from trove.instance.tasks import InstanceTasks
from trove.taskmanager.manager import Manager
from trove.taskmanager import models
from trove.taskmanager import service
from trove.tests.unittests import trove_testtools
class TestManager(trove_testtools.TestCase):
def setUp(self):
super(TestManager, self).setUp()
self.manager = Manager()
self.context = trove_testtools.TroveTestContext(self)
self.mock_slave1 = Mock()
self.mock_slave2 = Mock()
type(self.mock_slave1).id = PropertyMock(return_value='some-inst-id')
type(self.mock_slave2).id = PropertyMock(return_value='inst1')
self.mock_old_master = Mock()
type(self.mock_old_master).slaves = PropertyMock(
return_value=[self.mock_slave1, self.mock_slave2])
self.mock_master = Mock()
type(self.mock_master).slaves = PropertyMock(
return_value=[self.mock_slave1, self.mock_slave2])
def tearDown(self):
super(TestManager, self).tearDown()
self.manager = None
def test_getattr_lookup(self):
self.assertTrue(callable(self.manager.delete_cluster))
self.assertTrue(callable(self.manager.mongodb_add_shard_cluster))
def test_most_current_replica(self):
master = Mock()
master.id = 32
def test_case(txn_list, selected_master):
with patch.object(self.manager, '_get_replica_txns',
return_value=txn_list):
result = self.manager._most_current_replica(master, None)
assert_equal(result, selected_master)
with self.assertRaisesRegex(TroveError,
'not all replicating from same'):
test_case([['a', '2a99e-32bf', 2], ['b', '2a', 1]], None)
test_case([['a', '2a99e-32bf', 2]], 'a')
test_case([['a', '2a', 1], ['b', '2a', 2]], 'b')
test_case([['a', '2a', 2], ['b', '2a', 1]], 'a')
test_case([['a', '2a', 1], ['b', '2a', 1]], 'a')
test_case([['a', None, 0]], 'a')
test_case([['a', None, 0], ['b', '2a', 1]], 'b')
def test_detach_replica(self):
slave = Mock()
master = Mock()
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[slave, master]):
self.manager.detach_replica(self.context, 'some-inst-id')
slave.detach_replica.assert_called_with(master)
@patch.object(Manager, '_set_task_status')
def test_promote_to_replica_source(self, mock_set_task_status):
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[self.mock_slave1,
self.mock_old_master,
self.mock_slave2]):
self.manager.promote_to_replica_source(
self.context, 'some-inst-id')
self.mock_slave1.detach_replica.assert_called_with(
self.mock_old_master, for_failover=True)
self.mock_old_master.attach_replica.assert_called_with(
self.mock_slave1, restart=False)
self.mock_slave1.make_read_only.assert_called_with(False)
self.mock_slave2.detach_replica.assert_called_with(
self.mock_old_master, for_failover=True)
self.mock_slave2.attach_replica.assert_called_with(self.mock_slave1,
restart=True)
self.mock_old_master.demote_replication_master.assert_any_call()
mock_set_task_status.assert_called_with(([self.mock_old_master] +
[self.mock_slave1,
self.mock_slave2]),
InstanceTasks.NONE)
@patch.object(Manager, '_set_task_status')
@patch.object(Manager, '_most_current_replica')
def test_eject_replica_source(self, mock_most_current_replica,
mock_set_task_status):
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[self.mock_master, self.mock_slave1,
self.mock_slave2]):
self.manager.eject_replica_source(self.context, 'some-inst-id')
mock_most_current_replica.assert_called_with(self.mock_master,
[self.mock_slave1,
self.mock_slave2])
mock_set_task_status.assert_called_with(([self.mock_master] +
[self.mock_slave1,
self.mock_slave2]),
InstanceTasks.NONE)
@patch.object(Manager, '_set_task_status')
@patch('trove.taskmanager.manager.LOG')
def test_exception_TroveError_promote_to_replica_source(self, *args):
self.mock_slave2.detach_replica = Mock(side_effect=TroveError)
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[self.mock_slave1, self.mock_old_master,
self.mock_slave2]):
self.assertRaises(ReplicationSlaveAttachError,
self.manager.promote_to_replica_source,
self.context, 'some-inst-id')
@patch.object(Manager, '_set_task_status')
@patch.object(Manager, '_most_current_replica')
@patch('trove.taskmanager.manager.LOG')
def test_exception_TroveError_eject_replica_source(
self, mock_logging, mock_most_current_replica,
mock_set_task_status):
self.mock_slave2.detach_replica = Mock(side_effect=TroveError)
mock_most_current_replica.return_value = self.mock_slave1
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[self.mock_master, self.mock_slave1,
self.mock_slave2]):
self.assertRaises(ReplicationSlaveAttachError,
self.manager.eject_replica_source,
self.context, 'some-inst-id')
@patch.object(Manager, '_set_task_status')
def test_error_promote_to_replica_source(self, *args):
self.mock_slave2.detach_replica = Mock(
side_effect=RuntimeError('Error'))
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[self.mock_slave1, self.mock_old_master,
self.mock_slave2]):
self.assertRaisesRegex(RuntimeError, 'Error',
self.manager.promote_to_replica_source,
self.context, 'some-inst-id')
@patch('trove.taskmanager.manager.LOG')
def test_error_demote_replication_master_promote_to_replica_source(
self, mock_logging):
self.mock_old_master.demote_replication_master = Mock(
side_effect=RuntimeError('Error'))
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[self.mock_slave1, self.mock_old_master,
self.mock_slave2]):
self.assertRaises(ReplicationSlaveAttachError,
self.manager.promote_to_replica_source,
self.context, 'some-inst-id')
@patch.object(Manager, '_set_task_status')
@patch.object(Manager, '_most_current_replica')
def test_error_eject_replica_source(self, mock_most_current_replica,
mock_set_task_status):
self.mock_slave2.detach_replica = Mock(
side_effect=RuntimeError('Error'))
mock_most_current_replica.return_value = self.mock_slave1
with patch.object(models.BuiltInstanceTasks, 'load',
side_effect=[self.mock_master, self.mock_slave1,
self.mock_slave2]):
self.assertRaisesRegex(RuntimeError, 'Error',
self.manager.eject_replica_source,
self.context, 'some-inst-id')
@patch.object(Backup, 'delete')
@patch.object(models.BuiltInstanceTasks, 'load')
def test_create_replication_slave(self, mock_load, mock_backup_delete):
mock_tasks = Mock()
mock_snapshot = {'dataset': {'snapshot_id': 'test-id'}}
mock_tasks.get_replication_master_snapshot = Mock(
return_value=mock_snapshot)
mock_flavor = Mock()
with patch.object(models.FreshInstanceTasks, 'load',
return_value=mock_tasks):
self.manager.create_instance(self.context, ['id1'], Mock(),
mock_flavor, Mock(), None, None,
'mysql', 'mysql-server', 2,
'temp-backup-id', None,
'some_password', None, Mock(),
'some-master-id', None, None,
None, None)
mock_tasks.get_replication_master_snapshot.assert_called_with(
self.context, 'some-master-id', mock_flavor,
parent_backup_id='temp-backup-id')
mock_backup_delete.assert_called_with(self.context, 'test-id')
@patch.object(models.FreshInstanceTasks, 'load')
@patch.object(Backup, 'delete')
@patch.object(models.BuiltInstanceTasks, 'load')
@patch('trove.taskmanager.manager.LOG')
def test_exception_create_replication_slave(self, mock_logging, mock_tasks,
mock_delete, mock_load):
mock_load.return_value.create_instance = Mock(side_effect=TroveError)
self.assertRaises(TroveError, self.manager.create_instance,
self.context, ['id1', 'id2'], Mock(), Mock(),
Mock(), None, None, 'mysql', 'mysql-server', 2,
'temp-backup-id', None, 'some_password', None,
Mock(), 'some-master-id', None, None, None, None)
def test_AttributeError_create_instance(self):
self.assertRaisesRegex(
AttributeError, 'Cannot create multiple non-replica instances.',
self.manager.create_instance, self.context, ['id1', 'id2'],
Mock(), Mock(), Mock(), None, None, 'mysql', 'mysql-server', 2,
'temp-backup-id', None, 'some_password', None, Mock(), None, None,
None, None, None)
def test_create_instance(self):
mock_tasks = Mock()
mock_flavor = Mock()
mock_override = Mock()
mock_csg = Mock()
type(mock_csg.return_value).id = PropertyMock(
return_value='sg-id')
with patch.object(models.FreshInstanceTasks, 'load',
return_value=mock_tasks):
with patch.object(srv_grp.ServerGroup, 'create', mock_csg):
self.manager.create_instance(
self.context, 'id1', 'inst1', mock_flavor,
'mysql-image-id', None, None, 'mysql', 'mysql-server', 2,
'temp-backup-id', None, 'password', None, mock_override,
None, None, None, None, 'affinity')
mock_tasks.create_instance.assert_called_with(
mock_flavor,
'mysql-image-id', None,
None, 'mysql',
'mysql-server', 2,
'temp-backup-id', None,
'password', None,
mock_override,
None, None, None, None,
{'group': 'sg-id'},
access=None, ds_version=None)
mock_tasks.wait_for_instance.assert_called_with(3600, mock_flavor)
def test_create_cluster(self):
mock_tasks = Mock()
with patch.object(models, 'load_cluster_tasks',
return_value=mock_tasks):
self.manager.create_cluster(self.context, 'some-cluster-id')
mock_tasks.create_cluster.assert_called_with(self.context,
'some-cluster-id')
def test_delete_cluster(self):
mock_tasks = Mock()
with patch.object(models, 'load_cluster_tasks',
return_value=mock_tasks):
self.manager.delete_cluster(self.context, 'some-cluster-id')
mock_tasks.delete_cluster.assert_called_with(self.context,
'some-cluster-id')
def test_shrink_cluster_with_success(self):
self._assert_shrink_cluster(True)
def test_shrink_cluster_with_error(self):
self._assert_shrink_cluster(False)
@patch('trove.taskmanager.manager.EndNotification')
@patch('trove.taskmanager.manager.models.load_cluster_tasks')
def _assert_shrink_cluster(self, success, mock_load, mock_notification):
if success:
mock_load.side_effect = Mock()
else:
mock_load.side_effect = Exception
end_notification = MagicMock()
mock_notification.return_value = end_notification
context = Mock()
cluster_id = Mock()
instance_ids = Mock()
try:
self.manager.shrink_cluster(context, cluster_id, instance_ids)
self.assertTrue(success)
except Exception:
self.assertFalse(success)
mock_load.assert_called_once_with(context, cluster_id)
mock_notification.assert_called_once_with(context,
cluster_id=cluster_id,
instance_ids=instance_ids)
exit_error_type = end_notification.__exit__.call_args_list[0][0][0]
if success:
self.assertFalse(exit_error_type)
else:
self.assertTrue(exit_error_type)
class TestTaskManagerService(trove_testtools.TestCase):
def test_app_factory(self):
test_service = service.app_factory(Mock())
self.assertIsInstance(test_service, service.TaskService)