Merge "Add name scheme update script for alembic version table"
This commit is contained in:
commit
fab8d8a785
@ -19,10 +19,13 @@
|
||||
from __future__ import with_statement
|
||||
|
||||
from alembic import context
|
||||
from oslo_config import cfg
|
||||
|
||||
from refstack.db.sqlalchemy import api as db_api
|
||||
from refstack.db.sqlalchemy import models as db_models
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
@ -33,9 +36,9 @@ def run_migrations_online():
|
||||
engine = db_api.get_engine()
|
||||
connection = engine.connect()
|
||||
target_metadata = db_models.RefStackBase.metadata
|
||||
context.configure(
|
||||
connection=connection,
|
||||
target_metadata=target_metadata)
|
||||
context.configure(connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
version_table=getattr(CONF, 'version_table'))
|
||||
|
||||
try:
|
||||
with context.begin_transaction():
|
||||
|
@ -13,24 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Implementation of Alembic commands."""
|
||||
import os
|
||||
|
||||
import alembic
|
||||
from alembic import config as alembic_config
|
||||
import alembic.migration as alembic_migration
|
||||
from oslo_config import cfg
|
||||
|
||||
from refstack.db.sqlalchemy import api as db_api
|
||||
from refstack.db.migrations.alembic import utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def _alembic_config():
|
||||
path = os.path.join(os.path.dirname(__file__), os.pardir, 'alembic.ini')
|
||||
config = alembic_config.Config(path)
|
||||
return config
|
||||
|
||||
|
||||
def version():
|
||||
"""Current database version.
|
||||
|
||||
@ -39,7 +30,10 @@ def version():
|
||||
"""
|
||||
engine = db_api.get_engine()
|
||||
with engine.connect() as conn:
|
||||
context = alembic_migration.MigrationContext.configure(conn)
|
||||
conf_table = getattr(CONF, 'version_table')
|
||||
utils.recheck_alembic_table(conn)
|
||||
context = alembic_migration.MigrationContext.configure(
|
||||
conn, opts={'version_table': conf_table})
|
||||
return context.get_current_revision()
|
||||
|
||||
|
||||
@ -49,7 +43,7 @@ def upgrade(revision):
|
||||
:param version: Desired database version
|
||||
:type version: string
|
||||
"""
|
||||
return alembic.command.upgrade(_alembic_config(), revision or 'head')
|
||||
return alembic.command.upgrade(utils.alembic_config(), revision or 'head')
|
||||
|
||||
|
||||
def downgrade(revision):
|
||||
@ -58,7 +52,8 @@ def downgrade(revision):
|
||||
:param version: Desired database version
|
||||
:type version: string
|
||||
"""
|
||||
return alembic.command.downgrade(_alembic_config(), revision or 'base')
|
||||
return alembic.command.downgrade(utils.alembic_config(),
|
||||
revision or 'base')
|
||||
|
||||
|
||||
def stamp(revision):
|
||||
@ -70,7 +65,7 @@ def stamp(revision):
|
||||
database with most recent revision
|
||||
:type revision: string
|
||||
"""
|
||||
return alembic.command.stamp(_alembic_config(), revision or 'head')
|
||||
return alembic.command.stamp(utils.alembic_config(), revision or 'head')
|
||||
|
||||
|
||||
def revision(message=None, autogenerate=False):
|
||||
@ -82,4 +77,5 @@ def revision(message=None, autogenerate=False):
|
||||
state
|
||||
:type autogenerate: bool
|
||||
"""
|
||||
return alembic.command.revision(_alembic_config(), message, autogenerate)
|
||||
return alembic.command.revision(utils.alembic_config(),
|
||||
message, autogenerate)
|
||||
|
127
refstack/db/migrations/alembic/utils.py
Normal file
127
refstack/db/migrations/alembic/utils.py
Normal file
@ -0,0 +1,127 @@
|
||||
# Copyright (c) 2015 Mirantis, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Utilities used in the implementation of Alembic commands."""
|
||||
import os
|
||||
|
||||
from alembic import config as alembic_conf
|
||||
from alembic.operations import Operations
|
||||
import alembic.migration as alembic_migration
|
||||
from collections import Iterable
|
||||
from oslo_config import cfg
|
||||
from sqlalchemy import text
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def alembic_config():
|
||||
"""Initialize config objext from .ini file.
|
||||
|
||||
:returns: config object.
|
||||
:type: object
|
||||
"""
|
||||
path = os.path.join(os.path.dirname(__file__), os.pardir, 'alembic.ini')
|
||||
config = alembic_conf.Config(path)
|
||||
return config
|
||||
|
||||
|
||||
def get_table_version(conn, version_table_name):
|
||||
"""Get table version.
|
||||
|
||||
:param engine: Initialized alembic engine object.
|
||||
:param version_table_name: Version table name to check.
|
||||
:type engine: object
|
||||
:type version_table_name: string
|
||||
:returns: string
|
||||
"""
|
||||
if not version_table_name:
|
||||
return None
|
||||
context = alembic_migration.MigrationContext.configure(
|
||||
conn, opts={'version_table': version_table_name})
|
||||
return context.get_current_revision()
|
||||
|
||||
|
||||
def get_db_tables(conn):
|
||||
"""Get current and default table values from the db.
|
||||
|
||||
:param engine: Initialized alembic engine object.
|
||||
:type engine: object
|
||||
:returns: tuple
|
||||
"""
|
||||
query = text("SELECT TABLE_NAME from information_schema.tables\
|
||||
WHERE TABLE_NAME\
|
||||
LIKE '%alembic_version%'\
|
||||
AND table_schema = 'refstack'")
|
||||
context = alembic_migration.MigrationContext.configure(conn)
|
||||
op = Operations(context)
|
||||
connection = op.get_bind()
|
||||
search = connection.execute(query)
|
||||
result = search.fetchall()
|
||||
if isinstance(result, Iterable):
|
||||
result = [table[0] for table in result]
|
||||
else:
|
||||
result = None
|
||||
# if there is more than one version table, modify the
|
||||
# one that does not have the default name, because subunit2sql uses the
|
||||
# default name.
|
||||
if result:
|
||||
current_name =\
|
||||
next((table for table in result if table != "alembic_version"),
|
||||
result[0])
|
||||
current_name = current_name.decode('utf-8')
|
||||
current_version = get_table_version(conn, current_name)
|
||||
default_name =\
|
||||
next((table for table in result
|
||||
if table == "alembic_version"), None)
|
||||
default_version = get_table_version(conn, default_name)
|
||||
if len(result) > 1 and not current_version:
|
||||
if not default_name:
|
||||
# this is the case where there is more than one
|
||||
# nonstandard-named alembic table, and no default
|
||||
current_name = next((table for table in result
|
||||
if table != current_name),
|
||||
result[0])
|
||||
current_name = current_name.decode('utf-8')
|
||||
elif current_name:
|
||||
# this is the case where the current-named table
|
||||
# exists, but is empty
|
||||
current_name = default_name
|
||||
current_version = default_version
|
||||
current_table = (current_name, current_version)
|
||||
default_table = (default_name, default_version)
|
||||
else:
|
||||
default_table = (None, None)
|
||||
current_table = default_table
|
||||
return current_table, default_table
|
||||
|
||||
|
||||
def recheck_alembic_table(conn):
|
||||
"""check and update alembic version table.
|
||||
|
||||
Should check current alembic version table against conf and rename the
|
||||
existing table if the two values don't match.
|
||||
"""
|
||||
conf_table = getattr(CONF, 'version_table')
|
||||
conf_table_version = get_table_version(conn, conf_table)
|
||||
current_table, default_table = get_db_tables(conn)
|
||||
if current_table[0]:
|
||||
if current_table[0] != conf_table:
|
||||
context = alembic_migration.MigrationContext.configure(conn)
|
||||
op = Operations(context)
|
||||
if conf_table and not conf_table_version:
|
||||
# make sure there is not present-but-empty table
|
||||
# that will prevent us from renaming the current table
|
||||
op.drop_table(conf_table)
|
||||
op.rename_table(current_table[0], conf_table)
|
@ -20,7 +20,7 @@ import mock
|
||||
from oslotest import base
|
||||
|
||||
from refstack.db import migration
|
||||
from refstack.db.migrations.alembic import migration as alembic_migration
|
||||
from refstack.db.migrations.alembic import utils
|
||||
|
||||
|
||||
class AlembicConfigTestCase(base.BaseTestCase):
|
||||
@ -30,7 +30,7 @@ class AlembicConfigTestCase(base.BaseTestCase):
|
||||
def test_alembic_config(self, os_join, alembic_config):
|
||||
os_join.return_value = 'fake_path'
|
||||
alembic_config.return_value = 'fake_config'
|
||||
result = alembic_migration._alembic_config()
|
||||
result = utils.alembic_config()
|
||||
self.assertEqual(result, 'fake_config')
|
||||
alembic_config.assert_called_once_with('fake_path')
|
||||
|
||||
@ -41,7 +41,7 @@ class MigrationTestCase(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(MigrationTestCase, self).setUp()
|
||||
self.config_patcher = mock.patch(
|
||||
'refstack.db.migrations.alembic.migration._alembic_config')
|
||||
'refstack.db.migrations.alembic.utils.alembic_config')
|
||||
self.config = self.config_patcher.start()
|
||||
self.config.return_value = 'fake_config'
|
||||
self.addCleanup(self.config_patcher.stop)
|
||||
@ -57,7 +57,7 @@ class MigrationTestCase(base.BaseTestCase):
|
||||
engine.connect = mock.MagicMock()
|
||||
get_engine.return_value = engine
|
||||
migration.version()
|
||||
context.get_current_revision.assert_called_once_with()
|
||||
context.get_current_revision.assert_called_with()
|
||||
engine.connect.assert_called_once_with()
|
||||
|
||||
@mock.patch('alembic.command.upgrade')
|
||||
|
@ -15,7 +15,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
ALLOWED_EXTRA_MISSING=4
|
||||
ALLOWED_EXTRA_MISSING=30
|
||||
|
||||
show_diff () {
|
||||
head -1 $1
|
||||
|
2
tox.ini
2
tox.ini
@ -57,7 +57,7 @@ commands = {posargs}
|
||||
|
||||
[testenv:gen-cover]
|
||||
commands = python setup.py testr --coverage \
|
||||
--omit='{toxinidir}/refstack/tests*,{toxinidir}/refstack/api/config.py,{toxinidir}/refstack/db/migrations/alembic/env.py,{toxinidir}/refstack/opts.py' \
|
||||
--omit='{toxinidir}/refstack/tests*,{toxinidir}/refstack/api/config.py,{toxinidir}/refstack/db/migrations/alembic/*,{toxinidir}/refstack/opts.py' \
|
||||
--testr-args='{posargs}'
|
||||
|
||||
[testenv:cover]
|
||||
|
Loading…
Reference in New Issue
Block a user