Ejemplo n.º 1
0
 def setUp(self) -> None:
     self.monitor = GunicornMonitor(
         gunicorn_master_pid=1,
         num_workers_expected=4,
         master_timeout=60,
         worker_refresh_interval=60,
         worker_refresh_batch_size=2,
         reload_on_plugin_change=True,
     )
     mock.patch.object(self.monitor,
                       '_generate_plugin_state',
                       return_value={}).start()
     mock.patch.object(self.monitor,
                       '_get_num_ready_workers_running',
                       return_value=4).start()
     mock.patch.object(self.monitor,
                       '_get_num_workers_running',
                       return_value=4).start()
     mock.patch.object(self.monitor,
                       '_spawn_new_workers',
                       return_value=None).start()
     mock.patch.object(self.monitor, '_kill_old_workers',
                       return_value=None).start()
     mock.patch.object(self.monitor, '_reload_gunicorn',
                       return_value=None).start()
Ejemplo n.º 2
0
 def setUp(self):
     self.children = mock.MagicMock()
     self.child = mock.MagicMock()
     self.process = mock.MagicMock()
     self.monitor = GunicornMonitor(
         gunicorn_master_pid=1,
         num_workers_expected=4,
         master_timeout=60,
         worker_refresh_interval=60,
         worker_refresh_batch_size=2,
         reload_on_plugin_change=True,
     )
