Merge "Wait for conductor start before notifying systemd"
This commit is contained in:
commit
0e3c885a34
@ -67,6 +67,12 @@ def main():
|
|||||||
issue_startup_warnings(CONF)
|
issue_startup_warnings(CONF)
|
||||||
|
|
||||||
launcher = service.launch(CONF, mgr, restart_method='mutate')
|
launcher = service.launch(CONF, mgr, restart_method='mutate')
|
||||||
|
|
||||||
|
# NOTE(dtantsur): handling start-up failures before launcher.wait() helps
|
||||||
|
# notify systemd about them. Otherwise the launcher will report successful
|
||||||
|
# service start-up before checking the threads.
|
||||||
|
mgr.wait_for_start()
|
||||||
|
|
||||||
sys.exit(launcher.wait())
|
sys.exit(launcher.wait())
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,4 +49,9 @@ def main():
|
|||||||
wsgi = wsgi_service.WSGIService('ironic_api', CONF.api.enable_ssl_api)
|
wsgi = wsgi_service.WSGIService('ironic_api', CONF.api.enable_ssl_api)
|
||||||
launcher.launch_service(wsgi)
|
launcher.launch_service(wsgi)
|
||||||
|
|
||||||
|
# NOTE(dtantsur): handling start-up failures before launcher.wait() helps
|
||||||
|
# notify systemd about them. Otherwise the launcher will report successful
|
||||||
|
# service start-up before checking the threads.
|
||||||
|
mgr.wait_for_start()
|
||||||
|
|
||||||
sys.exit(launcher.wait())
|
sys.exit(launcher.wait())
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import signal
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
from ironic_lib.json_rpc import server as json_rpc
|
from ironic_lib.json_rpc import server as json_rpc
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -42,9 +44,29 @@ class RPCService(service.Service):
|
|||||||
self.topic = self.manager.topic
|
self.topic = self.manager.topic
|
||||||
self.rpcserver = None
|
self.rpcserver = None
|
||||||
self.deregister = True
|
self.deregister = True
|
||||||
|
self._failure = None
|
||||||
|
self._started = False
|
||||||
|
|
||||||
|
def wait_for_start(self):
|
||||||
|
while not self._started and not self._failure:
|
||||||
|
time.sleep(0.1)
|
||||||
|
if self._failure:
|
||||||
|
LOG.critical(self._failure)
|
||||||
|
sys.exit(self._failure)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
self._failure = None
|
||||||
|
self._started = False
|
||||||
super(RPCService, self).start()
|
super(RPCService, self).start()
|
||||||
|
try:
|
||||||
|
self._real_start()
|
||||||
|
except Exception as exc:
|
||||||
|
self._failure = f"{exc.__class__.__name__}: {exc}"
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self._started = True
|
||||||
|
|
||||||
|
def _real_start(self):
|
||||||
admin_context = context.get_admin_context()
|
admin_context = context.get_admin_context()
|
||||||
|
|
||||||
serializer = objects_base.IronicObjectSerializer(is_server=True)
|
serializer = objects_base.IronicObjectSerializer(is_server=True)
|
||||||
|
@ -46,6 +46,8 @@ class TestRPCService(base.TestCase):
|
|||||||
mock_rpc, mock_ios, mock_target, mock_prepare_method):
|
mock_rpc, mock_ios, mock_target, mock_prepare_method):
|
||||||
mock_rpc.return_value.start = mock.MagicMock()
|
mock_rpc.return_value.start = mock.MagicMock()
|
||||||
self.rpc_svc.handle_signal = mock.MagicMock()
|
self.rpc_svc.handle_signal = mock.MagicMock()
|
||||||
|
self.assertFalse(self.rpc_svc._started)
|
||||||
|
self.assertFalse(self.rpc_svc._failure)
|
||||||
self.rpc_svc.start()
|
self.rpc_svc.start()
|
||||||
mock_ctx.assert_called_once_with()
|
mock_ctx.assert_called_once_with()
|
||||||
mock_target.assert_called_once_with(topic=self.rpc_svc.topic,
|
mock_target.assert_called_once_with(topic=self.rpc_svc.topic,
|
||||||
@ -55,6 +57,9 @@ class TestRPCService(base.TestCase):
|
|||||||
mock_init_method.assert_called_once_with(self.rpc_svc.manager,
|
mock_init_method.assert_called_once_with(self.rpc_svc.manager,
|
||||||
mock_ctx.return_value)
|
mock_ctx.return_value)
|
||||||
self.assertIs(rpc.GLOBAL_MANAGER, self.rpc_svc.manager)
|
self.assertIs(rpc.GLOBAL_MANAGER, self.rpc_svc.manager)
|
||||||
|
self.assertTrue(self.rpc_svc._started)
|
||||||
|
self.assertFalse(self.rpc_svc._failure)
|
||||||
|
self.rpc_svc.wait_for_start() # should be no-op
|
||||||
|
|
||||||
@mock.patch.object(manager.ConductorManager, 'prepare_host', autospec=True)
|
@mock.patch.object(manager.ConductorManager, 'prepare_host', autospec=True)
|
||||||
@mock.patch.object(oslo_messaging, 'Target', autospec=True)
|
@mock.patch.object(oslo_messaging, 'Target', autospec=True)
|
||||||
@ -77,3 +82,29 @@ class TestRPCService(base.TestCase):
|
|||||||
mock_init_method.assert_called_once_with(self.rpc_svc.manager,
|
mock_init_method.assert_called_once_with(self.rpc_svc.manager,
|
||||||
mock_ctx.return_value)
|
mock_ctx.return_value)
|
||||||
self.assertIs(rpc.GLOBAL_MANAGER, self.rpc_svc.manager)
|
self.assertIs(rpc.GLOBAL_MANAGER, self.rpc_svc.manager)
|
||||||
|
|
||||||
|
@mock.patch.object(manager.ConductorManager, 'prepare_host', autospec=True)
|
||||||
|
@mock.patch.object(oslo_messaging, 'Target', autospec=True)
|
||||||
|
@mock.patch.object(objects_base, 'IronicObjectSerializer', autospec=True)
|
||||||
|
@mock.patch.object(rpc, 'get_server', autospec=True)
|
||||||
|
@mock.patch.object(manager.ConductorManager, 'init_host', autospec=True)
|
||||||
|
@mock.patch.object(context, 'get_admin_context', autospec=True)
|
||||||
|
def test_start_failure(self, mock_ctx, mock_init_method, mock_rpc,
|
||||||
|
mock_ios, mock_target, mock_prepare_method):
|
||||||
|
mock_rpc.return_value.start = mock.MagicMock()
|
||||||
|
self.rpc_svc.handle_signal = mock.MagicMock()
|
||||||
|
mock_init_method.side_effect = RuntimeError("boom")
|
||||||
|
self.assertFalse(self.rpc_svc._started)
|
||||||
|
self.assertFalse(self.rpc_svc._failure)
|
||||||
|
self.assertRaises(RuntimeError, self.rpc_svc.start)
|
||||||
|
mock_ctx.assert_called_once_with()
|
||||||
|
mock_target.assert_called_once_with(topic=self.rpc_svc.topic,
|
||||||
|
server="fake_host")
|
||||||
|
mock_ios.assert_called_once_with(is_server=True)
|
||||||
|
mock_prepare_method.assert_called_once_with(self.rpc_svc.manager)
|
||||||
|
mock_init_method.assert_called_once_with(self.rpc_svc.manager,
|
||||||
|
mock_ctx.return_value)
|
||||||
|
self.assertIsNone(rpc.GLOBAL_MANAGER)
|
||||||
|
self.assertFalse(self.rpc_svc._started)
|
||||||
|
self.assertIn("boom", self.rpc_svc._failure)
|
||||||
|
self.assertRaises(SystemExit, self.rpc_svc.wait_for_start)
|
||||||
|
5
releasenotes/notes/service-wait-e85cbe7978f61764.yaml
Normal file
5
releasenotes/notes/service-wait-e85cbe7978f61764.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
The ``ironic`` and ``ironic-conductor`` services now wait for the conductor
|
||||||
|
manager to start before notifying systemd about the successful start-up.
|
Loading…
x
Reference in New Issue
Block a user