[neutron-db-manage] revision: properly bootstrap a new branch

If the intended branch is not present, bootstrap it:

- create directory;
- mark the new migration script with proper branch label;
- make the script down_revision == None to indicate it's a new branch;

One missing thing is making the script depends_on the previous release
branch. This is currently unsupported by alembic though.

Partially-Implements: blueprint online-schema-migrations
Change-Id: Ib3b9dfcbdb56db99b07a8e54629dda5933e1c1f5
This commit is contained in:
Ihar Hrachyshka 2015-07-18 14:52:53 +02:00 committed by Henry Gessau
parent e95bc6f5be
commit 87fff61b06
3 changed files with 49 additions and 23 deletions

View File

@ -24,6 +24,9 @@ Create Date: ${create_date}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
% if branch_labels:
branch_labels = ${repr(branch_labels)}
% endif
from alembic import op
import sqlalchemy as sa

View File

@ -24,6 +24,7 @@ from oslo_config import cfg
from oslo_utils import importutils
from neutron.common import repos
from neutron.common import utils
# TODO(ihrachyshka): maintain separate HEAD files per branch
@ -47,7 +48,10 @@ _core_opts = [
cfg.StrOpt('service',
choices=VALID_SERVICES,
help=_("The advanced service to execute the command against. "
"Can be one of '%s'.") % "', '".join(VALID_SERVICES))
"Can be one of '%s'.") % "', '".join(VALID_SERVICES)),
cfg.BoolOpt('split_branches',
default=False,
help=_("Enforce using split branches file structure."))
]
_quota_opts = [
@ -126,28 +130,46 @@ def do_stamp(config, cmd):
sql=CONF.command.sql)
def _get_branch_label(branch):
'''Get the latest branch label corresponding to release cycle.'''
return '%s_%s' % (CURRENT_RELEASE, branch)
def _get_branch_head(branch):
'''Get the latest @head specification for a branch.'''
return '%s_%s@head' % (CURRENT_RELEASE, branch)
return '%s@head' % _get_branch_label(branch)
def do_revision(config, cmd):
'''Generate new revision files, one per branch.'''
if _separate_migration_branches_supported(CONF):
addn_kwargs = {
'message': CONF.command.message,
'autogenerate': CONF.command.autogenerate,
'sql': CONF.command.sql,
}
if _use_separate_migration_branches(CONF):
for branch in MIGRATION_BRANCHES:
version_path = _get_version_branch_path(CONF, branch)
head = _get_branch_head(branch)
do_alembic_command(config, cmd,
message=CONF.command.message,
autogenerate=CONF.command.autogenerate,
sql=CONF.command.sql,
version_path=version_path,
head=head)
addn_kwargs['version_path'] = version_path
if not os.path.exists(version_path):
# Bootstrap initial directory structure
utils.ensure_dir(version_path)
# Each new release stream of migrations is detached from
# previous migration chains
addn_kwargs['head'] = 'base'
# Mark the very first revision in the new branch with its label
addn_kwargs['branch_label'] = _get_branch_label(branch)
# TODO(ihrachyshka): ideally, we would also add depends_on here
# to refer to the head of the previous release stream. But
# alembic API does not support it yet.
else:
addn_kwargs['head'] = _get_branch_head(branch)
do_alembic_command(config, cmd, **addn_kwargs)
else:
do_alembic_command(config, cmd,
message=CONF.command.message,
autogenerate=CONF.command.autogenerate,
sql=CONF.command.sql)
do_alembic_command(config, cmd, **addn_kwargs)
update_heads_file(config)
@ -266,7 +288,7 @@ def _get_active_head_file_path(neutron_config):
'''Return the path of the file that contains latest head(s), depending on
whether multiple branches are used.
'''
if _separate_migration_branches_supported(neutron_config):
if _use_separate_migration_branches(neutron_config):
return _get_heads_file_path(neutron_config)
return _get_head_file_path(neutron_config)
@ -278,10 +300,11 @@ def _get_version_branch_path(neutron_config, branch=None):
return version_path
def _separate_migration_branches_supported(neutron_config):
'''Detect whether split migration branches are supported.'''
# Use HEADS file to indicate the new, split migration world
return os.path.exists(_get_heads_file_path(neutron_config))
def _use_separate_migration_branches(neutron_config):
'''Detect whether split migration branches should be used.'''
return (neutron_config.split_branches or
# Use HEADS file to indicate the new, split migration world
os.path.exists(_get_heads_file_path(neutron_config)))
def _set_version_locations(config):
@ -289,7 +312,7 @@ def _set_version_locations(config):
version_paths = []
version_paths.append(_get_version_branch_path(CONF))
if _separate_migration_branches_supported(CONF):
if _use_separate_migration_branches(CONF):
for branch in MIGRATION_BRANCHES:
version_paths.append(_get_version_branch_path(CONF, branch))

View File

@ -150,7 +150,7 @@ class TestCli(base.BaseTestCase):
def test_database_sync_revision(self):
self._test_database_sync_revision()
@mock.patch.object(cli, '_separate_migration_branches_supported',
@mock.patch.object(cli, '_use_separate_migration_branches',
return_value=False)
def test_database_sync_revision_no_branches(self, *args):
# Test that old branchless approach is still supported
@ -242,12 +242,12 @@ class TestCli(base.BaseTestCase):
def test_validate_heads_success(self):
self._test_validate_heads_file_helper(['a'], ['a'])
@mock.patch.object(cli, '_separate_migration_branches_supported',
@mock.patch.object(cli, '_use_separate_migration_branches',
return_value=False)
def test_validate_heads_file_branchless_failure(self, *args):
self._test_validate_heads_file_helper(['a'], ['b'], branchless=True)
@mock.patch.object(cli, '_separate_migration_branches_supported',
@mock.patch.object(cli, '_use_separate_migration_branches',
return_value=False)
def test_validate_heads_file_branchless_success(self, *args):
self._test_validate_heads_file_helper(['a'], ['a'], branchless=True)