add systemd sd_notify support (#1897)

* add systemd sd_notify support

roughly based on sd_notify() from systemd and https://github.com/bb4242/sdnotify

only implements `READY=1` and `STATUS=Gunicorn arbiter booted` of the
protocol in the arbiter. in the future, reloads can be notified, and
possibly also other statuses.

see https://www.freedesktop.org/software/systemd/man/sd_notify.html for
more info

sd_notify() is a noop when not run in a systemd service (i.e
NOTIFY_SOCKET environment variable is not set)
This commit is contained in:
Дамјан Георгиевски 2019-01-11 04:41:09 +01:00 committed by Benoit Chesneau
parent ad1afe7b79
commit 9184ae8898
3 changed files with 34 additions and 1 deletions

View File

@ -227,7 +227,7 @@ unix socket:
After=network.target
[Service]
PIDFile=/run/gunicorn/pid
Type=notify
User=someuser
Group=someuser
RuntimeDirectory=gunicorn

View File

@ -158,6 +158,7 @@ class Arbiter(object):
self.log.debug("Arbiter booted")
self.log.info("Listening at: %s (%s)", listeners_str, self.pid)
self.log.info("Using worker: %s", self.cfg.worker_class_str)
systemd.sd_notify("READY=1\nSTATUS=Gunicorn arbiter booted", self.log)
# check worker class requirements
if hasattr(self.worker_class, "check_config"):

View File

@ -4,6 +4,7 @@
# See the NOTICE for more information.
import os
import socket
SD_LISTEN_FDS_START = 3
@ -43,3 +44,34 @@ def listen_fds(unset_environment=True):
os.environ.pop('LISTEN_FDS', None)
return fds
def sd_notify(state, logger, unset_environment=False):
"""Send a notification to systemd. state is a string; see
the man page of sd_notify (http://www.freedesktop.org/software/systemd/man/sd_notify.html)
for a description of the allowable values.
If the unset_environment parameter is True, sd_notify() will unset
the $NOTIFY_SOCKET environment variable before returning (regardless of
whether the function call itself succeeded or not). Further calls to
sd_notify() will then fail, but the variable is no longer inherited by
child processes.
"""
addr = os.environ.get('NOTIFY_SOCKET')
if addr is None:
# not run in a service, just a noop
return
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM | socket.SOCK_CLOEXEC)
if addr[0] == '@':
addr = '\0' + addr[1:]
sock.connect(addr)
sock.sendall(state.encode('utf-8'))
except:
logger.debug("Exception while invoking sd_notify()", exc_info=True)
finally:
if unset_environment:
os.environ.pop('NOTIFY_SOCKET')
sock.close()