Merge "Make wsgi server uses systemd's NOTIFY_SOCKET"

This commit is contained in:
Zuul 2020-04-02 01:40:43 +00:00 committed by Gerrit Code Review
commit b4b0ebd4aa
3 changed files with 94 additions and 1 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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):