Exemplo n.º 1
0
    def __init__(self, conf, db):
        """
        Initialize a LimitContainer.  This sets up an appropriate
        control daemon, as well as providing a container for the
        limits themselves.

        :param conf: A turnstile.config.Config instance containing the
                     configuration for the ControlDaemon.
        :param db: A database handle for the Redis database.
        """

        self.conf = conf
        self.db = db
        self.limits = []
        self.limit_map = {}
        self.limit_sum = None

        # Initialize the control daemon
        if conf.to_bool(conf['control'].get('remote', 'no'), False):
            self.control_daemon = remote.RemoteControlDaemon(self, conf)
        else:
            self.control_daemon = control.ControlDaemon(self, conf)

        # Now start the control daemon
        self.control_daemon.start()
Exemplo n.º 2
0
    def test_reload_exception_altkeys(self, mock_format_exc, mock_exception):
        cd = control.ControlDaemon(
            'middleware',
            config.Config(
                conf_dict={
                    'control.errors_key': 'alt_err',
                    'control.errors_channel': 'alt_chan',
                }))
        cd.pending = mock.Mock(**{'acquire.return_value': True})
        cd.limits = mock.Mock(
            **{
                'set_limits.side_effect': test_utils.TestException,
            })
        cd._db = mock.Mock(**{'zrange.return_value': ['limit1', 'limit2']})

        cd.reload()

        cd.pending.assert_has_calls([
            mock.call.acquire(False),
            mock.call.release(),
        ])
        self.assertEqual(len(cd.pending.method_calls), 2)
        cd.limits.set_limits.assert_called_once_with(['limit1', 'limit2'])
        cd._db.assert_has_calls([
            mock.call.zrange('limits', 0, -1),
            mock.call.sadd('alt_err', 'Failed to load limits: <traceback>'),
            mock.call.publish('alt_chan',
                              'Failed to load limits: <traceback>'),
        ])
        self.assertEqual(len(cd._db.method_calls), 3)
        mock_exception.assert_called_once_with('Could not load limits')
        mock_format_exc.assert_called_once_with()
Exemplo n.º 3
0
    def test_start(self, mock_reload, mock_spawn_n):
        cd = control.ControlDaemon('middleware', 'config')

        cd.start()

        mock_spawn_n.assert_called_once_with(cd.listen)
        self.assertEqual(cd.listen_thread, 'listen_thread')
        mock_reload.assert_called_once_with()
Exemplo n.º 4
0
    def test_init(self):
        cd = control.ControlDaemon('middleware', 'config')

        self.assertEqual(cd._db, None)
        self.assertEqual(cd.middleware, 'middleware')
        self.assertEqual(cd.config, 'config')
        self.assertIsInstance(cd.limits, control.LimitData)
        self.assertIsInstance(cd.pending, eventlet.semaphore.Semaphore)
        self.assertEqual(cd.listen_thread, None)
Exemplo n.º 5
0
    def test_reload_noacquire(self, mock_format_exc, mock_exception):
        cd = control.ControlDaemon('middleware', config.Config())
        cd.pending = mock.Mock(**{'acquire.return_value': False})
        cd.limits = mock.Mock()
        cd._db = mock.Mock()

        cd.reload()

        cd.pending.assert_has_calls([
            mock.call.acquire(False),
        ])
        self.assertEqual(len(cd.pending.method_calls), 1)
        self.assertEqual(len(cd.limits.method_calls), 0)
        self.assertEqual(len(cd._db.method_calls), 0)
        self.assertFalse(mock_exception.called)
        self.assertFalse(mock_format_exc.called)
Exemplo n.º 6
0
    def test_listen_shardhint(self, mock_get_database):
        pubsub = mock.Mock(**{'listen.return_value': []})
        db = mock.Mock(**{'pubsub.return_value': pubsub})
        mock_get_database.return_value = db
        cd = control.ControlDaemon(
            'middleware',
            config.Config(conf_dict={
                'control.shard_hint': 'shard',
            }))

        cd.listen()

        mock_get_database.assert_called_once_with('control')
        db.pubsub.assert_called_once_with(shard_hint='shard')
        pubsub.assert_has_calls([
            mock.call.subscribe('control'),
            mock.call.listen(),
        ])
