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:
Victor Sergeyev 2014-08-22 10:24:27 +03:00
parent 7ddc3c49e6
commit b081462046
3 changed files with 55 additions and 158 deletions

View File

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

View File

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

View File

@ -5,6 +5,8 @@ envlist = py26,py27,pep8
[testenv]
setenv = VIRTUAL_ENV={envdir}
# NOTE (viktors): Workaround for bug 1359982
PYTHONHASHSEED=0
usedevelop = True
install_command = pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt