]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
[neutron-db-manage] revision: properly bootstrap a new branch
authorIhar Hrachyshka <ihrachys@redhat.com>
Sat, 18 Jul 2015 12:52:53 +0000 (14:52 +0200)
committerHenry Gessau <gessau@cisco.com>
Mon, 27 Jul 2015 01:00:23 +0000 (21:00 -0400)
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

neutron/db/migration/alembic_migrations/script.py.mako
neutron/db/migration/cli.py
neutron/tests/unit/db/test_migration.py

index f35c9b7c87b7624fa850b9723a7cee1f1a34e8a7..121181a32cd9bf2a2b700fd328300c6d3e3c2882 100644 (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
index 0a960ac3dff6eb383ca44c089b0d551a6fcb5643..0881c72112bd54059ba95a76dfc66411978ace0a 100644 (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))
 
index 8d52fa2e48d9a7b2f61795f8a56bcea7b19a9c0a..955605aadca658d7dcbed4f07d02e457b8a25007 100644 (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)