Previously when we had one repo with one alembic branch we would
create a milestone revision on that single branch. Now we have
multiple repos and expand/contract branches for each repo.
So from now on we tag the final revision on every branch when we make
a milestone release. Update the cli to support the command:
neutron-db-manage upgrade <milestone>
where <milestone> becomes an alias for all the revisions for a
milestone.
Closes-Bug: #
1499033
Change-Id: I38623986dd574bec01fe147f9c6a747f3f512bb7
(cherry picked from commit
52236764a3a7c23ec24a86ccb84bc6737e2a4791)
Conflicts:
neutron/db/migration/cli.py
neutron/tests/unit/db/test_migration.py
import sqlalchemy as sa
from sqlalchemy.engine import reflection
+# Neutron milestones for upgrade aliases
+LIBERTY = 'liberty'
+
+NEUTRON_MILESTONES = [
+ # earlier milestones were not tagged
+ LIBERTY,
+]
CREATION_OPERATIONS = (sa.sql.ddl.CreateIndex,
sa.sql.ddl.CreateTable,
"""
+from alembic import op
+
+from neutron.db import migration
+
+
# revision identifiers, used by Alembic.
revision = '4af11ca47297'
down_revision = '11926bcfe72d'
-from alembic import op
+# milestone identifier, used by neutron-db-manage
+neutron_milestone = [migration.LIBERTY]
def upgrade():
"""
-# revision identifiers, used by Alembic.
-revision = '34af2b5c5a59'
-down_revision = '9859ac9c136'
-
from alembic import op
import sqlalchemy as sa
+from neutron.db import migration
from neutron.extensions import dns
+# revision identifiers, used by Alembic.
+revision = '34af2b5c5a59'
+down_revision = '9859ac9c136'
+
+# milestone identifier, used by neutron-db-manage
+neutron_milestone = [migration.LIBERTY]
+
+
def upgrade():
op.add_column('ports',
sa.Column('dns_name',
import pkg_resources
from neutron.common import utils
+from neutron.db import migration
# TODO(ihrachyshka): maintain separate HEAD files per branch
HEAD_FILENAME = 'HEAD'
HEADS_FILENAME = 'HEADS'
-CURRENT_RELEASE = "liberty"
+CURRENT_RELEASE = migration.LIBERTY
EXPAND_BRANCH = 'expand'
CONTRACT_BRANCH = 'contract'
return sub.add_parser(cmd, help=getattr(alembic_command, cmd).__doc__)
+def _find_milestone_revisions(config, milestone, branch=None):
+ """Return the revision(s) for a given milestone."""
+ script = alembic_script.ScriptDirectory.from_config(config)
+ return [
+ (m.revision, label)
+ for m in script.walk_revisions(base='base', head='heads')
+ for label in (m.branch_labels or [None])
+ if milestone in getattr(m.module, 'neutron_milestone', []) and
+ (branch is None or branch in m.branch_labels)
+ ]
+
+
def do_upgrade(config, cmd):
- desc = None
+ branch = None
if ((CONF.command.revision or CONF.command.delta) and
(CONF.command.expand or CONF.command.contract)):
'Phase upgrade options do not accept revision specification'))
if CONF.command.expand:
- desc = EXPAND_BRANCH
+ branch = EXPAND_BRANCH
revision = _get_branch_head(EXPAND_BRANCH)
elif CONF.command.contract:
- desc = CONTRACT_BRANCH
+ branch = CONTRACT_BRANCH
revision = _get_branch_head(CONTRACT_BRANCH)
elif not CONF.command.revision and not CONF.command.delta:
if revision == 'head':
revision = 'heads'
- if not CONF.command.sql:
- run_sanity_checks(config, revision)
- do_alembic_command(config, cmd, revision=revision,
- desc=desc, sql=CONF.command.sql)
+ if revision in migration.NEUTRON_MILESTONES:
+ revisions = _find_milestone_revisions(config, revision, branch)
+ else:
+ revisions = [(revision, branch)]
+
+ for revision, branch in revisions:
+ if not CONF.command.sql:
+ run_sanity_checks(config, revision)
+ do_alembic_command(config, cmd, revision=revision,
+ desc=branch, sql=CONF.command.sql)
def no_downgrade(config, cmd):
import pkg_resources
import sqlalchemy as sa
+from neutron.common import utils
from neutron.db import migration
from neutron.db.migration import autogen
from neutron.db.migration import cli
labels = set()
self.branch_labels = labels
self.down_revision = down_revision
+ self.revision = utils.get_random_string(10)
+ self.module = mock.MagicMock()
class MigrationEntrypointsMemento(fixtures.Fixture):
alembic_ag_api.render_python_code(contract.upgrade_ops)
)
+ @mock.patch('alembic.script.ScriptDirectory.walk_revisions')
+ def test__find_milestone_revisions_one_branch(self, walk_mock):
+ c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
+ c_revs[1].module.neutron_milestone = [migration.LIBERTY]
+
+ walk_mock.return_value = c_revs
+ m = cli._find_milestone_revisions(self.configs[0], 'liberty',
+ cli.CONTRACT_BRANCH)
+ self.assertEqual(1, len(m))
+ m = cli._find_milestone_revisions(self.configs[0], 'liberty',
+ cli.EXPAND_BRANCH)
+ self.assertEqual(0, len(m))
+
+ @mock.patch('alembic.script.ScriptDirectory.walk_revisions')
+ def test__find_milestone_revisions_two_branches(self, walk_mock):
+ c_revs = [FakeRevision(labels={cli.CONTRACT_BRANCH}) for r in range(5)]
+ c_revs[1].module.neutron_milestone = [migration.LIBERTY]
+ e_revs = [FakeRevision(labels={cli.EXPAND_BRANCH}) for r in range(5)]
+ e_revs[3].module.neutron_milestone = [migration.LIBERTY]
+
+ walk_mock.return_value = c_revs + e_revs
+ m = cli._find_milestone_revisions(self.configs[0], 'liberty')
+ self.assertEqual(2, len(m))
+
+ m = cli._find_milestone_revisions(self.configs[0], 'mitaka')
+ self.assertEqual(0, len(m))
+
+ @mock.patch('alembic.script.ScriptDirectory.walk_revisions')
+ def test__find_milestone_revisions_branchless(self, walk_mock):
+ revisions = [FakeRevision() for r in range(5)]
+ revisions[2].module.neutron_milestone = [migration.LIBERTY]
+
+ walk_mock.return_value = revisions
+ m = cli._find_milestone_revisions(self.configs[0], 'liberty')
+ self.assertEqual(1, len(m))
+
+ m = cli._find_milestone_revisions(self.configs[0], 'mitaka')
+ self.assertEqual(0, len(m))
+
class TestSafetyChecks(base.BaseTestCase):