Dropped release name from migration branch labels

Since the plan is to attach first Mitaka scripts to Liberty branches
with down_revision, and since labels are inherited from all other
revisions in the chain, using release names in branch labels would mean
that the following commands would be valid:

neutron-db-manage upgrade liberty_expand@<some_revision_from_mitaka>
neutron-db-manage upgrade mitaka_expand@<some_revision_from_liberty>

which may be confusing to users.

So let's drop release names from branch labels and use expand@head and
contract@head to access latest migrations.

Change-Id: Id524d7673ad248c831f6dbb3a6f2f3c50094acae
Partially-Implements: blueprint online-schema-migrations
This commit is contained in:
Ihar Hrachyshka 2015-08-20 13:01:46 +02:00
parent 905064eb61
commit af7fb6c9da
5 changed files with 54 additions and 54 deletions

View File

@ -294,12 +294,12 @@ Applying database migration rules
To apply just expansion rules, execute::
neutron-db-manage upgrade liberty_expand@head
neutron-db-manage upgrade expand@head
After the first step is done, you can stop neutron-server, apply remaining
non-expansive migration rules, if any::
neutron-db-manage upgrade liberty_contract@head
neutron-db-manage upgrade contract@head
and finally, start your neutron-server again.

View File

@ -19,10 +19,13 @@ Create Date: 2015-06-22 00:00:00.000000
"""
from neutron.db.migration import cli
# revision identifiers, used by Alembic.
revision = '30018084ec99'
down_revision = 'kilo'
branch_labels = ('liberty_contract',)
branch_labels = (cli.CONTRACT_BRANCH,)
def upgrade():

View File

@ -21,13 +21,16 @@ Create Date: 2015-04-19 14:59:15.102609
"""
from alembic import op
import sqlalchemy as sa
from neutron.db.migration import cli
# revision identifiers, used by Alembic.
revision = '354db87e3225'
down_revision = 'kilo'
branch_labels = ('liberty_expand',)
from alembic import op
import sqlalchemy as sa
branch_labels = (cli.EXPAND_BRANCH,)
def upgrade():

View File

@ -31,8 +31,10 @@ from neutron.common import utils
HEAD_FILENAME = 'HEAD'
HEADS_FILENAME = 'HEADS'
CURRENT_RELEASE = "liberty"
RELEASES = (CURRENT_RELEASE,)
MIGRATION_BRANCHES = ('expand', 'contract')
EXPAND_BRANCH = 'expand'
CONTRACT_BRANCH = 'contract'
MIGRATION_BRANCHES = (EXPAND_BRANCH, CONTRACT_BRANCH)
MIGRATION_ENTRYPOINTS = 'neutron.db.alembic_migrations'
migration_entrypoints = {
@ -160,14 +162,9 @@ def do_stamp(config, cmd):
sql=CONF.command.sql)
def _get_branch_label(branch, release=None):
'''Get the latest branch label corresponding to release cycle.'''
return '%s_%s' % (release or CURRENT_RELEASE, branch)
def _get_branch_head(branch):
'''Get the latest @head specification for a branch.'''
return '%s@head' % _get_branch_label(branch)
return '%s@head' % branch
def do_revision(config, cmd):
@ -182,20 +179,13 @@ def do_revision(config, cmd):
for branch in MIGRATION_BRANCHES:
version_path = _get_version_branch_path(config, branch)
addn_kwargs['version_path'] = version_path
addn_kwargs['head'] = _get_branch_head(branch)
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)
addn_kwargs['branch_label'] = branch
do_alembic_command(config, cmd, **addn_kwargs)
else:
@ -203,10 +193,30 @@ def do_revision(config, cmd):
update_heads_file(config)
def _get_release_labels(labels):
result = set()
for label in labels:
result.add('%s_%s' % (CURRENT_RELEASE, label))
return result
def _compare_labels(revision, expected_labels):
# validate that the script has the only label that corresponds to path
# validate that the script has expected labels only
bad_labels = revision.branch_labels - expected_labels
if bad_labels:
# NOTE(ihrachyshka): this hack is temporary to accomodate 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)
alembic_util.err(
_('Unexpected label for script %(script_name)s: %(labels)s') %
@ -215,13 +225,10 @@ def _compare_labels(revision, expected_labels):
)
def _validate_single_revision_labels(script_dir, revision,
release=None, branch=None):
if branch is not None:
branch_label = _get_branch_label(branch, release=release)
expected_labels = set([branch_label])
else:
expected_labels = set()
def _validate_single_revision_labels(script_dir, revision, label=None):
expected_labels = set()
if label is not None:
expected_labels.add(label)
_compare_labels(revision, expected_labels)
@ -234,12 +241,10 @@ def _validate_single_revision_labels(script_dir, revision,
def _validate_revision(script_dir, revision):
for branch in MIGRATION_BRANCHES:
for release in RELEASES:
marker = os.path.join(release, branch)
if marker in revision.path:
_validate_single_revision_labels(
script_dir, revision, release=release, branch=branch)
return
if branch in revision.path:
_validate_single_revision_labels(
script_dir, revision, label=branch)
return
# validate script from branchless part of migration rules
_validate_single_revision_labels(script_dir, revision)

View File

@ -376,15 +376,6 @@ class TestCli(base.BaseTestCase):
self.assertRaises(
SystemExit, cli._get_subproject_base, 'not-installed')
def test__get_branch_label_current(self):
self.assertEqual('%s_fakebranch' % cli.CURRENT_RELEASE,
cli._get_branch_label('fakebranch'))
def test__get_branch_label_other_release(self):
self.assertEqual('fakerelease_fakebranch',
cli._get_branch_label('fakebranch',
release='fakerelease'))
def test__compare_labels_ok(self):
labels = {'label1', 'label2'}
fake_revision = FakeRevision(labels)
@ -407,7 +398,7 @@ class TestCli(base.BaseTestCase):
script_dir = mock.Mock()
script_dir.get_revision.return_value = fake_down_revision
cli._validate_single_revision_labels(script_dir, fake_revision,
branch=None)
label=None)
expected_labels = set()
compare_mock.assert_has_calls(
@ -425,10 +416,9 @@ class TestCli(base.BaseTestCase):
script_dir = mock.Mock()
script_dir.get_revision.return_value = fake_down_revision
cli._validate_single_revision_labels(
script_dir, fake_revision,
release='fakerelease', branch='fakebranch')
script_dir, fake_revision, label='fakebranch')
expected_labels = {'fakerelease_fakebranch'}
expected_labels = {'fakebranch'}
compare_mock.assert_has_calls(
[mock.call(revision, expected_labels)
for revision in (fake_revision, fake_down_revision)]
@ -438,12 +428,11 @@ class TestCli(base.BaseTestCase):
def test__validate_revision_validates_branches(self, validate_mock):
script_dir = mock.Mock()
fake_revision = FakeRevision()
release = cli.RELEASES[0]
branch = cli.MIGRATION_BRANCHES[0]
fake_revision.path = os.path.join('/fake/path', release, branch)
fake_revision.path = os.path.join('/fake/path', branch)
cli._validate_revision(script_dir, fake_revision)
validate_mock.assert_called_with(
script_dir, fake_revision, release=release, branch=branch)
script_dir, fake_revision, label=branch)
@mock.patch.object(cli, '_validate_single_revision_labels')
def test__validate_revision_validates_branchless_migrations(