From 8c0fd3f1386cc71c618a6711f03a9f220692bd07 Mon Sep 17 00:00:00 2001 From: Tim Burke Date: Wed, 11 Dec 2019 01:07:19 +0000 Subject: [PATCH] py3: Make seamless reloads work Starting with Python 3.4, newly-created file descriptors are non-inheritable [0], which causes trouble when we try to use a pipe for IPC. Fortunately, the same PEP that implemented this change *also* provided a new API to mark file descriptors as being inheritable -- so just do that. While we're at it, * Fix up the probe tests to work on py3 * Fix up the probe tests to work when policy-0 is erasure-coded * Decode the bytes read so py3 doesn't log a b'pid' * Log a warning if the read() is empty; something surely went wrong in the re-exec [0] https://www.python.org/dev/peps/pep-0446/ Change-Id: I2a8a9f3dc78abb99bf9cbcf6b44c32ca644bb07b Related-Change: I3e5229d2fb04be67e53533ff65b0870038accbb7 --- swift/common/wsgi.py | 16 +++++++++++++--- test/probe/test_signals.py | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/swift/common/wsgi.py b/swift/common/wsgi.py index c71d412860..ee80038e8c 100644 --- a/swift/common/wsgi.py +++ b/swift/common/wsgi.py @@ -1288,6 +1288,9 @@ def run_wsgi(conf_path, app_section, *args, **kwargs): myself = os.path.realpath(sys.argv[0]) logger.info("Old server PID=%d re'execing as: %r", orig_server_pid, [myself] + list(sys.argv)) + if hasattr(os, 'set_inheritable'): + # See https://www.python.org/dev/peps/pep-0446/ + os.set_inheritable(write_fd, True) os.execv(myself, sys.argv) logger.error('Somehow lived past os.execv()?!') exit('Somehow lived past os.execv()?!') @@ -1299,12 +1302,19 @@ def run_wsgi(conf_path, app_section, *args, **kwargs): os.getpid(), orig_server_pid) try: got_pid = os.read(read_fd, 30) - logger.info('Old server temporary child PID=%d notified ' - 'to shutdown old listen sockets by PID=%s', - os.getpid(), got_pid) except Exception as e: logger.warning('Unexpected exception while reading from ' 'pipe:', exc_info=True) + else: + got_pid = got_pid.decode('ascii') + if got_pid: + logger.info('Old server temporary child PID=%d notified ' + 'to shutdown old listen sockets by PID=%s', + os.getpid(), got_pid) + else: + logger.warning('Old server temporary child PID=%d *NOT* ' + 'notified to shutdown old listen sockets; ' + 'the pipe just *died*.', os.getpid()) try: os.close(read_fd) except Exception: diff --git a/test/probe/test_signals.py b/test/probe/test_signals.py index dbb3b01f45..c787265f5b 100644 --- a/test/probe/test_signals.py +++ b/test/probe/test_signals.py @@ -27,7 +27,6 @@ from uuid import uuid4 from six.moves import http_client as httplib -from swift.common.storage_policy import POLICIES from swift.common.ring import Ring from swift.common.manager import Manager @@ -230,9 +229,9 @@ class TestObjectServerReloadBase(TestWSGIServerProcessHandling): PID_TIMEOUT = 35 def get_ip_port(self): - policy = random.choice(list(POLICIES)) - policy.load_ring('/etc/swift') - self.ring_node = random.choice(policy.object_ring.get_part_nodes(1)) + self.policy.load_ring('/etc/swift') + self.ring_node = random.choice( + self.policy.object_ring.get_part_nodes(1)) return self.ring_node['ip'], self.ring_node['port'] def start_write_req(self, conn, suffix): @@ -240,7 +239,8 @@ class TestObjectServerReloadBase(TestWSGIServerProcessHandling): self.ring_node['device'], self.account, self.container, suffix), headers={'X-Timestamp': str(time.time()), 'Content-Type': 'application/octet-string', - 'Content-Length': len(self.BODY)}) + 'Content-Length': len(self.BODY), + 'X-Backend-Storage-Policy-Index': str(self.policy.idx)}) def finish_write_req(self, conn): conn.send(self.BODY) @@ -250,12 +250,12 @@ class TestObjectServerReloadBase(TestWSGIServerProcessHandling): got_body = resp.read() self.assertEqual(resp.status // 100, 2, 'Got status %d; %r' % (resp.status, got_body)) - self.assertEqual('', got_body) + self.assertEqual(b'', got_body) return resp class TestObjectServerReload(OldReloadMixin, TestObjectServerReloadBase): - BODY = 'test-object' * 10 + BODY = b'test-object' * 10 def test_object_reload(self): self._check_reload() @@ -263,7 +263,7 @@ class TestObjectServerReload(OldReloadMixin, TestObjectServerReloadBase): class TestObjectServerReloadSeamless(SeamlessReloadMixin, TestObjectServerReloadBase): - BODY = 'test-object' * 10 + BODY = b'test-object' * 10 def test_object_reload_seamless(self): self._check_reload() @@ -331,12 +331,12 @@ class TestProxyServerReloadBase(TestWSGIServerProcessHandling): got_body = resp.read() self.assertEqual(resp.status // 100, 2, 'Got status %d; %r' % (resp.status, got_body)) - self.assertEqual('', got_body) + self.assertEqual(b'', got_body) return resp class TestProxyServerReload(OldReloadMixin, TestProxyServerReloadBase): - BODY = 'proxy' * 10 + BODY = b'proxy' * 10 def test_proxy_reload(self): self._check_reload() @@ -344,7 +344,7 @@ class TestProxyServerReload(OldReloadMixin, TestProxyServerReloadBase): class TestProxyServerReloadSeamless(SeamlessReloadMixin, TestProxyServerReloadBase): - BODY = 'proxy-seamless' * 10 + BODY = b'proxy-seamless' * 10 def test_proxy_reload_seamless(self): self._check_reload()