def test_connect_slave_with_existing_dead_slave_creates_new_alive_instance( self): master = ClusterMaster() slave_registry = SlaveRegistry.singleton() master.connect_slave('existing-slave.turtles.gov', 10) existing_slave = slave_registry.get_slave( slave_id=None, slave_url='existing-slave.turtles.gov') existing_slave.set_is_alive(False) existing_slave_id = existing_slave.id connect_response = master.connect_slave('existing-slave.turtles.gov', 10) new_slave = slave_registry.get_slave( slave_url='existing-slave.turtles.gov') self.assertNotEqual( str(existing_slave_id), connect_response['slave_id'], 'The re-connected slave should have generated a new slave id.') self.assertTrue( new_slave.is_alive(use_cached=True), 'The new slave should have been marked as alive once instantiated.' ) self.assertEquals( 2, self.mock_slave_allocator.add_idle_slave.call_count, 'Expected slave to be added to the idle slaves list.')
def test_get_slave_raises_exception_on_invalid_arguments( self, get_slave_kwargs): master = ClusterMaster() master.connect_slave('raphael.turtles.gov', 10) with self.assertRaises(ValueError): master.get_slave(**get_slave_kwargs)
def test_updating_slave_to_nonexistent_state_should_raise_bad_request_error(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, 10) slave = master.get_slave(slave_url=slave_url) with self.assertRaises(BadRequestError): master.handle_slave_state_update(slave, 'NONEXISTENT_STATE')
def test_updating_slave_to_nonexistent_state_should_raise_bad_request_error(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, 10) slave = master.get_slave(slave_url=slave_url) with self.assertRaises(BadRequestError): master.handle_slave_state_update(slave, 'NONEXISTENT_STATE')
def test_connect_slave_adds_new_slave_if_slave_never_connected_before(self): master = ClusterMaster() master.connect_slave('never-before-seen.turtles.gov', 10) self.assertEqual(1, len(master.all_slaves_by_id()), 'Exactly one slave should be registered with the master.') self.assertIsNotNone(master.get_slave(slave_id=None, slave_url='never-before-seen.turtles.gov'), 'Registered slave does not have the expected url.')
def test_connect_slave_adds_new_slave_if_slave_never_connected_before(self): master = ClusterMaster() master.connect_slave('never-before-seen.turtles.gov', 10) self.assertEqual(1, len(master.all_slaves_by_id()), 'Exactly one slave should be registered with the master.') self.assertIsNotNone(master.get_slave(slave_id=None, slave_url='never-before-seen.turtles.gov'), 'Registered slave does not have the expected url.')
def test_updating_slave_to_disconnected_state_should_mark_slave_as_dead(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, num_executors=10) slave = master.get_slave(slave_url=slave_url) self.assertTrue(slave.is_alive()) master.handle_slave_state_update(slave, SlaveState.DISCONNECTED) self.assertFalse(slave.is_alive())
def test_updating_slave_to_disconnected_state_should_reset_slave_current_build_id(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, num_executors=10) slave = master.get_slave(slave_url=slave_url) slave.current_build_id = 4 master.handle_slave_state_update(slave, SlaveState.DISCONNECTED) self.assertIsNone(slave.current_build_id)
def test_updating_slave_to_shutdown_should_call_slave_set_shutdown_mode(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, 10) slave = master.get_slave(slave_url=slave_url) slave.set_shutdown_mode = Mock() master.handle_slave_state_update(slave, SlaveState.SHUTDOWN) slave.set_shutdown_mode.assert_called_once_with()
def test_updating_slave_to_disconnected_state_should_reset_slave_current_build_id(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, num_executors=10) slave = master.get_slave(slave_url=slave_url) slave.current_build_id = 4 master.handle_slave_state_update(slave, SlaveState.DISCONNECTED) self.assertIsNone(slave.current_build_id)
def test_updating_slave_to_disconnected_state_should_mark_slave_as_dead(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, num_executors=10) slave = master.get_slave(slave_url=slave_url) self.assertTrue(slave.is_alive()) master.handle_slave_state_update(slave, SlaveState.DISCONNECTED) self.assertFalse(slave.is_alive())
def test_updating_slave_to_shutdown_should_call_slave_set_shutdown_mode(self): master = ClusterMaster() slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, 10) slave = master.get_slave(slave_url=slave_url) slave.set_shutdown_mode = Mock() master.handle_slave_state_update(slave, SlaveState.SHUTDOWN) slave.set_shutdown_mode.assert_called_once_with()
def test_updating_slave_to_setup_completed_state_should_tell_build_to_begin_subjob_execution(self): master = ClusterMaster() fake_build = MagicMock() master.get_build = MagicMock(return_value=fake_build) slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, 10) slave = master.get_slave(slave_url=slave_url) master.handle_slave_state_update(slave, SlaveState.SETUP_COMPLETED) fake_build.begin_subjob_executions_on_slave.assert_called_once_with(slave)
def test_connect_slave_with_existing_slave_running_build_cancels_build(self): master = ClusterMaster() master.connect_slave('running-slave.turtles.gov', 10) build_mock = MagicMock(spec_set=Build) master._all_builds_by_id[1] = build_mock existing_slave = master.get_slave(slave_id=None, slave_url='running-slave.turtles.gov') existing_slave.current_build_id = 1 master.connect_slave('running-slave.turtles.gov', 10) self.assertTrue(build_mock.cancel.called, 'The build was not cancelled.')
def test_connect_slave_with_existing_slave_running_build_cancels_build(self): master = ClusterMaster() master.connect_slave('running-slave.turtles.gov', 10) build_mock = MagicMock(spec_set=Build) master._all_builds_by_id[1] = build_mock existing_slave = master.get_slave(slave_id=None, slave_url='running-slave.turtles.gov') existing_slave.current_build_id = 1 master.connect_slave('running-slave.turtles.gov', 10) self.assertTrue(build_mock.cancel.called, 'The build was not cancelled.')
def test_updating_slave_to_setup_completed_state_should_tell_build_to_begin_subjob_execution(self): master = ClusterMaster() fake_build = MagicMock(spec_set=Build) master.get_build = MagicMock(return_value=fake_build) slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, 10) slave = master.get_slave(slave_url=slave_url) mock_scheduler = self.mock_scheduler_pool.get(fake_build) master.handle_slave_state_update(slave, SlaveState.SETUP_COMPLETED) mock_scheduler.begin_subjob_executions_on_slave.assert_called_once_with(slave)
def test_connect_slave_with_existing_dead_slave_removes_old_slave_entry_from_registry(self): master = ClusterMaster() slave_registry = SlaveRegistry.singleton() master.connect_slave('existing-slave.turtles.gov', 10) old_existing_slave = slave_registry.get_slave(slave_id=None, slave_url='existing-slave.turtles.gov') old_existing_slave_id = old_existing_slave.id connect_response = master.connect_slave('existing-slave.turtles.gov', 10) with self.assertRaises(ItemNotFoundError): slave_registry.get_slave(slave_id=old_existing_slave_id)
def test_connect_slave_with_existing_dead_slave_marks_it_as_alive(self): master = ClusterMaster() master.connect_slave('existing-slave.turtles.gov', 10) existing_slave = master.get_slave(slave_id=None, slave_url='existing-slave.turtles.gov') existing_slave.set_is_alive(False) existing_slave_id = existing_slave.id connect_response = master.connect_slave('existing-slave.turtles.gov', 10) self.assertEqual(str(existing_slave_id), connect_response['slave_id'], 'The re-connected slave should not have generated a new slave id.') self.assertTrue(existing_slave.is_alive(use_cached=True), 'The re-connected slave should have been marked as alive once reconnected.') self.assertEquals(2, self.mock_slave_allocator.add_idle_slave.call_count, 'Expected slave to be added to the idle slaves list.')
def test_connect_slave_with_existing_slave_running_build_gets_added_to_idle_slaves_list_and_cancels_build(self): master = ClusterMaster() master.connect_slave('running-slave.turtles.gov', 10) build_mock = MagicMock(spec_set=Build) master._all_builds_by_id[1] = build_mock existing_slave = master.get_slave(slave_id=None, slave_url='running-slave.turtles.gov') existing_slave.current_build_id = 1 existing_slave_id = existing_slave.id connect_response = master.connect_slave('running-slave.turtles.gov', 10) self.assertEqual(str(existing_slave_id), connect_response['slave_id'], 'The re-connected slave should not have generated a new slave id.') self.assertEqual(2, self.mock_slave_allocator.add_idle_slave.call_count, 'Expected slave to be added to the idle slaves list.') self.assertTrue(build_mock.cancel.called, 'The build was not cancelled.')
def test_heartbeat_disconnects_unresponsive_slave(self, slave_alive, seconds_since_last_heartbeat): last_heartbeat_time = self._mock_current_datetime - timedelta(seconds=seconds_since_last_heartbeat) master = ClusterMaster() mock_slave = Mock() self.patch('app.master.cluster_master.Slave', new=lambda *args: mock_slave) master.connect_slave('slave_url', 1) mock_slave.is_alive.return_value = slave_alive mock_slave.get_last_heartbeat_time.return_value = last_heartbeat_time master._disconnect_non_heartbeating_slaves() if slave_alive and seconds_since_last_heartbeat == 1000: self.assertEqual(mock_slave.mark_dead.call_count, 1, 'master disconnects unresponsive slave') else: self.assertEqual(mock_slave.mark_dead.call_count, 0, 'master should not disconnect a dead or responsive slave')
def test_updating_slave_to_setup_completed_state_should_tell_build_to_begin_subjob_execution(self): master = ClusterMaster() slave_registry = SlaveRegistry.singleton() fake_build = MagicMock(spec_set=Build) master.get_build = MagicMock(return_value=fake_build) slave_url = 'raphael.turtles.gov' master.connect_slave(slave_url, 10) slave = slave_registry.get_slave(slave_url=slave_url) mock_scheduler = self.mock_scheduler_pool.get(fake_build) scheduler_begin_event = Event() mock_scheduler.begin_subjob_executions_on_slave.side_effect = lambda **_: scheduler_begin_event.set() master.handle_slave_state_update(slave, SlaveState.SETUP_COMPLETED) was_called = scheduler_begin_event.wait(timeout=5) self.assertTrue(was_called, 'scheduler.begin_subjob_executions_on_slave should be called in response ' 'to slave setup completing.') _, call_kwargs = mock_scheduler.begin_subjob_executions_on_slave.call_args self.assertEqual(call_kwargs.get('slave'), slave)
def test_get_slave_raises_exception_on_slave_not_found(self, get_slave_kwargs): master = ClusterMaster() master.connect_slave('raphael.turtles.gov', 10) master.connect_slave('leonardo.turtles.gov', 10) master.connect_slave('donatello.turtles.gov', 10) with self.assertRaises(ItemNotFoundError): master.get_slave(**get_slave_kwargs)
def test_get_slave_raises_exception_on_slave_not_found(self, get_slave_kwargs): master = ClusterMaster() master.connect_slave('raphael.turtles.gov', 10) master.connect_slave('leonardo.turtles.gov', 10) master.connect_slave('donatello.turtles.gov', 10) with self.assertRaises(ItemNotFoundError): master.get_slave(**get_slave_kwargs)
def test_connect_slave_with_existing_dead_slave_creates_new_alive_instance(self): master = ClusterMaster() master.connect_slave("existing-slave.turtles.gov", 10) existing_slave = master.get_slave(slave_id=None, slave_url="existing-slave.turtles.gov") existing_slave.set_is_alive(False) existing_slave_id = existing_slave.id connect_response = master.connect_slave("existing-slave.turtles.gov", 10) new_slave = master._all_slaves_by_url.get("existing-slave.turtles.gov") self.assertNotEqual( str(existing_slave_id), connect_response["slave_id"], "The re-connected slave should have generated a new slave id.", ) self.assertTrue( new_slave.is_alive(use_cached=True), "The new slave should have been marked as alive once instantiated." ) self.assertEquals( 2, self.mock_slave_allocator.add_idle_slave.call_count, "Expected slave to be added to the idle slaves list.", )
def test_get_slave_returns_expected_value_given_valid_arguments(self): master = ClusterMaster() master.connect_slave('raphael.turtles.gov', 10) master.connect_slave('leonardo.turtles.gov', 10) master.connect_slave('donatello.turtles.gov', 10) actual_slave_by_id = master.get_slave(slave_id=2) actual_slave_by_url = master.get_slave(slave_url='leonardo.turtles.gov') self.assertEqual(2, actual_slave_by_id.id, 'Retrieved slave should have the same id as requested.') self.assertEqual('leonardo.turtles.gov', actual_slave_by_url.url, 'Retrieved slave should have the same url as requested.')
def test_get_slave_returns_expected_value_given_valid_arguments(self): master = ClusterMaster() master.connect_slave('raphael.turtles.gov', 10) master.connect_slave('leonardo.turtles.gov', 10) master.connect_slave('donatello.turtles.gov', 10) actual_slave_by_id = master.get_slave(slave_id=2) actual_slave_by_url = master.get_slave(slave_url='leonardo.turtles.gov') self.assertEqual(2, actual_slave_by_id.id, 'Retrieved slave should have the same id as requested.') self.assertEqual('leonardo.turtles.gov', actual_slave_by_url.url, 'Retrieved slave should have the same url as requested.')
def test_get_slave_raises_exception_on_invalid_arguments(self, get_slave_kwargs): master = ClusterMaster() master.connect_slave('raphael.turtles.gov', 10) with self.assertRaises(ValueError): master.get_slave(**get_slave_kwargs)