Centralize sqlite FK constraint enforcement

There's a TODO in the code to centralize foreign key constraint
enforcement for sqlite for unit and functional tests and we're missing
enforcement of FK constraints in a couple of test classes that should
have it.

This resolves the TODO and turns on FK constraint enforcement where it
is missing. Do this to enhance testing in preparation for a proposed
change to the database archiving logic later in this patch series.

Conflicts:
    nova/test.py

NOTE(melwitt): The conflicts are because of the following changes not
in Victoria:

  * Ib2c406327fef2fb4868d8050fc476a7d17706e23 (Remove six.moves)
  * Ide65686cf02463045f5c32771ca949802b19636f (Remove
    six.binary_type/integer_types/string_types)

Change-Id: Idcf026d020e63e4e6ece1db46e4cdc7b7742b76f
(cherry picked from commit 172024db71)
This commit is contained in:
melanie witt 2021-01-29 23:42:23 +00:00
parent 24595b6ca9
commit 382d64ea36
4 changed files with 22 additions and 30 deletions

View File

@ -51,10 +51,12 @@ from oslotest import base
from oslotest import mock_fixture from oslotest import mock_fixture
import six import six
from six.moves import builtins from six.moves import builtins
from sqlalchemy.dialects import sqlite
import testtools import testtools
from nova.compute import rpcapi as compute_rpcapi from nova.compute import rpcapi as compute_rpcapi
from nova import context from nova import context
from nova.db.sqlalchemy import api as sqlalchemy_api
from nova import exception from nova import exception
from nova import objects from nova import objects
from nova.objects import base as objects_base from nova.objects import base as objects_base
@ -376,6 +378,22 @@ class TestCase(base.BaseTestCase):
for k, v in kw.items(): for k, v in kw.items():
CONF.set_override(k, v, group) CONF.set_override(k, v, group)
def enforce_fk_constraints(self, engine=None):
if engine is None:
engine = sqlalchemy_api.get_engine()
dialect = engine.url.get_dialect()
if dialect == sqlite.dialect:
# We're seeing issues with foreign key support in SQLite 3.6.20
# SQLAlchemy doesn't support it at all with < SQLite 3.6.19
# It works fine in SQLite 3.7.
# So return early to skip this test if running SQLite < 3.7
import sqlite3
tup = sqlite3.sqlite_version_info
if tup[0] < 3 or (tup[0] == 3 and tup[1] < 7):
self.skipTest(
'sqlite version too old for reliable SQLA foreign_keys')
engine.connect().execute("PRAGMA foreign_keys = ON")
def start_service(self, name, host=None, cell_name=None, **kwargs): def start_service(self, name, host=None, cell_name=None, **kwargs):
# Disallow starting multiple scheduler services # Disallow starting multiple scheduler services
if name == 'scheduler' and self._service_fixture_count[name]: if name == 'scheduler' and self._service_fixture_count[name]:

View File

@ -17,7 +17,6 @@ import re
from dateutil import parser as dateutil_parser from dateutil import parser as dateutil_parser
from oslo_utils import timeutils from oslo_utils import timeutils
from sqlalchemy.dialects import sqlite
from sqlalchemy import func from sqlalchemy import func
from sqlalchemy import MetaData from sqlalchemy import MetaData
from sqlalchemy import select from sqlalchemy import select
@ -33,22 +32,7 @@ class TestDatabaseArchive(integrated_helpers._IntegratedTestBase):
def setUp(self): def setUp(self):
super(TestDatabaseArchive, self).setUp() super(TestDatabaseArchive, self).setUp()
# TODO(mriedem): pull this out so we can re-use it in self.enforce_fk_constraints()
# test_archive_deleted_rows_fk_constraint
# SQLite doesn't enforce foreign key constraints without a pragma.
engine = sqlalchemy_api.get_engine()
dialect = engine.url.get_dialect()
if dialect == sqlite.dialect:
# We're seeing issues with foreign key support in SQLite 3.6.20
# SQLAlchemy doesn't support it at all with < SQLite 3.6.19
# It works fine in SQLite 3.7.
# So return early to skip this test if running SQLite < 3.7
import sqlite3
tup = sqlite3.sqlite_version_info
if tup[0] < 3 or (tup[0] == 3 and tup[1] < 7):
self.skipTest(
'sqlite version too old for reliable SQLA foreign_keys')
engine.connect().execute("PRAGMA foreign_keys = ON")
def test_archive_deleted_rows(self): def test_archive_deleted_rows(self):
# Boots a server, deletes it, and then tries to archive it. # Boots a server, deletes it, and then tries to archive it.

View File

@ -1767,6 +1767,7 @@ class TestDBArchiveDeletedRows(integrated_helpers._IntegratedTestBase):
def setUp(self): def setUp(self):
super(TestDBArchiveDeletedRows, self).setUp() super(TestDBArchiveDeletedRows, self).setUp()
self.enforce_fk_constraints()
self.cli = manage.DbCommands() self.cli = manage.DbCommands()
self.output = StringIO() self.output = StringIO()
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.output)) self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.output))
@ -1812,6 +1813,7 @@ class TestDBArchiveDeletedRowsMultiCell(integrated_helpers.InstanceHelperMixin,
def setUp(self): def setUp(self):
super(TestDBArchiveDeletedRowsMultiCell, self).setUp() super(TestDBArchiveDeletedRowsMultiCell, self).setUp()
self.enforce_fk_constraints()
self.useFixture(nova_fixtures.NeutronFixture(self)) self.useFixture(nova_fixtures.NeutronFixture(self))
self.useFixture(nova_fixtures.GlanceFixture(self)) self.useFixture(nova_fixtures.GlanceFixture(self))
self.useFixture(func_fixtures.PlacementFixture()) self.useFixture(func_fixtures.PlacementFixture())

View File

@ -39,7 +39,6 @@ from oslo_utils import uuidutils
import six import six
from six.moves import range from six.moves import range
from sqlalchemy import Column from sqlalchemy import Column
from sqlalchemy.dialects import sqlite
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import inspect from sqlalchemy import inspect
@ -6440,18 +6439,7 @@ class ArchiveTestCase(test.TestCase, ModelsObjectComparatorMixin):
def _check_sqlite_version_less_than_3_7(self): def _check_sqlite_version_less_than_3_7(self):
# SQLite doesn't enforce foreign key constraints without a pragma. # SQLite doesn't enforce foreign key constraints without a pragma.
dialect = self.engine.url.get_dialect() self.enforce_fk_constraints(engine=self.engine)
if dialect == sqlite.dialect:
# We're seeing issues with foreign key support in SQLite 3.6.20
# SQLAlchemy doesn't support it at all with < SQLite 3.6.19
# It works fine in SQLite 3.7.
# So return early to skip this test if running SQLite < 3.7
import sqlite3
tup = sqlite3.sqlite_version_info
if tup[0] < 3 or (tup[0] == 3 and tup[1] < 7):
self.skipTest(
'sqlite version too old for reliable SQLA foreign_keys')
self.conn.execute("PRAGMA foreign_keys = ON")
def test_archive_deleted_rows_for_migrations(self): def test_archive_deleted_rows_for_migrations(self):
# migrations.instance_uuid depends on instances.uuid # migrations.instance_uuid depends on instances.uuid