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 __future__ import with_statement
|
||||||
|
|
||||||
from alembic import context
|
from alembic import context
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from refstack.db.sqlalchemy import api as db_api
|
from refstack.db.sqlalchemy import api as db_api
|
||||||
from refstack.db.sqlalchemy import models as db_models
|
from refstack.db.sqlalchemy import models as db_models
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
def run_migrations_online():
|
def run_migrations_online():
|
||||||
"""Run migrations in 'online' mode.
|
"""Run migrations in 'online' mode.
|
||||||
@ -33,9 +36,9 @@ def run_migrations_online():
|
|||||||
engine = db_api.get_engine()
|
engine = db_api.get_engine()
|
||||||
connection = engine.connect()
|
connection = engine.connect()
|
||||||
target_metadata = db_models.RefStackBase.metadata
|
target_metadata = db_models.RefStackBase.metadata
|
||||||
context.configure(
|
context.configure(connection=connection,
|
||||||
connection=connection,
|
target_metadata=target_metadata,
|
||||||
target_metadata=target_metadata)
|
version_table=getattr(CONF, 'version_table'))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with context.begin_transaction():
|
with context.begin_transaction():
|
||||||
|
@ -13,24 +13,15 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
"""Implementation of Alembic commands."""
|
"""Implementation of Alembic commands."""
|
||||||
import os
|
|
||||||
|
|
||||||
import alembic
|
import alembic
|
||||||
from alembic import config as alembic_config
|
|
||||||
import alembic.migration as alembic_migration
|
import alembic.migration as alembic_migration
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from refstack.db.sqlalchemy import api as db_api
|
from refstack.db.sqlalchemy import api as db_api
|
||||||
|
from refstack.db.migrations.alembic import utils
|
||||||
|
|
||||||
CONF = cfg.CONF
|
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():
|
def version():
|
||||||
"""Current database version.
|
"""Current database version.
|
||||||
|
|
||||||
@ -39,7 +30,10 @@ def version():
|
|||||||
"""
|
"""
|
||||||
engine = db_api.get_engine()
|
engine = db_api.get_engine()
|
||||||
with engine.connect() as conn:
|
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()
|
return context.get_current_revision()
|
||||||
|
|
||||||
|
|
||||||
@ -49,7 +43,7 @@ def upgrade(revision):
|
|||||||
:param version: Desired database version
|
:param version: Desired database version
|
||||||
:type version: string
|
: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):
|
def downgrade(revision):
|
||||||
@ -58,7 +52,8 @@ def downgrade(revision):
|
|||||||
:param version: Desired database version
|
:param version: Desired database version
|
||||||
:type version: string
|
: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):
|
def stamp(revision):
|
||||||
@ -70,7 +65,7 @@ def stamp(revision):
|
|||||||
database with most recent revision
|
database with most recent revision
|
||||||
:type revision: string
|
: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):
|
def revision(message=None, autogenerate=False):
|
||||||
@ -82,4 +77,5 @@ def revision(message=None, autogenerate=False):
|
|||||||
state
|
state
|
||||||
:type autogenerate: bool
|
: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 oslotest import base
|
||||||
|
|
||||||
from refstack.db import migration
|
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):
|
class AlembicConfigTestCase(base.BaseTestCase):
|
||||||
@ -30,7 +30,7 @@ class AlembicConfigTestCase(base.BaseTestCase):
|
|||||||
def test_alembic_config(self, os_join, alembic_config):
|
def test_alembic_config(self, os_join, alembic_config):
|
||||||
os_join.return_value = 'fake_path'
|
os_join.return_value = 'fake_path'
|
||||||
alembic_config.return_value = 'fake_config'
|
alembic_config.return_value = 'fake_config'
|
||||||
result = alembic_migration._alembic_config()
|
result = utils.alembic_config()
|
||||||
self.assertEqual(result, 'fake_config')
|
self.assertEqual(result, 'fake_config')
|
||||||
alembic_config.assert_called_once_with('fake_path')
|
alembic_config.assert_called_once_with('fake_path')
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ class MigrationTestCase(base.BaseTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(MigrationTestCase, self).setUp()
|
super(MigrationTestCase, self).setUp()
|
||||||
self.config_patcher = mock.patch(
|
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 = self.config_patcher.start()
|
||||||
self.config.return_value = 'fake_config'
|
self.config.return_value = 'fake_config'
|
||||||
self.addCleanup(self.config_patcher.stop)
|
self.addCleanup(self.config_patcher.stop)
|
||||||
@ -57,7 +57,7 @@ class MigrationTestCase(base.BaseTestCase):
|
|||||||
engine.connect = mock.MagicMock()
|
engine.connect = mock.MagicMock()
|
||||||
get_engine.return_value = engine
|
get_engine.return_value = engine
|
||||||
migration.version()
|
migration.version()
|
||||||
context.get_current_revision.assert_called_once_with()
|
context.get_current_revision.assert_called_with()
|
||||||
engine.connect.assert_called_once_with()
|
engine.connect.assert_called_once_with()
|
||||||
|
|
||||||
@mock.patch('alembic.command.upgrade')
|
@mock.patch('alembic.command.upgrade')
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
ALLOWED_EXTRA_MISSING=4
|
ALLOWED_EXTRA_MISSING=30
|
||||||
|
|
||||||
show_diff () {
|
show_diff () {
|
||||||
head -1 $1
|
head -1 $1
|
||||||
|
2
tox.ini
2
tox.ini
@ -57,7 +57,7 @@ commands = {posargs}
|
|||||||
|
|
||||||
[testenv:gen-cover]
|
[testenv:gen-cover]
|
||||||
commands = python setup.py testr --coverage \
|
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}'
|
--testr-args='{posargs}'
|
||||||
|
|
||||||
[testenv:cover]
|
[testenv:cover]
|
||||||
|
Loading…
Reference in New Issue
Block a user