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 <gibi@redhat.com>
This commit is contained in:
Balazs Gibizer
2025-06-26 14:14:57 +02:00
parent 2a9cbdabce
commit b278240370
3 changed files with 46 additions and 11 deletions

View File

@@ -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())

View File

@@ -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

View File

@@ -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'