From 87fff61b06b1dfca7008e0482141d48329039f5a Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Sat, 18 Jul 2015 14:52:53 +0200 Subject: [PATCH] [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 --- .../alembic_migrations/script.py.mako | 3 + neutron/db/migration/cli.py | 63 +++++++++++++------ neutron/tests/unit/db/test_migration.py | 6 +- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/neutron/db/migration/alembic_migrations/script.py.mako b/neutron/db/migration/alembic_migrations/script.py.mako index f35c9b7c8..121181a32 100644 --- a/neutron/db/migration/alembic_migrations/script.py.mako +++ b/neutron/db/migration/alembic_migrations/script.py.mako @@ -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 diff --git a/neutron/db/migration/cli.py b/neutron/db/migration/cli.py index 0a960ac3d..0881c7211 100644 --- a/neutron/db/migration/cli.py +++ b/neutron/db/migration/cli.py @@ -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)) diff --git a/neutron/tests/unit/db/test_migration.py b/neutron/tests/unit/db/test_migration.py index 8d52fa2e4..955605aad 100644 --- a/neutron/tests/unit/db/test_migration.py +++ b/neutron/tests/unit/db/test_migration.py @@ -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) -- 2.45.2