Exemplo n.º 7
0
    def test_reload(self, mock_format_exc, mock_exception):
        cd = control.ControlDaemon('middleware', config.Config())
        cd.pending = mock.Mock(**{'acquire.return_value': True})
        cd.limits = mock.Mock()
        cd._db = mock.Mock(**{'zrange.return_value': ['limit1', 'limit2']})

        cd.reload()

        cd.pending.assert_has_calls([
            mock.call.acquire(False),
            mock.call.release(),
        ])
        self.assertEqual(len(cd.pending.method_calls), 2)
        cd.limits.set_limits.assert_called_once_with(['limit1', 'limit2'])
        cd._db.assert_has_calls([
            mock.call.zrange('limits', 0, -1),
        ])
        self.assertEqual(len(cd._db.method_calls), 1)
        self.assertFalse(mock_exception.called)
        self.assertFalse(mock_format_exc.called)
Exemplo n.º 8
0
    def test_db_middleware(self):
        middleware = mock.Mock(db='midware_db')
        cd = control.ControlDaemon(middleware, config.Config())

        self.assertEqual(cd.db, 'midware_db')
Exemplo n.º 9
0
    def test_db_present(self):
        middleware = mock.Mock(db='midware_db')
        cd = control.ControlDaemon(middleware, config.Config())
        cd._db = 'cached_db'

        self.assertEqual(cd.db, 'cached_db')
Exemplo n.º 10
0
    def test_get_limits(self):
        cd = control.ControlDaemon('middleware', 'config')
        cd.limits = 'limits'

        self.assertEqual(cd.get_limits(), 'limits')
Exemplo n.º 11
0
    def test_listen_altchan(self, mock_exception, mock_error,
                            mock_get_database, mock_find_entrypoint):
        pubsub = mock.Mock(
            **{
                'listen.return_value': [
                    {
                        'type': 'other',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                ]
            })
        db = mock.Mock(**{'pubsub.return_value': pubsub})
        mock_get_database.return_value = db
        cd = control.ControlDaemon(
            'middleware',
            config.Config(conf_dict={
                'control.channel': 'other',
            }))

        cd.listen()

        mock_get_database.assert_called_once_with('control')
        db.pubsub.assert_called_once_with()
        pubsub.assert_has_calls([
            mock.call.subscribe('other'),
            mock.call.listen(),
        ])
        self.assertFalse(mock_error.called)
        self.assertFalse(mock_exception.called)
        control.ControlDaemon._commands['ping'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
        ])
        self.assertFalse(control.ControlDaemon._commands['_ping'].called)
        self.assertFalse(control.ControlDaemon._commands['fail'].called)
        self.assertFalse(mock_find_entrypoint.called)
