From 5caa65b5dd451e6099123f9dd7b3a4e6822b8904 Mon Sep 17 00:00:00 2001 From: Matthias Dellweg Date: Thu, 3 Sep 2026 18:30:15 +0200 Subject: [PATCH] Fix post-migrate hooks Post migrate hooks are called after every migration. So they must be resilient against missing or differently looking models. Either they take the models from the passed apps and suppress LookupError or they should suppress both LookupError and ProgrammingError. (cherry picked from commit bed13b0be7be8421e345338795f5506f8693a736) --- CHANGES/+fix_post_migrate_hooks.bugfix | 1 + pulpcore/app/apps.py | 49 ++++++++++++++------------ 2 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 CHANGES/+fix_post_migrate_hooks.bugfix diff --git a/CHANGES/+fix_post_migrate_hooks.bugfix b/CHANGES/+fix_post_migrate_hooks.bugfix new file mode 100644 index 00000000000..97c19b32e3d --- /dev/null +++ b/CHANGES/+fix_post_migrate_hooks.bugfix @@ -0,0 +1 @@ +Fixed post-migrate hooks to prevent failing on incomplete or rolled back migrations. diff --git a/pulpcore/app/apps.py b/pulpcore/app/apps.py index 67a6048b4c9..eec2a944061 100644 --- a/pulpcore/app/apps.py +++ b/pulpcore/app/apps.py @@ -1,12 +1,13 @@ import random from collections import defaultdict +from contextlib import suppress from gettext import gettext as _ from importlib import import_module from django import apps from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from django.db import connection, transaction +from django.db import ProgrammingError, connection, transaction from django.db.models.signals import post_migrate, pre_migrate from django.utils.module_loading import module_has_submodule @@ -320,27 +321,31 @@ def _populate_access_policies(sender, apps, verbosity, **kwargs): def _populate_system_id(sender, apps, verbosity, **kwargs): - SystemID = apps.get_model("core", "SystemID") - if not SystemID.objects.exists(): - SystemID().save() - - -def _ensure_default_domain(sender, **kwargs): - table_names = connection.introspection.table_names() - if "core_domain" in table_names: - from pulpcore.app.util import get_default_domain - - default = get_default_domain() # Cache the default domain - # Match the Pulp settings - if ( - settings.HIDE_GUARDED_DISTRIBUTIONS != default.hide_guarded_distributions - or settings.REDIRECT_TO_OBJECT_STORAGE != default.redirect_to_object_storage - or settings.STORAGES["default"]["BACKEND"] != default.storage_class - ): - default.hide_guarded_distributions = settings.HIDE_GUARDED_DISTRIBUTIONS - default.redirect_to_object_storage = settings.REDIRECT_TO_OBJECT_STORAGE - default.storage_class = settings.STORAGES["default"]["BACKEND"] - default.save(skip_hooks=True) + with suppress(LookupError): + SystemID = apps.get_model("core", "SystemID") + if not SystemID.objects.exists(): + SystemID().save() + + +def _ensure_default_domain(sender, apps, **kwargs): + # This is a post migrate hook. + # But on rolling back the database may not match what this function expects. + with suppress(LookupError, ProgrammingError): + table_names = connection.introspection.table_names() + if "core_domain" in table_names: + from pulpcore.app.util import get_default_domain + + default = get_default_domain() # Cache the default domain + # Match the Pulp settings + if ( + settings.HIDE_GUARDED_DISTRIBUTIONS != default.hide_guarded_distributions + or settings.REDIRECT_TO_OBJECT_STORAGE != default.redirect_to_object_storage + or settings.STORAGES["default"]["BACKEND"] != default.storage_class + ): + default.hide_guarded_distributions = settings.HIDE_GUARDED_DISTRIBUTIONS + default.redirect_to_object_storage = settings.REDIRECT_TO_OBJECT_STORAGE + default.storage_class = settings.STORAGES["default"]["BACKEND"] + default.save(skip_hooks=True) def _populate_roles(sender, apps, verbosity, **kwargs):