Merge "add HEAD sentinel file that contains migration revision"
This commit is contained in:
commit
640b225917
1
neutron/db/migration/alembic_migrations/versions/HEAD
Normal file
1
neutron/db/migration/alembic_migrations/versions/HEAD
Normal file
@ -0,0 +1 @@
|
|||||||
|
2447ad0e9585
|
@ -18,11 +18,14 @@ import os
|
|||||||
|
|
||||||
from alembic import command as alembic_command
|
from alembic import command as alembic_command
|
||||||
from alembic import config as alembic_config
|
from alembic import config as alembic_config
|
||||||
|
from alembic import script as alembic_script
|
||||||
from alembic import util as alembic_util
|
from alembic import util as alembic_util
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from neutron.common import legacy
|
from neutron.common import legacy
|
||||||
|
|
||||||
|
HEAD_FILENAME = 'HEAD'
|
||||||
|
|
||||||
|
|
||||||
_core_opts = [
|
_core_opts = [
|
||||||
cfg.StrOpt('core_plugin',
|
cfg.StrOpt('core_plugin',
|
||||||
@ -61,6 +64,7 @@ def do_alembic_command(config, cmd, *args, **kwargs):
|
|||||||
|
|
||||||
def do_check_migration(config, cmd):
|
def do_check_migration(config, cmd):
|
||||||
do_alembic_command(config, 'branches')
|
do_alembic_command(config, 'branches')
|
||||||
|
validate_head_file(config)
|
||||||
|
|
||||||
|
|
||||||
def do_upgrade_downgrade(config, cmd):
|
def do_upgrade_downgrade(config, cmd):
|
||||||
@ -89,6 +93,30 @@ def do_revision(config, cmd):
|
|||||||
message=CONF.command.message,
|
message=CONF.command.message,
|
||||||
autogenerate=CONF.command.autogenerate,
|
autogenerate=CONF.command.autogenerate,
|
||||||
sql=CONF.command.sql)
|
sql=CONF.command.sql)
|
||||||
|
update_head_file(config)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_head_file(config):
|
||||||
|
script = alembic_script.ScriptDirectory.from_config(config)
|
||||||
|
if len(script.get_heads()) > 1:
|
||||||
|
alembic_util.err(_('Timeline branches unable to generate timeline'))
|
||||||
|
|
||||||
|
head_path = os.path.join(script.versions, HEAD_FILENAME)
|
||||||
|
if (os.path.isfile(head_path) and
|
||||||
|
open(head_path).read().strip() == script.get_current_head()):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
alembic_util.err(_('HEAD file does not match migration timeline head'))
|
||||||
|
|
||||||
|
|
||||||
|
def update_head_file(config):
|
||||||
|
script = alembic_script.ScriptDirectory.from_config(config)
|
||||||
|
if len(script.get_heads()) > 1:
|
||||||
|
alembic_util.err(_('Timeline branches unable to generate timeline'))
|
||||||
|
|
||||||
|
head_path = os.path.join(script.versions, HEAD_FILENAME)
|
||||||
|
with open(head_path, 'w+') as f:
|
||||||
|
f.write(script.get_current_head())
|
||||||
|
|
||||||
|
|
||||||
def add_command_parsers(subparsers):
|
def add_command_parsers(subparsers):
|
||||||
|
@ -40,6 +40,8 @@ class TestCli(base.BaseTestCase):
|
|||||||
super(TestCli, self).setUp()
|
super(TestCli, self).setUp()
|
||||||
self.do_alembic_cmd_p = mock.patch.object(cli, 'do_alembic_command')
|
self.do_alembic_cmd_p = mock.patch.object(cli, 'do_alembic_command')
|
||||||
self.do_alembic_cmd = self.do_alembic_cmd_p.start()
|
self.do_alembic_cmd = self.do_alembic_cmd_p.start()
|
||||||
|
self.mock_alembic_err = mock.patch('alembic.util.err').start()
|
||||||
|
self.mock_alembic_err.side_effect = SystemExit
|
||||||
self.addCleanup(self.do_alembic_cmd_p.stop)
|
self.addCleanup(self.do_alembic_cmd_p.stop)
|
||||||
|
|
||||||
def _main_test_helper(self, argv, func_name, exp_args=(), exp_kwargs={}):
|
def _main_test_helper(self, argv, func_name, exp_args=(), exp_kwargs={}):
|
||||||
@ -71,22 +73,28 @@ class TestCli(base.BaseTestCase):
|
|||||||
self._main_test_helper(['prog', 'history'], 'history')
|
self._main_test_helper(['prog', 'history'], 'history')
|
||||||
|
|
||||||
def test_check_migration(self):
|
def test_check_migration(self):
|
||||||
self._main_test_helper(['prog', 'check_migration'], 'branches')
|
with mock.patch.object(cli, 'validate_head_file') as validate:
|
||||||
|
self._main_test_helper(['prog', 'check_migration'], 'branches')
|
||||||
|
validate.assert_called_once_with(mock.ANY)
|
||||||
|
|
||||||
def test_database_sync_revision(self):
|
def test_database_sync_revision(self):
|
||||||
self._main_test_helper(
|
with mock.patch.object(cli, 'update_head_file') as update:
|
||||||
['prog', 'revision', '--autogenerate', '-m', 'message'],
|
self._main_test_helper(
|
||||||
'revision',
|
['prog', 'revision', '--autogenerate', '-m', 'message'],
|
||||||
(),
|
'revision',
|
||||||
{'message': 'message', 'sql': False, 'autogenerate': True}
|
(),
|
||||||
)
|
{'message': 'message', 'sql': False, 'autogenerate': True}
|
||||||
|
)
|
||||||
|
update.assert_called_once_with(mock.ANY)
|
||||||
|
|
||||||
self._main_test_helper(
|
update.reset_mock()
|
||||||
['prog', 'revision', '--sql', '-m', 'message'],
|
self._main_test_helper(
|
||||||
'revision',
|
['prog', 'revision', '--sql', '-m', 'message'],
|
||||||
(),
|
'revision',
|
||||||
{'message': 'message', 'sql': True, 'autogenerate': False}
|
(),
|
||||||
)
|
{'message': 'message', 'sql': True, 'autogenerate': False}
|
||||||
|
)
|
||||||
|
update.assert_called_once_with(mock.ANY)
|
||||||
|
|
||||||
def test_upgrade(self):
|
def test_upgrade(self):
|
||||||
self._main_test_helper(
|
self._main_test_helper(
|
||||||
@ -117,3 +125,61 @@ class TestCli(base.BaseTestCase):
|
|||||||
('-2',),
|
('-2',),
|
||||||
{'sql': False}
|
{'sql': False}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _test_validate_head_file_helper(self, heads, file_content=None):
|
||||||
|
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
||||||
|
fc.return_value.get_heads.return_value = heads
|
||||||
|
fc.return_value.get_current_head.return_value = heads[0]
|
||||||
|
with mock.patch('__builtin__.open') as mock_open:
|
||||||
|
mock_open.return_value.__enter__ = lambda s: s
|
||||||
|
mock_open.return_value.__exit__ = mock.Mock()
|
||||||
|
mock_open.return_value.read.return_value = file_content
|
||||||
|
|
||||||
|
with mock.patch('os.path.isfile') as is_file:
|
||||||
|
is_file.return_value = file_content is not None
|
||||||
|
|
||||||
|
if file_content in heads:
|
||||||
|
cli.validate_head_file(mock.sentinel.config)
|
||||||
|
else:
|
||||||
|
self.assertRaises(
|
||||||
|
SystemExit,
|
||||||
|
cli.validate_head_file,
|
||||||
|
mock.sentinel.config
|
||||||
|
)
|
||||||
|
self.mock_alembic_err.assert_called_once_with(mock.ANY)
|
||||||
|
fc.assert_called_once_with(mock.sentinel.config)
|
||||||
|
|
||||||
|
def test_validate_head_file_multiple_heads(self):
|
||||||
|
self._test_validate_head_file_helper(['a', 'b'])
|
||||||
|
|
||||||
|
def test_validate_head_file_missing_file(self):
|
||||||
|
self._test_validate_head_file_helper(['a'])
|
||||||
|
|
||||||
|
def test_validate_head_file_wrong_contents(self):
|
||||||
|
self._test_validate_head_file_helper(['a'], 'b')
|
||||||
|
|
||||||
|
def test_validate_head_success(self):
|
||||||
|
self._test_validate_head_file_helper(['a'], 'a')
|
||||||
|
|
||||||
|
def test_update_head_file_multiple_heads(self):
|
||||||
|
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
||||||
|
fc.return_value.get_heads.return_value = ['a', 'b']
|
||||||
|
self.assertRaises(
|
||||||
|
SystemExit,
|
||||||
|
cli.update_head_file,
|
||||||
|
mock.sentinel.config
|
||||||
|
)
|
||||||
|
self.mock_alembic_err.assert_called_once_with(mock.ANY)
|
||||||
|
fc.assert_called_once_with(mock.sentinel.config)
|
||||||
|
|
||||||
|
def test_update_head_file_success(self):
|
||||||
|
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
||||||
|
fc.return_value.get_heads.return_value = ['a']
|
||||||
|
fc.return_value.get_current_head.return_value = 'a'
|
||||||
|
with mock.patch('__builtin__.open') as mock_open:
|
||||||
|
mock_open.return_value.__enter__ = lambda s: s
|
||||||
|
mock_open.return_value.__exit__ = mock.Mock()
|
||||||
|
|
||||||
|
cli.update_head_file(mock.sentinel.config)
|
||||||
|
mock_open.write.called_once_with('a')
|
||||||
|
fc.assert_called_once_with(mock.sentinel.config)
|
||||||
|
1
tox.ini
1
tox.ini
@ -25,6 +25,7 @@ downloadcache = ~/cache/pip
|
|||||||
[testenv:pep8]
|
[testenv:pep8]
|
||||||
commands =
|
commands =
|
||||||
flake8
|
flake8
|
||||||
|
neutron-db-manage check_migration
|
||||||
|
|
||||||
[testenv:i18n]
|
[testenv:i18n]
|
||||||
commands = python ./tools/check_i18n.py ./neutron ./tools/i18n_cfg.py
|
commands = python ./tools/check_i18n.py ./neutron ./tools/i18n_cfg.py
|
||||||
|
Loading…
x
Reference in New Issue
Block a user