예제 #1
0
    def test_get_slave_raises_exception_on_invalid_arguments(self, get_slave_kwargs):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave_registry.add_slave(slave1)

        with self.assertRaises(ValueError):
            slave_registry.get_slave(**get_slave_kwargs)
    def post(self):
        shutdown_all = self.decoded_body.get('shutdown_all')
        if shutdown_all:
            slaves_to_shutdown = SlaveRegistry.singleton().get_all_slaves_by_id().keys()
        else:
            slaves_to_shutdown = [int(slave_id) for slave_id in self.decoded_body.get('slaves')]

        self._cluster_master.set_shutdown_mode_on_slaves(slaves_to_shutdown)
    def put(self, slave_id):
        new_slave_state = self.decoded_body.get('slave', {}).get('state')
        slave = SlaveRegistry.singleton().get_slave(slave_id=int(slave_id))
        self._cluster_master.handle_slave_state_update(slave, new_slave_state)
        self._cluster_master.update_slave_last_heartbeat_time(slave)

        self._write_status({
            'slave': slave.api_representation()
        })
예제 #4
0
    def test_get_slave_raises_exception_on_slave_not_found(self, get_slave_kwargs):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave2 = Slave('leonardo.turtles.gov', 1)
        slave_registry.add_slave(slave1)
        slave_registry.add_slave(slave2)

        with self.assertRaises(ItemNotFoundError):
            slave_registry.get_slave(**get_slave_kwargs)
예제 #5
0
    def test_updating_slave_to_nonexistent_state_should_raise_bad_request_error(self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        slave_url = 'raphael.turtles.gov'
        master.connect_slave(slave_url, 10)
        slave = slave_registry.get_slave(slave_url=slave_url)

        with self.assertRaises(BadRequestError):
            master.handle_slave_state_update(slave, 'NONEXISTENT_STATE')
예제 #6
0
    def test_connect_slave_adds_new_slave_if_slave_never_connected_before(self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()

        master.connect_slave('never-before-seen.turtles.gov', 10)

        self.assertEqual(1, len(slave_registry.get_all_slaves_by_id()),
                         'Exactly one slave should be registered with the master.')
        self.assertIsNotNone(slave_registry.get_slave(slave_id=None, slave_url='never-before-seen.turtles.gov'),
                             'Registered slave does not have the expected url.')
예제 #7
0
    def test_remove_slave_raises_exception_on_invalid_arguments(self):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave_registry.add_slave(slave1)

        with self.assertRaises(ValueError):
            # Both arguments specified
            slave_registry.remove_slave(slave=slave1, slave_url=slave1.url)
            # No arguments specified
            slave_registry.remove_slave(slave=None, slave_url=None)
예제 #8
0
    def test_updating_slave_to_disconnected_state_should_reset_slave_current_build_id(self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        slave_url = 'raphael.turtles.gov'
        master.connect_slave(slave_url, num_executors=10)
        slave = slave_registry.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)
예제 #9
0
    def test_get_slave_returns_valid_slave(self):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave2 = Slave('leonardo.turtles.gov', 1)
        slave_registry.add_slave(slave1)
        slave_registry.add_slave(slave2)

        self.assertEquals(slave_registry.get_slave(slave_url=slave1.url), slave1,
                          'Get slave with url should return valid slave.')
        self.assertEquals(slave_registry.get_slave(slave_id=slave2.id), slave2,
                          'Get slave with id should return valid slave.')
    def post(self):
        shutdown_all = self.decoded_body.get('shutdown_all')
        if shutdown_all:
            slaves_to_shutdown = SlaveRegistry.singleton(
            ).get_all_slaves_by_id().keys()
        else:
            slaves_to_shutdown = [
                int(slave_id) for slave_id in self.decoded_body.get('slaves')
            ]

        self._cluster_master.set_shutdown_mode_on_slaves(slaves_to_shutdown)
예제 #11
0
    def test_add_slave_adds_slave_in_both_dicts(self):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave2 = Slave('leonardo.turtles.gov', 1)
        slave_registry.add_slave(slave1)
        slave_registry.add_slave(slave2)

        self.assertEqual(2, len(slave_registry.get_all_slaves_by_id()),
                         'Exactly two slaves should be in the all_slaves_by_id dict.')
        self.assertEqual(2, len(slave_registry.get_all_slaves_by_url()),
                         'Exactly two slaves should be in the all_slaves_by_url dict.')
예제 #12
0
    def test_updating_slave_to_disconnected_state_should_mark_slave_as_dead(self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        slave_url = 'raphael.turtles.gov'
        master.connect_slave(slave_url, num_executors=10)
        slave = slave_registry.get_slave(slave_url=slave_url)
        self.assertTrue(slave.is_alive())

        master.handle_slave_state_update(slave, SlaveState.DISCONNECTED)

        self.assertFalse(slave.is_alive())
예제 #13
0
    def test_updating_slave_to_shutdown_should_call_slave_set_shutdown_mode(self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        slave_url = 'raphael.turtles.gov'
        master.connect_slave(slave_url, 10)
        slave = slave_registry.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()
예제 #14
0
    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)
예제 #15
0
    def test_get_slave_returns_valid_slave(self):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave2 = Slave('leonardo.turtles.gov', 1)
        slave_registry.add_slave(slave1)
        slave_registry.add_slave(slave2)

        self.assertEquals(slave_registry.get_slave(slave_url=slave1.url),
                          slave1,
                          'Get slave with url should return valid slave.')
        self.assertEquals(slave_registry.get_slave(slave_id=slave2.id), slave2,
                          'Get slave with id should return valid slave.')
예제 #16
0
    def test_updating_slave_to_shutdown_should_call_slave_set_shutdown_mode(
            self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        slave_url = 'raphael.turtles.gov'
        master.connect_slave(slave_url, 10)
        slave = slave_registry.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()
예제 #17
0
    def test_updating_slave_to_disconnected_state_should_reset_slave_current_build_id(
            self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        slave_url = 'raphael.turtles.gov'
        master.connect_slave(slave_url, num_executors=10)
        slave = slave_registry.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)
예제 #18
0
    def test_updating_slave_to_disconnected_state_should_mark_slave_as_dead(
            self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        slave_url = 'raphael.turtles.gov'
        master.connect_slave(slave_url, num_executors=10)
        slave = slave_registry.get_slave(slave_url=slave_url)
        self.assertTrue(slave.is_alive())

        master.handle_slave_state_update(slave, SlaveState.DISCONNECTED)

        self.assertFalse(slave.is_alive())
예제 #19
0
    def test_add_slave_adds_slave_in_both_dicts(self):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave2 = Slave('leonardo.turtles.gov', 1)
        slave_registry.add_slave(slave1)
        slave_registry.add_slave(slave2)

        self.assertEqual(
            2, len(slave_registry.get_all_slaves_by_id()),
            'Exactly two slaves should be in the all_slaves_by_id dict.')
        self.assertEqual(
            2, len(slave_registry.get_all_slaves_by_url()),
            'Exactly two slaves should be in the all_slaves_by_url dict.')
예제 #20
0
    def test_connect_slave_with_existing_slave_running_build_cancels_build(self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()

        master.connect_slave('running-slave.turtles.gov', 10)
        build_mock = MagicMock(spec_set=Build)
        BuildStore._all_builds_by_id[1] = build_mock
        existing_slave = slave_registry.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.')
예제 #21
0
    def post(self, slave_id):
        slave = SlaveRegistry.singleton().get_slave(slave_id=int(slave_id))

        # If the slave has been marked dead, but still sends heartbeat, the master does not update the last
        # heartbeat time and the method returns false. Additionally, master responds to the slave with slave
        # status. The slave will treat a is_alive=false response as a heartbeat failure, and die.
        #
        # The reason master returns the status to the slave instead of simply marking the slave as alive is
        # because the master or slave do not maintain an explicit state about when and why the slave was marked
        # dead. It is a lot cleaner for the heartbeat functionality to indicate an heartbeat failure and let the
        # slave make a decision based on that.

        is_alive = self._cluster_master.update_slave_last_heartbeat_time(slave)
        self.write({'is_alive': is_alive})
    def post(self, build_id, subjob_id):
        slave_url = self.decoded_body.get('slave')
        slave = SlaveRegistry.singleton().get_slave(slave_url=slave_url)
        file_payload = self.request.files.get('file')
        if not file_payload:
            raise RuntimeError('Result file not provided')

        slave_executor_id = self.decoded_body.get('metric_data', {}).get('executor_id')
        analytics.record_event(analytics.MASTER_RECEIVED_RESULT, executor_id=slave_executor_id, build_id=int(build_id),
                               subjob_id=int(subjob_id), slave_id=slave.id)

        self._cluster_master.handle_result_reported_from_slave(
            slave_url, int(build_id), int(subjob_id), file_payload[0])
        self._write_status()
    def post(self, slave_id):
        slave = SlaveRegistry.singleton().get_slave(slave_id=int(slave_id))

        # If the slave has been marked dead, but still sends heartbeat, the master does not update the last
        # heartbeat time and the method returns false. Additionally, master responds to the slave with slave
        # status. The slave will treat a is_alive=false response as a heartbeat failure, and die.
        #
        # The reason master returns the status to the slave instead of simply marking the slave as alive is
        # because the master or slave do not maintain an explicit state about when and why the slave was marked
        # dead. It is a lot cleaner for the heartbeat functionality to indicate an heartbeat failure and let the
        # slave make a decision based on that.

        is_alive = self._cluster_master.update_slave_last_heartbeat_time(slave)
        self.write({'is_alive': is_alive})
예제 #24
0
    def test_connect_slave_adds_new_slave_if_slave_never_connected_before(
            self):
        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()

        master.connect_slave('never-before-seen.turtles.gov', 10)

        self.assertEqual(
            1, len(slave_registry.get_all_slaves_by_id()),
            'Exactly one slave should be registered with the master.')
        self.assertIsNotNone(
            slave_registry.get_slave(
                slave_id=None, slave_url='never-before-seen.turtles.gov'),
            'Registered slave does not have the expected url.')
예제 #25
0
    def test_exception_raised_during_complete_subjob_does_not_prevent_slave_teardown(self):
        slave_url = 'raphael.turtles.gov'
        mock_build = Mock(spec_set=Build, build_id=lambda: 777, is_finished=False)
        mock_build.complete_subjob.side_effect = [RuntimeError('Write failed')]

        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        BuildStore._all_builds_by_id[mock_build.build_id()] = mock_build
        slave_registry._all_slaves_by_url[slave_url] = Mock()
        mock_scheduler = self.mock_scheduler_pool.get(mock_build)

        with self.assertRaisesRegex(RuntimeError, 'Write failed'):
            master.handle_result_reported_from_slave(slave_url, mock_build.build_id(), subjob_id=888)

        self.assertEqual(mock_scheduler.execute_next_subjob_or_free_executor.call_count, 1)
예제 #26
0
    def test_remove_slave_by_slave_url_removes_slave_from_both_dicts(self):
        slave_registry = SlaveRegistry.singleton()
        slave1 = Slave('raphael.turtles.gov', 1)
        slave2 = Slave('leonardo.turtles.gov', 1)
        slave_registry.add_slave(slave1)
        slave_registry.add_slave(slave2)

        self.assertEqual(2, len(slave_registry.get_all_slaves_by_id()),
                         'Exactly two slaves should be in the all_slaves_by_id dict.')
        self.assertEqual(2, len(slave_registry.get_all_slaves_by_url()),
                         'Exactly two slaves should be in the all_slaves_by_url dict.')

        slave_registry.remove_slave(slave_url=slave1.url)

        self.assertEqual(1, len(slave_registry.get_all_slaves_by_id()),
                         'Exactly one slave should be in the all_slaves_by_id dict after removing one slave.')
        self.assertEqual(1, len(slave_registry.get_all_slaves_by_url()),
                         'Exactly one slave should be in the all_slaves_by_url dict after removing one slave.')
예제 #27
0
    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 post(self, build_id, subjob_id):
        slave_url = self.decoded_body.get('slave')
        slave = SlaveRegistry.singleton().get_slave(slave_url=slave_url)
        file_payload = self.request.files.get('file')
        if not file_payload:
            raise RuntimeError('Result file not provided')

        slave_executor_id = self.decoded_body.get('metric_data',
                                                  {}).get('executor_id')
        analytics.record_event(analytics.MASTER_RECEIVED_RESULT,
                               executor_id=slave_executor_id,
                               build_id=int(build_id),
                               subjob_id=int(subjob_id),
                               slave_id=slave.id)

        self._cluster_master.handle_result_reported_from_slave(
            slave_url, int(build_id), int(subjob_id), file_payload[0])
        self._write_status()
예제 #29
0
    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)
예제 #30
0
    def test_handle_result_reported_from_slave_when_build_is_canceled(self):
        build_id = 1
        slave_url = "url"
        build = Build(BuildRequest({}))
        self.patch('app.master.build.util')
        build.generate_project_type()
        build.cancel()

        self.patch_object(build, '_handle_subjob_payload')
        self.patch_object(build, '_mark_subjob_complete')

        master = ClusterMaster()
        slave_registry = SlaveRegistry.singleton()
        BuildStore._all_builds_by_id[build_id] = build
        slave_registry._all_slaves_by_url[slave_url] = Mock()
        mock_scheduler = self.mock_scheduler_pool.get(build)

        master.handle_result_reported_from_slave(slave_url, build_id, 1)

        self.assertEqual(build._handle_subjob_payload.call_count, 1, "Canceled builds should "
                                                                     "handle payload")
        self.assertEqual(build._mark_subjob_complete.call_count, 1, "Canceled builds should mark "
                                                                    "their subjobs complete")
        self.assertTrue(mock_scheduler.execute_next_subjob_or_free_executor.called)
 def get(self, slave_id):
     slave = SlaveRegistry.singleton().get_slave(slave_id=int(slave_id))
     response = {'slave': slave.api_representation()}
     self.write(response)
 def post(self, slave_id):
     slave = SlaveRegistry.singleton().get_slave(slave_id=int(slave_id))
     self._cluster_master.update_slave_last_heartbeat_time(slave)
    def get(self):

        response = {
            'slaves': [slave.api_representation() for slave in SlaveRegistry.singleton().get_all_slaves_by_id().values()]
        }
        self.write(response)
 def get(self, slave_id):
     slave = SlaveRegistry.singleton().get_slave(slave_id=int(slave_id))
     response = {
         'slave': slave.api_representation()
     }
     self.write(response)