Merge "Support unique labels for alembic branches"
This commit is contained in:
commit
d112b65c5f
@ -418,6 +418,20 @@ following directive should be added in the contraction script::
|
|||||||
depends_on = ('<expansion-revision>',)
|
depends_on = ('<expansion-revision>',)
|
||||||
|
|
||||||
|
|
||||||
|
Expand and Contract Branch Labeling
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Before the Newton release, neutron and each sub-project had its own alembic
|
||||||
|
environment and each repo could re-use the labels ``contract`` and ``expand``
|
||||||
|
for their alembic branches. With the Newton release, all neutron sub-projects
|
||||||
|
use neutron's alembic environment, and this requires globally unique branch
|
||||||
|
labels.
|
||||||
|
|
||||||
|
To be compatible with the Newton release of neutron, all projects must use
|
||||||
|
unique alembic branch labels of the form ``<project>-contract`` and
|
||||||
|
``<project>-expand``.
|
||||||
|
|
||||||
|
|
||||||
HEAD files for conflict management
|
HEAD files for conflict management
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ from neutron.db.migration import cli
|
|||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = '30018084ec99'
|
revision = '30018084ec99'
|
||||||
down_revision = 'kilo'
|
down_revision = 'kilo'
|
||||||
branch_labels = (cli.CONTRACT_BRANCH,)
|
branch_labels = ('neutron-' + cli.CONTRACT_BRANCH,)
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
|
@ -30,7 +30,7 @@ from neutron.db.migration import cli
|
|||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision = '354db87e3225'
|
revision = '354db87e3225'
|
||||||
down_revision = 'kilo'
|
down_revision = 'kilo'
|
||||||
branch_labels = (cli.EXPAND_BRANCH,)
|
branch_labels = ('neutron-' + cli.EXPAND_BRANCH,)
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
|
@ -54,7 +54,8 @@ def _migration_script_ops(context, directive, phase):
|
|||||||
version_path = cli._get_version_branch_path(
|
version_path = cli._get_version_branch_path(
|
||||||
context.config, release=cli.CURRENT_RELEASE, branch=phase)
|
context.config, release=cli.CURRENT_RELEASE, branch=phase)
|
||||||
autogen_kwargs = {}
|
autogen_kwargs = {}
|
||||||
cli._check_bootstrap_new_branch(phase, version_path, autogen_kwargs)
|
cli._check_bootstrap_new_branch(context.config, phase, version_path,
|
||||||
|
autogen_kwargs)
|
||||||
|
|
||||||
op = ops.MigrationScript(
|
op = ops.MigrationScript(
|
||||||
new_rev_id(),
|
new_rev_id(),
|
||||||
|
@ -93,11 +93,14 @@ CONF.register_cli_opts(_db_opts, 'database')
|
|||||||
|
|
||||||
|
|
||||||
def do_alembic_command(config, cmd, revision=None, desc=None, **kwargs):
|
def do_alembic_command(config, cmd, revision=None, desc=None, **kwargs):
|
||||||
|
project = config.get_main_option('neutron_project')
|
||||||
args = []
|
args = []
|
||||||
if revision:
|
if revision:
|
||||||
|
# We use unique branch labels from Newton onwards.
|
||||||
|
if revision.split('@')[0] in MIGRATION_BRANCHES:
|
||||||
|
revision = '-'.join([project, revision])
|
||||||
args.append(revision)
|
args.append(revision)
|
||||||
|
|
||||||
project = config.get_main_option('neutron_project')
|
|
||||||
if desc:
|
if desc:
|
||||||
alembic_util.msg(_('Running %(cmd)s (%(desc)s) for %(project)s ...') %
|
alembic_util.msg(_('Running %(cmd)s (%(desc)s) for %(project)s ...') %
|
||||||
{'cmd': cmd, 'desc': desc, 'project': project})
|
{'cmd': cmd, 'desc': desc, 'project': project})
|
||||||
@ -142,13 +145,13 @@ def add_branch_options(parser):
|
|||||||
def _find_milestone_revisions(config, milestone, branch=None):
|
def _find_milestone_revisions(config, milestone, branch=None):
|
||||||
"""Return the revision(s) for a given milestone."""
|
"""Return the revision(s) for a given milestone."""
|
||||||
script = alembic_script.ScriptDirectory.from_config(config)
|
script = alembic_script.ScriptDirectory.from_config(config)
|
||||||
return [
|
milestone_revisions = []
|
||||||
(m.revision, label)
|
for m in _get_revisions(script):
|
||||||
for m in _get_revisions(script)
|
for branch_label in (m.branch_labels or [None]):
|
||||||
for label in (m.branch_labels or [None])
|
if milestone in getattr(m.module, 'neutron_milestone', []):
|
||||||
if milestone in getattr(m.module, 'neutron_milestone', []) and
|
if branch is None or branch_label.endswith(branch):
|
||||||
(branch is None or branch in m.branch_labels)
|
milestone_revisions.append((m.revision, branch))
|
||||||
]
|
return milestone_revisions
|
||||||
|
|
||||||
|
|
||||||
def do_upgrade(config, cmd):
|
def do_upgrade(config, cmd):
|
||||||
@ -161,11 +164,11 @@ def do_upgrade(config, cmd):
|
|||||||
|
|
||||||
if CONF.command.expand:
|
if CONF.command.expand:
|
||||||
branch = EXPAND_BRANCH
|
branch = EXPAND_BRANCH
|
||||||
revision = _get_branch_head(EXPAND_BRANCH)
|
revision = _get_branch_head(config, EXPAND_BRANCH)
|
||||||
|
|
||||||
elif CONF.command.contract:
|
elif CONF.command.contract:
|
||||||
branch = CONTRACT_BRANCH
|
branch = CONTRACT_BRANCH
|
||||||
revision = _get_branch_head(CONTRACT_BRANCH)
|
revision = _get_branch_head(config, CONTRACT_BRANCH)
|
||||||
|
|
||||||
elif not CONF.command.revision and not CONF.command.delta:
|
elif not CONF.command.revision and not CONF.command.delta:
|
||||||
raise SystemExit(_('You must provide a revision or relative delta'))
|
raise SystemExit(_('You must provide a revision or relative delta'))
|
||||||
@ -217,14 +220,26 @@ def do_stamp(config, cmd):
|
|||||||
sql=CONF.command.sql)
|
sql=CONF.command.sql)
|
||||||
|
|
||||||
|
|
||||||
def _get_branch_head(branch):
|
def _get_branch_head(config, branch):
|
||||||
'''Get the latest @head specification for a branch.'''
|
'''Get the latest @head specification for a branch.'''
|
||||||
|
script_dir = alembic_script.ScriptDirectory.from_config(config)
|
||||||
|
revs = script_dir.revision_map.get_revisions('heads')
|
||||||
|
for rev in revs:
|
||||||
|
for branch_label in rev.branch_labels:
|
||||||
|
# For forwards and backwards compatibility we handle branch
|
||||||
|
# names of either
|
||||||
|
# 'contract/expand'
|
||||||
|
# or
|
||||||
|
# 'subproject-contract/subproject-expand'.
|
||||||
|
if branch_label.endswith(branch):
|
||||||
|
branch = branch_label
|
||||||
|
break
|
||||||
return '%s@head' % branch
|
return '%s@head' % branch
|
||||||
|
|
||||||
|
|
||||||
def _check_bootstrap_new_branch(branch, version_path, addn_kwargs):
|
def _check_bootstrap_new_branch(config, branch, version_path, addn_kwargs):
|
||||||
addn_kwargs['version_path'] = version_path
|
addn_kwargs['version_path'] = version_path
|
||||||
addn_kwargs['head'] = _get_branch_head(branch)
|
addn_kwargs['head'] = _get_branch_head(config, branch)
|
||||||
if not os.path.exists(version_path):
|
if not os.path.exists(version_path):
|
||||||
# Bootstrap initial directory structure
|
# Bootstrap initial directory structure
|
||||||
utils.ensure_dir(version_path)
|
utils.ensure_dir(version_path)
|
||||||
@ -251,7 +266,7 @@ def do_revision(config, cmd):
|
|||||||
args = copy.copy(kwargs)
|
args = copy.copy(kwargs)
|
||||||
version_path = _get_version_branch_path(
|
version_path = _get_version_branch_path(
|
||||||
config, release=CURRENT_RELEASE, branch=branch)
|
config, release=CURRENT_RELEASE, branch=branch)
|
||||||
_check_bootstrap_new_branch(branch, version_path, args)
|
_check_bootstrap_new_branch(config, branch, version_path, args)
|
||||||
do_alembic_command(config, cmd, **args)
|
do_alembic_command(config, cmd, **args)
|
||||||
else:
|
else:
|
||||||
# autogeneration code will take care of enforcing proper directories
|
# autogeneration code will take care of enforcing proper directories
|
||||||
@ -260,32 +275,15 @@ def do_revision(config, cmd):
|
|||||||
update_head_files(config)
|
update_head_files(config)
|
||||||
|
|
||||||
|
|
||||||
def _get_release_labels(labels):
|
|
||||||
result = set()
|
|
||||||
for label in labels:
|
|
||||||
# release labels were introduced Liberty for a short time and dropped
|
|
||||||
# in that same release cycle
|
|
||||||
result.add('%s_%s' % (migration.LIBERTY, label))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def _compare_labels(revision, expected_labels):
|
def _compare_labels(revision, expected_labels):
|
||||||
# validate that the script has expected labels only
|
# validate that the script has expected labels only
|
||||||
bad_labels = revision.branch_labels - expected_labels
|
expected_branch_labels = set()
|
||||||
|
for label in revision.branch_labels:
|
||||||
|
for expected_label in expected_labels:
|
||||||
|
if label.endswith(expected_label):
|
||||||
|
expected_branch_labels.add(label)
|
||||||
|
bad_labels = revision.branch_labels - expected_branch_labels
|
||||||
if bad_labels:
|
if bad_labels:
|
||||||
# NOTE(ihrachyshka): this hack is temporary to accommodate those
|
|
||||||
# projects that already initialized their branches with liberty_*
|
|
||||||
# labels. Let's notify them about the deprecation for now and drop it
|
|
||||||
# later.
|
|
||||||
bad_labels_with_release = (revision.branch_labels -
|
|
||||||
_get_release_labels(expected_labels))
|
|
||||||
if not bad_labels_with_release:
|
|
||||||
alembic_util.warn(
|
|
||||||
_('Release aware branch labels (%s) are deprecated. '
|
|
||||||
'Please switch to expand@ and contract@ '
|
|
||||||
'labels.') % bad_labels)
|
|
||||||
return
|
|
||||||
|
|
||||||
script_name = os.path.basename(revision.path)
|
script_name = os.path.basename(revision.path)
|
||||||
alembic_util.err(
|
alembic_util.err(
|
||||||
_('Unexpected label for script %(script_name)s: %(labels)s') %
|
_('Unexpected label for script %(script_name)s: %(labels)s') %
|
||||||
@ -339,6 +337,14 @@ def _get_revisions(script):
|
|||||||
return list(script.walk_revisions(base='base', head='heads'))
|
return list(script.walk_revisions(base='base', head='heads'))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_branch_type(revision):
|
||||||
|
for branch_label in revision.branch_labels:
|
||||||
|
if branch_label.endswith(CONTRACT_BRANCH):
|
||||||
|
return CONTRACT_BRANCH
|
||||||
|
if branch_label.endswith(EXPAND_BRANCH):
|
||||||
|
return EXPAND_BRANCH
|
||||||
|
|
||||||
|
|
||||||
def _get_branch_points(script):
|
def _get_branch_points(script):
|
||||||
branchpoints = []
|
branchpoints = []
|
||||||
for revision in _get_revisions(script):
|
for revision in _get_revisions(script):
|
||||||
@ -352,10 +358,7 @@ def _get_heads_map(config):
|
|||||||
heads = script.get_heads()
|
heads = script.get_heads()
|
||||||
head_map = {}
|
head_map = {}
|
||||||
for head in heads:
|
for head in heads:
|
||||||
if CONTRACT_BRANCH in script.get_revision(head).branch_labels:
|
head_map[_get_branch_type(script.get_revision(head))] = head
|
||||||
head_map[CONTRACT_BRANCH] = head
|
|
||||||
else:
|
|
||||||
head_map[EXPAND_BRANCH] = head
|
|
||||||
return head_map
|
return head_map
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,7 +235,12 @@ class TestModelsMigrationsMysql(testlib_api.MySQLTestCaseMixin,
|
|||||||
script = alembic_script.ScriptDirectory.from_config(
|
script = alembic_script.ScriptDirectory.from_config(
|
||||||
self.alembic_config)
|
self.alembic_config)
|
||||||
for m in list(script.walk_revisions(base='base', head='heads')):
|
for m in list(script.walk_revisions(base='base', head='heads')):
|
||||||
branches = m.branch_labels or []
|
branches = set()
|
||||||
|
for bl in m.branch_labels:
|
||||||
|
if bl.endswith(migration.CONTRACT_BRANCH):
|
||||||
|
branches.add(migration.CONTRACT_BRANCH)
|
||||||
|
elif bl.endswith(migration.EXPAND_BRANCH):
|
||||||
|
branches.add(migration.EXPAND_BRANCH)
|
||||||
if migration.CONTRACT_BRANCH in branches:
|
if migration.CONTRACT_BRANCH in branches:
|
||||||
method_name = 'contract_creation_exceptions'
|
method_name = 'contract_creation_exceptions'
|
||||||
exceptions_dict = creation_exceptions
|
exceptions_dict = creation_exceptions
|
||||||
|
@ -148,7 +148,28 @@ class TestCli(base.BaseTestCase):
|
|||||||
attrs=attrs)
|
attrs=attrs)
|
||||||
cli.migration_entrypoints[project] = entrypoint
|
cli.migration_entrypoints[project] = entrypoint
|
||||||
|
|
||||||
def _main_test_helper(self, argv, func_name, exp_kwargs=[{}]):
|
self.old_labels_revs = {
|
||||||
|
'a': FakeRevision(labels={cli.CONTRACT_BRANCH}),
|
||||||
|
'b': FakeRevision(labels={cli.EXPAND_BRANCH})
|
||||||
|
}
|
||||||
|
self.new_labels_revs = {
|
||||||
|
'x': FakeRevision(labels={'foo-' + cli.CONTRACT_BRANCH}),
|
||||||
|
'y': FakeRevision(labels={'foo-' + cli.EXPAND_BRANCH})
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _old_and_new_label_combination_helper(test_method):
|
||||||
|
old_cb = cli.CONTRACT_BRANCH
|
||||||
|
new_cb = 'foo-' + cli.CONTRACT_BRANCH
|
||||||
|
old_eb = cli.EXPAND_BRANCH
|
||||||
|
new_eb = 'foo-' + cli.EXPAND_BRANCH
|
||||||
|
# Test all combinations of old and new branch labels.
|
||||||
|
test_method(old_cb, old_eb)
|
||||||
|
test_method(old_cb, new_eb)
|
||||||
|
test_method(new_cb, old_eb)
|
||||||
|
test_method(new_cb, new_eb)
|
||||||
|
|
||||||
|
def _main_test_helper(self, argv, func_name, exp_kwargs=None):
|
||||||
with mock.patch.object(sys, 'argv', argv),\
|
with mock.patch.object(sys, 'argv', argv),\
|
||||||
mock.patch.object(cli, 'run_sanity_checks'),\
|
mock.patch.object(cli, 'run_sanity_checks'),\
|
||||||
mock.patch.object(cli, 'validate_revisions'):
|
mock.patch.object(cli, 'validate_revisions'):
|
||||||
@ -163,7 +184,7 @@ class TestCli(base.BaseTestCase):
|
|||||||
|
|
||||||
self.do_alembic_cmd.assert_has_calls(
|
self.do_alembic_cmd.assert_has_calls(
|
||||||
[mock.call(mock.ANY, func_name, **_append_version_path(kwargs))
|
[mock.call(mock.ANY, func_name, **_append_version_path(kwargs))
|
||||||
for kwargs in exp_kwargs]
|
for kwargs in (exp_kwargs or [{}])]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_stamp(self):
|
def test_stamp(self):
|
||||||
@ -207,10 +228,11 @@ class TestCli(base.BaseTestCase):
|
|||||||
self._main_test_helper(['prog', 'check_migration'], 'branches')
|
self._main_test_helper(['prog', 'check_migration'], 'branches')
|
||||||
self.assertEqual(len(self.projects), validate.call_count)
|
self.assertEqual(len(self.projects), validate.call_count)
|
||||||
|
|
||||||
def _test_database_sync_revision(self, separate_branches=True):
|
def _test_database_sync_revision(self, revs):
|
||||||
with mock.patch.object(cli, 'update_head_files') as update:
|
with mock.patch.object(cli, 'update_head_files') as update,\
|
||||||
if separate_branches:
|
mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
||||||
mock.patch('os.path.exists').start()
|
fc.return_value.get_revisions.side_effect = revs
|
||||||
|
mock.patch('os.path.exists').start()
|
||||||
expected_kwargs = [{
|
expected_kwargs = [{
|
||||||
'message': 'message', 'sql': False, 'autogenerate': True,
|
'message': 'message', 'sql': False, 'autogenerate': True,
|
||||||
}]
|
}]
|
||||||
@ -226,7 +248,7 @@ class TestCli(base.BaseTestCase):
|
|||||||
'message': 'message',
|
'message': 'message',
|
||||||
'sql': True,
|
'sql': True,
|
||||||
'autogenerate': False,
|
'autogenerate': False,
|
||||||
'head': cli._get_branch_head(branch)
|
'head': cli._get_branch_head(self.configs[0], branch)
|
||||||
} for branch in cli.MIGRATION_BRANCHES]
|
} for branch in cli.MIGRATION_BRANCHES]
|
||||||
for kwarg in expected_kwargs:
|
for kwarg in expected_kwargs:
|
||||||
kwarg['autogenerate'] = False
|
kwarg['autogenerate'] = False
|
||||||
@ -264,12 +286,11 @@ class TestCli(base.BaseTestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(len(self.projects), update.call_count)
|
self.assertEqual(len(self.projects), update.call_count)
|
||||||
|
|
||||||
def test_database_sync_revision(self):
|
def test_database_sync_revision_old_branch_labels(self):
|
||||||
self._test_database_sync_revision()
|
self._test_database_sync_revision(self.old_labels_revs)
|
||||||
|
|
||||||
def test_database_sync_revision_no_branches(self):
|
def test_database_sync_revision_new_branch_labels(self):
|
||||||
# Test that old branchless approach is still supported
|
self._test_database_sync_revision(self.new_labels_revs)
|
||||||
self._test_database_sync_revision(separate_branches=False)
|
|
||||||
|
|
||||||
def test_upgrade_revision(self):
|
def test_upgrade_revision(self):
|
||||||
self._main_test_helper(
|
self._main_test_helper(
|
||||||
@ -292,14 +313,22 @@ class TestCli(base.BaseTestCase):
|
|||||||
[{'desc': None, 'revision': 'kilo+3', 'sql': False}]
|
[{'desc': None, 'revision': 'kilo+3', 'sql': False}]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_upgrade_expand(self):
|
def _test_upgrade_expand(self, revs):
|
||||||
self._main_test_helper(
|
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
||||||
['prog', 'upgrade', '--expand'],
|
fc.return_value.get_revisions.side_effect = revs
|
||||||
'upgrade',
|
self._main_test_helper(
|
||||||
[{'desc': cli.EXPAND_BRANCH,
|
['prog', 'upgrade', '--expand'],
|
||||||
'revision': 'expand@head',
|
'upgrade',
|
||||||
'sql': False}]
|
[{'desc': cli.EXPAND_BRANCH,
|
||||||
)
|
'revision': 'expand@head',
|
||||||
|
'sql': False}]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_upgrade_expand_old_labels(self):
|
||||||
|
self._test_upgrade_expand(self.old_labels_revs)
|
||||||
|
|
||||||
|
def test_upgrade_expand_new_labels(self):
|
||||||
|
self._test_upgrade_expand(self.new_labels_revs)
|
||||||
|
|
||||||
def test_upgrade_expand_contract_are_mutually_exclusive(self):
|
def test_upgrade_expand_contract_are_mutually_exclusive(self):
|
||||||
with testlib_api.ExpectedException(SystemExit):
|
with testlib_api.ExpectedException(SystemExit):
|
||||||
@ -342,20 +371,31 @@ class TestCli(base.BaseTestCase):
|
|||||||
def test_upgrade_contract_conflicts_with_delta(self):
|
def test_upgrade_contract_conflicts_with_delta(self):
|
||||||
self._test_upgrade_conflicts_with_delta('contract')
|
self._test_upgrade_conflicts_with_delta('contract')
|
||||||
|
|
||||||
def test_upgrade_contract(self):
|
def _test_upgrade_contract(self, revs):
|
||||||
self._main_test_helper(
|
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
||||||
['prog', 'upgrade', '--contract'],
|
fc.return_value.get_revisions.side_effect = revs
|
||||||
'upgrade',
|
self._main_test_helper(
|
||||||
[{'desc': cli.CONTRACT_BRANCH,
|
['prog', 'upgrade', '--contract'],
|
||||||
'revision': 'contract@head',
|
'upgrade',
|
||||||
'sql': False}]
|
[{'desc': cli.CONTRACT_BRANCH,
|
||||||
)
|
'revision': 'contract@head',
|
||||||
|
'sql': False}]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_upgrade_contract_old_labels(self):
|
||||||
|
self._test_upgrade_contract(self.old_labels_revs)
|
||||||
|
|
||||||
|
def test_upgrade_contract_new_labels(self):
|
||||||
|
self._test_upgrade_contract(self.new_labels_revs)
|
||||||
|
|
||||||
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
||||||
def test_upgrade_milestone_expand_before_contract(self, walk_mock):
|
def _test_upgrade_milestone_expand_before_contract(self,
|
||||||
c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
|
contract_label,
|
||||||
|
expand_label,
|
||||||
|
walk_mock):
|
||||||
|
c_revs = [FakeRevision(labels={contract_label}) for r in range(5)]
|
||||||
c_revs[1].module.neutron_milestone = [migration.LIBERTY]
|
c_revs[1].module.neutron_milestone = [migration.LIBERTY]
|
||||||
e_revs = [FakeRevision(labels={cli.EXPAND_BRANCH}) for r in range(5)]
|
e_revs = [FakeRevision(labels={expand_label}) for r in range(5)]
|
||||||
e_revs[3].module.neutron_milestone = [migration.LIBERTY]
|
e_revs[3].module.neutron_milestone = [migration.LIBERTY]
|
||||||
walk_mock.return_value = c_revs + e_revs
|
walk_mock.return_value = c_revs + e_revs
|
||||||
self._main_test_helper(
|
self._main_test_helper(
|
||||||
@ -369,6 +409,10 @@ class TestCli(base.BaseTestCase):
|
|||||||
'sql': False}]
|
'sql': False}]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_upgrade_milestone_expand_before_contract(self):
|
||||||
|
self._old_and_new_label_combination_helper(
|
||||||
|
self._test_upgrade_milestone_expand_before_contract)
|
||||||
|
|
||||||
def assert_command_fails(self, command):
|
def assert_command_fails(self, command):
|
||||||
# Avoid cluttering stdout with argparse error messages
|
# Avoid cluttering stdout with argparse error messages
|
||||||
mock.patch('argparse.ArgumentParser._print_message').start()
|
mock.patch('argparse.ArgumentParser._print_message').start()
|
||||||
@ -388,8 +432,8 @@ class TestCli(base.BaseTestCase):
|
|||||||
def test_upgrade_rejects_delta_with_relative_revision(self):
|
def test_upgrade_rejects_delta_with_relative_revision(self):
|
||||||
self.assert_command_fails(['prog', 'upgrade', '+2', '--delta', '3'])
|
self.assert_command_fails(['prog', 'upgrade', '+2', '--delta', '3'])
|
||||||
|
|
||||||
def _test_validate_head_files_helper(self, heads, contract_head='',
|
def _test_validate_head_files_common(self, heads, revs,
|
||||||
expand_head=''):
|
contract_head='', expand_head=''):
|
||||||
fake_config = self.configs[0]
|
fake_config = self.configs[0]
|
||||||
head_files_not_exist = (contract_head == expand_head == '')
|
head_files_not_exist = (contract_head == expand_head == '')
|
||||||
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc,\
|
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc,\
|
||||||
@ -400,9 +444,6 @@ class TestCli(base.BaseTestCase):
|
|||||||
os_mock.return_value = True
|
os_mock.return_value = True
|
||||||
|
|
||||||
fc.return_value.get_heads.return_value = heads
|
fc.return_value.get_heads.return_value = heads
|
||||||
|
|
||||||
revs = {heads[0]: FakeRevision(labels=cli.CONTRACT_BRANCH),
|
|
||||||
heads[1]: FakeRevision(labels=cli.EXPAND_BRANCH)}
|
|
||||||
fc.return_value.get_revision.side_effect = revs.__getitem__
|
fc.return_value.get_revision.side_effect = revs.__getitem__
|
||||||
mock_open_con = self.useFixture(
|
mock_open_con = self.useFixture(
|
||||||
tools.OpenFixture(cli._get_contract_head_file_path(
|
tools.OpenFixture(cli._get_contract_head_file_path(
|
||||||
@ -433,6 +474,23 @@ class TestCli(base.BaseTestCase):
|
|||||||
if not head_files_not_exist:
|
if not head_files_not_exist:
|
||||||
fc.assert_called_once_with(fake_config)
|
fc.assert_called_once_with(fake_config)
|
||||||
|
|
||||||
|
def _test_validate_head_files_helper(self, heads,
|
||||||
|
contract_head='', expand_head=''):
|
||||||
|
old_labels_revs = {
|
||||||
|
heads[0]: FakeRevision(labels={cli.CONTRACT_BRANCH}),
|
||||||
|
heads[1]: FakeRevision(labels={cli.EXPAND_BRANCH})
|
||||||
|
}
|
||||||
|
new_labels_revs = {
|
||||||
|
heads[0]: FakeRevision(labels={'foo-' + cli.CONTRACT_BRANCH}),
|
||||||
|
heads[1]: FakeRevision(labels={'foo-' + cli.EXPAND_BRANCH})
|
||||||
|
}
|
||||||
|
self._test_validate_head_files_common(heads, old_labels_revs,
|
||||||
|
contract_head=contract_head,
|
||||||
|
expand_head=expand_head)
|
||||||
|
self._test_validate_head_files_common(heads, new_labels_revs,
|
||||||
|
contract_head=contract_head,
|
||||||
|
expand_head=expand_head)
|
||||||
|
|
||||||
def test_validate_head_files_success(self):
|
def test_validate_head_files_success(self):
|
||||||
self._test_validate_head_files_helper(['a', 'b'], contract_head='a',
|
self._test_validate_head_files_helper(['a', 'b'], contract_head='a',
|
||||||
expand_head='b')
|
expand_head='b')
|
||||||
@ -445,8 +503,14 @@ class TestCli(base.BaseTestCase):
|
|||||||
expand_head='d')
|
expand_head='d')
|
||||||
|
|
||||||
@mock.patch.object(fileutils, 'delete_if_exists')
|
@mock.patch.object(fileutils, 'delete_if_exists')
|
||||||
def test_update_head_files_success(self, *mocks):
|
def _test_update_head_files_success(self, revs, *mocks):
|
||||||
heads = ['a', 'b']
|
contract_head = expand_head = None
|
||||||
|
for rev, revision in revs.items():
|
||||||
|
for branch_label in revision.branch_labels:
|
||||||
|
if branch_label.endswith(cli.CONTRACT_BRANCH):
|
||||||
|
contract_head = rev
|
||||||
|
if branch_label.endswith(cli.EXPAND_BRANCH):
|
||||||
|
expand_head = rev
|
||||||
mock_open_con = self.useFixture(
|
mock_open_con = self.useFixture(
|
||||||
tools.OpenFixture(cli._get_contract_head_file_path(
|
tools.OpenFixture(cli._get_contract_head_file_path(
|
||||||
self.configs[0]))).mock_open
|
self.configs[0]))).mock_open
|
||||||
@ -454,14 +518,13 @@ class TestCli(base.BaseTestCase):
|
|||||||
tools.OpenFixture(cli._get_expand_head_file_path(
|
tools.OpenFixture(cli._get_expand_head_file_path(
|
||||||
self.configs[0]))).mock_open
|
self.configs[0]))).mock_open
|
||||||
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
with mock.patch('alembic.script.ScriptDirectory.from_config') as fc:
|
||||||
fc.return_value.get_heads.return_value = heads
|
fc.return_value.get_heads.return_value = list(revs)
|
||||||
revs = {heads[0]: FakeRevision(labels=cli.CONTRACT_BRANCH),
|
|
||||||
heads[1]: FakeRevision(labels=cli.EXPAND_BRANCH)}
|
|
||||||
fc.return_value.get_revision.side_effect = revs.__getitem__
|
fc.return_value.get_revision.side_effect = revs.__getitem__
|
||||||
cli.update_head_files(self.configs[0])
|
cli.update_head_files(self.configs[0])
|
||||||
mock_open_con.return_value.write.assert_called_with(
|
mock_open_con.return_value.write.assert_called_with(
|
||||||
heads[0] + '\n')
|
contract_head + '\n')
|
||||||
mock_open_ex.return_value.write.assert_called_with(heads[1] + '\n')
|
mock_open_ex.return_value.write.assert_called_with(
|
||||||
|
expand_head + '\n')
|
||||||
|
|
||||||
old_head_file = cli._get_head_file_path(
|
old_head_file = cli._get_head_file_path(
|
||||||
self.configs[0])
|
self.configs[0])
|
||||||
@ -473,6 +536,12 @@ class TestCli(base.BaseTestCase):
|
|||||||
self.assertIn(mock.call(old_heads_file),
|
self.assertIn(mock.call(old_heads_file),
|
||||||
delete_if_exists.call_args_list)
|
delete_if_exists.call_args_list)
|
||||||
|
|
||||||
|
def test_update_head_files_success_old_labels(self):
|
||||||
|
self._test_update_head_files_success(self.old_labels_revs)
|
||||||
|
|
||||||
|
def test_update_head_files_success_new_labels(self):
|
||||||
|
self._test_update_head_files_success(self.new_labels_revs)
|
||||||
|
|
||||||
def test_get_project_base(self):
|
def test_get_project_base(self):
|
||||||
config = alembic_config.Config()
|
config = alembic_config.Config()
|
||||||
config.set_main_option('script_location', 'a.b.c:d')
|
config.set_main_option('script_location', 'a.b.c:d')
|
||||||
@ -589,7 +658,7 @@ class TestCli(base.BaseTestCase):
|
|||||||
|
|
||||||
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
||||||
def test__get_branch_points(self, walk_mock):
|
def test__get_branch_points(self, walk_mock):
|
||||||
revisions = [FakeRevision(is_branch_point=tools.get_random_boolean)
|
revisions = [FakeRevision(is_branch_point=tools.get_random_boolean())
|
||||||
for i in range(50)]
|
for i in range(50)]
|
||||||
walk_mock.return_value = revisions
|
walk_mock.return_value = revisions
|
||||||
script_dir = alembic_script.ScriptDirectory.from_config(
|
script_dir = alembic_script.ScriptDirectory.from_config(
|
||||||
@ -663,8 +732,10 @@ class TestCli(base.BaseTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
directives = [migration_script]
|
directives = [migration_script]
|
||||||
|
context = mock.Mock()
|
||||||
|
context.config = self.configs[0]
|
||||||
autogen.process_revision_directives(
|
autogen.process_revision_directives(
|
||||||
mock.Mock(), mock.Mock(), directives
|
context, mock.Mock(), directives
|
||||||
)
|
)
|
||||||
|
|
||||||
expand = directives[0]
|
expand = directives[0]
|
||||||
@ -699,23 +770,33 @@ class TestCli(base.BaseTestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
||||||
def test__find_milestone_revisions_one_branch(self, walk_mock):
|
def _test__find_milestone_revisions_one_branch(self,
|
||||||
c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
|
contract_label,
|
||||||
|
expand_label,
|
||||||
|
walk_mock):
|
||||||
|
c_revs = [FakeRevision(labels={contract_label}) for r in range(5)]
|
||||||
c_revs[1].module.neutron_milestone = [migration.LIBERTY]
|
c_revs[1].module.neutron_milestone = [migration.LIBERTY]
|
||||||
|
|
||||||
walk_mock.return_value = c_revs
|
walk_mock.return_value = c_revs
|
||||||
m = cli._find_milestone_revisions(self.configs[0], 'liberty',
|
m = cli._find_milestone_revisions(self.configs[0], 'liberty',
|
||||||
cli.CONTRACT_BRANCH)
|
contract_label)
|
||||||
self.assertEqual(1, len(m))
|
self.assertEqual(1, len(m))
|
||||||
m = cli._find_milestone_revisions(self.configs[0], 'liberty',
|
m = cli._find_milestone_revisions(self.configs[0], 'liberty',
|
||||||
cli.EXPAND_BRANCH)
|
expand_label)
|
||||||
self.assertEqual(0, len(m))
|
self.assertEqual(0, len(m))
|
||||||
|
|
||||||
|
def test__find_milestone_revisions_one_branch(self):
|
||||||
|
self._old_and_new_label_combination_helper(
|
||||||
|
self._test__find_milestone_revisions_one_branch)
|
||||||
|
|
||||||
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
||||||
def test__find_milestone_revisions_two_branches(self, walk_mock):
|
def _test__find_milestone_revisions_two_branches(self,
|
||||||
c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
|
contract_label,
|
||||||
|
expand_label,
|
||||||
|
walk_mock):
|
||||||
|
c_revs = [FakeRevision(labels={contract_label}) for r in range(5)]
|
||||||
c_revs[1].module.neutron_milestone = [migration.LIBERTY]
|
c_revs[1].module.neutron_milestone = [migration.LIBERTY]
|
||||||
e_revs = [FakeRevision(labels={cli.EXPAND_BRANCH}) for r in range(5)]
|
e_revs = [FakeRevision(labels={expand_label}) for r in range(5)]
|
||||||
e_revs[3].module.neutron_milestone = [migration.LIBERTY]
|
e_revs[3].module.neutron_milestone = [migration.LIBERTY]
|
||||||
|
|
||||||
walk_mock.return_value = c_revs + e_revs
|
walk_mock.return_value = c_revs + e_revs
|
||||||
@ -725,6 +806,10 @@ class TestCli(base.BaseTestCase):
|
|||||||
m = cli._find_milestone_revisions(self.configs[0], 'mitaka')
|
m = cli._find_milestone_revisions(self.configs[0], 'mitaka')
|
||||||
self.assertEqual(0, len(m))
|
self.assertEqual(0, len(m))
|
||||||
|
|
||||||
|
def test__find_milestone_revisions_two_branches(self):
|
||||||
|
self._old_and_new_label_combination_helper(
|
||||||
|
self._test__find_milestone_revisions_two_branches)
|
||||||
|
|
||||||
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
@mock.patch('alembic.script.ScriptDirectory.walk_revisions')
|
||||||
def test__find_milestone_revisions_branchless(self, walk_mock):
|
def test__find_milestone_revisions_branchless(self, walk_mock):
|
||||||
revisions = [FakeRevision() for r in range(5)]
|
revisions = [FakeRevision() for r in range(5)]
|
||||||
|
Loading…
Reference in New Issue
Block a user