Exemplo n.º 12
0
    def test_listen(self, mock_exception, mock_error, mock_get_database,
                    mock_find_entrypoint):
        entrypoints = dict(discovered=mock.Mock())
        mock_find_entrypoint.side_effect = (
            lambda x, y, compat: entrypoints.get(y))
        pubsub = mock.Mock(
            **{
                'listen.return_value': [
                    {
                        'type': 'other',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'discovered',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'discovered',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'discovered:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'discovered:arg1:arg2',
                    },
                ]
            })
        db = mock.Mock(**{'pubsub.return_value': pubsub})
        mock_get_database.return_value = db
        cd = control.ControlDaemon('middleware', config.Config())

        cd.listen()

        mock_get_database.assert_called_once_with('control')
        db.pubsub.assert_called_once_with()
        pubsub.assert_has_calls([
            mock.call.subscribe('control'),
            mock.call.listen(),
        ])
        mock_error.assert_has_calls([
            mock.call("Cannot call internal command '_ping'"),
            mock.call("Cannot call internal command '_ping'"),
            mock.call("No such command 'nosuch'"),
            mock.call("No such command 'nosuch'"),
        ])
        mock_exception.assert_has_calls([
            mock.call("Failed to execute command 'fail' arguments []"),
            mock.call("Failed to execute command 'fail' arguments []"),
            mock.call("Failed to execute command 'fail' arguments "
                      "['arg1', 'arg2']"),
            mock.call("Failed to execute command 'fail' arguments "
                      "['arg1', 'arg2']"),
        ])
        control.ControlDaemon._commands['ping'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
            mock.call(cd, 'arg1', 'arg2'),
            mock.call(cd, 'arg1', 'arg2'),
        ])
        self.assertFalse(control.ControlDaemon._commands['_ping'].called)
        control.ControlDaemon._commands['fail'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
            mock.call(cd, 'arg1', 'arg2'),
            mock.call(cd, 'arg1', 'arg2'),
        ])
        entrypoints['discovered'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
            mock.call(cd, 'arg1', 'arg2'),
            mock.call(cd, 'arg1', 'arg2'),
        ])
        mock_find_entrypoint.assert_has_calls([
            mock.call('turnstile.command', 'nosuch', compat=False),
            mock.call('turnstile.command', 'discovered', compat=False),
        ])
        self.assertEqual(len(mock_find_entrypoint.mock_calls), 2)
        self.assertEqual(control.ControlDaemon._commands['discovered'],
                         entrypoints['discovered'])
        self.assertEqual(control.ControlDaemon._commands['nosuch'], None)
Exemplo n.º 13
0
    def __init__(self, app, local_conf):
        """
        Initialize the turnstile middleware.  Saves the configuration
        and sets up the list of preprocessors, connects to the
        database, and initiates the control daemon thread.
        """

        # Save the application
        self.app = app
        self.limits = []
        self.limit_sum = None
        self.mapper = None
        self.mapper_lock = eventlet.semaphore.Semaphore()

        # Save the configuration
        self.conf = config.Config(conf_dict=local_conf)

        # We will lazy-load the database
        self._db = None

        # Set up request pre- and post-processors
        self.preprocessors = []
        self.postprocessors = []
        enable = self.conf.get('enable')
        if enable is not None:
            # Use the enabler syntax
            for proc in enable.split():
                # Try the preprocessor
                preproc = utils.find_entrypoint('turnstile.preprocessor',
                                                proc,
                                                compat=False)
                if preproc:
                    self.preprocessors.append(preproc)

                # Now the postprocessor
                postproc = utils.find_entrypoint('turnstile.postprocessor',
                                                 proc,
                                                 compat=False)
                if postproc:
                    # Note the reversed order
                    self.postprocessors.insert(0, postproc)
        else:
            # Using the classic syntax; grab preprocessors...
            for preproc in self.conf.get('preprocess', '').split():
                klass = utils.find_entrypoint('turnstile.preprocessor',
                                              preproc,
                                              required=True)
                self.preprocessors.append(klass)

            # And now the postprocessors...
            for postproc in self.conf.get('postprocess', '').split():
                klass = utils.find_entrypoint('turnstile.postprocessor',
                                              postproc,
                                              required=True)
                self.postprocessors.append(klass)

        # Set up the alternative formatter
        formatter = self.conf.get('formatter')
        if formatter:
            formatter = utils.find_entrypoint('turnstile.formatter',
                                              formatter,
                                              required=True)
            self.formatter = lambda a, b, c, d, e: formatter(
                self.conf.status, a, b, c, d, e)
        else:
            self.formatter = self.format_delay

        # Initialize the control daemon
        if self.conf.to_bool(self.conf['control'].get('remote', 'no'), False):
            self.control_daemon = remote.RemoteControlDaemon(self, self.conf)
        else:
            self.control_daemon = control.ControlDaemon(self, self.conf)

        # Now start the control daemon
        self.control_daemon.start()

        # Emit a log message to indicate that we're running
        LOG.info("Turnstile middleware initialized")