diff --git a/oslo_service/_options.py b/oslo_service/_options.py index f70f5b4a..0b356d21 100644 --- a/oslo_service/_options.py +++ b/oslo_service/_options.py @@ -31,7 +31,8 @@ eventlet_backdoor_opts = [ " option is mutually exclusive with 'backdoor_port' in" " that only one should be provided. If both are provided" " then the existence of this option overrides the usage of" - " that option.") + " that option. Inside the path {pid} will be replaced with" + " the PID of the current process.") ] periodic_opts = [ diff --git a/oslo_service/eventlet_backdoor.py b/oslo_service/eventlet_backdoor.py index 5e3186f3..16daffbf 100644 --- a/oslo_service/eventlet_backdoor.py +++ b/oslo_service/eventlet_backdoor.py @@ -214,8 +214,16 @@ def _initialize_if_enabled(conf): # listen(). In any case, pull the port number out here. where_running = sock.getsockname()[1] else: - sock = _try_open_unix_domain_socket(conf.backdoor_socket) - where_running = conf.backdoor_socket + try: + backdoor_socket_path = conf.backdoor_socket.format(pid=os.getpid()) + except (KeyError, IndexError, ValueError) as e: + backdoor_socket_path = conf.backdoor_socket + LOG.warning("Could not apply format string to eventlet " + "backdoor socket path ({}) - continuing with " + "unformatted path" + "".format(e)) + sock = _try_open_unix_domain_socket(backdoor_socket_path) + where_running = backdoor_socket_path # NOTE(johannes): The standard sys.displayhook will print the value of # the last expression and set it to __builtin__._, which overwrites diff --git a/oslo_service/tests/test_eventlet_backdoor.py b/oslo_service/tests/test_eventlet_backdoor.py index 480761e1..93d81054 100644 --- a/oslo_service/tests/test_eventlet_backdoor.py +++ b/oslo_service/tests/test_eventlet_backdoor.py @@ -38,6 +38,30 @@ class BackdoorSocketPathTest(base.ServiceBaseTestCase): path = eventlet_backdoor.initialize_if_enabled(self.conf) self.assertEqual("/tmp/my_special_socket", path) + @mock.patch.object(eventlet, 'spawn') + @mock.patch.object(eventlet, 'listen') + def test_backdoor_path_with_format_string(self, listen_mock, spawn_mock): + self.config(backdoor_socket="/tmp/my_special_socket-{pid}") + listen_mock.side_effect = mock.Mock() + path = eventlet_backdoor.initialize_if_enabled(self.conf) + expected_path = "/tmp/my_special_socket-{}".format(os.getpid()) + self.assertEqual(expected_path, path) + + @mock.patch.object(eventlet, 'spawn') + @mock.patch.object(eventlet, 'listen') + def test_backdoor_path_with_broken_format_string(self, listen_mock, + spawn_mock): + broken_socket_paths = [ + "/tmp/my_special_socket-{}", + "/tmp/my_special_socket-{broken", + "/tmp/my_special_socket-{broken}", + ] + for socket_path in broken_socket_paths: + self.config(backdoor_socket=socket_path) + listen_mock.side_effect = mock.Mock() + path = eventlet_backdoor.initialize_if_enabled(self.conf) + self.assertEqual(socket_path, path) + @mock.patch.object(os, 'unlink') @mock.patch.object(eventlet, 'spawn') @mock.patch.object(eventlet, 'listen') diff --git a/releasenotes/notes/support-pid-in-eventlet-backdoor-socket-path-1863eaad1dd08556.yaml b/releasenotes/notes/support-pid-in-eventlet-backdoor-socket-path-1863eaad1dd08556.yaml new file mode 100644 index 00000000..1cbd221f --- /dev/null +++ b/releasenotes/notes/support-pid-in-eventlet-backdoor-socket-path-1863eaad1dd08556.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + The config option backdoor_socket_path now is a format string that + supports {pid}, which will be replaced with the PID of the current + process. This makes the eventlet backdoor accessible when spawning + multiple processes with the same backdoor_socket_path inside the + configuration.