From c552b4f0063a6a57c82b5e5bc721493c9f6d6e8e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 2 Apr 2021 04:28:52 +0200 Subject: [PATCH] Be able to define directly in migrations hooks that are called when restoring system/apps prior to the introduction of the migration --- src/yunohost/backup.py | 26 +++---------- .../0019_extend_permissions_features.py | 26 +++++++++++++ src/yunohost/tools.py | 39 +++++++++++++++++++ 3 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index b20c68412..d270de189 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -61,7 +61,7 @@ from yunohost.hook import ( hook_exec, CUSTOM_HOOK_FOLDER, ) -from yunohost.tools import tools_postinstall +from yunohost.tools import tools_postinstall, _tools_migrations_run_after_system_restore, _tools_migrations_run_before_app_restore from yunohost.regenconf import regen_conf from yunohost.log import OperationLogger from yunohost.utils.error import YunohostError, YunohostValidationError @@ -1282,16 +1282,15 @@ class RestoreManager: regen_conf() - # TODO : here, we should have a way to go through all migrations - # and apply stuff if needed + _tools_migrations_run_after_system_restore(backup_version=self.info["from_yunohost_version"]) - # Remove all permission for all app which is still in the LDAP + # Remove all permission for all app still in the LDAP for permission_name in user_permission_list(ignore_system_perms=True)[ "permissions" ].keys(): permission_delete(permission_name, force=True, sync_perm=False) - # Restore permission for the app which is installed + # Restore permission for apps installed for permission_name, permission_infos in old_apps_permission.items(): app_name, perm_name = permission_name.split(".") if _is_installed(app_name): @@ -1456,22 +1455,7 @@ class RestoreManager: os.remove("%s/permissions.yml" % app_settings_new_path) - # Migrate old settings - legacy_permission_settings = [ - "skipped_uris", - "unprotected_uris", - "protected_uris", - "skipped_regex", - "unprotected_regex", - "protected_regex", - ] - if any( - app_setting(app_instance_name, setting) is not None - for setting in legacy_permission_settings - ): - from yunohost.utils.legacy import migrate_legacy_permission_settings - - migrate_legacy_permission_settings(app=app_instance_name) + _tools_migrations_run_before_app_restore(backup_version=self.info["from_yunohost_version"], app_id=app_instance_name) # Prepare env. var. to pass to script env_dict = _make_environment_for_app_script(app_instance_name) diff --git a/src/yunohost/data_migrations/0019_extend_permissions_features.py b/src/yunohost/data_migrations/0019_extend_permissions_features.py index 07f740a2b..fe1fb20b7 100644 --- a/src/yunohost/data_migrations/0019_extend_permissions_features.py +++ b/src/yunohost/data_migrations/0019_extend_permissions_features.py @@ -78,6 +78,32 @@ class MyMigration(Migration): ldap.update("cn=%s,ou=permission" % permission, update) + introduced_in_version = "4.1" + + def run_after_system_restore(self): + # Update LDAP database + self.add_new_ldap_attributes() + + def run_before_system_restore(self, app_id): + from yunohost.app import app_setting + from yunohost.utils.legacy import migrate_legacy_permission_settings + + # Migrate old settings + legacy_permission_settings = [ + "skipped_uris", + "unprotected_uris", + "protected_uris", + "skipped_regex", + "unprotected_regex", + "protected_regex", + ] + if any( + app_setting(app_id, setting) is not None + for setting in legacy_permission_settings + ): + migrate_legacy_permission_settings(app=app_id) + + def run(self): # FIXME : what do we really want to do here ... diff --git a/src/yunohost/tools.py b/src/yunohost/tools.py index e5699dede..bf33984c3 100644 --- a/src/yunohost/tools.py +++ b/src/yunohost/tools.py @@ -29,6 +29,7 @@ import yaml import subprocess import pwd from importlib import import_module +from packaging import version from moulinette import msignals, m18n from moulinette.utils.log import getActionLogger @@ -1101,6 +1102,44 @@ def _skip_all_migrations(): write_to_yaml(MIGRATIONS_STATE_PATH, new_states) +def _tools_migrations_run_after_system_restore(backup_version): + + all_migrations = _get_migrations_list() + + for migration in all_migrations: + if hasattr(migration, "introduced_in_version") \ + and version.parse(migration.introduced_in_version) > version.parse(backup_version) \ + and hasattr(migration, "run_after_system_restore"): + try: + logger.info(m18n.n("migrations_running_forward", id=migration.id)) + migration.run_after_system_restore() + except Exception as e: + msg = m18n.n( + "migrations_migration_has_failed", exception=e, id=migration.id + ) + logger.error(msg, exc_info=1) + raise + + +def _tools_migrations_run_after_system_restore(backup_version, app_id): + + all_migrations = _get_migrations_list() + + for migration in all_migrations: + if hasattr(migration, "introduced_in_version") \ + and version.parse(migration.introduced_in_version) > version.parse(backup_version) \ + and hasattr(migration, "run_before_app_restore"): + try: + logger.info(m18n.n("migrations_running_forward", id=migration.id)) + migration.run_before_app_restore(app_id) + except Exception as e: + msg = m18n.n( + "migrations_migration_has_failed", exception=e, id=migration.id + ) + logger.error(msg, exc_info=1) + raise + + class Migration(object): # Those are to be implemented by daughter classes