From ee19e189fe08209e8fe465044001a5913735c1e0 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Tue, 9 Mar 2021 10:48:30 +0100 Subject: [PATCH 1/3] misc: add uwsgi spooler (#43153) --- debian/authentic2-multitenant-uwsgi.ini | 3 + debian/authentic2-uwsgi.ini | 3 + debian/authentic2.dirs | 1 + debian/authentic2.init | 1 + debian/authentic2.postinst | 1 + debian/authentic2.service | 3 +- debian/control | 1 + src/authentic2/utils/spooler.py | 86 +++++++++++++++++++++++++ 8 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/authentic2/utils/spooler.py diff --git a/debian/authentic2-multitenant-uwsgi.ini b/debian/authentic2-multitenant-uwsgi.ini index 2ed63348..a5063247 100644 --- a/debian/authentic2-multitenant-uwsgi.ini +++ b/debian/authentic2-multitenant-uwsgi.ini @@ -12,6 +12,9 @@ http-socket = /run/authentic2-multitenant/authentic2-multitenant.sock chmod-socket = 666 vacuum = true +spooler-processes = 3 +spooler-python-import = authentic2.utils.spooler + master = true enable-threads = true harakiri = 120 diff --git a/debian/authentic2-uwsgi.ini b/debian/authentic2-uwsgi.ini index 97b2abb1..9b07db57 100644 --- a/debian/authentic2-uwsgi.ini +++ b/debian/authentic2-uwsgi.ini @@ -12,6 +12,9 @@ http-socket = /run/authentic2/authentic2.sock chmod-socket = 666 vacuum = true +spooler-processes = 3 +spooler-python-import = authentic2.utils.spooler + master = true enable-threads = true harakiri = 120 diff --git a/debian/authentic2.dirs b/debian/authentic2.dirs index 31134cff..583f2be2 100644 --- a/debian/authentic2.dirs +++ b/debian/authentic2.dirs @@ -6,4 +6,5 @@ var/lib/authentic2/static var/lib/authentic2/collectstatic var/lib/authentic2/locale var/lib/authentic2/templates +var/lib/authentic2/spooler var/log/authentic2 diff --git a/debian/authentic2.init b/debian/authentic2.init index 560fb97d..b79f8228 100644 --- a/debian/authentic2.init +++ b/debian/authentic2.init @@ -38,6 +38,7 @@ GROUP=authentic2 DAEMON_ARGS=${DAEMON_ARGS:-"--pidfile=$PIDFILE --uid $USER --gid $GROUP --ini /etc/$NAME/$NAME-uwsgi.ini +--spooler /var/lib/$NAME/spooler/ --daemonize /var/log/uwsgi.$NAME.log"} # Load the VERBOSE setting and other rcS variables diff --git a/debian/authentic2.postinst b/debian/authentic2.postinst index 2a311018..0f0f3f8a 100644 --- a/debian/authentic2.postinst +++ b/debian/authentic2.postinst @@ -98,6 +98,7 @@ case "$1" in $AUTHENTIC_HOME/locale \ $AUTHENTIC_HOME/media \ $AUTHENTIC_HOME/templates \ + $AUTHENTIC_HOME/spooler \ /var/log/$NAME \ ;; diff --git a/debian/authentic2.service b/debian/authentic2.service index 8ee92b22..63cc2f01 100644 --- a/debian/authentic2.service +++ b/debian/authentic2.service @@ -10,7 +10,8 @@ User=authentic2 Group=authentic2 ExecStartPre=/usr/bin/authentic2-manage migrate --noinput ExecStartPre=/usr/bin/authentic2-manage collectstatic --noinput -ExecStart=/usr/lib/authentic2/launch-authentic2.sh --ini /etc/%p/%p-uwsgi.ini +ExecStartPre=/bin/mkdir -p /var/lib/authentic2/spooler/%m/ +ExecStart=/usr/lib/authentic2/launch-authentic2.sh --ini /etc/%p/%p-uwsgi.ini --spooler /var/lib/authentic2/spooler/%m/ ExecReload=/bin/kill -HUP $MAINPID KillSignal=SIGQUIT TimeoutStartSec=0 diff --git a/debian/control b/debian/control index c19cc828..53e79ad5 100644 --- a/debian/control +++ b/debian/control @@ -57,6 +57,7 @@ Depends: ${misc:Depends}, adduser, python3-psycopg2, uwsgi, uwsgi-plugin-python3, + python3-uwsgidecorators, dbconfig-common, debconf | debconf-2.0, ucf Recommends: postgresql-client diff --git a/src/authentic2/utils/spooler.py b/src/authentic2/utils/spooler.py new file mode 100644 index 00000000..9c839e9c --- /dev/null +++ b/src/authentic2/utils/spooler.py @@ -0,0 +1,86 @@ +# authentic2 - versatile identity manager +# Copyright (C) 2010-2021 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import contextlib +import logging +import sys +from functools import wraps + +from django.db import close_old_connections, connection + +USE_UWSGI = 'uwsgi' in sys.modules + + +logger = logging.getLogger(__name__) + + +def ensure_db(func): + """Emulate Django"s setup/teardown of database connections before/after + each request""" + + @wraps(func) + def f(*args, **kwargs): + close_old_connections() + try: + return func(*args, **kwargs) + finally: + close_old_connections() + + return f + + +@contextlib.contextmanager +def tenant_context(domain): + from hobo.multitenant.middleware import TenantMiddleware + from tenant_schemas.utils import tenant_context + + tenant = TenantMiddleware.get_tenant_by_hostname(domain) + with tenant_context(tenant): + yield + + +def tenantspool(func): + """Wrap a function with uwsgidecorators.spool storing and restoring the + current tenant.""" + if not USE_UWSGI: + return func + + from uwsgidecorators import spool + + @ensure_db + @wraps(func) + def spooler_func(*args, **kwargs): + with contextlib.ExitStack() as stack: + if 'domain' in kwargs: + stack.enter_context(tenant_context(kwargs.pop('domain'))) + try: + func(*args, **kwargs) + except Exception: + logger.exception('spooler: exception during %s(%s, %s)' % (func.__name__, args, kwargs)) + else: + logger.info('spooler: success of %s(%s, %s)' % (func.__name__, args, kwargs)) + + # pass arguments as pickles + base_spooler = spool(pass_arguments=True)(spooler_func) + + @wraps(func) + def spooler(*args, **kwargs): + domain = getattr(getattr(connection, 'tenant', None), 'domain_url', None) + if domain is not None: + kwargs['domain'] = domain + return base_spooler(*args, **kwargs) + + return spooler -- 2.20.1