Add support for sqlalchemy migration based on alembic
Implement blueprint: sqlalchemy-migration Change-Id: Ifd0d5186299611907a388f8ab8b27e312a3cb60d
This commit is contained in:
parent
e306d72268
commit
fb8da5d535
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Add migration support for Zaqar's sqlalchemy storage driver.
|
@ -3,6 +3,7 @@
|
|||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
pbr>=1.6 # Apache-2.0
|
pbr>=1.6 # Apache-2.0
|
||||||
|
|
||||||
|
alembic>=0.8.4 # MIT
|
||||||
Babel>=2.3.4 # BSD
|
Babel>=2.3.4 # BSD
|
||||||
falcon>=0.1.6 # Apache-2.0
|
falcon>=0.1.6 # Apache-2.0
|
||||||
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
|
jsonschema!=2.5.0,<3.0.0,>=2.0.0 # MIT
|
||||||
|
@ -32,6 +32,7 @@ console_scripts =
|
|||||||
zaqar-bench = zaqar.bench.conductor:main
|
zaqar-bench = zaqar.bench.conductor:main
|
||||||
zaqar-server = zaqar.cmd.server:run
|
zaqar-server = zaqar.cmd.server:run
|
||||||
zaqar-gc = zaqar.cmd.gc:run
|
zaqar-gc = zaqar.cmd.gc:run
|
||||||
|
zaqar-sql-db-manage = zaqar.storage.sqlalchemy.migration.cli:main
|
||||||
|
|
||||||
zaqar.data.storage =
|
zaqar.data.storage =
|
||||||
mongodb = zaqar.storage.mongodb.driver:DataDriver
|
mongodb = zaqar.storage.mongodb.driver:DataDriver
|
||||||
|
@ -19,6 +19,8 @@ fixtures>=3.0.0 # Apache-2.0/BSD
|
|||||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||||
testtools>=1.4.0 # MIT
|
testtools>=1.4.0 # MIT
|
||||||
|
oslo.db>=4.11.0,!=4.13.1,!=4.13.2 # Apache-2.0
|
||||||
|
testresources>=0.2.4 # Apache-2.0/BSD
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
|
sphinx!=1.3b1,<1.4,>=1.2.1 # BSD
|
||||||
|
0
zaqar/storage/sqlalchemy/migration/__init__.py
Normal file
0
zaqar/storage/sqlalchemy/migration/__init__.py
Normal file
54
zaqar/storage/sqlalchemy/migration/alembic.ini
Normal file
54
zaqar/storage/sqlalchemy/migration/alembic.ini
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# path to migration scripts
|
||||||
|
script_location = zaqar/storage/sqlalchemy/migration/alembic_migrations
|
||||||
|
|
||||||
|
# template used to generate migration files
|
||||||
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# max length of characters to apply to the
|
||||||
|
# "slug" field
|
||||||
|
#truncate_slug_length = 40
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
sqlalchemy.url =
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
@ -0,0 +1,73 @@
|
|||||||
|
<!--
|
||||||
|
Copyright 2012 New Dream Network, LLC (DreamHost)
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
The migrations in `alembic_migrations/versions` contain the changes needed to migrate
|
||||||
|
between Zaqar database revisions. A migration occurs by executing a script that
|
||||||
|
details the changes needed to upgrade the database. The migration scripts
|
||||||
|
are ordered so that multiple scripts can run sequentially. The scripts are executed by
|
||||||
|
Zaqar's migration wrapper which uses the Alembic library to manage the migration. Zaqar
|
||||||
|
supports migration from Liberty or later.
|
||||||
|
|
||||||
|
You can upgrade to the latest database version via:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf upgrade head
|
||||||
|
```
|
||||||
|
|
||||||
|
To check the current database version:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf current
|
||||||
|
```
|
||||||
|
|
||||||
|
To create a script to run the migration offline:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf upgrade head --sql
|
||||||
|
```
|
||||||
|
|
||||||
|
To run the offline migration between specific migration versions:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf upgrade <start version>:<end version> --sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Upgrade the database incrementally:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf upgrade --delta <# of revs>
|
||||||
|
```
|
||||||
|
|
||||||
|
Create new revision:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf revision -m "description of revision" --autogenerate
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a blank file:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf revision -m "description of revision"
|
||||||
|
```
|
||||||
|
|
||||||
|
This command does not perform any migrations, it only sets the revision.
|
||||||
|
Revision may be any existing revision. Use this command carefully.
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf stamp <revision>
|
||||||
|
```
|
||||||
|
|
||||||
|
To verify that the timeline does branch, you can run this command:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf check_migration
|
||||||
|
```
|
||||||
|
|
||||||
|
If the migration path does branch, you can find the branch point via:
|
||||||
|
```
|
||||||
|
$ zaqar-sql-db-manage --config-file /path/to/zaqar.conf history
|
||||||
|
```
|
96
zaqar/storage/sqlalchemy/migration/alembic_migrations/env.py
Normal file
96
zaqar/storage/sqlalchemy/migration/alembic_migrations/env.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# Copyright (c) 2013 Mirantis Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Based on Neutron's migration/cli.py
|
||||||
|
|
||||||
|
from __future__ import with_statement
|
||||||
|
from logging import config as c
|
||||||
|
|
||||||
|
from alembic import context
|
||||||
|
from oslo_utils import importutils
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy import pool
|
||||||
|
|
||||||
|
from zaqar.storage.sqlalchemy import tables
|
||||||
|
|
||||||
|
|
||||||
|
importutils.try_import('zaqar.storage.sqlalchemy.tables')
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
zaqar_config = config.zaqar_config
|
||||||
|
|
||||||
|
# Interpret the config file for Python logging.
|
||||||
|
# This line sets up loggers basically.
|
||||||
|
c.fileConfig(config.config_file_name)
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
target_metadata = tables.metadata
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline():
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
context.configure(
|
||||||
|
url=zaqar_config['drivers:management_store:sqlalchemy'].uri)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
engine = create_engine(
|
||||||
|
zaqar_config['drivers:management_store:sqlalchemy'].uri,
|
||||||
|
poolclass=pool.NullPool)
|
||||||
|
|
||||||
|
connection = engine.connect()
|
||||||
|
context.configure(
|
||||||
|
connection=connection,
|
||||||
|
target_metadata=target_metadata)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
finally:
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
@ -0,0 +1,34 @@
|
|||||||
|
# Copyright ${create_date.year} OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = ${repr(up_revision)}
|
||||||
|
down_revision = ${repr(down_revision)}
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
${upgrades if upgrades else "pass"}
|
@ -0,0 +1,72 @@
|
|||||||
|
# Copyright 2016 OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Liberty release
|
||||||
|
|
||||||
|
Revision ID: 001
|
||||||
|
Revises: None
|
||||||
|
Create Date: 2015-09-13 20:46:25.783444
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '001'
|
||||||
|
down_revision = None
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
MYSQL_ENGINE = 'InnoDB'
|
||||||
|
MYSQL_CHARSET = 'utf8'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.create_table('queues',
|
||||||
|
sa.Column('id', sa.INTEGER, primary_key=True),
|
||||||
|
sa.Column('project', sa.String(64)),
|
||||||
|
sa.Column('name', sa.String(64)),
|
||||||
|
sa.Column('metadata', sa.LargeBinary),
|
||||||
|
sa.UniqueConstraint('project', 'name'))
|
||||||
|
|
||||||
|
op.create_table('poolgroup',
|
||||||
|
sa.Column('name', sa.String(64), primary_key=True))
|
||||||
|
|
||||||
|
op.create_table('pools',
|
||||||
|
sa.Column('name', sa.String(64), primary_key=True),
|
||||||
|
sa.Column('group', sa.String(64),
|
||||||
|
sa.ForeignKey('poolgroup.name',
|
||||||
|
ondelete='CASCADE'),
|
||||||
|
nullable=True),
|
||||||
|
sa.Column('uri', sa.String(255),
|
||||||
|
unique=True, nullable=False),
|
||||||
|
sa.Column('weight', sa.INTEGER, nullable=False),
|
||||||
|
sa.Column('options', sa.Text()))
|
||||||
|
|
||||||
|
op.create_table('flavors',
|
||||||
|
sa.Column('name', sa.String(64), primary_key=True),
|
||||||
|
sa.Column('project', sa.String(64)),
|
||||||
|
sa.Column('pool_group', sa.String(64),
|
||||||
|
sa.ForeignKey('poolgroup.name',
|
||||||
|
ondelete='CASCADE'),
|
||||||
|
nullable=False),
|
||||||
|
sa.Column('capabilities', sa.Text()))
|
||||||
|
|
||||||
|
op.create_table('catalogue',
|
||||||
|
sa.Column('pool', sa.String(64),
|
||||||
|
sa.ForeignKey('pools.name',
|
||||||
|
ondelete='CASCADE')),
|
||||||
|
sa.Column('project', sa.String(64)),
|
||||||
|
sa.Column('queue', sa.String(64), nullable=False),
|
||||||
|
sa.UniqueConstraint('project', 'queue'))
|
@ -0,0 +1,30 @@
|
|||||||
|
# Copyright 2016 OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""placeholder
|
||||||
|
|
||||||
|
Revision ID: 002
|
||||||
|
Revises: 001
|
||||||
|
Create Date: 2014-04-01 21:04:47.941098
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '002'
|
||||||
|
down_revision = '001'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
pass
|
@ -0,0 +1,30 @@
|
|||||||
|
# Copyright 2014 OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""placeholder
|
||||||
|
|
||||||
|
Revision ID: 003
|
||||||
|
Revises: 002
|
||||||
|
Create Date: 2014-04-01 21:05:00.270366
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '003'
|
||||||
|
down_revision = '002'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
pass
|
@ -0,0 +1,30 @@
|
|||||||
|
# Copyright 2014 OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""placeholder
|
||||||
|
|
||||||
|
Revision ID: 004
|
||||||
|
Revises: 003
|
||||||
|
Create Date: 2014-04-01 21:04:57.627883
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '004'
|
||||||
|
down_revision = '003'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
pass
|
@ -0,0 +1,30 @@
|
|||||||
|
# Copyright 2014 OpenStack Foundation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""placeholder
|
||||||
|
|
||||||
|
Revision ID: 005
|
||||||
|
Revises: 004
|
||||||
|
Create Date: 2014-04-01 21:04:54.928605
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '005'
|
||||||
|
down_revision = '004'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
pass
|
118
zaqar/storage/sqlalchemy/migration/cli.py
Normal file
118
zaqar/storage/sqlalchemy/migration/cli.py
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
# Copyright (c) 2016 Catalyst IT Ltd.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from alembic import command as alembic_cmd
|
||||||
|
from alembic import config as alembic_cfg
|
||||||
|
from alembic import util as alembic_u
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
def do_alembic_command(config, cmd, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
getattr(alembic_cmd, cmd)(config, *args, **kwargs)
|
||||||
|
except alembic_u.CommandError as e:
|
||||||
|
alembic_u.err(str(e))
|
||||||
|
|
||||||
|
|
||||||
|
def do_check_migration(config, _cmd):
|
||||||
|
do_alembic_command(config, 'branches')
|
||||||
|
|
||||||
|
|
||||||
|
def do_upgrade_downgrade(config, cmd):
|
||||||
|
if not CONF.command.revision and not CONF.command.delta:
|
||||||
|
raise SystemExit('You must provide a revision or relative delta')
|
||||||
|
|
||||||
|
revision = CONF.command.revision
|
||||||
|
|
||||||
|
if CONF.command.delta:
|
||||||
|
sign = '+' if CONF.command.name == 'upgrade' else '-'
|
||||||
|
revision = sign + str(CONF.command.delta)
|
||||||
|
|
||||||
|
do_alembic_command(config, cmd, revision, sql=CONF.command.sql)
|
||||||
|
|
||||||
|
|
||||||
|
def do_stamp(config, cmd):
|
||||||
|
do_alembic_command(config, cmd,
|
||||||
|
CONF.command.revision,
|
||||||
|
sql=CONF.command.sql)
|
||||||
|
|
||||||
|
|
||||||
|
def do_revision(config, cmd):
|
||||||
|
do_alembic_command(config, cmd,
|
||||||
|
message=CONF.command.message,
|
||||||
|
autogenerate=CONF.command.autogenerate,
|
||||||
|
sql=CONF.command.sql)
|
||||||
|
|
||||||
|
|
||||||
|
def add_command_parsers(subparsers):
|
||||||
|
for name in ['current', 'history', 'branches']:
|
||||||
|
parser = subparsers.add_parser(name)
|
||||||
|
parser.set_defaults(func=do_alembic_command)
|
||||||
|
|
||||||
|
parser = subparsers.add_parser('check_migration')
|
||||||
|
parser.set_defaults(func=do_check_migration)
|
||||||
|
|
||||||
|
for name in ['upgrade', 'downgrade']:
|
||||||
|
parser = subparsers.add_parser(name)
|
||||||
|
parser.add_argument('--delta', type=int)
|
||||||
|
parser.add_argument('--sql', action='store_true')
|
||||||
|
parser.add_argument('revision', nargs='?')
|
||||||
|
parser.set_defaults(func=do_upgrade_downgrade)
|
||||||
|
|
||||||
|
parser = subparsers.add_parser('stamp')
|
||||||
|
parser.add_argument('--sql', action='store_true')
|
||||||
|
parser.add_argument('revision')
|
||||||
|
parser.set_defaults(func=do_stamp)
|
||||||
|
|
||||||
|
parser = subparsers.add_parser('revision')
|
||||||
|
parser.add_argument('-m', '--message')
|
||||||
|
parser.add_argument('--autogenerate', action='store_true')
|
||||||
|
parser.add_argument('--sql', action='store_true')
|
||||||
|
parser.set_defaults(func=do_revision)
|
||||||
|
|
||||||
|
|
||||||
|
command_opt = cfg.SubCommandOpt('command',
|
||||||
|
title='Command',
|
||||||
|
help='Available commands',
|
||||||
|
handler=add_command_parsers)
|
||||||
|
|
||||||
|
CONF.register_cli_opt(command_opt)
|
||||||
|
|
||||||
|
sqlalchemy_opts = [cfg.StrOpt('uri',
|
||||||
|
help='The SQLAlchemy connection string to'
|
||||||
|
' use to connect to the database.',
|
||||||
|
secret=True)]
|
||||||
|
|
||||||
|
CONF.register_opts(sqlalchemy_opts,
|
||||||
|
group='drivers:management_store:sqlalchemy')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
config = alembic_cfg.Config(
|
||||||
|
os.path.join(os.path.dirname(__file__), 'alembic.ini')
|
||||||
|
)
|
||||||
|
config.set_main_option('script_location',
|
||||||
|
'zaqar.storage.sqlalchemy.'
|
||||||
|
'migration:alembic_migrations')
|
||||||
|
|
||||||
|
# attach the octavia conf to the Alembic conf
|
||||||
|
config.zaqar_config = CONF
|
||||||
|
|
||||||
|
CONF(project='zaqar')
|
||||||
|
CONF.command.func(config, CONF.command.name)
|
@ -0,0 +1,89 @@
|
|||||||
|
# Copyright 2012 New Dream Network, LLC (DreamHost)
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import mock
|
||||||
|
import testscenarios
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
from zaqar.storage.sqlalchemy.migration import cli
|
||||||
|
|
||||||
|
|
||||||
|
class TestCli(testtools.TestCase):
|
||||||
|
func_name = ''
|
||||||
|
exp_args = ()
|
||||||
|
exp_kwargs = {}
|
||||||
|
|
||||||
|
scenarios = [
|
||||||
|
('stamp',
|
||||||
|
dict(argv=['prog', 'stamp', 'foo'], func_name='stamp',
|
||||||
|
exp_args=('foo',), exp_kwargs={'sql': False})),
|
||||||
|
('stamp-sql',
|
||||||
|
dict(argv=['prog', 'stamp', 'foo', '--sql'], func_name='stamp',
|
||||||
|
exp_args=('foo',), exp_kwargs={'sql': True})),
|
||||||
|
('current',
|
||||||
|
dict(argv=['prog', 'current'], func_name='current',
|
||||||
|
exp_args=[], exp_kwargs=dict())),
|
||||||
|
('history',
|
||||||
|
dict(argv=['prog', 'history'], func_name='history',
|
||||||
|
exp_args=[], exp_kwargs=dict())),
|
||||||
|
('check_migration',
|
||||||
|
dict(argv=['prog', 'check_migration'], func_name='branches',
|
||||||
|
exp_args=[], exp_kwargs=dict())),
|
||||||
|
('sync_revision_autogenerate',
|
||||||
|
dict(argv=['prog', 'revision', '--autogenerate', '-m', 'message'],
|
||||||
|
func_name='revision',
|
||||||
|
exp_args=(),
|
||||||
|
exp_kwargs={
|
||||||
|
'message': 'message', 'sql': False, 'autogenerate': True})),
|
||||||
|
('sync_revision_sql',
|
||||||
|
dict(argv=['prog', 'revision', '--sql', '-m', 'message'],
|
||||||
|
func_name='revision',
|
||||||
|
exp_args=(),
|
||||||
|
exp_kwargs={
|
||||||
|
'message': 'message', 'sql': True, 'autogenerate': False})),
|
||||||
|
('upgrade-sql',
|
||||||
|
dict(argv=['prog', 'upgrade', '--sql', 'head'],
|
||||||
|
func_name='upgrade',
|
||||||
|
exp_args=('head',),
|
||||||
|
exp_kwargs={'sql': True})),
|
||||||
|
|
||||||
|
('upgrade-delta',
|
||||||
|
dict(argv=['prog', 'upgrade', '--delta', '3'],
|
||||||
|
func_name='upgrade',
|
||||||
|
exp_args=('+3',),
|
||||||
|
exp_kwargs={'sql': False}))
|
||||||
|
]
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestCli, self).setUp()
|
||||||
|
do_alembic_cmd_p = mock.patch.object(cli, 'do_alembic_command')
|
||||||
|
self.addCleanup(do_alembic_cmd_p.stop)
|
||||||
|
self.do_alembic_cmd = do_alembic_cmd_p.start()
|
||||||
|
self.addCleanup(cli.CONF.reset)
|
||||||
|
|
||||||
|
def test_cli(self):
|
||||||
|
with mock.patch.object(sys, 'argv', self.argv):
|
||||||
|
cli.main()
|
||||||
|
self.do_alembic_cmd.assert_has_calls(
|
||||||
|
[mock.call(
|
||||||
|
mock.ANY, self.func_name,
|
||||||
|
*self.exp_args, **self.exp_kwargs)]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_tests(loader, in_tests, pattern):
|
||||||
|
return testscenarios.load_tests_apply_scenarios(loader, in_tests, pattern)
|
175
zaqar/tests/unit/storage/sqlalchemy_migration/test_migrations.py
Normal file
175
zaqar/tests/unit/storage/sqlalchemy_migration/test_migrations.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
# Copyright 2014 OpenStack Foundation
|
||||||
|
# Copyright 2014 Mirantis Inc
|
||||||
|
# Copyright 2016 Catalyst IT Ltd.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Tests for database migrations.
|
||||||
|
|
||||||
|
For the opportunistic testing you need to set up a db named 'openstack_citest'
|
||||||
|
with user 'openstack_citest' and password 'openstack_citest' on localhost.
|
||||||
|
The test will then use that db and u/p combo to run the tests.
|
||||||
|
|
||||||
|
For postgres on Ubuntu this can be done with the following commands:
|
||||||
|
|
||||||
|
sudo -u postgres psql
|
||||||
|
postgres=# create user openstack_citest with createdb login password
|
||||||
|
'openstack_citest';
|
||||||
|
postgres=# create database openstack_citest with owner openstack_citest;
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from oslo_db.sqlalchemy import test_base
|
||||||
|
from oslo_db.sqlalchemy import utils as db_utils
|
||||||
|
|
||||||
|
from zaqar.tests.unit.storage.sqlalchemy_migration import \
|
||||||
|
test_migrations_base as base
|
||||||
|
|
||||||
|
|
||||||
|
class ZaqarMigrationsCheckers(object):
|
||||||
|
|
||||||
|
def assertColumnExists(self, engine, table, column):
|
||||||
|
t = db_utils.get_table(engine, table)
|
||||||
|
self.assertIn(column, t.c)
|
||||||
|
|
||||||
|
def assertColumnsExist(self, engine, table, columns):
|
||||||
|
for column in columns:
|
||||||
|
self.assertColumnExists(engine, table, column)
|
||||||
|
|
||||||
|
def assertColumnType(self, engine, table, column, column_type):
|
||||||
|
t = db_utils.get_table(engine, table)
|
||||||
|
column_ref_type = str(t.c[column].type)
|
||||||
|
self.assertEqual(column_ref_type, column_type)
|
||||||
|
|
||||||
|
def assertColumnCount(self, engine, table, columns):
|
||||||
|
t = db_utils.get_table(engine, table)
|
||||||
|
self.assertEqual(len(columns), len(t.columns))
|
||||||
|
|
||||||
|
def assertColumnNotExists(self, engine, table, column):
|
||||||
|
t = db_utils.get_table(engine, table)
|
||||||
|
self.assertNotIn(column, t.c)
|
||||||
|
|
||||||
|
def assertIndexExists(self, engine, table, index):
|
||||||
|
t = db_utils.get_table(engine, table)
|
||||||
|
index_names = [idx.name for idx in t.indexes]
|
||||||
|
self.assertIn(index, index_names)
|
||||||
|
|
||||||
|
def assertIndexMembers(self, engine, table, index, members):
|
||||||
|
self.assertIndexExists(engine, table, index)
|
||||||
|
|
||||||
|
t = db_utils.get_table(engine, table)
|
||||||
|
index_columns = None
|
||||||
|
for idx in t.indexes:
|
||||||
|
if idx.name == index:
|
||||||
|
index_columns = idx.columns.keys()
|
||||||
|
break
|
||||||
|
|
||||||
|
self.assertEqual(sorted(members), sorted(index_columns))
|
||||||
|
|
||||||
|
def test_walk_versions(self):
|
||||||
|
self.walk_versions(self.engine)
|
||||||
|
|
||||||
|
def _pre_upgrade_001(self, engine):
|
||||||
|
# Anything returned from this method will be
|
||||||
|
# passed to corresponding _check_xxx method as 'data'.
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_001(self, engine, data):
|
||||||
|
queues_columns = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'project',
|
||||||
|
'metadata'
|
||||||
|
]
|
||||||
|
self.assertColumnsExist(
|
||||||
|
engine, 'queues', queues_columns)
|
||||||
|
self.assertColumnCount(
|
||||||
|
engine, 'queues', queues_columns)
|
||||||
|
|
||||||
|
poolgroup_columns = [
|
||||||
|
'name',
|
||||||
|
]
|
||||||
|
self.assertColumnsExist(
|
||||||
|
engine, 'poolgroup', poolgroup_columns)
|
||||||
|
self.assertColumnCount(
|
||||||
|
engine, 'poolgroup', poolgroup_columns)
|
||||||
|
|
||||||
|
pools_columns = [
|
||||||
|
'name',
|
||||||
|
'group',
|
||||||
|
'uri',
|
||||||
|
'weight',
|
||||||
|
'options',
|
||||||
|
]
|
||||||
|
self.assertColumnsExist(
|
||||||
|
engine, 'pools', pools_columns)
|
||||||
|
self.assertColumnCount(
|
||||||
|
engine, 'pools', pools_columns)
|
||||||
|
|
||||||
|
flavors_columns = [
|
||||||
|
'name',
|
||||||
|
'project',
|
||||||
|
'pool_group',
|
||||||
|
'capabilities',
|
||||||
|
]
|
||||||
|
self.assertColumnsExist(
|
||||||
|
engine, 'flavors', flavors_columns)
|
||||||
|
self.assertColumnCount(
|
||||||
|
engine, 'flavors', flavors_columns)
|
||||||
|
|
||||||
|
catalogue_columns = [
|
||||||
|
'pool',
|
||||||
|
'project',
|
||||||
|
'queue',
|
||||||
|
]
|
||||||
|
self.assertColumnsExist(
|
||||||
|
engine, 'catalogue', catalogue_columns)
|
||||||
|
self.assertColumnCount(
|
||||||
|
engine, 'catalogue', catalogue_columns)
|
||||||
|
|
||||||
|
self._data_001(engine, data)
|
||||||
|
|
||||||
|
def _data_001(self, engine, data):
|
||||||
|
datasize = 512 * 1024 # 512kB
|
||||||
|
data = os.urandom(datasize)
|
||||||
|
t = db_utils.get_table(engine, 'job_binary_internal')
|
||||||
|
engine.execute(t.insert(), data=data, id='123', name='name')
|
||||||
|
new_data = engine.execute(t.select()).fetchone().data
|
||||||
|
self.assertEqual(data, new_data)
|
||||||
|
engine.execute(t.delete())
|
||||||
|
|
||||||
|
def _check_002(self, engine, data):
|
||||||
|
# currently, 002 is just a placeholder
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_003(self, engine, data):
|
||||||
|
# currently, 003 is just a placeholder
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_004(self, engine, data):
|
||||||
|
# currently, 004 is just a placeholder
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _check_005(self, engine, data):
|
||||||
|
# currently, 005 is just a placeholder
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TestMigrationsMySQL(ZaqarMigrationsCheckers,
|
||||||
|
base.BaseWalkMigrationTestCase,
|
||||||
|
base.TestModelsMigrationsSync,
|
||||||
|
test_base.MySQLOpportunisticTestCase):
|
||||||
|
pass
|
@ -0,0 +1,188 @@
|
|||||||
|
# Copyright 2010-2011 OpenStack Foundation
|
||||||
|
# Copyright 2012-2013 IBM Corp.
|
||||||
|
# Copyright 2016 Catalyst IT Ltd.
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Ripped off from Nova's test_migrations.py
|
||||||
|
# The only difference between Nova and this code is usage of alembic instead
|
||||||
|
# of sqlalchemy migrations.
|
||||||
|
#
|
||||||
|
# There is an ongoing work to extact similar code to oslo incubator. Once it is
|
||||||
|
# extracted we'll be able to remove this file and use oslo.
|
||||||
|
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
import alembic
|
||||||
|
from alembic import command
|
||||||
|
from alembic import config as alembic_config
|
||||||
|
from alembic import migration
|
||||||
|
from alembic import script as alembic_script
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_db.sqlalchemy import test_migrations as t_m
|
||||||
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from zaqar.i18n import _LE
|
||||||
|
import zaqar.storage.sqlalchemy.migration
|
||||||
|
from zaqar.storage.sqlalchemy import tables
|
||||||
|
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class BaseWalkMigrationTestCase(object):
|
||||||
|
|
||||||
|
ALEMBIC_CONFIG = alembic_config.Config(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(zaqar.storage.sqlalchemy.migration.__file__),
|
||||||
|
'alembic.ini')
|
||||||
|
)
|
||||||
|
|
||||||
|
ALEMBIC_CONFIG.zaqar_config = CONF
|
||||||
|
|
||||||
|
def _configure(self, engine):
|
||||||
|
"""For each type of repository we should do some of configure steps.
|
||||||
|
|
||||||
|
For migrate_repo we should set under version control our database.
|
||||||
|
For alembic we should configure database settings. For this goal we
|
||||||
|
should use oslo_config and openstack.commom.db.sqlalchemy.session with
|
||||||
|
database functionality (reset default settings and session cleanup).
|
||||||
|
"""
|
||||||
|
CONF.set_override('uri', str(engine.url),
|
||||||
|
group='drivers:management_store:sqlalchemy',
|
||||||
|
enforce_type=True)
|
||||||
|
sa.cleanup()
|
||||||
|
|
||||||
|
def _alembic_command(self, alembic_command, engine, *args, **kwargs):
|
||||||
|
"""Most of alembic command return data into output.
|
||||||
|
|
||||||
|
We should redefine this setting for getting info.
|
||||||
|
"""
|
||||||
|
self.ALEMBIC_CONFIG.stdout = buf = io.StringIO()
|
||||||
|
CONF.set_override('uri', str(engine.url),
|
||||||
|
group='drivers:management_store:sqlalchemy',
|
||||||
|
enforce_type=True)
|
||||||
|
sa.cleanup()
|
||||||
|
getattr(command, alembic_command)(*args, **kwargs)
|
||||||
|
res = buf.getvalue().strip()
|
||||||
|
LOG.debug('Alembic command {command} returns: {result}'.format(
|
||||||
|
command=alembic_command, result=res))
|
||||||
|
sa.cleanup()
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _get_versions(self):
|
||||||
|
"""Stores a list of versions.
|
||||||
|
|
||||||
|
Since alembic version has a random algorithm of generation
|
||||||
|
(SA-migrate has an ordered autoincrement naming) we should store
|
||||||
|
a list of versions (version for upgrade)
|
||||||
|
for successful testing of migrations in up mode.
|
||||||
|
"""
|
||||||
|
|
||||||
|
env = alembic_script.ScriptDirectory.from_config(self.ALEMBIC_CONFIG)
|
||||||
|
versions = []
|
||||||
|
for rev in env.walk_revisions():
|
||||||
|
versions.append(rev.revision)
|
||||||
|
|
||||||
|
versions.reverse()
|
||||||
|
return versions
|
||||||
|
|
||||||
|
def walk_versions(self, engine=None):
|
||||||
|
# Determine latest version script from the repo, then
|
||||||
|
# upgrade from 1 through to the latest, with no data
|
||||||
|
# in the databases. This just checks that the schema itself
|
||||||
|
# upgrades successfully.
|
||||||
|
|
||||||
|
self._configure(engine)
|
||||||
|
versions = self._get_versions()
|
||||||
|
for ver in versions:
|
||||||
|
self._migrate_up(engine, ver, with_data=True)
|
||||||
|
|
||||||
|
def _get_version_from_db(self, engine):
|
||||||
|
"""Returns latest version from db for each type of migrate repo."""
|
||||||
|
|
||||||
|
conn = engine.connect()
|
||||||
|
try:
|
||||||
|
context = migration.MigrationContext.configure(conn)
|
||||||
|
version = context.get_current_revision() or '-1'
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
return version
|
||||||
|
|
||||||
|
def _migrate(self, engine, version, cmd):
|
||||||
|
"""Base method for manipulation with migrate repo.
|
||||||
|
|
||||||
|
It will upgrade or downgrade the actual database.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._alembic_command(cmd, engine, self.ALEMBIC_CONFIG, version)
|
||||||
|
|
||||||
|
def _migrate_up(self, engine, version, with_data=False):
|
||||||
|
"""migrate up to a new version of the db.
|
||||||
|
|
||||||
|
We allow for data insertion and post checks at every
|
||||||
|
migration version with special _pre_upgrade_### and
|
||||||
|
_check_### functions in the main test.
|
||||||
|
"""
|
||||||
|
# NOTE(sdague): try block is here because it's impossible to debug
|
||||||
|
# where a failed data migration happens otherwise
|
||||||
|
check_version = version
|
||||||
|
try:
|
||||||
|
if with_data:
|
||||||
|
data = None
|
||||||
|
pre_upgrade = getattr(
|
||||||
|
self, "_pre_upgrade_%s" % check_version, None)
|
||||||
|
if pre_upgrade:
|
||||||
|
data = pre_upgrade(engine)
|
||||||
|
self._migrate(engine, version, 'upgrade')
|
||||||
|
self.assertEqual(version, self._get_version_from_db(engine))
|
||||||
|
if with_data:
|
||||||
|
check = getattr(self, "_check_%s" % check_version, None)
|
||||||
|
if check:
|
||||||
|
check(engine, data)
|
||||||
|
except Exception:
|
||||||
|
LOG.error(_LE("Failed to migrate to version {version} on engine "
|
||||||
|
"{engine}").format(version=version, engine=engine))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class TestModelsMigrationsSync(t_m.ModelsMigrationsSync):
|
||||||
|
"""Class for comparison of DB migration scripts and models.
|
||||||
|
|
||||||
|
Allows to check if the DB schema obtained by applying of migration
|
||||||
|
scripts is equal to the one produced from models definitions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
ALEMBIC_CONFIG = alembic_config.Config(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(zaqar.storage.sqlalchemy.migration.__file__),
|
||||||
|
'alembic.ini')
|
||||||
|
)
|
||||||
|
ALEMBIC_CONFIG.zaqar_config = CONF
|
||||||
|
|
||||||
|
def get_engine(self):
|
||||||
|
return self.engine
|
||||||
|
|
||||||
|
def db_sync(self, engine):
|
||||||
|
CONF.set_override('uri', str(engine.url),
|
||||||
|
group='drivers:management_store:sqlalchemy',
|
||||||
|
enforce_type=True)
|
||||||
|
alembic.command.upgrade(self.ALEMBIC_CONFIG, 'head')
|
||||||
|
|
||||||
|
def get_metadata(self):
|
||||||
|
return tables.metadata
|
Loading…
Reference in New Issue
Block a user