From 9184ae889894912b105e8b39a3dde8c07c780047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BC=D1=98=D0=B0=D0=BD=20=D0=93=D0=B5=D0=BE?= =?UTF-8?q?=D1=80=D0=B3=D0=B8=D0=B5=D0=B2=D1=81=D0=BA=D0=B8?= Date: Fri, 11 Jan 2019 04:41:09 +0100 Subject: [PATCH] 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) --- docs/source/deploy.rst | 2 +- gunicorn/arbiter.py | 1 + gunicorn/systemd.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/source/deploy.rst b/docs/source/deploy.rst index c7a0212c..7a289aed 100644 --- a/docs/source/deploy.rst +++ b/docs/source/deploy.rst @@ -227,7 +227,7 @@ unix socket: After=network.target [Service] - PIDFile=/run/gunicorn/pid + Type=notify User=someuser Group=someuser RuntimeDirectory=gunicorn diff --git a/gunicorn/arbiter.py b/gunicorn/arbiter.py index adca13d3..7eaa2c17 100644 --- a/gunicorn/arbiter.py +++ b/gunicorn/arbiter.py @@ -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"): diff --git a/gunicorn/systemd.py b/gunicorn/systemd.py index 10ffb8d8..cea48220 100644 --- a/gunicorn/systemd.py +++ b/gunicorn/systemd.py @@ -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()