From b278240370493470b21cbb01ad25e143852b4b78 Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Thu, 26 Jun 2025 14:14:57 +0200 Subject: [PATCH] Allow to start unit test without eventlet The end goals is to be able to run at least some of the unit tests without eventlet. But there are things preventing that for now. We need to make sure that the oslo.sevice backed is not initialized to eventlet by any early import code before our monkey_patch module can do the selective backed selection based on the env variable. The nova.tests.unit module had some import time code execution that is forcing imports that initialize the oslo.service backend too early, way before nova would do it in normal execution. We could remove objects.register_all() from nova/tests/unit/__init__.py as it seems tests are passing without it. Still that would not be enough so I eventually decide to keep it. The other issue is that the unit test discovery imports all modules under nova.tests.unit and that eventually imports oslo.messaging and that also forces oslo.service backend selection. So we injected an early call to our smart monkey_patch module to preempt that. This does not change the imported modules as monkey_patch module imported anyhow via nova.test module. Just changed the order to allow oslo.service backend selection explicitly. After this patch the unit test can be run via OS_NOVA_DISABLE_EVENTLET_PATCHING=true tox -e py312 Most of the test will pass but there are a bunch of test timing out or hanging. Change-Id: I210cb6a30deaee779d55f88f0f57584c65b0dc05 Signed-off-by: Balazs Gibizer --- nova/monkey_patch.py | 39 ++++++++++++++++++++++++++----------- nova/tests/unit/__init__.py | 15 ++++++++++++++ tox.ini | 3 +++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/nova/monkey_patch.py b/nova/monkey_patch.py index 68d1c17faa4c..f97dd8ae32f2 100644 --- a/nova/monkey_patch.py +++ b/nova/monkey_patch.py @@ -94,17 +94,7 @@ def patch(): # NOTE(gibi): We were asked not to monkey patch. Let's enforce it by # removing the possibility to monkey_patch accidentally - def poison(*args, **kwargs): - raise RuntimeError( - "The service is started with native threading via " - "OS_NOVA_DISABLE_EVENTLET_PATCHING set to '%s', but then the " - "service tried to call eventlet.monkey_patch(). This is a " - "bug." - % os.environ.get('OS_NOVA_DISABLE_EVENTLET_PATCHING', '')) - - import eventlet - eventlet.monkey_patch = poison - eventlet.patcher.monkey_patch = poison + poison_eventlet() from oslo_log import log as logging LOG = logging.getLogger(__name__) @@ -112,3 +102,30 @@ def patch(): "Service is starting with native threading. This is currently " "experimental. Do not use it in production without first " "testing it in pre-production.") + + +def _poison(*args, **kwargs): + raise RuntimeError( + "The service is started with native threading via " + "OS_NOVA_DISABLE_EVENTLET_PATCHING set to '%s', but then the " + "service tried to call eventlet.monkey_patch(). This is a bug." + % os.environ.get('OS_NOVA_DISABLE_EVENTLET_PATCHING', '')) + + +def poison_eventlet(): + import eventlet + eventlet.monkey_patch = _poison + eventlet.patcher.monkey_patch = _poison + + # We want to have this but cannot have this yet as we still have common + # code that imports eventlet like nova.utils.tpool + # + # class PoisonEventletImport: + # def find_spec(self, fullname, path, target=None): + # if fullname.startswith('eventlet'): + # raise ImportError( + # "The service started in threading mode so it should " + # "not import eventlet") + + # import sys + # sys.meta_path.insert(0, PoisonEventletImport()) diff --git a/nova/tests/unit/__init__.py b/nova/tests/unit/__init__.py index 5f48fd3ec937..fe940203e21c 100644 --- a/nova/tests/unit/__init__.py +++ b/nova/tests/unit/__init__.py @@ -22,6 +22,21 @@ :platform: Unix """ +# The import order in the test environment is different from the import +# order in a real env. During unit testing nova.test.unit is imported first +# to discover the unit tests, then the modules under it are imported. This +# test discovery import forces the import of oslo.messaging eventually that +# import time run code that uses the oslo.service backend which forces the +# initialization of the backed to the default eventlet. This prevents +# our tests to run with the threading backend. To avoid this we force the +# backend registration early here by using our common monkey_patch mode +# that already does smart backend selection based on env variables. +# The monkey_patching would be used anyhow when nova.test is imported so +# this does not change the end result just the order of imports +# autopep8: off +from nova import monkey_patch ; monkey_patch.patch() # noqa +# autopep8: on + from nova import objects # NOTE(comstud): Make sure we have all of the objects loaded. We do this diff --git a/tox.ini b/tox.ini index 941f3639b183..0908e8d8dd22 100644 --- a/tox.ini +++ b/tox.ini @@ -48,6 +48,9 @@ passenv = PYTHONOPTIMIZE # there is also secret magic in subunit-trace which lets you run in a fail only # mode. To do this define the TRACE_FAILONLY environmental variable. + +# allow running without eventlet + OS_NOVA_DISABLE_EVENTLET_PATCHING commands = stestr run {posargs} env TEST_OSPROFILER=1 stestr run --combine --no-discover 'nova.tests.unit.test_profiler'