Merge "Make wsgi server uses systemd's NOTIFY_SOCKET"
This commit is contained in:
commit
b4b0ebd4aa
@ -5820,3 +5820,30 @@ def get_db_files(db_path):
|
||||
continue
|
||||
results.append(os.path.join(db_dir, f))
|
||||
return sorted(results)
|
||||
|
||||
|
||||
def systemd_notify(logger=None):
|
||||
"""
|
||||
Notify the service manager that started this process, if it is
|
||||
systemd-compatible, that this process correctly started. To do so,
|
||||
it communicates through a Unix socket stored in environment variable
|
||||
NOTIFY_SOCKET. More information can be found in systemd documentation:
|
||||
https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||
|
||||
:param logger: a logger object
|
||||
"""
|
||||
msg = b'READY=1'
|
||||
notify_socket = os.getenv('NOTIFY_SOCKET')
|
||||
if notify_socket:
|
||||
if notify_socket.startswith('@'):
|
||||
# abstract namespace socket
|
||||
notify_socket = '\0%s' % notify_socket[1:]
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
with closing(sock):
|
||||
try:
|
||||
sock.connect(notify_socket)
|
||||
sock.sendall(msg)
|
||||
del os.environ['NOTIFY_SOCKET']
|
||||
except EnvironmentError:
|
||||
if logger:
|
||||
logger.debug("Systemd notification failed", exc_info=True)
|
||||
|
@ -45,7 +45,7 @@ from swift.common.swob import Request, wsgi_quote, wsgi_unquote, \
|
||||
from swift.common.utils import capture_stdio, disable_fallocate, \
|
||||
drop_privileges, get_logger, NullLogger, config_true_value, \
|
||||
validate_configuration, get_hub, config_auto_int_value, \
|
||||
reiterate, clean_up_daemon_hygiene
|
||||
reiterate, clean_up_daemon_hygiene, systemd_notify
|
||||
|
||||
SIGNUM_TO_NAME = {getattr(signal, n): n for n in dir(signal)
|
||||
if n.startswith('SIG') and '_' not in n}
|
||||
@ -1185,6 +1185,9 @@ def run_wsgi(conf_path, app_section, *args, **kwargs):
|
||||
os.write(reexec_signal_fd, str(os.getpid()).encode('utf8'))
|
||||
os.close(reexec_signal_fd)
|
||||
|
||||
# Finally, signal systemd (if appropriate) that process started properly.
|
||||
systemd_notify(logger=logger)
|
||||
|
||||
no_fork_sock = strategy.no_fork_sock()
|
||||
if no_fork_sock:
|
||||
run_server(conf, logger, no_fork_sock, global_conf=global_conf)
|
||||
|
@ -4372,6 +4372,69 @@ cluster_dfw1 = http://dfw1.host/v1/
|
||||
utils.load_pkg_resource(*args)
|
||||
self.assertEqual("Unhandled URI scheme: 'nog'", str(cm.exception))
|
||||
|
||||
def test_systemd_notify(self):
|
||||
m_sock = mock.Mock(connect=mock.Mock(), sendall=mock.Mock())
|
||||
with mock.patch('swift.common.utils.socket.socket',
|
||||
return_value=m_sock) as m_socket:
|
||||
# No notification socket
|
||||
m_socket.reset_mock()
|
||||
m_sock.reset_mock()
|
||||
utils.systemd_notify()
|
||||
self.assertEqual(m_socket.call_count, 0)
|
||||
self.assertEqual(m_sock.connect.call_count, 0)
|
||||
self.assertEqual(m_sock.sendall.call_count, 0)
|
||||
|
||||
# File notification socket
|
||||
m_socket.reset_mock()
|
||||
m_sock.reset_mock()
|
||||
os.environ['NOTIFY_SOCKET'] = 'foobar'
|
||||
utils.systemd_notify()
|
||||
m_socket.assert_called_once_with(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
m_sock.connect.assert_called_once_with('foobar')
|
||||
m_sock.sendall.assert_called_once_with(b'READY=1')
|
||||
self.assertNotIn('NOTIFY_SOCKET', os.environ)
|
||||
|
||||
# Abstract notification socket
|
||||
m_socket.reset_mock()
|
||||
m_sock.reset_mock()
|
||||
os.environ['NOTIFY_SOCKET'] = '@foobar'
|
||||
utils.systemd_notify()
|
||||
m_socket.assert_called_once_with(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
m_sock.connect.assert_called_once_with('\0foobar')
|
||||
m_sock.sendall.assert_called_once_with(b'READY=1')
|
||||
self.assertNotIn('NOTIFY_SOCKET', os.environ)
|
||||
|
||||
# Test logger with connection error
|
||||
m_sock = mock.Mock(connect=mock.Mock(side_effect=EnvironmentError),
|
||||
sendall=mock.Mock())
|
||||
m_logger = mock.Mock(debug=mock.Mock())
|
||||
with mock.patch('swift.common.utils.socket.socket',
|
||||
return_value=m_sock) as m_socket:
|
||||
os.environ['NOTIFY_SOCKET'] = '@foobar'
|
||||
m_sock.reset_mock()
|
||||
m_logger.reset_mock()
|
||||
utils.systemd_notify()
|
||||
self.assertEqual(0, m_sock.sendall.call_count)
|
||||
self.assertEqual(0, m_logger.debug.call_count)
|
||||
|
||||
m_sock.reset_mock()
|
||||
m_logger.reset_mock()
|
||||
utils.systemd_notify(logger=m_logger)
|
||||
self.assertEqual(0, m_sock.sendall.call_count)
|
||||
m_logger.debug.assert_called_once_with(
|
||||
"Systemd notification failed", exc_info=True)
|
||||
|
||||
# Test it for real
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
sock.settimeout(5)
|
||||
sock.bind('\0foobar')
|
||||
os.environ['NOTIFY_SOCKET'] = '@foobar'
|
||||
utils.systemd_notify()
|
||||
msg = sock.recv(512)
|
||||
sock.close()
|
||||
self.assertEqual(msg, b'READY=1')
|
||||
self.assertNotIn('NOTIFY_SOCKET', os.environ)
|
||||
|
||||
|
||||
class ResellerConfReader(unittest.TestCase):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user