Skip to content

Commit

Permalink
implemented migrate --fake, closes #15
Browse files Browse the repository at this point in the history
  • Loading branch information
rooterkyberian committed Oct 16, 2020
1 parent a85b6c9 commit 486a3dd
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 35 deletions.
44 changes: 27 additions & 17 deletions src/pymongo_migrate/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,15 @@ def wrap_with_client(uri, migrations, collection, verbose, *args, **kwargs):
return wrap_with_client


def _decorate(f, *decorators):
for decorator in reversed(decorators):
f = decorator(f)
return f


def mongo_migration_options(f):
decorators = [
return _decorate(
f,
click.option(
"-u",
"--uri",
Expand All @@ -99,10 +106,7 @@ def mongo_migration_options(f):
),
click.option("-v", "--verbose", count=True),
mongo_migrate_decor,
]
for decorator in reversed(decorators):
f = decorator(f)
return f
)


@cli.command(short_help="show migrations and their status")
Expand All @@ -123,27 +127,33 @@ def show(mongo_migrate):
click.echo(f"{migration.name.ljust(name_len_max)}\t" + applied_text)


def migrate_cmd_options(f):
return _decorate(
f,
mongo_migration_options,
click.argument("migration", required=False),
click.option("--fake", is_flag=True),
)


@cli.command(
short_help="automagically apply necessary upgrades or downgrades to reach target migration"
)
@mongo_migration_options
@click.argument("migration", required=False)
def migrate(mongo_migrate, migration=None):
mongo_migrate.migrate(migration)
@migrate_cmd_options
def migrate(mongo_migrate, migration=None, fake=False):
mongo_migrate.migrate(migration, fake=fake)


@cli.command(short_help="apply necessary upgrades to reach target migration")
@mongo_migration_options
@click.argument("migration", required=False)
def upgrade(mongo_migrate, migration=None):
mongo_migrate.upgrade(migration)
@migrate_cmd_options
def upgrade(mongo_migrate, migration=None, fake=False):
mongo_migrate.upgrade(migration, fake=fake)


@cli.command(short_help="apply necessary downgrades to reach target migration")
@mongo_migration_options
@click.argument("migration", required=False)
def downgrade(mongo_migrate, migration):
mongo_migrate.downgrade(migration)
@migrate_cmd_options
def downgrade(mongo_migrate, migration, fake=False):
mongo_migrate.downgrade(migration, fake=fake)


@cli.command()
Expand Down
51 changes: 33 additions & 18 deletions src/pymongo_migrate/mongo_migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,36 +116,42 @@ def _check_for_migration(
raise ValueError(f"No such migration: {migration_name}")
return migration

def migrate(self, migration_name: Optional[str] = None):
def migrate(self, migration_name: Optional[str] = None, fake: bool = False):
"""
Automatically detects if upgrades or downgrades should be applied to
reach target migration state.
:param migration_name:
target migration
None if all upgrades should be applied
:param fake:
If True, only migration state in database will be modified and
no actual migration will be run.
"""
if migration_name is None:
self.logger.debug("Migration target not specified, assuming upgrade")
self.upgrade()
self.upgrade(fake=fake)
return
migration = self._check_for_migration(migration_name)
assert migration, "No matching migration, something went wrong"
migration_state = self.get_state(migration)
if migration_state.applied:
self.logger.debug("Migration target already applied, assuming downgrade")
self.downgrade(migration_name)
self.downgrade(migration_name, fake)
else:
self.logger.debug("Migration target not applied, assuming upgrade")
self.upgrade(migration_name)
self.upgrade(migration_name, fake)

def upgrade(self, migration_name: Optional[str] = None):
def upgrade(self, migration_name: Optional[str] = None, fake: bool = False):
"""
Apply upgrade migrations.
:param migration_name:
name of migration up to which (including) upgrades should be executed
None if all migrations should be run
:param fake:
If True, only migration state in database will be modified and
no actual migration will be run.
"""
self._check_for_migration(migration_name)
for migration in self.graph:
Expand All @@ -155,24 +161,30 @@ def upgrade(self, migration_name: Optional[str] = None):
"Migration %r already applied, skipping", migration.name
)
continue
self.logger.info("Running upgrade migration %r", migration.name)
with _MeasureTime() as mt:
migration.upgrade(self.db)
self.logger.info(
"Execution time of %r: %s seconds", migration.name, mt.elapsed
)
if fake:
self.logger.info("Fake running upgrade migration %r", migration.name)
else:
self.logger.info("Running upgrade migration %r", migration.name)
with _MeasureTime() as mt:
migration.upgrade(self.db)
self.logger.info(
"Execution time of %r: %s seconds", migration.name, mt.elapsed
)
migration_state.applied = dt()
self.set_state(migration_state)
if migration.name == migration_name:
break