Ejemplo n.º 3
0
class TestCLIGetNumReadyWorkersRunning(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.parser = cli_parser.get_parser()

    def setUp(self):
        self.gunicorn_master_proc = mock.Mock(pid=2137)
        self.children = mock.MagicMock()
        self.child = mock.MagicMock()
        self.process = mock.MagicMock()
        self.monitor = GunicornMonitor(
            gunicorn_master_proc=self.gunicorn_master_proc,
            num_workers_expected=4,
            master_timeout=60,
            worker_refresh_interval=60,
            worker_refresh_batch_size=2,
            reload_on_plugin_change=True,
        )

    def test_ready_prefix_on_cmdline(self):
        self.child.cmdline.return_value = [
            settings.GUNICORN_WORKER_READY_PREFIX
        ]
        self.process.children.return_value = [self.child]

        with mock.patch('psutil.Process', return_value=self.process):
            self.assertEqual(self.monitor._get_num_ready_workers_running(), 1)

    def test_ready_prefix_on_cmdline_no_children(self):
        self.process.children.return_value = []

        with mock.patch('psutil.Process', return_value=self.process):
            self.assertEqual(self.monitor._get_num_ready_workers_running(), 0)

    def test_ready_prefix_on_cmdline_zombie(self):
        self.child.cmdline.return_value = []
        self.process.children.return_value = [self.child]

        with mock.patch('psutil.Process', return_value=self.process):
            self.assertEqual(self.monitor._get_num_ready_workers_running(), 0)

    def test_ready_prefix_on_cmdline_dead_process(self):
        self.child.cmdline.side_effect = psutil.NoSuchProcess(11347)
        self.process.children.return_value = [self.child]

        with mock.patch('psutil.Process', return_value=self.process):
            self.assertEqual(self.monitor._get_num_ready_workers_running(), 0)
Ejemplo n.º 4
0
    def test_should_detect_changes_in_directory(self):
        with tempfile.TemporaryDirectory() as tempdir, mock.patch(
                "airflow.cli.commands.webserver_command.settings.PLUGINS_FOLDER",
                tempdir):
            self._prepare_test_file(f"{tempdir}/file1.txt", 100)
            self._prepare_test_file(
                f"{tempdir}/nested/nested/nested/nested/file2.txt", 200)
            self._prepare_test_file(f"{tempdir}/file3.txt", 300)

            monitor = GunicornMonitor(
                gunicorn_master_pid=1,
                num_workers_expected=4,
                master_timeout=60,
                worker_refresh_interval=60,
                worker_refresh_batch_size=2,
                reload_on_plugin_change=True,
            )

            # When the files have not changed, the result should be constant
            state_a = monitor._generate_plugin_state()
            state_b = monitor._generate_plugin_state()

            assert state_a == state_b
            assert 3 == len(state_a)

            # Should detect new file
            self._prepare_test_file(f"{tempdir}/file4.txt", 400)

            state_c = monitor._generate_plugin_state()

            assert state_b != state_c
            assert 4 == len(state_c)

            # Should detect changes in files
            self._prepare_test_file(f"{tempdir}/file4.txt", 450)

            state_d = monitor._generate_plugin_state()

            assert state_c != state_d
            assert 4 == len(state_d)

            # Should support large files
            self._prepare_test_file(f"{tempdir}/file4.txt", 4000000)

            state_d = monitor._generate_plugin_state()

            assert state_c != state_d
            assert 4 == len(state_d)
Ejemplo n.º 5
0
class TestGunicornMonitor(unittest.TestCase):
    def setUp(self) -> None:
        self.monitor = GunicornMonitor(
            gunicorn_master_pid=1,
            num_workers_expected=4,
            master_timeout=60,
            worker_refresh_interval=60,
            worker_refresh_batch_size=2,
            reload_on_plugin_change=True,
        )
        mock.patch.object(self.monitor,
                          '_generate_plugin_state',
                          return_value={}).start()
        mock.patch.object(self.monitor,
                          '_get_num_ready_workers_running',
                          return_value=4).start()
        mock.patch.object(self.monitor,
                          '_get_num_workers_running',
                          return_value=4).start()
        mock.patch.object(self.monitor,
                          '_spawn_new_workers',
                          return_value=None).start()
        mock.patch.object(self.monitor, '_kill_old_workers',
                          return_value=None).start()
        mock.patch.object(self.monitor, '_reload_gunicorn',
                          return_value=None).start()

    @mock.patch('airflow.cli.commands.webserver_command.sleep')
    def test_should_wait_for_workers_to_start(self, mock_sleep):
        self.monitor._get_num_ready_workers_running.return_value = 0
        self.monitor._get_num_workers_running.return_value = 4
        self.monitor._check_workers()
        self.monitor._spawn_new_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_not_called()  # pylint: disable=no-member

    @mock.patch('airflow.cli.commands.webserver_command.sleep')
    def test_should_kill_excess_workers(self, mock_sleep):
        self.monitor._get_num_ready_workers_running.return_value = 10
        self.monitor._get_num_workers_running.return_value = 10
        self.monitor._check_workers()
        self.monitor._spawn_new_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_called_once_with(2)  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_not_called()  # pylint: disable=no-member

    @mock.patch('airflow.cli.commands.webserver_command.sleep')
    def test_should_start_new_workers_when_missing(self, mock_sleep):
        self.monitor._get_num_ready_workers_running.return_value = 3
        self.monitor._get_num_workers_running.return_value = 3
        self.monitor._check_workers()
        # missing one worker, starting just 1
        self.monitor._spawn_new_workers.assert_called_once_with(1)  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_not_called()  # pylint: disable=no-member

    @mock.patch('airflow.cli.commands.webserver_command.sleep')
    def test_should_start_new_batch_when_missing_many_workers(
            self, mock_sleep):
        self.monitor._get_num_ready_workers_running.return_value = 1
        self.monitor._get_num_workers_running.return_value = 1
        self.monitor._check_workers()
        # missing 3 workers, but starting single batch (2)
        self.monitor._spawn_new_workers.assert_called_once_with(2)  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_not_called()  # pylint: disable=no-member

    @mock.patch('airflow.cli.commands.webserver_command.sleep')
    def test_should_start_new_workers_when_refresh_interval_has_passed(
            self, mock_sleep):
        self.monitor._last_refresh_time -= 200
        self.monitor._check_workers()
        self.monitor._spawn_new_workers.assert_called_once_with(2)  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_not_called()  # pylint: disable=no-member
        assert abs(self.monitor._last_refresh_time - time.monotonic()) < 5

    @mock.patch('airflow.cli.commands.webserver_command.sleep')
    def test_should_reload_when_plugin_has_been_changed(self, mock_sleep):
        self.monitor._generate_plugin_state.return_value = {'AA': 12}

        self.monitor._check_workers()

        self.monitor._spawn_new_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_not_called()  # pylint: disable=no-member

        self.monitor._generate_plugin_state.return_value = {'AA': 32}

        self.monitor._check_workers()

        self.monitor._spawn_new_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_not_called()  # pylint: disable=no-member

        self.monitor._generate_plugin_state.return_value = {'AA': 32}

        self.monitor._check_workers()

        self.monitor._spawn_new_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._kill_old_workers.assert_not_called()  # pylint: disable=no-member
        self.monitor._reload_gunicorn.assert_called_once_with()  # pylint: disable=no-member
        assert abs(self.monitor._last_refresh_time - time.monotonic()) < 5