Projet

Général

Profil

0002-add-uwsgidecorators-module-57019.patch

Benjamin Dauvergne, 01 octobre 2021 15:39

Télécharger (5,31 ko)

Voir les différences:

Subject: [PATCH 2/2] add uwsgidecorators module (#57019)

 hobo/multitenant/uwsgidecorators.py       | 102 ++++++++++++++++++++++
 tests_multitenant/test_uwsgidecorators.py |  59 +++++++++++++
 2 files changed, 161 insertions(+)
 create mode 100644 hobo/multitenant/uwsgidecorators.py
 create mode 100644 tests_multitenant/test_uwsgidecorators.py
hobo/multitenant/uwsgidecorators.py
1
# Copyright (C) 2021 Entr'ouvert
2

  
3
import contextlib
4
import logging
5
import pickle
6
import sys
7

  
8
try:
9
    import uwsgi
10
except ImportError:
11
    uwsgi = None
12

  
13
logger = logging.getLogger(__name__)
14

  
15
spooler_registry = {}
16

  
17

  
18
@contextlib.contextmanager
19
def close_db():
20
    if 'django' in sys.modules:
21
        from django.db import close_old_connections
22

  
23
        close_old_connections()
24
        try:
25
            yield None
26
        finally:
27
            close_old_connections()
28
    else:
29
        yield
30

  
31

  
32
@contextlib.contextmanager
33
def tenant_context(domain):
34
    if domain:
35
        from tenant_schemas.utils import tenant_context
36

  
37
        from hobo.multitenant.middleware import TenantMiddleware
38

  
39
        tenant = TenantMiddleware.get_tenant_by_hostname(domain)
40
        with tenant_context(tenant):
41
            yield
42
    else:
43
        yield
44

  
45

  
46
def get_tenant():
47
    if 'django.db' not in sys.modules:
48
        return ''
49
    from django.db import connection
50

  
51
    tenant_model = getattr(connection, 'tenant', None)
52
    return getattr(tenant_model, 'domain_url', '')
53

  
54

  
55
def spool(func):
56
    if uwsgi:
57
        name = '%s.%s' % (func.__module__, func.__name__)
58
        spooler_registry[name] = func
59

  
60
        def spool_function(*args, **kwargs):
61
            uwsgi.spool(
62
                name=name.encode(),
63
                tenant=get_tenant().encode(),
64
                body=pickle.dumps({'args': args, 'kwargs': kwargs}),
65
            )
66
            logger.debug('spooler: spooled function %s', name)
67

  
68
        func.spool = spool_function
69
    return func
70

  
71

  
72
if uwsgi:
73

  
74
    def spooler_function(env):
75
        try:
76
            try:
77
                name = env.get('name').decode()
78
                tenant = env.get('tenant', b'').decode()
79
                body = env.get('body')
80
            except Exception:
81
                logger.error('spooler: no name or body found: env.keys()=%s', env.keys())
82
                return uwsgi.SPOOL_OK
83
            try:
84
                params = pickle.loads(body)
85
                args = params['args']
86
                kwargs = params['kwargs']
87
            except Exception:
88
                logger.exception('spooler: depickling of body failed')
89
                return uwsgi.SPOOL_OK
90
            try:
91
                function = spooler_registry[name]
92
            except KeyError:
93
                logger.error('spooler: no function named "%s"', name)
94
            # prevent connections to leak between jobs
95
            # maintain current tenant when spool is launched
96
            with close_db(), tenant_context(tenant):
97
                function(*args, **kwargs)
98
        except Exception:
99
            logger.exception('spooler: function "%s" raised' % name)
100
        return uwsgi.SPOOL_OK
101

  
102
    uwsgi.spooler = spooler_function
tests_multitenant/test_uwsgidecorators.py
1
import importlib
2
import pickle
3

  
4
import mock
5
import pytest
6

  
7
import hobo.multitenant.uwsgidecorators
8

  
9

  
10
@pytest.fixture
11
def uwsgi():
12
    import sys
13

  
14
    uwsgi = mock.Mock()
15
    uwsgi.SPOOL_OK = -2
16
    sys.modules['uwsgi'] = uwsgi
17
    importlib.reload(hobo.multitenant.uwsgidecorators)
18
    yield uwsgi
19
    del sys.modules['uwsgi']
20
    importlib.reload(hobo.multitenant.uwsgidecorators)
21

  
22

  
23
def test_basic():
24
    @hobo.multitenant.uwsgidecorators.spool
25
    def function(a, b):
26
        pass
27

  
28
    function(1, 2)
29
    with pytest.raises(AttributeError):
30
        function.spool(1, 2)
31

  
32

  
33
def test_mocked_uwsgi(uwsgi):
34
    @hobo.multitenant.uwsgidecorators.spool
35
    def function(a, b):
36
        pass
37

  
38
    function(1, 2)
39
    function.spool(1, 2)
40
    assert set(uwsgi.spool.call_args[1].keys()) == {'body', 'tenant', 'name'}
41
    assert pickle.loads(uwsgi.spool.call_args[1]['body']) == {'args': (1, 2), 'kwargs': {}}
42
    assert uwsgi.spool.call_args[1]['name'] == b'test_uwsgidecorators.function'
43
    assert uwsgi.spool.call_args[1]['tenant'] == b''
44

  
45

  
46
def test_mocked_uwsgi_tenant(uwsgi, tenant):
47
    from tenant_schemas.utils import tenant_context
48

  
49
    @hobo.multitenant.uwsgidecorators.spool
50
    def function(a, b):
51
        pass
52

  
53
    with tenant_context(tenant):
54
        function.spool(1, 2)
55

  
56
    assert set(uwsgi.spool.call_args[1].keys()) == {'body', 'tenant', 'name'}
57
    assert pickle.loads(uwsgi.spool.call_args[1]['body']) == {'args': (1, 2), 'kwargs': {}}
58
    assert uwsgi.spool.call_args[1]['name'] == b'test_uwsgidecorators.function'
59
    assert uwsgi.spool.call_args[1]['tenant'] == b'tenant.example.net'
0
-