def downgrade(self, migration_name: Optional[str]):
def downgrade(self, migration_name: Optional[str] = None, fake: bool = False):
"""
Reverse migrations.
:param migration_name:
name of migration down to which (excluding) downgrades should be executed
None if all migrations should be run
:param fake:
If True, only migration state in database will be modified and
no actual migration will be run.
"""
self._check_for_migration(migration_name)
for migration in reversed(list(self.get_migrations())):
Expand All @@ -184,12 +196,15 @@ def downgrade(self, migration_name: Optional[str]):
"Migration %r not yet applied, skipping", migration.name
)
continue
self.logger.info("Running downgrade migration %r", migration.name)
with _MeasureTime() as mt:
migration.downgrade(self.db)
self.logger.info(
"Execution time of %r: %s seconds", migration.name, mt.elapsed
)
if fake:
self.logger.info("Fake running downgrade migration %r", migration.name)
else:
self.logger.info("Running downgrade migration %r", migration.name)
with _MeasureTime() as mt:
migration.downgrade(self.db)
self.logger.info(
"Execution time of %r: %s seconds", migration.name, mt.elapsed
)
migration_state.applied = None
self.set_state(migration_state)

Expand Down
9 changes: 9 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ def test_migrate(invoker, db, db_uri, migrations_dir):
assert "Running upgrade migration '20150612230153'" in result.stdout


def test_migrate_fake(invoker, db, db_uri, migrations_dir):
result = invoker(
["migrate", "-u", db_uri, "-m", migrations_dir, "--fake"],
catch_exceptions=False,
)

assert "Fake running upgrade migration '20150612230153'" in result.stdout


def test_migrate_verbose(invoker, db, db_uri, migrations_dir):
result = invoker(
["migrate", "-u", db_uri, "-m", migrations_dir, "-vv"], catch_exceptions=False
Expand Down
26 changes: 26 additions & 0 deletions tests/test_mongo_migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ def test_upgrade(mongo_migrate, db, get_db_migrations):
assert db.numbers_collection.count_documents({}) == 499


@freezegun.freeze_time("2019-02-03 04:05:06")
def test_upgrade_fake(mongo_migrate, db, get_db_migrations):
mongo_migrate.upgrade(fake=True)

db_migrations = get_db_migrations()
assert db_migrations == [
{"applied": dt(2019, 2, 3, 4, 5, 6), "name": "20150612230153"},
{"applied": dt(2019, 2, 3, 4, 5, 6), "name": "20181123000000_gt_500"},
]

assert db.numbers_collection.count_documents({}) == 0


@freezegun.freeze_time("2019-02-03 04:05:06")
def test_upgrade_skip_applied(mongo_migrate, db, db_collection, get_db_migrations):
db_collection.insert_one(
Expand Down Expand Up @@ -56,6 +69,19 @@ def test_downgrade(mongo_migrate, db, get_db_migrations):
assert db.list_collection_names() == []


def test_downgrade_fake(mongo_migrate, db, get_db_migrations):
mongo_migrate.upgrade()
mongo_migrate.downgrade(fake=True)

db_migrations = get_db_migrations()
assert db_migrations == [
{"applied": None, "name": "20150612230153"},
{"applied": None, "name": "20181123000000_gt_500"},
]

assert db.numbers_collection.count_documents({}) == 499


def test_upgrade_n_downgrade(mongo_migrate, db, get_db_migrations):
mongo_migrate.upgrade()
mongo_migrate.downgrade(None)
Expand Down

0 comments on commit 486a3dd

Please sign in to comment.