Use opportunistic migrations
Refactored migration tests to use OpportunisticTestCase, removed unused code, BaseMigrationTestCase class and ``test_migrations.conf`` file. Added workaround for bug 1359982 to be able to merge this patch. Closes-Bug: #1359888 Related-Bug: #1359982 Change-Id: Iade2bd2bf50880e167c5a5be8b1236c437f28702
This commit is contained in:
parent
7ddc3c49e6
commit
b081462046
|
@ -1,9 +0,0 @@
|
|||
[DEFAULT]
|
||||
# Set up any number of migration data stores you want, one
|
||||
# The "name" used in the test is the config variable key.
|
||||
#sqlite=sqlite:///test_migrations.db
|
||||
sqlite=sqlite://
|
||||
#mysql=mysql://root:@localhost/test_migrations
|
||||
#postgresql=postgresql://user:pass@localhost/test_migrations
|
||||
[walk_style]
|
||||
snake_walk=yes
|
|
@ -16,186 +16,90 @@
|
|||
# under the License.
|
||||
|
||||
"""
|
||||
Tests for database migrations. This test case reads the configuration
|
||||
file test_migrations.conf for database connection settings
|
||||
to use in the tests. For each connection found in the config file,
|
||||
the test case runs a series of test cases to ensure that migrations work
|
||||
properly both upgrading and downgrading, and that no data loss occurs
|
||||
if possible.
|
||||
Tests for database migrations.
|
||||
"""
|
||||
|
||||
import ConfigParser
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from migrate.versioning import api as migration_api
|
||||
from migrate.versioning import repository
|
||||
from oslo.db.sqlalchemy import test_base
|
||||
from oslo.db.sqlalchemy import test_migrations
|
||||
import sqlalchemy
|
||||
import testtools
|
||||
from sqlalchemy.sql import text
|
||||
|
||||
import manila.db.sqlalchemy.migrate_repo
|
||||
from manila.openstack.common import log as logging
|
||||
from manila import test
|
||||
|
||||
LOG = logging.getLogger('manila.tests.test_migrations')
|
||||
|
||||
|
||||
def _get_connect_string(backend,
|
||||
user="openstack_citest",
|
||||
passwd="openstack_citest",
|
||||
database="openstack_citest"):
|
||||
"""
|
||||
Try to get a connection with a very specific set of values, if we get
|
||||
these then we'll run the tests, otherwise they are skipped
|
||||
"""
|
||||
if backend == "postgres":
|
||||
backend = "postgresql+psycopg2"
|
||||
|
||||
return ("%(backend)s://%(user)s:%(passwd)s@localhost/%(database)s"
|
||||
% locals())
|
||||
|
||||
|
||||
def _is_mysql_avail(**kwargs):
|
||||
return _is_backend_avail('mysql', **kwargs)
|
||||
|
||||
|
||||
def _is_backend_avail(backend,
|
||||
user="openstack_citest",
|
||||
passwd="openstack_citest",
|
||||
database="openstack_citest"):
|
||||
try:
|
||||
if backend == "mysql":
|
||||
connect_uri = _get_connect_string("mysql", user=user,
|
||||
passwd=passwd, database=database)
|
||||
elif backend == "postgres":
|
||||
connect_uri = _get_connect_string("postgres", user=user,
|
||||
passwd=passwd, database=database)
|
||||
engine = sqlalchemy.create_engine(connect_uri)
|
||||
connection = engine.connect()
|
||||
except Exception:
|
||||
# intentionally catch all to handle exceptions even if we don't
|
||||
# have any backend code loaded.
|
||||
return False
|
||||
else:
|
||||
connection.close()
|
||||
engine.dispose()
|
||||
return True
|
||||
|
||||
|
||||
class TestMigrations(test.TestCase,
|
||||
test_migrations.BaseMigrationTestCase,
|
||||
test_migrations.WalkVersionsMixin):
|
||||
class ManilaMigrationsCheckers(test_migrations.WalkVersionsMixin):
|
||||
"""Test sqlalchemy-migrate migrations."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestMigrations, self).__init__(*args, **kwargs)
|
||||
snake_walk = False
|
||||
downgrade = False
|
||||
|
||||
self.DEFAULT_CONFIG_FILE = os.path.join(os.path.dirname(__file__),
|
||||
'test_migrations.conf')
|
||||
# Test machines can set the MANILA_TEST_MIGRATIONS_CONF variable
|
||||
# to override the location of the config file for migration testing
|
||||
self.CONFIG_FILE_PATH = os.environ.get('MANILA_TEST_MIGRATIONS_CONF',
|
||||
self.DEFAULT_CONFIG_FILE)
|
||||
self.MIGRATE_FILE = manila.db.sqlalchemy.migrate_repo.__file__
|
||||
self.REPOSITORY = repository.Repository(
|
||||
os.path.abspath(os.path.dirname(self.MIGRATE_FILE)))
|
||||
self.migration_api = migration_api
|
||||
self.INIT_VERSION = 000
|
||||
@property
|
||||
def INIT_VERSION(self):
|
||||
return 000
|
||||
|
||||
def setUp(self):
|
||||
if not os.environ.get("OSLO_LOCK_PATH"):
|
||||
lock_dir = tempfile.mkdtemp()
|
||||
os.environ["OSLO_LOCK_PATH"] = lock_dir
|
||||
self.addCleanup(self._cleanup)
|
||||
@property
|
||||
def REPOSITORY(self):
|
||||
migrate_file = manila.db.sqlalchemy.migrate_repo.__file__
|
||||
return repository.Repository(
|
||||
os.path.abspath(os.path.dirname(migrate_file)))
|
||||
|
||||
self.snake_walk = False
|
||||
if not self.test_databases:
|
||||
super(TestMigrations, self).setUp()
|
||||
cp = ConfigParser.RawConfigParser()
|
||||
try:
|
||||
cp.read(self.CONFIG_FILE_PATH)
|
||||
self.snake_walk = cp.getboolean('walk_style', 'snake_walk')
|
||||
except ConfigParser.ParsingError as e:
|
||||
self.fail("Failed to read test_migrations.conf config "
|
||||
"file. Got error: %s" % e)
|
||||
@property
|
||||
def migration_api(self):
|
||||
return migration_api
|
||||
|
||||
def _cleanup(self):
|
||||
shutil.rmtree(os.environ["OSLO_LOCK_PATH"], ignore_errors=True)
|
||||
del os.environ["OSLO_LOCK_PATH"]
|
||||
@property
|
||||
def migrate_engine(self):
|
||||
return self.engine
|
||||
|
||||
def test_walk_versions(self):
|
||||
"""
|
||||
Walks all version scripts for each tested database, ensuring
|
||||
that there are no errors in the version scripts for each engine
|
||||
"""
|
||||
for key, engine in self.engines.items():
|
||||
self._walk_versions(engine, self.snake_walk)
|
||||
self._walk_versions(snake_walk=self.snake_walk,
|
||||
downgrade=self.downgrade)
|
||||
|
||||
def test_mysql_connect_fail(self):
|
||||
"""
|
||||
Test that we can trigger a mysql connection failure and we fail
|
||||
gracefully to ensure we don't break people without mysql
|
||||
"""
|
||||
if _is_mysql_avail(user="openstack_cifail"):
|
||||
self.fail("Shouldn't have connected")
|
||||
|
||||
@testtools.skipUnless(test_migrations._have_mysql("openstack_citest",
|
||||
"openstack_citest",
|
||||
"openstack_citest"),
|
||||
"mysql not available")
|
||||
class TestManilaMigrationsMySQL(ManilaMigrationsCheckers,
|
||||
test_base.MySQLOpportunisticTestCase):
|
||||
"""Run migration tests on MySQL backend."""
|
||||
|
||||
def test_mysql_innodb(self):
|
||||
"""
|
||||
Test that table creation on mysql only builds InnoDB tables
|
||||
"""
|
||||
# add this to the global lists to make parent _reset_databases method
|
||||
# work with it, it's removed automaticaly in parent tearDown method so
|
||||
# no need to clean it up here.
|
||||
connect_string = _get_connect_string('mysql')
|
||||
engine = sqlalchemy.create_engine(connect_string)
|
||||
self.engines["mysqlcitest"] = engine
|
||||
self.test_databases["mysqlcitest"] = connect_string
|
||||
|
||||
# build a fully populated mysql database with all the tables
|
||||
self._reset_databases()
|
||||
self._walk_versions(engine, False, False)
|
||||
|
||||
uri = _get_connect_string('mysql', database="information_schema")
|
||||
connection = sqlalchemy.create_engine(uri).connect()
|
||||
"""Test that table creation on mysql only builds InnoDB tables."""
|
||||
self._walk_versions(snake_walk=False, downgrade=False)
|
||||
|
||||
# sanity check
|
||||
total = connection.execute("SELECT count(*) "
|
||||
"from information_schema.TABLES "
|
||||
"where TABLE_SCHEMA='openstack_citest'")
|
||||
sanity_check = """SELECT count(*)
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = :database;"""
|
||||
total = self.engine.execute(
|
||||
text(sanity_check),
|
||||
database=self.engine.url.database)
|
||||
|
||||
self.assertTrue(total.scalar() > 0, "No tables found. Wrong schema?")
|
||||
|
||||
noninnodb = connection.execute("SELECT count(*) "
|
||||
"from information_schema.TABLES "
|
||||
"where TABLE_SCHEMA='openstack_citest' "
|
||||
"and ENGINE!='InnoDB' "
|
||||
"and TABLE_NAME!='migrate_version'")
|
||||
count = noninnodb.scalar()
|
||||
noninnodb_query = """
|
||||
SELECT count(*)
|
||||
FROM information_schema.TABLES
|
||||
WHERE table_schema = :database
|
||||
AND engine != 'InnoDB'
|
||||
AND table_name != 'migrate_version';"""
|
||||
|
||||
count = self.engine.execute(
|
||||
text(noninnodb_query),
|
||||
database=self.engine.url.database
|
||||
).scalar()
|
||||
self.assertEqual(count, 0, "%d non InnoDB tables created" % count)
|
||||
|
||||
def test_postgresql_connect_fail(self):
|
||||
"""
|
||||
Test that we can trigger a postgres connection failure and we fail
|
||||
gracefully to ensure we don't break people without postgres
|
||||
"""
|
||||
if _is_backend_avail('postgres', user="openstack_cifail"):
|
||||
self.fail("Shouldn't have connected")
|
||||
|
||||
@testtools.skipUnless(_is_backend_avail('postgres'),
|
||||
"postgresql not available")
|
||||
def test_postgresql_opportunistically(self):
|
||||
# add this to the global lists to make reset work with it, it's removed
|
||||
# automatically in tearDown so no need to clean it up here.
|
||||
connect_string = _get_connect_string("postgres")
|
||||
engine = sqlalchemy.create_engine(connect_string)
|
||||
self.engines["postgresqlcitest"] = engine
|
||||
self.test_databases["postgresqlcitest"] = connect_string
|
||||
class TestManilaMigrationsPostgreSQL(
|
||||
ManilaMigrationsCheckers, test_base.PostgreSQLOpportunisticTestCase):
|
||||
"""Run migration tests on PostgreSQL backend."""
|
||||
|
||||
# build a fully populated postgresql database with all the tables
|
||||
self._reset_databases()
|
||||
self._walk_versions(engine, False, False)
|
||||
|
||||
class TestManilaMigrationsSQLite(ManilaMigrationsCheckers,
|
||||
test_base.DbTestCase):
|
||||
"""Run migration tests on SQLite backend."""
|
||||
|
|
Loading…
Reference in New Issue