daemon: avoid raising UnboundLocalError to callers
If something in the daemon_start() function fails before server variable is initialized, we get the following exception: UnboundLocalError: local variable 'server' referenced before assignment We should not attempt to close connections or kill all threads for a daemon that failed to start (or that hasn't even reached the moment of the start). Closes-Bug: #1465350 Change-Id: I7769e40c13e3bd740d5b8a949a61d1bcc127f137
This commit is contained in:
parent
293def2d23
commit
e0bf7767da
@ -90,46 +90,48 @@ def daemon_start(config, filters):
|
||||
manager_cls = get_manager_class(config, filters)
|
||||
manager = manager_cls(address=socket_path)
|
||||
server = manager.get_server()
|
||||
# allow everybody to connect to the socket
|
||||
rw_rw_rw_ = (stat.S_IRUSR | stat.S_IWUSR |
|
||||
stat.S_IRGRP | stat.S_IWGRP |
|
||||
stat.S_IROTH | stat.S_IWOTH)
|
||||
os.chmod(socket_path, rw_rw_rw_)
|
||||
try:
|
||||
# In Python 3 we have to use buffer to push in bytes directly
|
||||
stdout = sys.stdout.buffer
|
||||
except AttributeError:
|
||||
stdout = sys.stdout
|
||||
stdout.write(socket_path.encode('utf-8'))
|
||||
stdout.write(b'\n')
|
||||
stdout.write(bytes(server.authkey))
|
||||
sys.stdin.close()
|
||||
sys.stdout.close()
|
||||
sys.stderr.close()
|
||||
# Gracefully shutdown on INT or TERM signals
|
||||
stop = functools.partial(daemon_stop, server)
|
||||
signal.signal(signal.SIGTERM, stop)
|
||||
signal.signal(signal.SIGINT, stop)
|
||||
LOG.info("Starting rootwrap daemon main loop")
|
||||
server.serve_forever()
|
||||
finally:
|
||||
conn = server.listener
|
||||
# This will break accept() loop with EOFError if it was not in the main
|
||||
# thread (as in Python 3.x)
|
||||
conn.close()
|
||||
# Closing all currently connected client sockets for reading to break
|
||||
# worker threads blocked on recv()
|
||||
for cl_conn in conn.get_accepted():
|
||||
# allow everybody to connect to the socket
|
||||
rw_rw_rw_ = (stat.S_IRUSR | stat.S_IWUSR |
|
||||
stat.S_IRGRP | stat.S_IWGRP |
|
||||
stat.S_IROTH | stat.S_IWOTH)
|
||||
os.chmod(socket_path, rw_rw_rw_)
|
||||
try:
|
||||
cl_conn.half_close()
|
||||
except Exception:
|
||||
# Most likely the socket have already been closed
|
||||
LOG.debug("Failed to close connection")
|
||||
LOG.info("Waiting for all client threads to finish.")
|
||||
for thread in threading.enumerate():
|
||||
if thread.daemon:
|
||||
LOG.debug("Joining thread %s", thread)
|
||||
thread.join()
|
||||
# In Python 3 we have to use buffer to push in bytes directly
|
||||
stdout = sys.stdout.buffer
|
||||
except AttributeError:
|
||||
stdout = sys.stdout
|
||||
stdout.write(socket_path.encode('utf-8'))
|
||||
stdout.write(b'\n')
|
||||
stdout.write(bytes(server.authkey))
|
||||
sys.stdin.close()
|
||||
sys.stdout.close()
|
||||
sys.stderr.close()
|
||||
# Gracefully shutdown on INT or TERM signals
|
||||
stop = functools.partial(daemon_stop, server)
|
||||
signal.signal(signal.SIGTERM, stop)
|
||||
signal.signal(signal.SIGINT, stop)
|
||||
LOG.info("Starting rootwrap daemon main loop")
|
||||
server.serve_forever()
|
||||
finally:
|
||||
conn = server.listener
|
||||
# This will break accept() loop with EOFError if it was not in the
|
||||
# main thread (as in Python 3.x)
|
||||
conn.close()
|
||||
# Closing all currently connected client sockets for reading to
|
||||
# break worker threads blocked on recv()
|
||||
for cl_conn in conn.get_accepted():
|
||||
try:
|
||||
cl_conn.half_close()
|
||||
except Exception:
|
||||
# Most likely the socket have already been closed
|
||||
LOG.debug("Failed to close connection")
|
||||
LOG.info("Waiting for all client threads to finish.")
|
||||
for thread in threading.enumerate():
|
||||
if thread.daemon:
|
||||
LOG.debug("Joining thread %s", thread)
|
||||
thread.join()
|
||||
finally:
|
||||
LOG.debug("Removing temporary directory %s", temp_dir)
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
@ -24,6 +24,7 @@ from six import moves
|
||||
import testtools
|
||||
|
||||
from oslo_rootwrap import cmd
|
||||
from oslo_rootwrap import daemon
|
||||
from oslo_rootwrap import filters
|
||||
from oslo_rootwrap import wrapper
|
||||
|
||||
@ -583,3 +584,19 @@ class RunOneCommandTestCase(testtools.TestCase):
|
||||
|
||||
def test_negative_returncode(self):
|
||||
self._test_returncode_helper(-1, 129)
|
||||
|
||||
|
||||
class DaemonCleanupException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class DaemonCleanupTestCase(testtools.TestCase):
|
||||
|
||||
@mock.patch('os.chmod')
|
||||
@mock.patch('shutil.rmtree')
|
||||
@mock.patch('tempfile.mkdtemp')
|
||||
@mock.patch('multiprocessing.managers.BaseManager.get_server',
|
||||
side_effect=DaemonCleanupException)
|
||||
def test_daemon_no_cleanup_for_uninitialized_server(self, gs, *args):
|
||||
self.assertRaises(DaemonCleanupException, daemon.daemon_start,
|
||||
config=None, filters=None)
|
||||
|
Loading…
Reference in New Issue
Block a user