Skip to content

Commit

Permalink
Allow specifying the current version in 'glance-manage version_control'
Browse files Browse the repository at this point in the history
Fixes bug #966242

The version_control command is a bit useless since you can't actually
specify what version to  use and it defaults to version=0.

Allow the user to specify a version:

  $> glance-manage version_control 9 # set the diablo version

and default to the latest version if none is specified.

Also, allow db_sync to be supplied a version for the case where we're
upgrading an unversioned DB.

Finally, re-work the argument handling in glance-manage to more easily
handle optional args.

The tests are extended to test using db_sync for upgrades and to test
placing an existing database under version control.

Change-Id: I231dc710554198bfd1fdcb82c3c3768963f64bd8
  • Loading branch information
markmc authored and Eoghan Glynn committed Apr 3, 2012
1 parent 3a431de commit 2f9eba1
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 30 deletions.
29 changes: 11 additions & 18 deletions bin/glance-manage
Expand Up @@ -67,42 +67,35 @@ def do_db_version(options, args):

def do_upgrade(options, args):
"""Upgrade the database's migration level"""
try:
db_version = args[1]
except IndexError:
db_version = None

glance.registry.db.migration.upgrade(options, version=db_version)
version = args.pop(0) if args else None
glance.registry.db.migration.upgrade(options, version)


def do_downgrade(options, args):
"""Downgrade the database's migration level"""
try:
db_version = args[1]
except IndexError:
if not args:
raise exception.MissingArgumentError(
"downgrade requires a version argument")

glance.registry.db.migration.downgrade(options, version=db_version)
version = args.pop(0)
glance.registry.db.migration.downgrade(options, version)


def do_version_control(options, args):
"""Place a database under migration control"""
glance.registry.db.migration.version_control(options)
version = args.pop(0) if args else None
glance.registry.db.migration.version_control(options, version)


def do_db_sync(options, args):
"""Place a database under migration control and upgrade"""
try:
db_version = args[1]
except IndexError:
db_version = None
glance.registry.db.migration.db_sync(options, version=db_version)
version = args.pop(0) if args else None
current_version = args.pop(0) if args else None
glance.registry.db.migration.db_sync(options, version, current_version)


def dispatch_cmd(options, args):
"""Search for do_* cmd in this module and then run it"""
cmd = args[0]
cmd = args.pop(0)
try:
cmd_func = globals()['do_%s' % cmd]
except KeyError:
Expand Down
20 changes: 14 additions & 6 deletions glance/registry/db/migration.py
Expand Up @@ -25,6 +25,7 @@
from migrate.versioning import exceptions as versioning_exceptions
except ImportError:
from migrate import exceptions as versioning_exceptions
from migrate.versioning import repository as versioning_repository

from glance.common import exception

Expand Down Expand Up @@ -82,43 +83,50 @@ def downgrade(options, version):
return versioning_api.downgrade(sql_connection, repo_path, version)


def version_control(options):
def version_control(options, version=None):
"""
Place a database under migration control
:param options: options dict
"""
sql_connection = options['sql_connection']
try:
_version_control(options)
_version_control(options, version)
except versioning_exceptions.DatabaseAlreadyControlledError, e:
msg = (_("database '%(sql_connection)s' is already under migration "
"control") % locals())
raise exception.DatabaseMigrationError(msg)


def _version_control(options):
def _version_control(options, version=None):
"""
Place a database under migration control
:param options: options dict
"""
repo_path = get_migrate_repo_path()
sql_connection = options['sql_connection']
return versioning_api.version_control(sql_connection, repo_path)
if version is None:
version = versioning_repository.Repository(repo_path).latest
return versioning_api.version_control(sql_connection, repo_path, version)


def db_sync(options, version=None):
def db_sync(options, version=None, current_version=None):
"""
Place a database under migration control and perform an upgrade
:param options: options dict
:retval version number
"""
sql_connection = options['sql_connection']
try:
_version_control(options)
_version_control(options, current_version)
except versioning_exceptions.DatabaseAlreadyControlledError, e:
pass
if current_version is not None:
msg = (_("database '%(sql_connection)s' is already under "
"migration control") % locals())
raise exception.DatabaseMigrationError(msg)

upgrade(options, version=version)

Expand Down
46 changes: 40 additions & 6 deletions glance/tests/unit/test_migrations.py
Expand Up @@ -35,6 +35,7 @@
from sqlalchemy.pool import NullPool

from glance.common import exception
from glance.registry.db import models
import glance.registry.db.migration as migration_api
from glance.tests.utils import execute

Expand Down Expand Up @@ -127,7 +128,39 @@ def test_walk_versions(self):
options = {'sql_connection': TestMigrations.TEST_DATABASES[key]}
self._walk_versions(options)

def _walk_versions(self, options):
def test_version_control_existing_db(self):
"""
Creates a DB without version control information, places it
under version control and checks that it can be upgraded
without errors.
"""
for key, engine in self.engines.items():
#conf = utils.TestConfigOpts({
# 'sql_connection': TestMigrations.TEST_DATABASES[key]})
#conf.register_opt(cfg.StrOpt('sql_connection'))
options = {'sql_connection': TestMigrations.TEST_DATABASES[key]}
self._create_unversioned_001_db(engine)
self._walk_versions(options, initial_version=1)

def _create_unversioned_001_db(self, engine):
# Create the initial version of the images table
meta = MetaData()
meta.bind = engine
images_001 = Table('images', meta,
Column('id', models.Integer, primary_key=True),
Column('name', String(255)),
Column('type', String(30)),
Column('size', Integer),
Column('status', String(30)),
Column('is_public', Boolean, default=False),
Column('location', Text),
Column('created_at', DateTime(), nullable=False),
Column('updated_at', DateTime()),
Column('deleted_at', DateTime()),
Column('deleted', Boolean(), nullable=False, default=False))
images_001.create()

def _walk_versions(self, options, initial_version=0):
# Determine latest version script from the repo, then
# upgrade from 1 through to the latest, with no data
# in the databases. This just checks that the schema itself
Expand All @@ -138,13 +171,14 @@ def _walk_versions(self, options):
migration_api.db_version,
options)
# Place the database under version control
migration_api.version_control(options)
migration_api.version_control(options, version=initial_version)

cur_version = migration_api.db_version(options)
self.assertEqual(0, cur_version)
self.assertEqual(initial_version, cur_version)

for version in xrange(1, TestMigrations.REPOSITORY.latest + 1):
migration_api.upgrade(options, version)
for version in xrange(initial_version + 1,
TestMigrations.REPOSITORY.latest + 1):
migration_api.db_sync(options, version)
cur_version = migration_api.db_version(options)
self.assertEqual(cur_version, version)

Expand All @@ -169,7 +203,7 @@ def test_no_data_loss_2_to_3_to_2(self):
self._no_data_loss_2_to_3_to_2(engine, options)

def _no_data_loss_2_to_3_to_2(self, engine, options):
migration_api.version_control(options)
migration_api.version_control(options, version=0)
migration_api.upgrade(options, 2)

cur_version = migration_api.db_version(options)
Expand Down

0 comments on commit 2f9eba1

Please sign in to comment.