Upgrade to Django 5.2 LTS (#16185)

Upgrade to Django 5.2 LTS with compatibility fixes across fields, migrations, dispatch config, tests, and dev deps.

Dependencies:
- Upgrade django to 5.2.8 and relax requirements.in to >=5.2,<5.3.
- Bump django-debug-toolbar to >=6.0 for compatibility.

Backend:
- awx/conf/fields.py: switch URL TLD regex to use DomainNameValidator.ul in custom URLField.
- awx/main/management/commands/gather_analytics.py: use datetime.timezone.utc for naïve datetime handling.
- awx/main/dispatch/config.py: add mock_publish option; avoid DB access for test runs, set default max_workers, and support a noop broker.

Migrations (SQLite/Postgres compatibility):
- Add awx/main/migrations/_sqlite_helper.py with db-aware AlterIndexTogether/RenameIndex wrappers; consume in 0144_event_partitions.py and 0184_django_indexes.py.
- Update 0187_hop_nodes.py to use CheckConstraint(condition=...).
- Add 0205_alter_instance_peers_alter_job_hosts_and_more.py adjusting through_fields/relations on instance.peers, job.hosts, and role.ancestors.
- _dab_rbac.py: iterate roles with chunk_size=1000 for migration performance.

Tests:
Include hcp_terraform in default credential types in test_credential.py.
---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Alan Rominger <arominge@redhat.com>
This commit is contained in:
Hao Liu
2025-12-03 14:22:52 -05:00
committed by GitHub
parent 711b018ae7
commit b24156805a
12 changed files with 152 additions and 35 deletions

View File

@@ -237,7 +237,7 @@ class Migration(migrations.Migration):
db_index=False, editable=False, on_delete=models.deletion.DO_NOTHING, related_name='system_job_events', to='main.SystemJob'
),
),
migrations.AlterIndexTogether(
dbawaremigrations.AlterIndexTogether(
name='adhoccommandevent',
index_together={
('ad_hoc_command', 'job_created', 'event'),
@@ -245,11 +245,11 @@ class Migration(migrations.Migration):
('ad_hoc_command', 'job_created', 'uuid'),
},
),
migrations.AlterIndexTogether(
dbawaremigrations.AlterIndexTogether(
name='inventoryupdateevent',
index_together={('inventory_update', 'job_created', 'counter'), ('inventory_update', 'job_created', 'uuid')},
),
migrations.AlterIndexTogether(
dbawaremigrations.AlterIndexTogether(
name='jobevent',
index_together={
('job', 'job_created', 'counter'),
@@ -258,7 +258,7 @@ class Migration(migrations.Migration):
('job', 'job_created', 'parent_uuid'),
},
),
migrations.AlterIndexTogether(
dbawaremigrations.AlterIndexTogether(
name='projectupdateevent',
index_together={
('project_update', 'job_created', 'uuid'),
@@ -266,7 +266,7 @@ class Migration(migrations.Migration):
('project_update', 'job_created', 'counter'),
},
),
migrations.AlterIndexTogether(
dbawaremigrations.AlterIndexTogether(
name='systemjobevent',
index_together={('system_job', 'job_created', 'uuid'), ('system_job', 'job_created', 'counter')},
),

View File

@@ -6,6 +6,8 @@ from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
from ._sqlite_helper import dbawaremigrations
class Migration(migrations.Migration):
dependencies = [
@@ -15,92 +17,92 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='adhoccommandevent',
new_name='main_adhocc_ad_hoc__1e4d24_idx',
old_fields=('ad_hoc_command', 'job_created', 'uuid'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='adhoccommandevent',
new_name='main_adhocc_ad_hoc__e72142_idx',
old_fields=('ad_hoc_command', 'job_created', 'event'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='adhoccommandevent',
new_name='main_adhocc_ad_hoc__a57777_idx',
old_fields=('ad_hoc_command', 'job_created', 'counter'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='inventoryupdateevent',
new_name='main_invent_invento_f72b21_idx',
old_fields=('inventory_update', 'job_created', 'uuid'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='inventoryupdateevent',
new_name='main_invent_invento_364dcb_idx',
old_fields=('inventory_update', 'job_created', 'counter'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='jobevent',
new_name='main_jobeve_job_id_40a56d_idx',
old_fields=('job', 'job_created', 'parent_uuid'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='jobevent',
new_name='main_jobeve_job_id_3c4a4a_idx',
old_fields=('job', 'job_created', 'uuid'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='jobevent',
new_name='main_jobeve_job_id_51c382_idx',
old_fields=('job', 'job_created', 'counter'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='jobevent',
new_name='main_jobeve_job_id_0ddc6b_idx',
old_fields=('job', 'job_created', 'event'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='projectupdateevent',
new_name='main_projec_project_449bbd_idx',
old_fields=('project_update', 'job_created', 'uuid'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='projectupdateevent',
new_name='main_projec_project_69559a_idx',
old_fields=('project_update', 'job_created', 'counter'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='projectupdateevent',
new_name='main_projec_project_c44b7c_idx',
old_fields=('project_update', 'job_created', 'event'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='role',
new_name='main_rbac_r_content_979bdd_idx',
old_fields=('content_type', 'object_id'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='roleancestorentry',
new_name='main_rbac_r_ancesto_b44606_idx',
old_fields=('ancestor', 'content_type_id', 'role_field'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='roleancestorentry',
new_name='main_rbac_r_ancesto_22b9f0_idx',
old_fields=('ancestor', 'content_type_id', 'object_id'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='roleancestorentry',
new_name='main_rbac_r_ancesto_c87b87_idx',
old_fields=('ancestor', 'descendent'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='systemjobevent',
new_name='main_system_system__e39825_idx',
old_fields=('system_job', 'job_created', 'uuid'),
),
migrations.RenameIndex(
dbawaremigrations.RenameIndex(
model_name='systemjobevent',
new_name='main_system_system__73537a_idx',
old_fields=('system_job', 'job_created', 'counter'),

View File

@@ -69,7 +69,7 @@ class Migration(migrations.Migration):
),
migrations.AddConstraint(
model_name='instancelink',
constraint=models.CheckConstraint(check=models.Q(('source', models.F('target')), _negated=True), name='source_and_target_can_not_be_equal'),
constraint=models.CheckConstraint(condition=models.Q(('source', models.F('target')), _negated=True), name='source_and_target_can_not_be_equal'),
),
migrations.RunPython(automatically_peer_from_control_plane),
]

View File

@@ -0,0 +1,32 @@
# Generated by Django 5.2.8 on 2025-11-20 18:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0204_squashed_deletions'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='peers',
field=models.ManyToManyField(
related_name='peers_from', through='main.InstanceLink', through_fields=('source', 'target'), to='main.receptoraddress'
),
),
migrations.AlterField(
model_name='job',
name='hosts',
field=models.ManyToManyField(editable=False, related_name='jobs', through='main.JobHostSummary', through_fields=('job', 'host'), to='main.host'),
),
migrations.AlterField(
model_name='role',
name='ancestors',
field=models.ManyToManyField(
related_name='descendents', through='main.RoleAncestorEntry', through_fields=('descendent', 'ancestor'), to='main.role'
),
),
]

View File

@@ -194,7 +194,7 @@ def migrate_to_new_rbac(apps, schema_editor):
# NOTE: this import is expected to break at some point, and then just move the data here
from awx.main.models.rbac import role_descriptions
for role in Role.objects.prefetch_related('members', 'parents').iterator():
for role in Role.objects.prefetch_related('members', 'parents').iterator(chunk_size=1000):
if role.singleton_name:
continue # only bothering to migrate object roles

View File

@@ -1,6 +1,78 @@
from django.db import migrations
class AlterIndexTogether(migrations.AlterIndexTogether):
"""
Database-aware AlterIndexTogether that handles SQLite's missing indexes gracefully.
In Django 5.2+, SQLite table rewrites (triggered by AlterField operations)
can drop multi-column indexes. For SQLite, this catches the ValueError and
ignores it when the index doesn't exist. For PostgreSQL, uses standard behavior.
"""
def database_forwards(self, app_label, schema_editor, from_state, to_state):
if not schema_editor.connection.vendor.startswith('postgres'):
# SQLite-specific handling: ignore missing indexes from table rewrites
try:
super().database_forwards(app_label, schema_editor, from_state, to_state)
except ValueError as exc:
if "Found wrong number (0) of constraints" in str(exc) or "Found wrong number (0) of indexes" in str(exc):
return
raise
else:
# PostgreSQL: standard behavior
super().database_forwards(app_label, schema_editor, from_state, to_state)
def database_backwards(self, app_label, schema_editor, from_state, to_state):
if not schema_editor.connection.vendor.startswith('postgres'):
# SQLite-specific handling: ignore missing indexes from table rewrites
try:
super().database_backwards(app_label, schema_editor, from_state, to_state)
except ValueError as exc:
if "Found wrong number (0) of constraints" in str(exc) or "Found wrong number (0) of indexes" in str(exc):
return
raise
else:
# PostgreSQL: standard behavior
super().database_backwards(app_label, schema_editor, from_state, to_state)
class RenameIndex(migrations.RenameIndex):
"""
Database-aware RenameIndex that handles SQLite's missing indexes gracefully.
In Django 5.2+, SQLite table rewrites (triggered by AlterField operations)
can drop multi-column indexes. For SQLite, this catches the ValueError and
ignores it when the index doesn't exist. For PostgreSQL, uses standard behavior.
"""
def database_forwards(self, app_label, schema_editor, from_state, to_state):
if not schema_editor.connection.vendor.startswith('postgres'):
# SQLite-specific handling: ignore missing indexes from table rewrites
try:
super().database_forwards(app_label, schema_editor, from_state, to_state)
except ValueError as exc:
if "Found wrong number (0) of constraints" in str(exc) or "wrong number (0) of indexes" in str(exc):
return
raise
else:
# PostgreSQL: standard behavior
super().database_forwards(app_label, schema_editor, from_state, to_state)
def database_backwards(self, app_label, schema_editor, from_state, to_state):
if not schema_editor.connection.vendor.startswith('postgres'):
# SQLite-specific handling: ignore missing indexes from table rewrites
try:
super().database_backwards(app_label, schema_editor, from_state, to_state)
except ValueError as exc:
if "Found wrong number (0) of constraints" in str(exc) or "wrong number (0) of indexes" in str(exc):
return
raise
else:
# PostgreSQL: standard behavior
super().database_backwards(app_label, schema_editor, from_state, to_state)
class RunSQL(migrations.operations.special.RunSQL):
"""
Bit of a hack here. Django actually wants this decision made in the router
@@ -56,6 +128,8 @@ class RunPython(migrations.operations.special.RunPython):
class _sqlitemigrations:
RunPython = RunPython
RunSQL = RunSQL
AlterIndexTogether = AlterIndexTogether
RenameIndex = RenameIndex
dbawaremigrations = _sqlitemigrations()