From add3bd29b014cfe87ce4cc77b5b0eeeebc24ea8a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 5 Aug 2019 21:10:03 +0200 Subject: [PATCH] Add dependencies between migrations --- locales/en.json | 2 +- .../0003_migrate_to_stretch.py | 2 +- .../0004_php5_to_php7_pools.py | 2 ++ .../0005_postgresql_9p4_to_9p6.py | 2 ++ ...0008_ssh_conf_managed_by_yunohost_step2.py | 4 ++- src/yunohost/tools.py | 25 +++++++++++++------ 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/locales/en.json b/locales/en.json index ecfba2947..14aee512d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -321,6 +321,7 @@ "migrate_tsig_wait_3": "1min…", "migrate_tsig_wait_4": "30 secondes…", "migrate_tsig_not_needed": "You do not appear to use a dyndns domain, so no migration is needed!", + "migration_backward_impossible": "The migration {name} cannot be reverted.", "migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'", "migration_description_0002_migrate_to_tsig_sha256": "Improve security of dyndns TSIG by using SHA512 instead of MD5", "migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0", @@ -332,7 +333,6 @@ "migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services", "migration_description_0010_migrate_to_apps_json": "Remove deprecated appslists and use the new unified 'apps.json' list instead", "migration_description_0011_setup_group_permission": "Setup user group and setup permission for apps and services", - "migration_0003_backward_impossible": "The stretch migration cannot be reverted.", "migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.", "migration_0003_patching_sources_list": "Patching the sources.lists…", "migration_0003_main_upgrade": "Starting main upgrade…", diff --git a/src/yunohost/data_migrations/0003_migrate_to_stretch.py b/src/yunohost/data_migrations/0003_migrate_to_stretch.py index 0db719e15..8b7586701 100644 --- a/src/yunohost/data_migrations/0003_migrate_to_stretch.py +++ b/src/yunohost/data_migrations/0003_migrate_to_stretch.py @@ -31,7 +31,7 @@ class MyMigration(Migration): def backward(self): - raise YunohostError("migration_0003_backward_impossible") + raise YunohostError("migration_backward_impossible", name=self.name) def migrate(self): diff --git a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py index 46a5eb91d..faf9ccbbc 100644 --- a/src/yunohost/data_migrations/0004_php5_to_php7_pools.py +++ b/src/yunohost/data_migrations/0004_php5_to_php7_pools.py @@ -22,6 +22,8 @@ class MyMigration(Migration): "Migrate php5-fpm 'pool' conf files to php7 stuff" + dependencies = ["migrate_to_stretch"] + def migrate(self): # Get list of php5 pool files diff --git a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py index 5ae729b60..50c0561cb 100644 --- a/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py +++ b/src/yunohost/data_migrations/0005_postgresql_9p4_to_9p6.py @@ -14,6 +14,8 @@ class MyMigration(Migration): "Migrate DBs from Postgresql 9.4 to 9.6 after migrating to Stretch" + dependencies = ["migrate_to_stretch"] + def migrate(self): if not self.package_is_installed("postgresql-9.4"): diff --git a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py index 8984440bd..0ea0ddc3a 100644 --- a/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py +++ b/src/yunohost/data_migrations/0008_ssh_conf_managed_by_yunohost_step2.py @@ -33,6 +33,8 @@ class MyMigration(Migration): shown - and the user may also choose to skip this migration. """ + dependencies = ["ssh_conf_managed_by_yunohost_step1"] + def migrate(self): settings_set("service.ssh.allow_deprecated_dsa_hostkey", False) regen_conf(names=['ssh'], force=True) @@ -44,7 +46,7 @@ class MyMigration(Migration): def backward(self): - raise YunohostError("migration_0008_backward_impossible") + raise YunohostError("migration_backward_impossible", name=self.name) @property def mode(self): diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index a15b5593e..9117f42de 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -1052,6 +1052,15 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal all_migrations = _get_migrations_list() + # Small utility that allows up to get a migration given a name, id or number later + def get_matching_migration(target): + for m in all_migrations: + if m.id == target or m.name == target or m.id.split("_")[0] == target: + return m + + raise YunohostError("No such migration called %s" % target) + + # auto, skip, revert and force are exclusive options if auto + skip + revert + force_rerun > 1: raise YunohostError("--auto, --skip, --revert and --force-rerun are exclusive options.") @@ -1067,13 +1076,6 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal # If explicit targets are provided, we shall validate them else: - def get_matching_migration(target): - for m in all_migrations: - if m.id == target or m.name == target or m.id.split("_")[0] == target: - return m - - raise YunohostError("No such migration called %s" % target) - targets = [get_matching_migration(t) for t in targets] done = [t.id for t in targets if t.state != "pending"] pending = [t.id for t in targets if t.state == "pending"] @@ -1103,6 +1105,14 @@ def tools_migrations_migrate(targets=[], skip=False, auto=False, force_rerun=Fal # We go to the next migration continue + # Check for migration dependencies + if not revert and not skip: + dependencies = [get_matching_migration(dep) for dep in migration.dependencies] + pending_dependencies = [dep.id for dep in dependencies if dep.state == "pending"] + if pending_dependencies: + logger.error("Can't run migration %s because first you need to run these migrations: %s" % (migration.id, ', '.join(pending_dependencies))) + continue + # If some migrations have disclaimers (and we're not trying to skip them) if migration.disclaimer and not skip: # require the --accept-disclaimer option. @@ -1308,6 +1318,7 @@ class Migration(object): # Those are to be implemented by daughter classes mode = "auto" + dependencies = [] # List of migration ids required before running this migration def forward(self): raise NotImplementedError()